Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 18 additions & 1 deletion .stylelintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,23 @@ module.exports = {
"selector-class-pattern": null,
"value-keyword-case": null,
"length-zero-no-unit": null,
"font-family-no-missing-generic-family-keyword": null
"font-family-no-missing-generic-family-keyword": null,
"property-no-unknown": [
true,
{
ignoreProperties: [
"container-type",
"container-name",
],
},
],
"scss/at-rule-no-unknown": [
true,
{
ignoreAtRules: [
"container",
],
},
],
}
}
2 changes: 1 addition & 1 deletion src/block-components/columns/style.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,5 +181,5 @@ export const addStyles = ( blockStyleGenerator, props = {} ) => {
},
} )
} )
} )
}, [ 'columnArrangement' ] )
}
16 changes: 11 additions & 5 deletions src/block-components/image/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@
}
}

// Hide the size tooltip when the image resizer is too narrow.
.stk-img-resizer {
container-type: inline-size;
}

@container (max-width: 139px) {
.stk-resizer-tooltip {
display: none !important;
}
}

.stk-img-resizer-tooltip {
background: rgba(0, 0, 0, 0.75);
border-radius: 2px;
Expand Down Expand Up @@ -136,11 +147,6 @@
}
}

// If the image its too small, don't show the tooltip.
.stk--too-small .stk-img-resizer-tooltip {
display: none !important;
}

.stk-image-size-popup {
.components-popover__content {
padding: 16px;
Expand Down
17 changes: 0 additions & 17 deletions src/block-components/image/image.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ const Image = memo( props => {

const [ currentHeight, setCurrentHeight ] = useState()
const [ currentWidth, setCurrentWidth ] = useState()
const [ imageWidthIsTooSmall, setImageWidthIsTooSmall ] = useState( false )
const imageRef = useRef()
const wrapperRef = useRef()

Expand Down Expand Up @@ -109,24 +108,8 @@ const Image = memo( props => {
'stk--never-resized': ( ! src || hasImageError ) && neverResized,
'stk--is-resizing': isResizing,
'stk--no-click-to-edit': ! props.enableClickToEdit,
// If the image is too small, hide the size tooltip.
'stk--too-small': imageWidthIsTooSmall,
} )

// Observe the size of the image, if it's too small, we shouldn't show the
// size tooltip.
useEffect( () => {
if ( imageRef.current ) {
const resizeObserver = new ResizeObserver( entries => { // eslint-disable-line compat/compat
for ( const entry of entries ) {
setImageWidthIsTooSmall( entry.contentRect.width < 140 )
}
} )
resizeObserver.observe( imageRef.current )
return () => resizeObserver.disconnect()
}
}, [ imageRef.current ] )

const imageClasses = getImageClasses( props )

return (
Expand Down
30 changes: 6 additions & 24 deletions src/block-components/typography/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ import { escapeHTMLIfInvalid } from './util'
/**
* WordPress dependencies
*/
import {
useEffect, useState, useCallback, memo,
} from '@wordpress/element'
import { useCallback, memo } from '@wordpress/element'
import { __ } from '@wordpress/i18n'
import { applyFilters } from '@wordpress/hooks'
import { useSelect } from '@wordpress/data'
Expand Down Expand Up @@ -80,29 +78,13 @@ export const Controls = props => {
} = useAttributeEditHandlers( attrNameTemplate )
const attributeName = getAttrNameFunction( attrNameTemplate )
const text = getAttribute( 'text' )
const [ debouncedText, setDebouncedText ] = useState( text )
const useTypographyAsPresets = useSelect( select =>
select( 'stackable/global-preset-controls.custom' )?.getUseTypographyAsPresets() ?? false
)

useEffect( () => {
if ( text !== debouncedText ) {
setDebouncedText( text )
}
}, [ text ] )

useEffect( () => {
let timeout
if ( debouncedText !== text ) {
timeout = setTimeout( () => {
updateAttribute( 'text', debouncedText )
}, 300 )
}

return () => clearTimeout( timeout )
}, [ updateAttribute, debouncedText, text ] )

const onChangeContent = useCallback( text => setDebouncedText( escapeHTMLIfInvalid( text ) ), [] )
const onChangeContent = useCallback( newText => {
updateAttribute( 'text', escapeHTMLIfInvalid( newText ) )
}, [ updateAttribute ] )

const presetMarks = usePresetControls( 'fontSizes' )
?.getPresetMarks( { customOnly: useTypographyAsPresets } ) || null
Expand All @@ -115,14 +97,14 @@ export const Controls = props => {
label={ __( 'Content', i18n ) }
hasPanelModifiedIndicator={ false }
isMultiline={ isMultiline }
value={ unescape( debouncedText ) }
value={ unescape( text ) }
onChange={ onChangeContent }
/**
* Pass the unescaped Dynamic Content `onChange` function.
*
* @param {string} text Text with dynamic content.
*/
changeDynamicContent={ setDebouncedText }
changeDynamicContent={ onChangeContent }
isDynamic={ true }
/>
) }
Expand Down
13 changes: 6 additions & 7 deletions src/block-components/typography/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export const Typography = memo( forwardRef( ( props, ref ) => {
defaultValue,
withoutInteractiveFormatting = false,
allowedFormats = null,
enableDebounce = true, // If false, onChange will be called immediately.
enableDebounce = false, // Set true to batch setAttributes (e.g. very large pages).
...propsToPass
} = props

Expand Down Expand Up @@ -105,13 +105,12 @@ export const Typography = memo( forwardRef( ( props, ref ) => {
className={ className }
tagName={ TagName }
value={ dynamicContentText }
onChange={ value => {
if ( enableDebounce ) {
setDebouncedText( value )
} else {
onChange( value )
onChange={ newValue => {
setDebouncedText( newValue )
if ( ! enableDebounce ) {
onChange( newValue )
}
} }
} }
ref={ ref }
withoutInteractiveFormatting={ withoutInteractiveFormatting }
allowedFormats={ allowedFormats }
Expand Down
1 change: 0 additions & 1 deletion src/block/icon-list-item/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,6 @@ const Edit = props => {
)
}
: undefined }
enableDebounce={ false }
/>
</div>
</BlockDiv>
Expand Down
61 changes: 60 additions & 1 deletion src/components/block-css/block-style-generator-class.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,19 @@ import { getAttrName, getUniqueBlockClass } from '~stackable/util'
import { getDynamicContentEdit } from '../dynamic-content-control'
import { applyFilters } from '@wordpress/hooks'
import { BlockCssFunc } from '.'
import {
formAllPossibleAttributeNames,
getCallbackAttributeDependencies,
getDependencyAttrnamesFast,
} from './util'
import { pickBy } from 'lodash'

export class BlockStyleGenerator {
constructor( commonProps ) {
this.commonProps = commonProps
this._blockStyles = {} // This holds all the blockStyles indices, keys are the attrName
this._dynamicBlockStyles = [] // Holds functions that will be called when generating blocks styles.
this._dynamicStyleDependencyRootNames = [] // Root attr names for addBlockStyleConditionally styles.
this._blockStyleNamesWithValuePreCallbacks = [] // This holds all block style keys that have valuePreCallbacks, becuase these will need to be run even if the attribute is blank.
this._orderedStyles = [] // This holds all the blockStyles added in order
}
Expand Down Expand Up @@ -89,11 +95,14 @@ export class BlockStyleGenerator {
* this is a less performant way to add block styles.
*
* @param {Function} fn function that's called when generating block styles
* @param {Array<string>} dependencyAttrNames Root attribute names that affect
* the conditional styles (e.g. columnArrangement for column order CSS).
*/
addBlockStyleConditionally( fn ) {
addBlockStyleConditionally( fn, dependencyAttrNames = [] ) {
this._orderedStyles.push( fn )
const blockStyleIndex = this._orderedStyles.length - 1
this._dynamicBlockStyles.push( blockStyleIndex )
this._dynamicStyleDependencyRootNames.push( ...dependencyAttrNames )
}

/**
Expand Down Expand Up @@ -172,6 +181,56 @@ export class BlockStyleGenerator {
return Object.keys( pickBy( attributes, test ) )
}

/**
* Returns all attribute names that can affect editor/save CSS for this block.
* Cached per BlockStyleGenerator instance (styles are registered at load time).
*
* @return {Array<string>} Sorted, deduplicated attribute names
*/
getStyleDependencyAttributeNames() {
if ( this._styleDependencyAttributeNames ) {
return this._styleDependencyAttributeNames
}

const attrNameSet = new Set()

const addNames = names => {
names.forEach( name => attrNameSet.add( name ) )
}

this._orderedStyles.forEach( blockStyle => {
if ( typeof blockStyle === 'function' ) {
return
}

addNames( getDependencyAttrnamesFast( {
...this.commonProps,
...blockStyle,
} ) )

if ( typeof blockStyle.renderCondition === 'string' && blockStyle.renderCondition ) {
addNames( formAllPossibleAttributeNames( [ blockStyle.renderCondition ] ) )
}

if ( typeof blockStyle.enabledCallback === 'function' ) {
addNames( formAllPossibleAttributeNames(
getCallbackAttributeDependencies( blockStyle.enabledCallback )
) )
}
} )

this._dynamicStyleDependencyRootNames.forEach( attrName => {
addNames( formAllPossibleAttributeNames( [ attrName ] ) )
} )

this._blockStyleNamesWithValuePreCallbacks.forEach( attrName => {
addNames( formAllPossibleAttributeNames( [ attrName ] ) )
} )

this._styleDependencyAttributeNames = Array.from( attrNameSet ).sort()
return this._styleDependencyAttributeNames
}

/**
* Checks the `renderCondition` property in blockStyle if it matches the
* condition to render this, if present.
Expand Down
58 changes: 36 additions & 22 deletions src/components/block-css/use-block-style-generator.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { useQueryLoopInstanceId } from '~stackable/util'
import { useMemo, useRef } from '@wordpress/element'
import {
useLayoutEffect, useMemo, useRef,
} from '@wordpress/element'
import { dispatch, select } from '@wordpress/data'
import { useRafEffect } from '~stackable/hooks'
import { dispatch } from '@wordpress/data'
import CssSaveCompiler from './css-save-compiler'
import { createStyleDependencyFingerprint } from './util'

export const useBlockCssGenerator = props => {
const {
Expand All @@ -21,41 +24,36 @@ export const useBlockCssGenerator = props => {
// Generate the CSS styles.
const instanceId = useQueryLoopInstanceId( attributes.uniqueId )

// Keep the old text attribute for comparison to prevent block style generation when only the text attribute has changed.
const oldText = useRef( attributes.text )
const styleDependencyAttrNames = useMemo(
() => blockStyles.getStyleDependencyAttributeNames(),
[ blockStyles ]
)

// Keep the generated CSS for editor and return it when only the text attribute has changed.
const oldCss = useRef( null )
// Cheap fingerprint of style-related attributes only. Recomputed when
// attributes change, but editCss only regenerates when the fingerprint changes.
const styleFingerprint = useMemo(
() => createStyleDependencyFingerprint( attributes, styleDependencyAttrNames ),
[ attributes, styleDependencyAttrNames ]
)

const editCss = useMemo( () => {
if ( oldText.current !== attributes.text ) {
oldText.current = attributes.text
return oldCss.current
}
// Gather only the attributes that have values and all their
// corresponding block style definitions.
const attrNamesWithValues = blockStyles.getAttributesWithValues( attributes )
blockStyleDefsRef.current = blockStyles.getBlockStyles( attrNamesWithValues )

// These are the styles to be displayed in the editor.
const css = blockStyles.generateBlockStylesForEditor( attributes, blockStyleDefsRef.current, {
return blockStyles.generateBlockStylesForEditor( attributes, blockStyleDefsRef.current, {
version,
blockState,
uniqueId: attributes.uniqueId,
instanceId, // This is used by the native Query Loop block.
clientId,
context, // This is used for dynamic content.
} )
oldCss.current = css
return css
}, [ attributes, version, blockState, clientId, attributes.uniqueId, instanceId, context ] )
}, [ styleFingerprint, version, blockState, clientId, attributes.uniqueId, instanceId, context, blockStyles ] )

useRafEffect( () => {
if ( oldText.current !== attributes.text ) {
oldText.current = attributes.text
return
}

const cssCompiler = new CssSaveCompiler()

// Generate the styles that are to be saved with the actual block.
Expand All @@ -73,12 +71,28 @@ export const useBlockCssGenerator = props => {
return
}

// Use setAttributes to realiably update the generated CSS.
// Use setAttributes to reliably update the generated CSS.
// Mutating the attributes directly will not trigger a re-render,
// but might not properly save the changes.
dispatch( 'core/block-editor' ).__unstableMarkNextChangeAsNotPersistent()
setAttributes( { generatedCss: saveCss } )
}, [ attributes, version, setAttributes ] )
}, [ styleFingerprint, version, blockStyles, setAttributes ] )

const styleKey = `${ clientId }-${ instanceId }`

useLayoutEffect( () => {
dispatch( 'stackable/editor-block-css' ).setBlockCss( styleKey, editCss || '' )
return () => {
// Keep CSS in the store across preview remounts. Only remove when the
// block was actually deleted from the editor.
const block = select( 'core/block-editor' )?.getBlock( clientId )
if ( ! block ) {
dispatch( 'stackable/editor-block-css' ).removeBlockCss( styleKey )
}
}
}, [ styleKey, editCss, clientId ] )

return editCss
// We used to return the CSS here, but for optimization, now
// CSS is injected via the unified editor stylesheet plugin.
return null
}
Loading
Loading