From 08ecb516eb9f1efce8da79abd70edd96343b6770 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 30 Sep 2021 15:44:28 +0100 Subject: [PATCH 01/15] Refactor how the Global Styles access and sets data --- .../components/global-styles/border-panel.js | 90 ++++---- .../global-styles/color-palette-panel.js | 62 ++---- .../components/global-styles/color-panel.js | 72 +++--- .../components/global-styles/context-menu.js | 10 +- .../global-styles/dimensions-panel.js | 46 ++-- .../src/components/global-styles/hooks.js | 210 ++++++++++++++++++ .../src/components/global-styles/palette.js | 13 +- .../src/components/global-styles/preview.js | 17 +- .../global-styles/screen-block-list.js | 20 +- .../components/global-styles/screen-block.js | 7 +- .../global-styles/screen-color-palette.js | 8 +- .../components/global-styles/screen-colors.js | 9 +- .../components/global-styles/screen-layout.js | 23 +- .../components/global-styles/screen-root.js | 5 +- .../global-styles/screen-typography.js | 9 +- .../global-styles/typography-panel.js | 106 +++++---- .../sidebar/global-styles-sidebar.js | 2 +- 17 files changed, 430 insertions(+), 279 deletions(-) create mode 100644 packages/edit-site/src/components/global-styles/hooks.js diff --git a/packages/edit-site/src/components/global-styles/border-panel.js b/packages/edit-site/src/components/global-styles/border-panel.js index 19cdcff68c6ee..2dd6675debe23 100644 --- a/packages/edit-site/src/components/global-styles/border-panel.js +++ b/packages/edit-site/src/components/global-styles/border-panel.js @@ -16,7 +16,7 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useSetting } from '../editor/utils'; +import { getSupportedGlobalStylesPanels, useSetting, useStyle } from './hooks'; const MIN_BORDER_WIDTH = 0; @@ -24,72 +24,82 @@ const MIN_BORDER_WIDTH = 0; // color control. const EMPTY_ARRAY = []; -export function useHasBorderPanel( { supports, name } ) { +export function useHasBorderPanel( name ) { const controls = [ - useHasBorderColorControl( { supports, name } ), - useHasBorderRadiusControl( { supports, name } ), - useHasBorderStyleControl( { supports, name } ), - useHasBorderWidthControl( { supports, name } ), + useHasBorderColorControl( name ), + useHasBorderRadiusControl( name ), + useHasBorderStyleControl( name ), + useHasBorderWidthControl( name ), ]; return controls.some( Boolean ); } -function useHasBorderColorControl( { supports, name } ) { +function useHasBorderColorControl( name ) { + const supports = getSupportedGlobalStylesPanels( name ); return ( - useSetting( 'border.customColor', name ) && + useSetting( 'border.customColor', name )[ 0 ] && supports.includes( 'borderColor' ) ); } -function useHasBorderRadiusControl( { supports, name } ) { +function useHasBorderRadiusControl( name ) { + const supports = getSupportedGlobalStylesPanels( name ); return ( - useSetting( 'border.customRadius', name ) && + useSetting( 'border.customRadius', name )[ 0 ] && supports.includes( 'borderRadius' ) ); } -function useHasBorderStyleControl( { supports, name } ) { +function useHasBorderStyleControl( name ) { + const supports = getSupportedGlobalStylesPanels( name ); return ( - useSetting( 'border.customStyle', name ) && + useSetting( 'border.customStyle', name )[ 0 ] && supports.includes( 'borderStyle' ) ); } -function useHasBorderWidthControl( { supports, name } ) { +function useHasBorderWidthControl( name ) { + const supports = getSupportedGlobalStylesPanels( name ); return ( - useSetting( 'border.customWidth', name ) && + useSetting( 'border.customWidth', name )[ 0 ] && supports.includes( 'borderWidth' ) ); } -export default function BorderPanel( { - context: { supports, name }, - getStyle, - setStyle, -} ) { +export default function BorderPanel( { name } ) { const units = useCustomUnits( { - availableUnits: useSetting( 'spacing.units' ) || [ 'px', 'em', 'rem' ], + availableUnits: useSetting( 'spacing.units' )[ 0 ] || [ + 'px', + 'em', + 'rem', + ], } ); // Border width. - const hasBorderWidth = useHasBorderWidthControl( { supports, name } ); - const borderWidthValue = getStyle( name, 'borderWidth' ); + const hasBorderWidth = useHasBorderWidthControl( name ); + const [ borderWidthValue, setBorderWidth ] = useStyle( + 'border.width', + name + ); // Border style. - const hasBorderStyle = useHasBorderStyleControl( { supports, name } ); - const borderStyle = getStyle( name, 'borderStyle' ); + const hasBorderStyle = useHasBorderStyleControl( name ); + const [ borderStyle, setBorderStyle ] = useStyle( 'border.style', name ); // Border color. - const colors = useSetting( 'color.palette' ) || EMPTY_ARRAY; - const disableCustomColors = ! useSetting( 'color.custom' ); - const disableCustomGradients = ! useSetting( 'color.customGradient' ); - const hasBorderColor = useHasBorderColorControl( { supports, name } ); - const borderColor = getStyle( name, 'borderColor' ); + const [ colors = EMPTY_ARRAY ] = useSetting( 'color.palette' ); + const disableCustomColors = ! useSetting( 'color.custom' )[ 0 ]; + const disableCustomGradients = ! useSetting( 'color.customGradient' )[ 0 ]; + const hasBorderColor = useHasBorderColorControl( name ); + const [ borderColor, setBorderColor ] = useStyle( 'border.color', name ); // Border radius. - const hasBorderRadius = useHasBorderRadiusControl( { supports, name } ); - const borderRadiusValues = getStyle( name, 'borderRadius' ); + const hasBorderRadius = useHasBorderRadiusControl( name ); + const [ borderRadiusValues, setBorderRadius ] = useStyle( + 'border.radius', + name + ); return ( @@ -101,11 +111,7 @@ export default function BorderPanel( { label={ __( 'Width' ) } min={ MIN_BORDER_WIDTH } onChange={ ( value ) => { - setStyle( - name, - 'borderWidth', - value || undefined - ); + setBorderWidth( value || undefined ); } } units={ units } /> @@ -113,9 +119,7 @@ export default function BorderPanel( { { hasBorderStyle && ( - setStyle( name, 'borderStyle', value ) - } + onChange={ setBorderStyle } /> ) } @@ -128,17 +132,13 @@ export default function BorderPanel( { gradients={ undefined } disableCustomColors={ disableCustomColors } disableCustomGradients={ disableCustomGradients } - onColorChange={ ( value ) => - setStyle( name, 'borderColor', value ) - } + onColorChange={ setBorderColor } /> ) } { hasBorderRadius && ( - setStyle( name, 'borderRadius', value ) - } + onChange={ setBorderRadius } /> ) } diff --git a/packages/edit-site/src/components/global-styles/color-palette-panel.js b/packages/edit-site/src/components/global-styles/color-palette-panel.js index ead8f64cc96d4..d66ad3511f1af 100644 --- a/packages/edit-site/src/components/global-styles/color-palette-panel.js +++ b/packages/edit-site/src/components/global-styles/color-palette-panel.js @@ -1,20 +1,14 @@ -/** - * External dependencies - */ -import { get } from 'lodash'; - /** * WordPress dependencies */ import { __experimentalColorEdit as ColorEdit } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; -import { useSelect } from '@wordpress/data'; +import { useMemo } from '@wordpress/element'; /** * Internal dependencies */ -import { useSetting } from '../editor/utils'; -import { store as editSiteStore } from '../../store'; +import { useSetting } from './hooks'; /** * Shared reference to an empty array for cases where it is important to avoid @@ -27,49 +21,25 @@ import { store as editSiteStore } from '../../store'; */ const EMPTY_ARRAY = []; -export default function ColorPalettePanel( { - contextName, - getSetting, - setSetting, -} ) { - const colors = useSetting( 'color.palette', contextName ); - const userColors = getSetting( contextName, 'color.palette' ); - const immutableColorSlugs = useSelect( - ( select ) => { - const baseStyles = select( editSiteStore ).getSettings() - .__experimentalGlobalStylesBaseStyles; - const contextualBasePalette = get( baseStyles, [ - 'settings', - 'blocks', - contextName, - 'color', - 'palette', - ] ); - const globalPalette = get( baseStyles, [ - 'settings', - 'color', - 'palette', - ] ); - const basePalette = - contextualBasePalette?.theme ?? - contextualBasePalette?.core ?? - globalPalette?.theme ?? - globalPalette?.core; - if ( ! basePalette ) { - return EMPTY_ARRAY; - } - return basePalette.map( ( { slug } ) => slug ); - }, - [ contextName ] - ); +export default function ColorPalettePanel( { name } ) { + const [ colors, setColors ] = useSetting( 'color.palette', name ); + const userColors = useSetting( 'color.palette', name, 'user' ); + const baseGlobalPalette = useSetting( 'color.palette', undefined, 'base' ); + const baseContextualPalette = useSetting( 'color.palette', name, 'base' ); + const immutableColorSlugs = useMemo( () => { + const basePalette = baseContextualPalette ?? baseGlobalPalette; + if ( ! basePalette ) { + return EMPTY_ARRAY; + } + return basePalette.map( ( { slug } ) => slug ); + }, [ baseContextualPalette, baseGlobalPalette ] ); + return (
{ - setSetting( contextName, 'color.palette', newColors ); - } } + onChange={ setColors } emptyUI={ __( 'Colors are empty! Add some colors to create your own color palette.' ) } diff --git a/packages/edit-site/src/components/global-styles/color-panel.js b/packages/edit-site/src/components/global-styles/color-panel.js index 97e6a1feaa698..b58f7b1d27f3f 100644 --- a/packages/edit-site/src/components/global-styles/color-panel.js +++ b/packages/edit-site/src/components/global-styles/color-panel.js @@ -8,10 +8,11 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ -import { useSetting } from '../editor/utils'; +import { getSupportedGlobalStylesPanels, useSetting, useStyle } from './hooks'; import Palette from './palette'; -export function useHasColorPanel( { supports } ) { +export function useHasColorPanel( name ) { + const supports = getSupportedGlobalStylesPanels( name ); return ( supports.includes( 'color' ) || supports.includes( 'backgroundColor' ) || @@ -20,21 +21,18 @@ export function useHasColorPanel( { supports } ) { ); } -export default function ColorPanel( { - context: { supports, name }, - getStyle, - setStyle, -} ) { - const solids = useSetting( 'color.palette', name ); - const gradients = useSetting( 'color.gradients', name ); - const areCustomSolidsEnabled = useSetting( 'color.custom', name ); - const areCustomGradientsEnabled = useSetting( +export default function ColorPanel( { name } ) { + const supports = getSupportedGlobalStylesPanels( name ); + const [ solids ] = useSetting( 'color.palette', name ); + const [ gradients ] = useSetting( 'color.gradients', name ); + const [ areCustomSolidsEnabled ] = useSetting( 'color.custom', name ); + const [ areCustomGradientsEnabled ] = useSetting( 'color.customGradient', name ); - const isLinkEnabled = useSetting( 'color.link', name ); - const isTextEnabled = useSetting( 'color.text', name ); - const isBackgroundEnabled = useSetting( 'color.background', name ); + const [ isLinkEnabled ] = useSetting( 'color.link', name ); + const [ isTextEnabled ] = useSetting( 'color.text', name ); + const [ isBackgroundEnabled ] = useSetting( 'color.background', name ); const hasLinkColor = supports.includes( 'linkColor' ) && @@ -52,14 +50,34 @@ export default function ColorPanel( { supports.includes( 'background' ) && ( gradients.length > 0 || areCustomGradientsEnabled ); - const settings = []; + const [ color, setColor ] = useStyle( 'color.text', name ); + const [ userColor ] = useStyle( 'color.text', name, 'user' ); + const [ backgroundColor, setBackgroundColor ] = useStyle( + 'color.background', + name + ); + const [ userBackgroundColor ] = useStyle( + 'color.background', + name, + 'user' + ); + const [ gradient, setGradient ] = useStyle( 'color.gradient', name ); + const [ userGradient ] = useStyle( 'color.gradient', name, 'user' ); + const [ linkColor, setLinkColor ] = useStyle( + 'elements.link.color.text', + name + ); + const [ userLinkColor ] = useStyle( + 'elements.link.color.text', + name, + 'user' + ); + const settings = []; if ( hasTextColor ) { - const color = getStyle( name, 'color' ); - const userColor = getStyle( name, 'color', 'user' ); settings.push( { colorValue: color, - onColorChange: ( value ) => setStyle( name, 'color', value ), + onColorChange: setColor, label: __( 'Text color' ), clearable: color === userColor, } ); @@ -67,12 +85,9 @@ export default function ColorPanel( { let backgroundSettings = {}; if ( hasBackgroundColor ) { - const backgroundColor = getStyle( name, 'backgroundColor' ); - const userBackgroundColor = getStyle( name, 'backgroundColor', 'user' ); backgroundSettings = { colorValue: backgroundColor, - onColorChange: ( value ) => - setStyle( name, 'backgroundColor', value ), + onColorChange: setBackgroundColor, }; if ( backgroundColor ) { backgroundSettings.clearable = @@ -82,12 +97,9 @@ export default function ColorPanel( { let gradientSettings = {}; if ( hasGradientColor ) { - const gradient = getStyle( name, 'background' ); - const userGradient = getStyle( name, 'background', 'user' ); gradientSettings = { gradientValue: gradient, - onGradientChange: ( value ) => - setStyle( name, 'background', value ), + onGradientChange: setGradient, }; if ( gradient ) { gradientSettings.clearable = gradient === userGradient; @@ -103,13 +115,11 @@ export default function ColorPanel( { } if ( hasLinkColor ) { - const color = getStyle( name, 'linkColor' ); - const userColor = getStyle( name, 'linkColor', 'user' ); settings.push( { - colorValue: color, - onColorChange: ( value ) => setStyle( name, 'linkColor', value ), + colorValue: linkColor, + onColorChange: setLinkColor, label: __( 'Link color' ), - clearable: color === userColor, + clearable: linkColor === userLinkColor, } ); } diff --git a/packages/edit-site/src/components/global-styles/context-menu.js b/packages/edit-site/src/components/global-styles/context-menu.js index c2a339a95046e..7686945e2621f 100644 --- a/packages/edit-site/src/components/global-styles/context-menu.js +++ b/packages/edit-site/src/components/global-styles/context-menu.js @@ -14,11 +14,11 @@ import { useHasDimensionsPanel } from './dimensions-panel'; import { useHasTypographyPanel } from './typography-panel'; import NavigationButton from './navigation-button'; -function ContextMenu( { context, parentMenu = '' } ) { - const hasTypographyPanel = useHasTypographyPanel( context ); - const hasColorPanel = useHasColorPanel( context ); - const hasBorderPanel = useHasBorderPanel( context ); - const hasDimensionsPanel = useHasDimensionsPanel( context ); +function ContextMenu( { name, parentMenu = '' } ) { + const hasTypographyPanel = useHasTypographyPanel( name ); + const hasColorPanel = useHasColorPanel( name ); + const hasBorderPanel = useHasBorderPanel( name ); + const hasDimensionsPanel = useHasDimensionsPanel( name ); const hasLayoutPanel = hasBorderPanel || hasDimensionsPanel; return ( diff --git a/packages/edit-site/src/components/global-styles/dimensions-panel.js b/packages/edit-site/src/components/global-styles/dimensions-panel.js index 9f30114697e46..bd2dfc8d5f0bf 100644 --- a/packages/edit-site/src/components/global-styles/dimensions-panel.js +++ b/packages/edit-site/src/components/global-styles/dimensions-panel.js @@ -14,31 +14,34 @@ import { __experimentalUseCustomSides as useCustomSides } from '@wordpress/block /** * Internal dependencies */ -import { useSetting } from '../editor/utils'; +import { getSupportedGlobalStylesPanels, useSetting, useStyle } from './hooks'; const AXIAL_SIDES = [ 'horizontal', 'vertical' ]; -export function useHasDimensionsPanel( context ) { - const hasPadding = useHasPadding( context ); - const hasMargin = useHasMargin( context ); - const hasGap = useHasGap( context ); +export function useHasDimensionsPanel( name ) { + const hasPadding = useHasPadding( name ); + const hasMargin = useHasMargin( name ); + const hasGap = useHasGap( name ); return hasPadding || hasMargin || hasGap; } -function useHasPadding( { name, supports } ) { +function useHasPadding( name ) { + const supports = getSupportedGlobalStylesPanels( name ); const settings = useSetting( 'spacing.customPadding', name ); return settings && supports.includes( 'padding' ); } -function useHasMargin( { name, supports } ) { +function useHasMargin( name ) { + const supports = getSupportedGlobalStylesPanels( name ); const settings = useSetting( 'spacing.customMargin', name ); return settings && supports.includes( 'margin' ); } -function useHasGap( { name, supports } ) { +function useHasGap( name ) { + const supports = getSupportedGlobalStylesPanels( name ); const settings = useSetting( 'spacing.blockGap', name ); return settings && supports.includes( '--wp--style--block-gap' ); @@ -82,13 +85,12 @@ function splitStyleValue( value ) { return value; } -export default function DimensionsPanel( { context, getStyle, setStyle } ) { - const { name } = context; - const showPaddingControl = useHasPadding( context ); - const showMarginControl = useHasMargin( context ); - const showGapControl = useHasGap( context ); +export default function DimensionsPanel( { name } ) { + const showPaddingControl = useHasPadding( name ); + const showMarginControl = useHasMargin( name ); + const showGapControl = useHasGap( name ); const units = useCustomUnits( { - availableUnits: useSetting( 'spacing.units', name ) || [ + availableUnits: useSetting( 'spacing.units', name )[ 0 ] || [ '%', 'px', 'em', @@ -97,7 +99,8 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) { ], } ); - const paddingValues = splitStyleValue( getStyle( name, 'padding' ) ); + const [ rawPadding, setRawPadding ] = useStyle( 'spacing.padding', name ); + const paddingValues = splitStyleValue( rawPadding ); const paddingSides = useCustomSides( name, 'padding' ); const isAxialPadding = paddingSides && @@ -105,13 +108,14 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) { const setPaddingValues = ( newPaddingValues ) => { const padding = filterValuesBySides( newPaddingValues, paddingSides ); - setStyle( name, 'padding', padding ); + setRawPadding( padding ); }; const resetPaddingValue = () => setPaddingValues( {} ); const hasPaddingValue = () => !! paddingValues && Object.keys( paddingValues ).length; - const marginValues = splitStyleValue( getStyle( name, 'margin' ) ); + const [ rawMargin, setRawMargin ] = useStyle( 'spacing.margin', name ); + const marginValues = splitStyleValue( rawMargin ); const marginSides = useCustomSides( name, 'margin' ); const isAxialMargin = marginSides && @@ -119,17 +123,13 @@ export default function DimensionsPanel( { context, getStyle, setStyle } ) { const setMarginValues = ( newMarginValues ) => { const margin = filterValuesBySides( newMarginValues, marginSides ); - setStyle( name, 'margin', margin ); + setRawMargin( margin ); }; const resetMarginValue = () => setMarginValues( {} ); const hasMarginValue = () => !! marginValues && Object.keys( marginValues ).length; - const gapValue = getStyle( name, '--wp--style--block-gap' ); - - const setGapValue = ( newGapValue ) => { - setStyle( name, '--wp--style--block-gap', newGapValue ); - }; + const [ gapValue, setGapValue ] = useStyle( 'spacing.blockGap', name ); const resetGapValue = () => setGapValue( undefined ); const hasGapValue = () => !! gapValue; diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js new file mode 100644 index 0000000000000..8584459d0c9b5 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -0,0 +1,210 @@ +/** + * External dependencies + */ +import { get, cloneDeep, set, isEqual, has } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useMemo, useCallback } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { useEntityProp } from '@wordpress/core-data'; +import { + getBlockType, + __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE, + __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, +} from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; + +const EMPTY_CONFIG = { isGlobalStylesUserThemeJSON: true, version: 1 }; + +function useGlobalStylesUserConfig() { + const globalStylesId = useSelect( ( select ) => { + return select( editSiteStore ).getSettings() + .__experimentalGlobalStylesUserEntityId; + }, [] ); + + const [ content, setContent ] = useEntityProp( + 'postType', + 'wp_global_styles', + 'content', + globalStylesId + ); + + const config = useMemo( () => { + let parsedConfig; + try { + parsedConfig = content ? JSON.parse( content ) : {}; + } catch ( e ) { + /* eslint-disable no-console */ + console.error( 'Global Styles User data is not valid' ); + console.error( e ); + /* eslint-enable no-console */ + parsedConfig = {}; + } + + return parsedConfig; + }, [ content ] ); + + const setConfig = useCallback( + ( newConfig ) => setContent( JSON.stringify( newConfig ) ), + [ setContent ] + ); + + return [ config, setConfig ]; +} + +function useGlobalStylesBaseConfig() { + const baseConfig = useSelect( ( select ) => { + return select( editSiteStore ).getSettings() + .__experimentalGlobalStylesBaseStyles; + }, [] ); + + return baseConfig; +} + +function useGlobalStylesConfig() { + const [ userConfig, setUserConfig ] = useGlobalStylesUserConfig(); + const baseConfig = useGlobalStylesBaseConfig(); + + return [ baseConfig, userConfig, setUserConfig ]; +} + +export const useGlobalStylesReset = () => { + const [ config, setConfig ] = useGlobalStylesUserConfig(); + const canReset = !! config && ! isEqual( config, EMPTY_CONFIG ); + return [ + canReset, + useCallback( () => setConfig( EMPTY_CONFIG ), [ setConfig ] ), + ]; +}; + +export function useSetting( path, blockName, source = 'all' ) { + const [ baseConfig, userConfig, setUserConfig ] = useGlobalStylesConfig(); + const finalPath = ! blockName + ? `settings.${ path }` + : `settings.blocks.${ blockName }.${ path }`; + + const getBaseSetting = () => { + const result = get( baseConfig, finalPath ); + if ( PATHS_WITH_MERGE[ path ] ) { + return result.theme ?? result.core; + } + }; + + const setSetting = ( newValue ) => { + const newUserConfig = cloneDeep( userConfig ); + set( newUserConfig, finalPath, newValue ); + setUserConfig( newUserConfig ); + }; + + let result; + switch ( source ) { + case 'all': + result = get( userConfig, finalPath ) ?? getBaseSetting(); + break; + case 'user': + result = get( userConfig, finalPath ); + break; + case 'base': + result = getBaseSetting(); + break; + default: + throw 'Unsupported source'; + } + + return [ result, setSetting ]; +} + +export function useStyle( path, blockName, source = 'all' ) { + const [ baseConfig, userConfig, setUserConfig ] = useGlobalStylesConfig(); + const finalPath = ! blockName + ? `styles.${ path }` + : `styles.blocks.${ blockName }.${ path }`; + + const setStyle = ( newValue ) => { + const newUserConfig = cloneDeep( userConfig ); + set( newUserConfig, finalPath, newValue ); + setUserConfig( newUserConfig ); + }; + + let result; + switch ( source ) { + case 'all': + result = + get( userConfig, finalPath ) ?? get( baseConfig, finalPath ); + break; + case 'user': + result = get( userConfig, finalPath ); + break; + case 'base': + result = get( baseConfig, finalPath ); + break; + default: + throw 'Unsupported source'; + } + + return [ result, setStyle ]; +} + +const ROOT_BLOCK_SUPPORTS = [ + 'background', + 'backgroundColor', + 'color', + 'linkColor', + 'fontFamily', + 'fontSize', + 'fontStyle', + 'fontWeight', + 'lineHeight', + 'textDecoration', + 'textTransform', + 'padding', +]; + +export function getSupportedGlobalStylesPanels( name ) { + if ( ! name ) { + return ROOT_BLOCK_SUPPORTS; + } + + const blockType = getBlockType( name ); + + if ( ! blockType ) { + return []; + } + + const supportKeys = []; + Object.keys( STYLE_PROPERTY ).forEach( ( styleName ) => { + if ( ! STYLE_PROPERTY[ styleName ].support ) { + return; + } + + // Opting out means that, for certain support keys like background color, + // blocks have to explicitly set the support value false. If the key is + // unset, we still enable it. + if ( STYLE_PROPERTY[ name ].requiresOptOut ) { + if ( + has( + blockType.supports, + STYLE_PROPERTY[ name ].support[ 0 ] + ) && + get( blockType.supports, STYLE_PROPERTY[ name ].support ) !== + false + ) { + return supportKeys.push( name ); + } + } + + if ( + get( blockType.supports, STYLE_PROPERTY[ name ].support, false ) + ) { + return supportKeys.push( name ); + } + } ); + + return supportKeys; +} diff --git a/packages/edit-site/src/components/global-styles/palette.js b/packages/edit-site/src/components/global-styles/palette.js index 0b30d6284c00b..021392fcc5ab6 100644 --- a/packages/edit-site/src/components/global-styles/palette.js +++ b/packages/edit-site/src/components/global-styles/palette.js @@ -16,15 +16,14 @@ import { __, _n, sprintf } from '@wordpress/i18n'; * Internal dependencies */ import Subtitle from './subtitle'; -import { useSetting } from '../editor/utils'; import NavigationButton from './navigation-button'; +import { useSetting } from './hooks'; -function Palette( { contextName } ) { - const colors = useSetting( 'color.palette', contextName ); - const screenPath = - contextName === 'root' - ? '/colors/palette' - : '/blocks/' + contextName + '/colors/palette'; +function Palette( { name } ) { + const [ colors ] = useSetting( 'color.palette', name ); + const screenPath = ! name + ? '/colors/palette' + : '/blocks/' + name + '/colors/palette'; return (
diff --git a/packages/edit-site/src/components/global-styles/preview.js b/packages/edit-site/src/components/global-styles/preview.js index 387f3d30ec0e2..c691e1134fe90 100644 --- a/packages/edit-site/src/components/global-styles/preview.js +++ b/packages/edit-site/src/components/global-styles/preview.js @@ -10,14 +10,14 @@ import { /** * Internal dependencies - */ import { useGlobalStylesContext } from '../editor/global-styles-provider'; + */ +import { useStyle } from './hooks'; const StylesPreview = () => { - const { getStyle } = useGlobalStylesContext(); - const fontFamily = getStyle( 'root', 'fontFamily' ) ?? 'serif'; - const textColor = getStyle( 'root', 'color' ) ?? 'black'; - const linkColor = getStyle( 'root', 'linkColor' ) ?? 'blue'; - const backgroundColor = getStyle( 'root', 'backgroundColor' ) ?? 'white'; + const [ fontFamily = 'serif' ] = useStyle( 'typography.fontFamily' ); + const [ textColor = 'black' ] = useStyle( 'color.text' ); + const [ linkColor = 'blue' ] = useStyle( 'elements.link.color.text' ); + const [ backgroundColor = 'white' ] = useStyle( 'color.background' ); return ( { style={ { background: backgroundColor } } > -
- A - a -
+
Aa
diff --git a/packages/edit-site/src/components/global-styles/screen-block-list.js b/packages/edit-site/src/components/global-styles/screen-block-list.js index d673226b5a8d0..159d273650675 100644 --- a/packages/edit-site/src/components/global-styles/screen-block-list.js +++ b/packages/edit-site/src/components/global-styles/screen-block-list.js @@ -1,13 +1,12 @@ /** * WordPress dependencies */ -import { getBlockType } from '@wordpress/blocks'; +import { getBlockTypes } from '@wordpress/blocks'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useGlobalStylesContext } from '../editor/global-styles-provider'; import { useHasBorderPanel } from './border-panel'; import { useHasColorPanel } from './color-panel'; import { useHasDimensionsPanel } from './dimensions-panel'; @@ -16,12 +15,10 @@ import ScreenHeader from './header'; import NavigationButton from './navigation-button'; function BlockMenuItem( { block } ) { - const { blocks } = useGlobalStylesContext(); - const context = blocks[ block.name ]; - const hasTypographyPanel = useHasTypographyPanel( context ); - const hasColorPanel = useHasColorPanel( context ); - const hasBorderPanel = useHasBorderPanel( context ); - const hasDimensionsPanel = useHasDimensionsPanel( context ); + const hasTypographyPanel = useHasTypographyPanel( block.name ); + const hasColorPanel = useHasColorPanel( block.name ); + const hasBorderPanel = useHasBorderPanel( block.name ); + const hasDimensionsPanel = useHasDimensionsPanel( block.name ); const hasLayoutPanel = hasBorderPanel || hasDimensionsPanel; const hasBlockMenuItem = hasTypographyPanel || hasColorPanel || hasLayoutPanel; @@ -38,15 +35,10 @@ function BlockMenuItem( { block } ) { } function ScreenBlockList() { - const { blocks } = useGlobalStylesContext(); - const visibleBlocks = Object.keys( blocks ) - .map( ( name ) => getBlockType( name ) ) - .filter( ( blockType ) => !! blockType ); - return ( <> - { visibleBlocks.map( ( block ) => ( + { getBlockTypes().map( ( block ) => ( - + ); } diff --git a/packages/edit-site/src/components/global-styles/screen-color-palette.js b/packages/edit-site/src/components/global-styles/screen-color-palette.js index a2518816b53f8..e207b76645ef3 100644 --- a/packages/edit-site/src/components/global-styles/screen-color-palette.js +++ b/packages/edit-site/src/components/global-styles/screen-color-palette.js @@ -6,12 +6,10 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useGlobalStylesContext } from '../editor/global-styles-provider'; import ColorPalettePanel from './color-palette-panel'; import ScreenHeader from './header'; function ScreenColorPalette( { name } ) { - const { getSetting, setSetting } = useGlobalStylesContext(); const parentMenu = name === undefined ? '' : '/blocks/' + name; return ( @@ -21,11 +19,7 @@ function ScreenColorPalette( { name } ) { title={ __( 'Color Palette' ) } description={ __( 'Manage the color palette of your site' ) } /> - + ); } diff --git a/packages/edit-site/src/components/global-styles/screen-colors.js b/packages/edit-site/src/components/global-styles/screen-colors.js index abb6f8152d11a..5e4e29b7dd3ef 100644 --- a/packages/edit-site/src/components/global-styles/screen-colors.js +++ b/packages/edit-site/src/components/global-styles/screen-colors.js @@ -6,13 +6,10 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useGlobalStylesContext } from '../editor/global-styles-provider'; import ColorPanel from './color-panel'; import ScreenHeader from './header'; function ScreenColors( { name } ) { - const { root, blocks, getStyle, setStyle } = useGlobalStylesContext(); - const context = name === undefined ? root : blocks[ name ]; const parentMenu = name === undefined ? '' : '/blocks/' + name; return ( @@ -24,11 +21,7 @@ function ScreenColors( { name } ) { 'Manage the color palette and how it applies to the elements of your site' ) } /> - + ); } diff --git a/packages/edit-site/src/components/global-styles/screen-layout.js b/packages/edit-site/src/components/global-styles/screen-layout.js index b1a8dc449ac1e..36d9b2444ee67 100644 --- a/packages/edit-site/src/components/global-styles/screen-layout.js +++ b/packages/edit-site/src/components/global-styles/screen-layout.js @@ -6,17 +6,14 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useGlobalStylesContext } from '../editor/global-styles-provider'; import BorderPanel, { useHasBorderPanel } from './border-panel'; import DimensionsPanel, { useHasDimensionsPanel } from './dimensions-panel'; import ScreenHeader from './header'; function ScreenLayout( { name } ) { - const { root, blocks, getStyle, setStyle } = useGlobalStylesContext(); - const context = name === undefined ? root : blocks[ name ]; const parentMenu = name === undefined ? '' : '/blocks/' + name; - const hasBorderPanel = useHasBorderPanel( context ); - const hasDimensionsPanel = useHasDimensionsPanel( context ); + const hasBorderPanel = useHasBorderPanel( name ); + const hasDimensionsPanel = useHasDimensionsPanel( name ); return ( <> @@ -24,20 +21,8 @@ function ScreenLayout( { name } ) { back={ parentMenu ? parentMenu : '/' } title={ __( 'Layout' ) } /> - { hasDimensionsPanel && ( - - ) } - { hasBorderPanel && ( - - ) } + { hasDimensionsPanel && } + { hasBorderPanel && } ); } diff --git a/packages/edit-site/src/components/global-styles/screen-root.js b/packages/edit-site/src/components/global-styles/screen-root.js index 20e0961825f45..2f22e6f4d3b65 100644 --- a/packages/edit-site/src/components/global-styles/screen-root.js +++ b/packages/edit-site/src/components/global-styles/screen-root.js @@ -12,17 +12,14 @@ import { __ } from '@wordpress/i18n'; */ import StylesPreview from './preview'; import NavigationButton from './navigation-button'; -import { useGlobalStylesContext } from '../editor/global-styles-provider'; import ContextMenu from './context-menu'; function ScreenRoot() { - const { root } = useGlobalStylesContext(); - return ( <> - + diff --git a/packages/edit-site/src/components/global-styles/screen-typography.js b/packages/edit-site/src/components/global-styles/screen-typography.js index edf8cfe9b53fb..56c1eee11540a 100644 --- a/packages/edit-site/src/components/global-styles/screen-typography.js +++ b/packages/edit-site/src/components/global-styles/screen-typography.js @@ -6,13 +6,10 @@ import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ -import { useGlobalStylesContext } from '../editor/global-styles-provider'; import TypographyPanel from './typography-panel'; import ScreenHeader from './header'; function ScreenTypography( { name } ) { - const { root, blocks, getStyle, setStyle } = useGlobalStylesContext(); - const context = name === undefined ? root : blocks[ name ]; const parentMenu = name === undefined ? '' : '/blocks/' + name; return ( @@ -21,11 +18,7 @@ function ScreenTypography( { name } ) { back={ parentMenu ? parentMenu : '/' } title={ __( 'Typography' ) } /> - + ); } diff --git a/packages/edit-site/src/components/global-styles/typography-panel.js b/packages/edit-site/src/components/global-styles/typography-panel.js index df44837a38cc6..2450ef9b27a6a 100644 --- a/packages/edit-site/src/components/global-styles/typography-panel.js +++ b/packages/edit-site/src/components/global-styles/typography-panel.js @@ -12,12 +12,13 @@ import { PanelBody, FontSizePicker } from '@wordpress/components'; /** * Internal dependencies */ -import { useSetting } from '../editor/utils'; +import { getSupportedGlobalStylesPanels, useSetting, useStyle } from './hooks'; -export function useHasTypographyPanel( { supports, name } ) { - const hasLineHeight = useHasLineHeightControl( { supports, name } ); - const hasFontAppearance = useHasAppearanceControl( { supports, name } ); - const hasLetterSpacing = useHasLetterSpacingControl( { supports, name } ); +export function useHasTypographyPanel( name ) { + const hasLineHeight = useHasLineHeightControl( name ); + const hasFontAppearance = useHasAppearanceControl( name ); + const hasLetterSpacing = useHasLetterSpacingControl( name ); + const supports = getSupportedGlobalStylesPanels( name ); return ( hasLineHeight || hasFontAppearance || @@ -26,14 +27,16 @@ export function useHasTypographyPanel( { supports, name } ) { ); } -function useHasLineHeightControl( { supports, name } ) { +function useHasLineHeightControl( name ) { + const supports = getSupportedGlobalStylesPanels( name ); return ( useSetting( 'typography.customLineHeight', name ) && supports.includes( 'lineHeight' ) ); } -function useHasAppearanceControl( { supports, name } ) { +function useHasAppearanceControl( name ) { + const supports = getSupportedGlobalStylesPanels( name ); const hasFontStyles = useSetting( 'typography.customFontStyle', name ) && supports.includes( 'fontStyle' ); @@ -43,75 +46,90 @@ function useHasAppearanceControl( { supports, name } ) { return hasFontStyles || hasFontWeights; } -function useHasLetterSpacingControl( { supports, name } ) { +function useHasLetterSpacingControl( name ) { + const supports = getSupportedGlobalStylesPanels( name ); return ( useSetting( 'typography.customLetterSpacing', name ) && supports.includes( 'letterSpacing' ) ); } -export default function TypographyPanel( { - context: { supports, name }, - getStyle, - setStyle, -} ) { - const fontSizes = useSetting( 'typography.fontSizes', name ); +export default function TypographyPanel( { name } ) { + const supports = getSupportedGlobalStylesPanels( name ); + const [ fontSizes ] = useSetting( 'typography.fontSizes', name ); const disableCustomFontSizes = ! useSetting( 'typography.customFontSize', name - ); - const fontFamilies = useSetting( 'typography.fontFamilies', name ); + )[ 0 ]; + const [ fontFamilies ] = useSetting( 'typography.fontFamilies', name ); const hasFontStyles = - useSetting( 'typography.customFontStyle', name ) && + useSetting( 'typography.customFontStyle', name )[ 0 ] && supports.includes( 'fontStyle' ); const hasFontWeights = - useSetting( 'typography.customFontWeight', name ) && + useSetting( 'typography.customFontWeight', name )[ 0 ] && supports.includes( 'fontWeight' ); - const hasLineHeightEnabled = useHasLineHeightControl( { supports, name } ); - const hasAppearanceControl = useHasAppearanceControl( { supports, name } ); - const hasLetterSpacingControl = useHasLetterSpacingControl( { - supports, - name, - } ); + const hasLineHeightEnabled = useHasLineHeightControl( name ); + const hasAppearanceControl = useHasAppearanceControl( name ); + const hasLetterSpacingControl = useHasLetterSpacingControl( name ); + + const [ fontFamily, setFontFamily ] = useStyle( + 'typography.fontFamily', + name + ); + const [ fontSize, setFontSize ] = useStyle( 'typography.fontSize', name ); + + const [ fontStyle, setFontStyle ] = useStyle( + 'typography.fontStyle', + name + ); + const [ fontWeight, setFontWeight ] = useStyle( + 'typography.fontWeight', + name + ); + const [ lineHeight, setLineHeight ] = useStyle( + 'typography.lineHeight', + name + ); + const [ letterSpacing, setLetterSpacing ] = useStyle( + 'typography.letterSpacing', + name + ); return ( { supports.includes( 'fontFamily' ) && ( - setStyle( name, 'fontFamily', value ) - } + value={ fontFamily } + onChange={ setFontFamily } /> ) } { supports.includes( 'fontSize' ) && ( - setStyle( name, 'fontSize', value ) - } + value={ fontSize } + onChange={ setFontSize } fontSizes={ fontSizes } disableCustomFontSizes={ disableCustomFontSizes } /> ) } { hasLineHeightEnabled && ( - setStyle( name, 'lineHeight', value ) - } + value={ lineHeight } + onChange={ setLineHeight } /> ) } { hasAppearanceControl && ( { - setStyle( name, 'fontStyle', fontStyle ); - setStyle( name, 'fontWeight', fontWeight ); + onChange={ ( { + fontStyle: newFontStyle, + fontWeight: newFontWeight, + } ) => { + setFontStyle( newFontStyle ); + setFontWeight( newFontWeight ); } } hasFontStyles={ hasFontStyles } hasFontWeights={ hasFontWeights } @@ -119,10 +137,8 @@ export default function TypographyPanel( { ) } { hasLetterSpacingControl && ( - setStyle( name, 'letterSpacing', value ) - } + value={ letterSpacing } + onChange={ setLetterSpacing } /> ) } diff --git a/packages/edit-site/src/components/sidebar/global-styles-sidebar.js b/packages/edit-site/src/components/sidebar/global-styles-sidebar.js index 78f106825117c..1dfd29e883c2c 100644 --- a/packages/edit-site/src/components/sidebar/global-styles-sidebar.js +++ b/packages/edit-site/src/components/sidebar/global-styles-sidebar.js @@ -8,9 +8,9 @@ import { styles } from '@wordpress/icons'; /** * Internal dependencies */ -import { useGlobalStylesReset } from '../editor/global-styles-provider'; import DefaultSidebar from './default-sidebar'; import GlobalStyles from '../global-styles'; +import { useGlobalStylesReset } from '../global-styles/hooks'; export default function GlobalStylesSidebar() { const [ canRestart, onReset ] = useGlobalStylesReset(); From 58527867e5b28d6d85a3c72bb0bab95d8681e163 Mon Sep 17 00:00:00 2001 From: Jorge Date: Sun, 3 Oct 2021 11:48:06 +0100 Subject: [PATCH 02/15] Wire hooks to the variable engine --- .../editor/global-styles-provider.js | 24 +++- .../edit-site/src/components/editor/utils.js | 123 +++++++++--------- .../components/global-styles/color-panel.js | 6 - .../src/components/global-styles/hooks.js | 97 ++++++++++++-- 4 files changed, 170 insertions(+), 80 deletions(-) diff --git a/packages/edit-site/src/components/editor/global-styles-provider.js b/packages/edit-site/src/components/editor/global-styles-provider.js index 5050be93563a8..55bfbe016e07f 100644 --- a/packages/edit-site/src/components/editor/global-styles-provider.js +++ b/packages/edit-site/src/components/editor/global-styles-provider.js @@ -29,7 +29,7 @@ import { ROOT_BLOCK_SELECTOR, ROOT_BLOCK_SUPPORTS, getValueFromVariable, - getPresetVariable, + getPresetVariableFromValue, PRESET_METADATA, } from './utils'; import { toCustomProperties, toStyles } from './global-styles-renderer'; @@ -256,7 +256,11 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { if ( origin === 'theme' ) { const value = get( themeStyles?.styles, path ); - return getValueFromVariable( themeStyles, context, value ); + return getValueFromVariable( + themeStyles.settings, + context, + value + ); } if ( origin === 'user' ) { @@ -265,11 +269,19 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { // We still need to use merged styles here because the // presets used to resolve user variable may be defined a // layer down ( core, theme, or user ). - return getValueFromVariable( mergedStyles, context, value ); + return getValueFromVariable( + mergedStyles.settings, + context, + value + ); } const value = get( mergedStyles?.styles, path ); - return getValueFromVariable( mergedStyles, context, value ); + return getValueFromVariable( + mergedStyles.settings, + context, + value + ); }, setStyle: ( context, propertyName, newValue ) => { const newContent = { ...userStyles }; @@ -288,8 +300,8 @@ export default function GlobalStylesProvider( { children, baseStyles } ) { set( newStyles, propertyPath, - getPresetVariable( - mergedStyles, + getPresetVariableFromValue( + mergedStyles.settings, context, propertyName, newValue diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/editor/utils.js index aa3a30da0218f..099e0d4a87c40 100644 --- a/packages/edit-site/src/components/editor/utils.js +++ b/packages/edit-site/src/components/editor/utils.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { get, find, forEach, camelCase, isString } from 'lodash'; +import { get, find, isString } from 'lodash'; /** * WordPress dependencies */ @@ -72,27 +72,15 @@ export const PRESET_METADATA = [ }, ]; -const STYLE_PROPERTIES_TO_CSS_VAR_INFIX = { - linkColor: 'color', - backgroundColor: 'color', - background: 'gradient', +const STYLE_PATH_TO_CSS_VAR_INFIX = { + 'color.background': 'color', + 'color.text': 'color', + 'elements.link.color.text': 'color', + 'color.gradient': 'gradient', + 'typography.fontSize': 'font-size', + 'typography.fontFamily': 'font-family', }; -function getPresetMetadataFromStyleProperty( styleProperty ) { - if ( ! getPresetMetadataFromStyleProperty.MAP ) { - getPresetMetadataFromStyleProperty.MAP = {}; - PRESET_METADATA.forEach( ( { cssVarInfix }, index ) => { - getPresetMetadataFromStyleProperty.MAP[ camelCase( cssVarInfix ) ] = - PRESET_METADATA[ index ]; - } ); - forEach( STYLE_PROPERTIES_TO_CSS_VAR_INFIX, ( value, key ) => { - getPresetMetadataFromStyleProperty.MAP[ key ] = - getPresetMetadataFromStyleProperty.MAP[ value ]; - } ); - } - return getPresetMetadataFromStyleProperty.MAP[ styleProperty ]; -} - export function useSetting( path, blockName = '' ) { const settings = useSelect( ( select ) => { return select( editSiteStore ).getSettings(); @@ -107,17 +95,18 @@ export function useSetting( path, blockName = '' ) { } function findInPresetsBy( - styles, - context, + features, + blockName, presetPath, presetProperty, presetValueValue ) { // Block presets take priority above root level presets. const orderedPresetsByOrigin = [ - get( styles, [ 'settings', 'blocks', context, ...presetPath ] ), - get( styles, [ 'settings', ...presetPath ] ), + get( features, [ 'blocks', blockName, ...presetPath ] ), + get( features, presetPath ), ]; + for ( const presetByOrigin of orderedPresetsByOrigin ) { if ( presetByOrigin ) { // Preset origins ordered by priority. @@ -136,8 +125,8 @@ function findInPresetsBy( } // if there is a highest priority preset with the same slug but different value the preset we found was overwritten and should be ignored. const highestPresetObjectWithSameSlug = findInPresetsBy( - styles, - context, + features, + blockName, presetPath, 'slug', presetObject.slug @@ -157,50 +146,57 @@ function findInPresetsBy( } } -export function getPresetVariable( styles, context, propertyName, value ) { - if ( ! value ) { - return value; +export function getPresetVariableFromValue( + features, + blockName, + variableStylePath, + presetPropertyValue +) { + if ( ! presetPropertyValue ) { + return presetPropertyValue; } - const metadata = getPresetMetadataFromStyleProperty( propertyName ); + const cssVarInfix = STYLE_PATH_TO_CSS_VAR_INFIX[ variableStylePath ]; + + const metadata = find( PRESET_METADATA, [ 'cssVarInfix', cssVarInfix ] ); + if ( ! metadata ) { // The property doesn't have preset data // so the value should be returned as it is. - return value; + return presetPropertyValue; } - const { valueKey, path, cssVarInfix } = metadata; + const { valueKey, path } = metadata; const presetObject = findInPresetsBy( - styles, - context, + features, + blockName, path, valueKey, - value + presetPropertyValue ); if ( ! presetObject ) { // Value wasn't found in the presets, // so it must be a custom value. - return value; + return presetPropertyValue; } return `var:preset|${ cssVarInfix }|${ presetObject.slug }`; } function getValueFromPresetVariable( - styles, + features, blockName, variable, [ presetType, slug ] ) { - presetType = camelCase( presetType ); - const metadata = getPresetMetadataFromStyleProperty( presetType ); + const metadata = find( PRESET_METADATA, [ 'cssVarInfix', presetType ] ); if ( ! metadata ) { return variable; } const presetObject = findInPresetsBy( - styles, + features, blockName, metadata.path, 'slug', @@ -210,54 +206,63 @@ function getValueFromPresetVariable( if ( presetObject ) { const { valueKey } = metadata; const result = presetObject[ valueKey ]; - return getValueFromVariable( styles, blockName, result ); + return getValueFromVariable( features, blockName, result ); } return variable; } -function getValueFromCustomVariable( styles, blockName, variable, path ) { +function getValueFromCustomVariable( features, blockName, variable, path ) { const result = - get( styles, [ 'settings', 'blocks', blockName, 'custom', ...path ] ) ?? - get( styles, [ 'settings', 'custom', ...path ] ); + get( features, [ 'blocks', blockName, 'custom', ...path ] ) ?? + get( features, [ 'custom', ...path ] ); if ( ! result ) { return variable; } // A variable may reference another variable so we need recursion until we find the value. - return getValueFromVariable( styles, blockName, result ); + return getValueFromVariable( features, blockName, result ); } -export function getValueFromVariable( styles, blockName, variable ) { +export function getValueFromVariable( features, blockName, variable ) { if ( ! variable || ! isString( variable ) ) { return variable; } + const USER_VALUE_PREFIX = 'var:'; + const THEME_VALUE_PREFIX = 'var(--wp--'; + const THEME_VALUE_SUFFIX = ')'; let parsedVar; - const INTERNAL_REFERENCE_PREFIX = 'var:'; - const CSS_REFERENCE_PREFIX = 'var(--wp--'; - const CSS_REFERENCE_SUFFIX = ')'; - if ( variable.startsWith( INTERNAL_REFERENCE_PREFIX ) ) { - parsedVar = variable - .slice( INTERNAL_REFERENCE_PREFIX.length ) - .split( '|' ); + + if ( variable.startsWith( USER_VALUE_PREFIX ) ) { + parsedVar = variable.slice( USER_VALUE_PREFIX.length ).split( '|' ); } else if ( - variable.startsWith( CSS_REFERENCE_PREFIX ) && - variable.endsWith( CSS_REFERENCE_SUFFIX ) + variable.startsWith( THEME_VALUE_PREFIX ) && + variable.endsWith( THEME_VALUE_SUFFIX ) ) { parsedVar = variable - .slice( CSS_REFERENCE_PREFIX.length, -CSS_REFERENCE_SUFFIX.length ) + .slice( THEME_VALUE_PREFIX.length, -THEME_VALUE_SUFFIX.length ) .split( '--' ); } else { - // Value is raw. + // We don't know how to parse the value: either is raw of uses complex CSS such as `calc(1px * var(--wp--variable) )` return variable; } const [ type, ...path ] = parsedVar; if ( type === 'preset' ) { - return getValueFromPresetVariable( styles, blockName, variable, path ); + return getValueFromPresetVariable( + features, + blockName, + variable, + path + ); } if ( type === 'custom' ) { - return getValueFromCustomVariable( styles, blockName, variable, path ); + return getValueFromCustomVariable( + features, + blockName, + variable, + path + ); } return variable; } diff --git a/packages/edit-site/src/components/global-styles/color-panel.js b/packages/edit-site/src/components/global-styles/color-panel.js index b58f7b1d27f3f..a809bb6b81aee 100644 --- a/packages/edit-site/src/components/global-styles/color-panel.js +++ b/packages/edit-site/src/components/global-styles/color-panel.js @@ -30,21 +30,15 @@ export default function ColorPanel( { name } ) { 'color.customGradient', name ); - const [ isLinkEnabled ] = useSetting( 'color.link', name ); - const [ isTextEnabled ] = useSetting( 'color.text', name ); - const [ isBackgroundEnabled ] = useSetting( 'color.background', name ); const hasLinkColor = supports.includes( 'linkColor' ) && - isLinkEnabled && ( solids.length > 0 || areCustomSolidsEnabled ); const hasTextColor = supports.includes( 'color' ) && - isTextEnabled && ( solids.length > 0 || areCustomSolidsEnabled ); const hasBackgroundColor = supports.includes( 'backgroundColor' ) && - isBackgroundEnabled && ( solids.length > 0 || areCustomSolidsEnabled ); const hasGradientColor = supports.includes( 'background' ) && diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index 8584459d0c9b5..2a50e7278147a 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -1,7 +1,7 @@ /** * External dependencies */ -import { get, cloneDeep, set, isEqual, has } from 'lodash'; +import { get, cloneDeep, set, isEqual, has, mergeWith } from 'lodash'; /** * WordPress dependencies @@ -19,9 +19,49 @@ import { * Internal dependencies */ import { store as editSiteStore } from '../../store'; +import { + PRESET_METADATA, + getValueFromVariable, + getPresetVariableFromValue, +} from '../editor/utils'; const EMPTY_CONFIG = { isGlobalStylesUserThemeJSON: true, version: 1 }; +function mergeTreesCustomizer( objValue, srcValue ) { + // We only pass as arrays the presets, + // in which case we want the new array of values + // to override the old array (no merging). + if ( Array.isArray( srcValue ) ) { + return srcValue; + } +} + +function mergeBaseAndUserConfigs( base, user ) { + return mergeWith( {}, base, user, mergeTreesCustomizer ); +} + +function addUserOriginToSettings( settingsToAdd ) { + PRESET_METADATA.forEach( ( { path } ) => { + const presetData = get( settingsToAdd, path ); + if ( presetData ) { + set( settingsToAdd, path, { + user: presetData, + } ); + } + } ); + return settingsToAdd; +} + +function removeUserOriginFromSettings( settingsToRemove ) { + PRESET_METADATA.forEach( ( { path } ) => { + const presetData = get( settingsToRemove, path ); + if ( presetData ) { + set( settingsToRemove, path, ( presetData ?? {} ).user ); + } + } ); + return settingsToRemove; +} + function useGlobalStylesUserConfig() { const globalStylesId = useSelect( ( select ) => { return select( editSiteStore ).getSettings() @@ -39,6 +79,16 @@ function useGlobalStylesUserConfig() { let parsedConfig; try { parsedConfig = content ? JSON.parse( content ) : {}; + // It is very important to verify if the flag isGlobalStylesUserThemeJSON is true. + // If it is not true the content was not escaped and is not safe. + if ( ! parsedConfig.isGlobalStylesUserThemeJSON ) { + parsedConfig = {}; + } else { + parsedConfig = { + ...parsedConfig, + settings: addUserOriginToSettings( parsedConfig.settings ), + }; + } } catch ( e ) { /* eslint-disable no-console */ console.error( 'Global Styles User data is not valid' ); @@ -51,7 +101,15 @@ function useGlobalStylesUserConfig() { }, [ content ] ); const setConfig = useCallback( - ( newConfig ) => setContent( JSON.stringify( newConfig ) ), + ( newConfig ) => + setContent( + JSON.stringify( { + ...newConfig, + settings: removeUserOriginFromSettings( + newConfig.settings + ), + } ) + ), [ setContent ] ); @@ -105,10 +163,10 @@ export function useSetting( path, blockName, source = 'all' ) { let result; switch ( source ) { case 'all': - result = get( userConfig, finalPath ) ?? getBaseSetting(); + result = get( userConfig, finalPath, {} ).user ?? getBaseSetting(); break; case 'user': - result = get( userConfig, finalPath ); + result = get( userConfig, finalPath, {} ).user; break; case 'base': result = getBaseSetting(); @@ -122,27 +180,48 @@ export function useSetting( path, blockName, source = 'all' ) { export function useStyle( path, blockName, source = 'all' ) { const [ baseConfig, userConfig, setUserConfig ] = useGlobalStylesConfig(); + const mergedConfig = mergeBaseAndUserConfigs( baseConfig, userConfig ); const finalPath = ! blockName ? `styles.${ path }` : `styles.blocks.${ blockName }.${ path }`; const setStyle = ( newValue ) => { const newUserConfig = cloneDeep( userConfig ); - set( newUserConfig, finalPath, newValue ); + set( + newUserConfig, + finalPath, + getPresetVariableFromValue( + mergedConfig.settings, + blockName, + path, + newValue + ) + ); setUserConfig( newUserConfig ); }; let result; switch ( source ) { case 'all': - result = - get( userConfig, finalPath ) ?? get( baseConfig, finalPath ); + result = getValueFromVariable( + mergedConfig.settings, + blockName, + get( userConfig, finalPath ) ?? get( baseConfig, finalPath ) + ); break; case 'user': - result = get( userConfig, finalPath ); + result = getValueFromVariable( + mergedConfig.settings, + blockName, + get( userConfig, finalPath ) + ); break; case 'base': - result = get( baseConfig, finalPath ); + result = getValueFromVariable( + baseConfig.settings, + blockName, + get( baseConfig, finalPath ) + ); break; default: throw 'Unsupported source'; From c8296e33eb9b6ee5ed2eb03d2e161316805fd04f Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 4 Oct 2021 11:20:03 +0100 Subject: [PATCH 03/15] Add missing return --- .../edit-site/src/components/global-styles/color-panel.js | 7 +++++++ packages/edit-site/src/components/global-styles/hooks.js | 1 + 2 files changed, 8 insertions(+) diff --git a/packages/edit-site/src/components/global-styles/color-panel.js b/packages/edit-site/src/components/global-styles/color-panel.js index a809bb6b81aee..64d7ff33f1e6a 100644 --- a/packages/edit-site/src/components/global-styles/color-panel.js +++ b/packages/edit-site/src/components/global-styles/color-panel.js @@ -31,14 +31,21 @@ export default function ColorPanel( { name } ) { name ); + const [ isLinkEnabled ] = useSetting( 'color.link', name ); + const [ isTextEnabled ] = useSetting( 'color.text', name ); + const [ isBackgroundEnabled ] = useSetting( 'color.background', name ); + const hasLinkColor = supports.includes( 'linkColor' ) && + isLinkEnabled && ( solids.length > 0 || areCustomSolidsEnabled ); const hasTextColor = supports.includes( 'color' ) && + isTextEnabled && ( solids.length > 0 || areCustomSolidsEnabled ); const hasBackgroundColor = supports.includes( 'backgroundColor' ) && + isBackgroundEnabled && ( solids.length > 0 || areCustomSolidsEnabled ); const hasGradientColor = supports.includes( 'background' ) && diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index 2a50e7278147a..9c5ba40b17c2f 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -152,6 +152,7 @@ export function useSetting( path, blockName, source = 'all' ) { if ( PATHS_WITH_MERGE[ path ] ) { return result.theme ?? result.core; } + return result; }; const setSetting = ( newValue ) => { From 88583515bb7c43fbaac110ceb2ad09c47311282c Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 4 Oct 2021 11:52:29 +0100 Subject: [PATCH 04/15] Make global styles updates independent of the component lifecycle --- .../src/components/global-styles/hooks.js | 94 +++++++++++-------- 1 file changed, 57 insertions(+), 37 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index 9c5ba40b17c2f..4cc9ef202ebae 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -7,8 +7,8 @@ import { get, cloneDeep, set, isEqual, has, mergeWith } from 'lodash'; * WordPress dependencies */ import { useMemo, useCallback } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; -import { useEntityProp } from '@wordpress/core-data'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; import { getBlockType, __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE, @@ -63,22 +63,25 @@ function removeUserOriginFromSettings( settingsToRemove ) { } function useGlobalStylesUserConfig() { - const globalStylesId = useSelect( ( select ) => { - return select( editSiteStore ).getSettings() + const { globalStylesId, content } = useSelect( ( select ) => { + const _globalStylesId = select( editSiteStore ).getSettings() .__experimentalGlobalStylesUserEntityId; + return { + globalStylesId: _globalStylesId, + content: select( coreStore ).getEditedEntityRecord( + 'postType', + 'wp_global_styles', + _globalStylesId + )?.content, + }; }, [] ); + const { getEditedEntityRecord } = useSelect( coreStore ); + const { editEntityRecord } = useDispatch( coreStore ); - const [ content, setContent ] = useEntityProp( - 'postType', - 'wp_global_styles', - 'content', - globalStylesId - ); - - const config = useMemo( () => { + const parseContent = ( contentToParse ) => { let parsedConfig; try { - parsedConfig = content ? JSON.parse( content ) : {}; + parsedConfig = contentToParse ? JSON.parse( contentToParse ) : {}; // It is very important to verify if the flag isGlobalStylesUserThemeJSON is true. // If it is not true the content was not escaped and is not safe. if ( ! parsedConfig.isGlobalStylesUserThemeJSON ) { @@ -98,19 +101,32 @@ function useGlobalStylesUserConfig() { } return parsedConfig; + }; + + const config = useMemo( () => { + return parseContent( content ); }, [ content ] ); const setConfig = useCallback( - ( newConfig ) => - setContent( - JSON.stringify( { - ...newConfig, + ( callback ) => { + const currentConfig = parseContent( + getEditedEntityRecord( + 'postType', + 'wp_global_styles', + globalStylesId + )?.content + ); + const updatedConfig = callback( currentConfig ); + editEntityRecord( 'postType', 'wp_global_styles', globalStylesId, { + content: JSON.stringify( { + ...updatedConfig, settings: removeUserOriginFromSettings( - newConfig.settings + updatedConfig.settings ), - } ) - ), - [ setContent ] + } ), + } ); + }, + [ globalStylesId ] ); return [ config, setConfig ]; @@ -137,7 +153,7 @@ export const useGlobalStylesReset = () => { const canReset = !! config && ! isEqual( config, EMPTY_CONFIG ); return [ canReset, - useCallback( () => setConfig( EMPTY_CONFIG ), [ setConfig ] ), + useCallback( () => setConfig( () => EMPTY_CONFIG ), [ setConfig ] ), ]; }; @@ -156,9 +172,11 @@ export function useSetting( path, blockName, source = 'all' ) { }; const setSetting = ( newValue ) => { - const newUserConfig = cloneDeep( userConfig ); - set( newUserConfig, finalPath, newValue ); - setUserConfig( newUserConfig ); + setUserConfig( ( currentConfig ) => { + const newUserConfig = cloneDeep( currentConfig ); + set( newUserConfig, finalPath, newValue ); + return newUserConfig; + } ); }; let result; @@ -187,18 +205,20 @@ export function useStyle( path, blockName, source = 'all' ) { : `styles.blocks.${ blockName }.${ path }`; const setStyle = ( newValue ) => { - const newUserConfig = cloneDeep( userConfig ); - set( - newUserConfig, - finalPath, - getPresetVariableFromValue( - mergedConfig.settings, - blockName, - path, - newValue - ) - ); - setUserConfig( newUserConfig ); + setUserConfig( ( currentConfig ) => { + const newUserConfig = cloneDeep( currentConfig ); + set( + newUserConfig, + finalPath, + getPresetVariableFromValue( + mergedConfig.settings, + blockName, + path, + newValue + ) + ); + return newUserConfig; + } ); }; let result; From 4599b514ec74a4d1d30f9dfd7609de44f9fef515 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 4 Oct 2021 12:37:43 +0100 Subject: [PATCH 05/15] Simplify global styles renderer and remove useless code --- .../editor/global-styles-provider.js | 346 ------------------ .../editor/global-styles-renderer.js | 65 +++- .../edit-site/src/components/editor/index.js | 174 ++++----- .../editor/test/global-styles-provider.js | 131 ------- .../src/components/global-styles/hooks.js | 32 +- .../src/components/global-styles/index.js | 24 +- 6 files changed, 176 insertions(+), 596 deletions(-) delete mode 100644 packages/edit-site/src/components/editor/global-styles-provider.js delete mode 100644 packages/edit-site/src/components/editor/test/global-styles-provider.js diff --git a/packages/edit-site/src/components/editor/global-styles-provider.js b/packages/edit-site/src/components/editor/global-styles-provider.js deleted file mode 100644 index 55bfbe016e07f..0000000000000 --- a/packages/edit-site/src/components/editor/global-styles-provider.js +++ /dev/null @@ -1,346 +0,0 @@ -/** - * External dependencies - */ -import { set, get, has, mergeWith, mapValues, setWith, clone } from 'lodash'; - -/** - * WordPress dependencies - */ -import { - createContext, - useCallback, - useContext, - useEffect, - useMemo, -} from '@wordpress/element'; -import { - __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, - __EXPERIMENTAL_ELEMENTS as ELEMENTS, - store as blocksStore, -} from '@wordpress/blocks'; -import { useEntityProp } from '@wordpress/core-data'; -import { useSelect, useDispatch } from '@wordpress/data'; - -/** - * Internal dependencies - */ -import { - ROOT_BLOCK_NAME, - ROOT_BLOCK_SELECTOR, - ROOT_BLOCK_SUPPORTS, - getValueFromVariable, - getPresetVariableFromValue, - PRESET_METADATA, -} from './utils'; -import { toCustomProperties, toStyles } from './global-styles-renderer'; -import { store as editSiteStore } from '../../store'; - -const EMPTY_CONTENT = { isGlobalStylesUserThemeJSON: true, version: 1 }; -const EMPTY_CONTENT_STRING = JSON.stringify( EMPTY_CONTENT ); - -const GlobalStylesContext = createContext( { - /* eslint-disable no-unused-vars */ - getSetting: ( context, path ) => {}, - setSetting: ( context, path, newValue ) => {}, - getStyle: ( context, propertyName, origin ) => {}, - setStyle: ( context, propertyName, newValue ) => {}, - contexts: {}, - /* eslint-enable no-unused-vars */ -} ); - -const mergeTreesCustomizer = ( objValue, srcValue ) => { - // We only pass as arrays the presets, - // in which case we want the new array of values - // to override the old array (no merging). - if ( Array.isArray( srcValue ) ) { - return srcValue; - } -}; - -export const useGlobalStylesContext = () => useContext( GlobalStylesContext ); - -const useGlobalStylesEntityContent = () => { - return useEntityProp( 'postType', 'wp_global_styles', 'content' ); -}; - -export const useGlobalStylesReset = () => { - const [ content, setContent ] = useGlobalStylesEntityContent(); - const canRestart = !! content && content !== EMPTY_CONTENT_STRING; - return [ - canRestart, - useCallback( () => setContent( EMPTY_CONTENT_STRING ), [ setContent ] ), - ]; -}; - -const extractSupportKeys = ( supports ) => { - const supportKeys = []; - Object.keys( STYLE_PROPERTY ).forEach( ( name ) => { - if ( ! STYLE_PROPERTY[ name ].support ) { - return; - } - - // Opting out means that, for certain support keys like background color, - // blocks have to explicitly set the support value false. If the key is - // unset, we still enable it. - if ( STYLE_PROPERTY[ name ].requiresOptOut ) { - if ( - has( supports, STYLE_PROPERTY[ name ].support[ 0 ] ) && - get( supports, STYLE_PROPERTY[ name ].support ) !== false - ) { - return supportKeys.push( name ); - } - } - - if ( get( supports, STYLE_PROPERTY[ name ].support, false ) ) { - return supportKeys.push( name ); - } - } ); - return supportKeys; -}; - -const getBlockMetadata = ( blockTypes ) => { - const result = {}; - - blockTypes.forEach( ( blockType ) => { - const name = blockType.name; - const supports = extractSupportKeys( blockType?.supports ); - - const selector = - blockType?.supports?.__experimentalSelector ?? - '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' ); - const blockSelectors = selector.split( ',' ); - const elements = []; - Object.keys( ELEMENTS ).forEach( ( key ) => { - const elementSelector = []; - blockSelectors.forEach( ( blockSelector ) => { - elementSelector.push( blockSelector + ' ' + ELEMENTS[ key ] ); - } ); - elements[ key ] = elementSelector.join( ',' ); - } ); - result[ name ] = { - name, - selector, - supports, - elements, - }; - } ); - - return result; -}; - -function immutableSet( object, path, value ) { - return setWith( object ? clone( object ) : {}, path, value, clone ); -} - -export default function GlobalStylesProvider( { children, baseStyles } ) { - const [ content, setContent ] = useGlobalStylesEntityContent(); - const { blockTypes, settings } = useSelect( ( select ) => { - return { - blockTypes: select( blocksStore ).getBlockTypes(), - settings: select( editSiteStore ).getSettings(), - }; - }, [] ); - const { updateSettings } = useDispatch( editSiteStore ); - - const blocks = useMemo( () => getBlockMetadata( blockTypes ), [ - blockTypes, - ] ); - - const { __experimentalGlobalStylesBaseStyles: themeStyles } = settings; - const { userStyles, mergedStyles } = useMemo( () => { - let newUserStyles; - try { - newUserStyles = content ? JSON.parse( content ) : EMPTY_CONTENT; - - // At the moment, we ignore previous user config that - // is in a different version than the theme config. - if ( newUserStyles?.version !== baseStyles?.version ) { - newUserStyles = EMPTY_CONTENT; - } - } catch ( e ) { - /* eslint-disable no-console */ - console.error( 'User data is not JSON' ); - console.error( e ); - /* eslint-enable no-console */ - newUserStyles = EMPTY_CONTENT; - } - - // It is very important to verify if the flag isGlobalStylesUserThemeJSON is true. - // If it is not true the content was not escaped and is not safe. - if ( ! newUserStyles.isGlobalStylesUserThemeJSON ) { - newUserStyles = EMPTY_CONTENT; - } - - const addUserToSettings = ( settingsToAdd ) => { - PRESET_METADATA.forEach( ( { path } ) => { - const presetData = get( settingsToAdd, path ); - if ( presetData ) { - settingsToAdd = immutableSet( settingsToAdd, path, { - user: presetData, - } ); - } - } ); - return settingsToAdd; - }; - - let userStylesWithOrigin = newUserStyles; - if ( userStylesWithOrigin.settings ) { - userStylesWithOrigin = { - ...userStylesWithOrigin, - settings: addUserToSettings( userStylesWithOrigin.settings ), - }; - if ( userStylesWithOrigin.settings.blocks ) { - userStylesWithOrigin.settings = { - ...userStylesWithOrigin.settings, - blocks: mapValues( - userStylesWithOrigin.settings.blocks, - addUserToSettings - ), - }; - } - } - - // At this point, the version schema of the theme & user - // is the same, so we can merge them. - const newMergedStyles = mergeWith( - {}, - baseStyles, - userStylesWithOrigin, - mergeTreesCustomizer - ); - - return { - userStyles: newUserStyles, - mergedStyles: newMergedStyles, - }; - }, [ content ] ); - - const nextValue = useMemo( - () => ( { - root: { - name: ROOT_BLOCK_NAME, - selector: ROOT_BLOCK_SELECTOR, - supports: ROOT_BLOCK_SUPPORTS, - elements: ELEMENTS, - }, - blocks, - getSetting: ( context, propertyPath ) => { - const path = - context === ROOT_BLOCK_NAME - ? propertyPath - : [ 'blocks', context, ...propertyPath ]; - get( userStyles?.settings, path ); - }, - setSetting: ( context, propertyPath, newValue ) => { - const newContent = { ...userStyles }; - const path = - context === ROOT_BLOCK_NAME - ? [ 'settings' ] - : [ 'settings', 'blocks', context ]; - - let newSettings = get( newContent, path ); - if ( ! newSettings ) { - newSettings = {}; - set( newContent, path, newSettings ); - } - set( newSettings, propertyPath, newValue ); - - setContent( JSON.stringify( newContent ) ); - }, - getStyle: ( context, propertyName, origin = 'merged' ) => { - const propertyPath = STYLE_PROPERTY[ propertyName ].value; - const path = - context === ROOT_BLOCK_NAME - ? propertyPath - : [ 'blocks', context, ...propertyPath ]; - - if ( origin === 'theme' ) { - const value = get( themeStyles?.styles, path ); - return getValueFromVariable( - themeStyles.settings, - context, - value - ); - } - - if ( origin === 'user' ) { - const value = get( userStyles?.styles, path ); - - // We still need to use merged styles here because the - // presets used to resolve user variable may be defined a - // layer down ( core, theme, or user ). - return getValueFromVariable( - mergedStyles.settings, - context, - value - ); - } - - const value = get( mergedStyles?.styles, path ); - return getValueFromVariable( - mergedStyles.settings, - context, - value - ); - }, - setStyle: ( context, propertyName, newValue ) => { - const newContent = { ...userStyles }; - - const path = - ROOT_BLOCK_NAME === context - ? [ 'styles' ] - : [ 'styles', 'blocks', context ]; - const propertyPath = STYLE_PROPERTY[ propertyName ].value; - - let newStyles = get( newContent, path ); - if ( ! newStyles ) { - newStyles = {}; - set( newContent, path, newStyles ); - } - set( - newStyles, - propertyPath, - getPresetVariableFromValue( - mergedStyles.settings, - context, - propertyName, - newValue - ) - ); - - setContent( JSON.stringify( newContent ) ); - }, - } ), - [ content, mergedStyles, themeStyles ] - ); - - useEffect( () => { - const nonGlobalStyles = settings.styles.filter( - ( style ) => ! style.isGlobalStyles - ); - const customProperties = toCustomProperties( mergedStyles, blocks ); - const globalStyles = toStyles( mergedStyles, blocks ); - updateSettings( { - ...settings, - styles: [ - ...nonGlobalStyles, - { - css: customProperties, - isGlobalStyles: true, - __experimentalNoWrapper: true, - }, - { - css: globalStyles, - isGlobalStyles: true, - }, - ], - __experimentalFeatures: mergedStyles.settings, - } ); - }, [ blocks, mergedStyles ] ); - - return ( - - { children } - - ); -} diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js index fdec33c3fc023..2ae1508763159 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -20,7 +20,16 @@ import { import { __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, __EXPERIMENTAL_ELEMENTS as ELEMENTS, + getBlockTypes, } from '@wordpress/blocks'; +import { useEffect } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; +import { useGlobalStylesConfig } from '../global-styles/hooks'; /** * Internal dependencies @@ -311,7 +320,6 @@ export const toStyles = ( tree, blockSelectors ) => { '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }'; nodesWithStyles.forEach( ( { selector, styles } ) => { const declarations = getStylesDeclarations( styles ); - if ( declarations.length === 0 ) { return; } @@ -332,3 +340,58 @@ export const toStyles = ( tree, blockSelectors ) => { return ruleset; }; + +const getBlockSelectors = ( blockTypes ) => { + const result = {}; + blockTypes.forEach( ( blockType ) => { + const name = blockType.name; + const selector = + blockType?.supports?.__experimentalSelector ?? + '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' ); + result[ name ] = { + name, + selector, + }; + } ); + + return result; +}; + +export function useGlobalStylesRenderer() { + const [ , , mergedConfig ] = useGlobalStylesConfig(); + const { getSettings } = useSelect( editSiteStore ); + const { updateSettings } = useDispatch( editSiteStore ); + + useEffect( () => { + if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) { + return; + } + + const currentStoreSettings = getSettings(); + const nonGlobalStyles = currentStoreSettings?.styles?.filter( + ( style ) => ! style.isGlobalStyles + ); + const blockSelectors = getBlockSelectors( getBlockTypes() ); + const customProperties = toCustomProperties( + mergedConfig, + blockSelectors + ); + const globalStyles = toStyles( mergedConfig, blockSelectors ); + updateSettings( { + ...currentStoreSettings, + styles: [ + ...nonGlobalStyles, + { + css: customProperties, + isGlobalStyles: true, + __experimentalNoWrapper: true, + }, + { + css: globalStyles, + isGlobalStyles: true, + }, + ], + __experimentalFeatures: mergedConfig.settings, + } ); + }, [ mergedConfig ] ); +} diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index c2358904049d9..525753c34f7bd 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -35,13 +35,13 @@ import Header from '../header'; import { SidebarComplementaryAreaFills } from '../sidebar'; import BlockEditor from '../block-editor'; import KeyboardShortcuts from '../keyboard-shortcuts'; -import GlobalStylesProvider from './global-styles-provider'; import NavigationSidebar from '../navigation-sidebar'; import URLQueryController from '../url-query-controller'; import InserterSidebar from '../secondary-sidebar/inserter-sidebar'; import ListViewSidebar from '../secondary-sidebar/list-view-sidebar'; import ErrorBoundary from '../error-boundary'; import { store as editSiteStore } from '../../store'; +import { useGlobalStylesRenderer } from './global-styles-renderer'; const interfaceLabels = { secondarySidebar: __( 'Block Library' ), @@ -159,6 +159,8 @@ function Editor( { initialSettings, onError } ) { } }, [ isNavigationOpen ] ); + useGlobalStylesRenderer(); + // Don't render the Editor until the settings are set and loaded if ( ! settings?.siteUrl ) { return null; @@ -184,105 +186,87 @@ function Editor( { initialSettings, onError } ) { type={ templateType } id={ entityId } > - - - + + + + + + } + secondarySidebar={ secondarySidebar() } + sidebar={ + sidebarIsOpened && ( + + ) } - > - - - - - - } - secondarySidebar={ secondarySidebar() } - sidebar={ - sidebarIsOpened && ( - - ) + header={ +
+ } + notices={ } + content={ + <> + + { template && ( + - } - notices={ } - content={ - <> - - { template && ( - - ) } - { templateResolved && - ! template && - settings?.siteUrl && - entityId && ( - - { __( - "You attempted to edit an item that doesn't exist. Perhaps it was deleted?" - ) } - + ) } + { templateResolved && + ! template && + settings?.siteUrl && + entityId && ( + + { __( + "You attempted to edit an item that doesn't exist. Perhaps it was deleted?" ) } - - - } - actions={ - <> - { isEntitiesSavedStatesOpen ? ( - - ) : ( -
- -
- ) } - - } - footer={ } - /> - - - - - - +
+ ) } + + + } + actions={ + <> + { isEntitiesSavedStatesOpen ? ( + + ) : ( +
+ +
+ ) } + + } + footer={ } + /> + + + + diff --git a/packages/edit-site/src/components/editor/test/global-styles-provider.js b/packages/edit-site/src/components/editor/test/global-styles-provider.js deleted file mode 100644 index 0a31516576e57..0000000000000 --- a/packages/edit-site/src/components/editor/test/global-styles-provider.js +++ /dev/null @@ -1,131 +0,0 @@ -/** - * WordPress dependencies - */ -import { dispatch } from '@wordpress/data'; - -/** - * External dependencies - */ -import { mount } from 'enzyme'; -import { act } from 'react-dom/test-utils'; - -/** - * Internal dependencies - */ -import GlobalStylesProvider, { - useGlobalStylesContext, -} from '../global-styles-provider'; - -const settings = { - styles: [ - { - css: 'body {\n\tmargin: 0;\n\tpadding: 0;\n}', - baseURL: 'http://localhost:4759/ponyfill.css', - }, - ], - __experimentalGlobalStylesBaseStyles: {}, -}; - -const generateCoverBlockType = ( colorSupports ) => { - return { - name: 'core/cover', - supports: { - color: colorSupports, - }, - }; -}; - -const FakeCmp = () => { - const globalStylesContext = useGlobalStylesContext(); - const coverBlockSupports = - globalStylesContext.blocks[ 'core/cover' ].supports; - - return
; -}; - -const generateWrapper = () => { - return mount( - - - - ); -}; - -describe( 'global styles provider', () => { - beforeAll( () => { - dispatch( 'core/edit-site' ).updateSettings( settings ); - } ); - - describe( 'when a block enables color support', () => { - describe( 'and disables background color support', () => { - it( 'still enables text color support', () => { - act( () => { - dispatch( 'core/blocks' ).addBlockTypes( - generateCoverBlockType( { - link: true, - background: false, - } ) - ); - } ); - - const wrapper = generateWrapper(); - const actual = wrapper - .findWhere( ( ele ) => Boolean( ele.prop( 'supports' ) ) ) - .prop( 'supports' ); - expect( actual ).not.toContain( 'backgroundColor' ); - expect( actual ).toContain( 'color' ); - - act( () => { - dispatch( 'core/blocks' ).removeBlockTypes( 'core/cover' ); - } ); - } ); - } ); - - describe( 'and both text color and background color support are disabled', () => { - it( 'disables text color and background color support', () => { - act( () => { - dispatch( 'core/blocks' ).addBlockTypes( - generateCoverBlockType( { - text: false, - background: false, - } ) - ); - } ); - - const wrapper = generateWrapper(); - const actual = wrapper - .findWhere( ( ele ) => Boolean( ele.prop( 'supports' ) ) ) - .prop( 'supports' ); - expect( actual ).not.toContain( 'backgroundColor' ); - expect( actual ).not.toContain( 'color' ); - - act( () => { - dispatch( 'core/blocks' ).removeBlockTypes( 'core/cover' ); - } ); - } ); - } ); - - describe( 'and text color and background color supports are omitted', () => { - it( 'still enables both text color and background color supports', () => { - act( () => { - dispatch( 'core/blocks' ).addBlockTypes( - generateCoverBlockType( { link: true } ) - ); - } ); - - const wrapper = generateWrapper(); - const actual = wrapper - .findWhere( ( ele ) => Boolean( ele.prop( 'supports' ) ) ) - .prop( 'supports' ); - expect( actual ).toContain( 'backgroundColor' ); - expect( actual ).toContain( 'color' ); - - act( () => { - dispatch( 'core/blocks' ).removeBlockTypes( 'core/cover' ); - } ); - } ); - } ); - } ); -} ); diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index 4cc9ef202ebae..71c70c233089c 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -141,11 +141,14 @@ function useGlobalStylesBaseConfig() { return baseConfig; } -function useGlobalStylesConfig() { +export function useGlobalStylesConfig() { const [ userConfig, setUserConfig ] = useGlobalStylesUserConfig(); const baseConfig = useGlobalStylesBaseConfig(); + const mergedConfig = useMemo( () => { + return mergeBaseAndUserConfigs( baseConfig, userConfig ); + }, [ userConfig, baseConfig ] ); - return [ baseConfig, userConfig, setUserConfig ]; + return [ baseConfig, userConfig, mergedConfig, setUserConfig ]; } export const useGlobalStylesReset = () => { @@ -158,13 +161,18 @@ export const useGlobalStylesReset = () => { }; export function useSetting( path, blockName, source = 'all' ) { - const [ baseConfig, userConfig, setUserConfig ] = useGlobalStylesConfig(); + const [ + baseConfig, + userConfig, + mergedConfig, + setUserConfig, + ] = useGlobalStylesConfig(); const finalPath = ! blockName ? `settings.${ path }` : `settings.blocks.${ blockName }.${ path }`; - const getBaseSetting = () => { - const result = get( baseConfig, finalPath ); + const getSettingValue = ( configToUse ) => { + const result = get( configToUse, finalPath ); if ( PATHS_WITH_MERGE[ path ] ) { return result.theme ?? result.core; } @@ -182,13 +190,13 @@ export function useSetting( path, blockName, source = 'all' ) { let result; switch ( source ) { case 'all': - result = get( userConfig, finalPath, {} ).user ?? getBaseSetting(); + result = getSettingValue( mergedConfig ); break; case 'user': - result = get( userConfig, finalPath, {} ).user; + result = getSettingValue( userConfig ); break; case 'base': - result = getBaseSetting(); + result = getSettingValue( baseConfig ); break; default: throw 'Unsupported source'; @@ -198,8 +206,12 @@ export function useSetting( path, blockName, source = 'all' ) { } export function useStyle( path, blockName, source = 'all' ) { - const [ baseConfig, userConfig, setUserConfig ] = useGlobalStylesConfig(); - const mergedConfig = mergeBaseAndUserConfigs( baseConfig, userConfig ); + const [ + baseConfig, + userConfig, + mergedConfig, + setUserConfig, + ] = useGlobalStylesConfig(); const finalPath = ! blockName ? `styles.${ path }` : `styles.blocks.${ blockName }.${ path }`; diff --git a/packages/edit-site/src/components/global-styles/index.js b/packages/edit-site/src/components/global-styles/index.js index e062747a8d211..9d4ba618e21b0 100644 --- a/packages/edit-site/src/components/global-styles/index.js +++ b/packages/edit-site/src/components/global-styles/index.js @@ -1,8 +1,3 @@ -/** - * External dependencies - */ -import { map } from 'lodash'; - /** * WordPress dependencies */ @@ -10,11 +5,11 @@ import { __experimentalNavigatorProvider as NavigatorProvider, __experimentalNavigatorScreen as NavigatorScreen, } from '@wordpress/components'; +import { getBlockTypes } from '@wordpress/blocks'; /** * Internal dependencies */ -import { useGlobalStylesContext } from '../editor/global-styles-provider'; import ScreenRoot from './screen-root'; import ScreenBlockList from './screen-block-list'; import ScreenBlock from './screen-block'; @@ -48,7 +43,7 @@ function ContextScreens( { name } ) { } function GlobalStyles() { - const { blocks } = useGlobalStylesContext(); + const blocks = getBlockTypes(); return ( @@ -60,19 +55,22 @@ function GlobalStyles() { - { map( blocks, ( block, name ) => ( + { blocks.map( ( block ) => ( - + ) ) } - { map( blocks, ( _, name ) => ( - + { blocks.map( ( block ) => ( + ) ) } ); From 8e1bef0d642549733654755638c286ad8f8817d6 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Mon, 4 Oct 2021 12:40:44 +0100 Subject: [PATCH 06/15] Remove old useSetting --- .../edit-site/src/components/editor/utils.js | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/editor/utils.js index 099e0d4a87c40..277758e9c5af4 100644 --- a/packages/edit-site/src/components/editor/utils.js +++ b/packages/edit-site/src/components/editor/utils.js @@ -2,15 +2,6 @@ * External dependencies */ import { get, find, isString } from 'lodash'; -/** - * WordPress dependencies - */ -import { useSelect } from '@wordpress/data'; -import { __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE } from '@wordpress/blocks'; -/** - * Internal dependencies - */ -import { store as editSiteStore } from '../../store'; /* Supporting data */ export const ROOT_BLOCK_NAME = 'root'; @@ -81,19 +72,6 @@ const STYLE_PATH_TO_CSS_VAR_INFIX = { 'typography.fontFamily': 'font-family', }; -export function useSetting( path, blockName = '' ) { - const settings = useSelect( ( select ) => { - return select( editSiteStore ).getSettings(); - } ); - const topLevelPath = `__experimentalFeatures.${ path }`; - const blockPath = `__experimentalFeatures.blocks.${ blockName }.${ path }`; - const result = get( settings, blockPath ) ?? get( settings, topLevelPath ); - if ( result && PATHS_WITH_MERGE[ path ] ) { - return result.user ?? result.theme ?? result.core; - } - return result; -} - function findInPresetsBy( features, blockName, From 0c6fbcf764338a3ca7b370a060dae53bd7d4da7a Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 5 Oct 2021 08:33:35 +0100 Subject: [PATCH 07/15] Fix block global styles --- .../src/components/global-styles/hooks.js | 83 ++++++++++++------- 1 file changed, 51 insertions(+), 32 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index 71c70c233089c..b39c3cd1d44a2 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -167,42 +167,55 @@ export function useSetting( path, blockName, source = 'all' ) { mergedConfig, setUserConfig, ] = useGlobalStylesConfig(); - const finalPath = ! blockName + + const fullPath = ! blockName ? `settings.${ path }` : `settings.blocks.${ blockName }.${ path }`; - const getSettingValue = ( configToUse ) => { - const result = get( configToUse, finalPath ); - if ( PATHS_WITH_MERGE[ path ] ) { - return result.theme ?? result.core; - } - return result; - }; - const setSetting = ( newValue ) => { setUserConfig( ( currentConfig ) => { const newUserConfig = cloneDeep( currentConfig ); - set( newUserConfig, finalPath, newValue ); + set( newUserConfig, fullPath, newValue ); return newUserConfig; } ); }; - let result; - switch ( source ) { - case 'all': - result = getSettingValue( mergedConfig ); - break; - case 'user': - result = getSettingValue( userConfig ); - break; - case 'base': - result = getSettingValue( baseConfig ); - break; - default: - throw 'Unsupported source'; - } + const getSettingValueForContext = ( name ) => { + const currentPath = ! name + ? `settings.${ path }` + : `settings.blocks.${ name }.${ path }`; - return [ result, setSetting ]; + const getSettingValue = ( configToUse ) => { + const result = get( configToUse, currentPath ); + if ( PATHS_WITH_MERGE[ path ] ) { + return result?.theme ?? result?.core; + } + return result; + }; + + let result; + switch ( source ) { + case 'all': + result = getSettingValue( mergedConfig ); + break; + case 'user': + result = getSettingValue( userConfig ); + break; + case 'base': + result = getSettingValue( baseConfig ); + break; + default: + throw 'Unsupported source'; + } + + return result; + }; + + // Unlike styles settings get inherited from top level settings. + const resultWithFallback = + getSettingValueForContext( blockName ) ?? getSettingValueForContext(); + + return [ resultWithFallback, setSetting ]; } export function useStyle( path, blockName, source = 'all' ) { @@ -298,23 +311,29 @@ export function getSupportedGlobalStylesPanels( name ) { // Opting out means that, for certain support keys like background color, // blocks have to explicitly set the support value false. If the key is // unset, we still enable it. - if ( STYLE_PROPERTY[ name ].requiresOptOut ) { + if ( STYLE_PROPERTY[ styleName ].requiresOptOut ) { if ( has( blockType.supports, - STYLE_PROPERTY[ name ].support[ 0 ] + STYLE_PROPERTY[ styleName ].support[ 0 ] ) && - get( blockType.supports, STYLE_PROPERTY[ name ].support ) !== - false + get( + blockType.supports, + STYLE_PROPERTY[ styleName ].support + ) !== false ) { - return supportKeys.push( name ); + return supportKeys.push( styleName ); } } if ( - get( blockType.supports, STYLE_PROPERTY[ name ].support, false ) + get( + blockType.supports, + STYLE_PROPERTY[ styleName ].support, + false + ) ) { - return supportKeys.push( name ); + return supportKeys.push( styleName ); } } ); From 6e11eb57bd03d281c310a407ba5291c008753d81 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 5 Oct 2021 08:54:03 +0100 Subject: [PATCH 08/15] Move all the global styles logic to the global styles folder --- .../editor/global-styles-renderer.js | 372 +---------------- .../src/components/global-styles/hooks.js | 2 +- .../test/use-global-styles-output.js} | 2 +- .../{editor => global-styles}/test/utils.js | 0 .../global-styles/use-global-styles-output.js | 389 ++++++++++++++++++ .../{editor => global-styles}/utils.js | 0 6 files changed, 397 insertions(+), 368 deletions(-) rename packages/edit-site/src/components/{editor/test/global-styles-renderer.js => global-styles/test/use-global-styles-output.js} (99%) rename packages/edit-site/src/components/{editor => global-styles}/test/utils.js (100%) create mode 100644 packages/edit-site/src/components/global-styles/use-global-styles-output.js rename packages/edit-site/src/components/{editor => global-styles}/utils.js (100%) diff --git a/packages/edit-site/src/components/editor/global-styles-renderer.js b/packages/edit-site/src/components/editor/global-styles-renderer.js index 2ae1508763159..27988d56a60e6 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -1,27 +1,6 @@ -/** - * External dependencies - */ -import { - first, - forEach, - get, - isEmpty, - isString, - kebabCase, - pickBy, - reduce, - set, - startsWith, -} from 'lodash'; - /** * WordPress dependencies */ -import { - __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, - __EXPERIMENTAL_ELEMENTS as ELEMENTS, - getBlockTypes, -} from '@wordpress/blocks'; import { useEffect } from '@wordpress/element'; import { useSelect, useDispatch } from '@wordpress/data'; @@ -29,341 +8,19 @@ import { useSelect, useDispatch } from '@wordpress/data'; * Internal dependencies */ import { store as editSiteStore } from '../../store'; -import { useGlobalStylesConfig } from '../global-styles/hooks'; /** * Internal dependencies */ -import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils'; - -function compileStyleValue( uncompiledValue ) { - const VARIABLE_REFERENCE_PREFIX = 'var:'; - const VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE = '|'; - const VARIABLE_PATH_SEPARATOR_TOKEN_STYLE = '--'; - if ( startsWith( uncompiledValue, VARIABLE_REFERENCE_PREFIX ) ) { - const variable = uncompiledValue - .slice( VARIABLE_REFERENCE_PREFIX.length ) - .split( VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE ) - .join( VARIABLE_PATH_SEPARATOR_TOKEN_STYLE ); - return `var(--wp--${ variable })`; - } - return uncompiledValue; -} - -/** - * Transform given preset tree into a set of style declarations. - * - * @param {Object} blockPresets - * - * @return {Array} An array of style declarations. - */ -function getPresetsDeclarations( blockPresets = {} ) { - return reduce( - PRESET_METADATA, - ( declarations, { path, valueKey, cssVarInfix } ) => { - const presetByOrigin = get( blockPresets, path, [] ); - [ 'core', 'theme', 'user' ].forEach( ( origin ) => { - if ( presetByOrigin[ origin ] ) { - presetByOrigin[ origin ].forEach( ( value ) => { - declarations.push( - `--wp--preset--${ cssVarInfix }--${ kebabCase( - value.slug - ) }: ${ value[ valueKey ] }` - ); - } ); - } - } ); - - return declarations; - }, - [] - ); -} - -/** - * Transform given preset tree into a set of preset class declarations. - * - * @param {string} blockSelector - * @param {Object} blockPresets - * @return {string} CSS declarations for the preset classes. - */ -function getPresetsClasses( blockSelector, blockPresets = {} ) { - return reduce( - PRESET_METADATA, - ( declarations, { path, cssVarInfix, classes } ) => { - if ( ! classes ) { - return declarations; - } - - const presetByOrigin = get( blockPresets, path, [] ); - [ 'core', 'theme', 'user' ].forEach( ( origin ) => { - if ( presetByOrigin[ origin ] ) { - presetByOrigin[ origin ].forEach( ( { slug } ) => { - classes.forEach( ( { classSuffix, propertyName } ) => { - const classSelectorToUse = `.has-${ kebabCase( - slug - ) }-${ classSuffix }`; - const selectorToUse = blockSelector - .split( ',' ) // Selector can be "h1, h2, h3" - .map( - ( selector ) => - `${ selector }${ classSelectorToUse }` - ) - .join( ',' ); - const value = `var(--wp--preset--${ cssVarInfix }--${ kebabCase( - slug - ) })`; - declarations += `${ selectorToUse }{${ propertyName }: ${ value } !important;}`; - } ); - } ); - } - } ); - return declarations; - }, - '' - ); -} - -function flattenTree( input = {}, prefix, token ) { - let result = []; - Object.keys( input ).forEach( ( key ) => { - const newKey = prefix + kebabCase( key.replace( '/', '-' ) ); - const newLeaf = input[ key ]; - - if ( newLeaf instanceof Object ) { - const newPrefix = newKey + token; - result = [ ...result, ...flattenTree( newLeaf, newPrefix, token ) ]; - } else { - result.push( `${ newKey }: ${ newLeaf }` ); - } - } ); - return result; -} - -/** - * Transform given style tree into a set of style declarations. - * - * @param {Object} blockStyles Block styles. - * - * @return {Array} An array of style declarations. - */ -function getStylesDeclarations( blockStyles = {} ) { - return reduce( - STYLE_PROPERTY, - ( declarations, { value, properties }, key ) => { - const pathToValue = value; - if ( first( pathToValue ) === 'elements' ) { - return declarations; - } - - const styleValue = get( blockStyles, pathToValue ); - - if ( !! properties && ! isString( styleValue ) ) { - Object.entries( properties ).forEach( ( entry ) => { - const [ name, prop ] = entry; - - if ( ! get( styleValue, [ prop ], false ) ) { - // Do not create a declaration - // for sub-properties that don't have any value. - return; - } - - const cssProperty = kebabCase( name ); - declarations.push( - `${ cssProperty }: ${ compileStyleValue( - get( styleValue, [ prop ] ) - ) }` - ); - } ); - } else if ( get( blockStyles, pathToValue, false ) ) { - const cssProperty = key.startsWith( '--' ) - ? key - : kebabCase( key ); - declarations.push( - `${ cssProperty }: ${ compileStyleValue( - get( blockStyles, pathToValue ) - ) }` - ); - } - - return declarations; - }, - [] - ); -} - -export const getNodesWithStyles = ( tree, blockSelectors ) => { - const nodes = []; - - if ( ! tree?.styles ) { - return nodes; - } - - const pickStyleKeys = ( treeToPickFrom ) => - pickBy( treeToPickFrom, ( value, key ) => - [ 'border', 'color', 'spacing', 'typography' ].includes( key ) - ); - - // Top-level. - const styles = pickStyleKeys( tree.styles ); - if ( !! styles ) { - nodes.push( { - styles, - selector: ROOT_BLOCK_SELECTOR, - } ); - } - forEach( tree.styles?.elements, ( value, key ) => { - if ( !! value && !! ELEMENTS[ key ] ) { - nodes.push( { - styles: value, - selector: ELEMENTS[ key ], - } ); - } - } ); - - // Iterate over blocks: they can have styles & elements. - forEach( tree.styles?.blocks, ( node, blockName ) => { - const blockStyles = pickStyleKeys( node ); - if ( !! blockStyles && !! blockSelectors?.[ blockName ]?.selector ) { - nodes.push( { - styles: blockStyles, - selector: blockSelectors[ blockName ].selector, - } ); - } - - forEach( node?.elements, ( value, elementName ) => { - if ( - !! value && - !! blockSelectors?.[ blockName ]?.elements?.[ elementName ] - ) { - nodes.push( { - styles: value, - selector: - blockSelectors[ blockName ].elements[ elementName ], - } ); - } - } ); - } ); - - return nodes; -}; - -export const getNodesWithSettings = ( tree, blockSelectors ) => { - const nodes = []; - - if ( ! tree?.settings ) { - return nodes; - } - - const pickPresets = ( treeToPickFrom ) => { - const presets = {}; - PRESET_METADATA.forEach( ( { path } ) => { - const value = get( treeToPickFrom, path, false ); - if ( value !== false ) { - set( presets, path, value ); - } - } ); - return presets; - }; - - // Top-level. - const presets = pickPresets( tree.settings ); - const custom = tree.settings?.custom; - if ( ! isEmpty( presets ) || !! custom ) { - nodes.push( { - presets, - custom, - selector: ROOT_BLOCK_SELECTOR, - } ); - } - - // Blocks. - forEach( tree.settings?.blocks, ( node, blockName ) => { - const blockPresets = pickPresets( node ); - const blockCustom = node.custom; - if ( ! isEmpty( blockPresets ) || !! blockCustom ) { - nodes.push( { - presets: blockPresets, - custom: blockCustom, - selector: blockSelectors[ blockName ].selector, - } ); - } - } ); - - return nodes; -}; - -export const toCustomProperties = ( tree, blockSelectors ) => { - const settings = getNodesWithSettings( tree, blockSelectors ); - - let ruleset = ''; - settings.forEach( ( { presets, custom, selector } ) => { - const declarations = getPresetsDeclarations( presets ); - const customProps = flattenTree( custom, '--wp--custom--', '--' ); - if ( customProps.length > 0 ) { - declarations.push( ...customProps ); - } - - if ( declarations.length > 0 ) { - ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`; - } - } ); - - return ruleset; -}; - -export const toStyles = ( tree, blockSelectors ) => { - const nodesWithStyles = getNodesWithStyles( tree, blockSelectors ); - const nodesWithSettings = getNodesWithSettings( tree, blockSelectors ); - - let ruleset = - '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }'; - nodesWithStyles.forEach( ( { selector, styles } ) => { - const declarations = getStylesDeclarations( styles ); - if ( declarations.length === 0 ) { - return; - } - ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`; - } ); - - nodesWithSettings.forEach( ( { selector, presets } ) => { - if ( ROOT_BLOCK_SELECTOR === selector ) { - // Do not add extra specificity for top-level classes. - selector = ''; - } - - const classes = getPresetsClasses( selector, presets ); - if ( ! isEmpty( classes ) ) { - ruleset = ruleset + classes; - } - } ); - - return ruleset; -}; - -const getBlockSelectors = ( blockTypes ) => { - const result = {}; - blockTypes.forEach( ( blockType ) => { - const name = blockType.name; - const selector = - blockType?.supports?.__experimentalSelector ?? - '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' ); - result[ name ] = { - name, - selector, - }; - } ); - - return result; -}; +import { useGlobalStylesOutput } from '../global-styles/use-global-styles-output'; export function useGlobalStylesRenderer() { - const [ , , mergedConfig ] = useGlobalStylesConfig(); + const [ styles, settings ] = useGlobalStylesOutput(); const { getSettings } = useSelect( editSiteStore ); const { updateSettings } = useDispatch( editSiteStore ); useEffect( () => { - if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) { + if ( ! styles || ! settings ) { return; } @@ -371,27 +28,10 @@ export function useGlobalStylesRenderer() { const nonGlobalStyles = currentStoreSettings?.styles?.filter( ( style ) => ! style.isGlobalStyles ); - const blockSelectors = getBlockSelectors( getBlockTypes() ); - const customProperties = toCustomProperties( - mergedConfig, - blockSelectors - ); - const globalStyles = toStyles( mergedConfig, blockSelectors ); updateSettings( { ...currentStoreSettings, - styles: [ - ...nonGlobalStyles, - { - css: customProperties, - isGlobalStyles: true, - __experimentalNoWrapper: true, - }, - { - css: globalStyles, - isGlobalStyles: true, - }, - ], - __experimentalFeatures: mergedConfig.settings, + styles: [ ...nonGlobalStyles, ...styles ], + __experimentalFeatures: settings, } ); - }, [ mergedConfig ] ); + }, [ styles, settings ] ); } diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index b39c3cd1d44a2..ea89b24423d16 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -23,7 +23,7 @@ import { PRESET_METADATA, getValueFromVariable, getPresetVariableFromValue, -} from '../editor/utils'; +} from './utils'; const EMPTY_CONFIG = { isGlobalStylesUserThemeJSON: true, version: 1 }; diff --git a/packages/edit-site/src/components/editor/test/global-styles-renderer.js b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js similarity index 99% rename from packages/edit-site/src/components/editor/test/global-styles-renderer.js rename to packages/edit-site/src/components/global-styles/test/use-global-styles-output.js index 24e862da6ee20..68555167ed72e 100644 --- a/packages/edit-site/src/components/editor/test/global-styles-renderer.js +++ b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js @@ -11,7 +11,7 @@ import { getNodesWithStyles, toCustomProperties, toStyles, -} from '../global-styles-renderer'; +} from '../use-global-styles-output'; import { ROOT_BLOCK_SELECTOR } from '../utils'; describe( 'global styles renderer', () => { diff --git a/packages/edit-site/src/components/editor/test/utils.js b/packages/edit-site/src/components/global-styles/test/utils.js similarity index 100% rename from packages/edit-site/src/components/editor/test/utils.js rename to packages/edit-site/src/components/global-styles/test/utils.js diff --git a/packages/edit-site/src/components/global-styles/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/use-global-styles-output.js new file mode 100644 index 0000000000000..88c14c85593b8 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/use-global-styles-output.js @@ -0,0 +1,389 @@ +/** + * External dependencies + */ +import { + first, + forEach, + get, + isEmpty, + isString, + kebabCase, + pickBy, + reduce, + set, + startsWith, +} from 'lodash'; + +/** + * WordPress dependencies + */ +import { + __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, + __EXPERIMENTAL_ELEMENTS as ELEMENTS, + getBlockTypes, +} from '@wordpress/blocks'; +import { useEffect, useState } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { useGlobalStylesConfig } from './hooks'; + +/** + * Internal dependencies + */ +import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils'; + +function compileStyleValue( uncompiledValue ) { + const VARIABLE_REFERENCE_PREFIX = 'var:'; + const VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE = '|'; + const VARIABLE_PATH_SEPARATOR_TOKEN_STYLE = '--'; + if ( startsWith( uncompiledValue, VARIABLE_REFERENCE_PREFIX ) ) { + const variable = uncompiledValue + .slice( VARIABLE_REFERENCE_PREFIX.length ) + .split( VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE ) + .join( VARIABLE_PATH_SEPARATOR_TOKEN_STYLE ); + return `var(--wp--${ variable })`; + } + return uncompiledValue; +} + +/** + * Transform given preset tree into a set of style declarations. + * + * @param {Object} blockPresets + * + * @return {Array} An array of style declarations. + */ +function getPresetsDeclarations( blockPresets = {} ) { + return reduce( + PRESET_METADATA, + ( declarations, { path, valueKey, cssVarInfix } ) => { + const presetByOrigin = get( blockPresets, path, [] ); + [ 'core', 'theme', 'user' ].forEach( ( origin ) => { + if ( presetByOrigin[ origin ] ) { + presetByOrigin[ origin ].forEach( ( value ) => { + declarations.push( + `--wp--preset--${ cssVarInfix }--${ kebabCase( + value.slug + ) }: ${ value[ valueKey ] }` + ); + } ); + } + } ); + + return declarations; + }, + [] + ); +} + +/** + * Transform given preset tree into a set of preset class declarations. + * + * @param {string} blockSelector + * @param {Object} blockPresets + * @return {string} CSS declarations for the preset classes. + */ +function getPresetsClasses( blockSelector, blockPresets = {} ) { + return reduce( + PRESET_METADATA, + ( declarations, { path, cssVarInfix, classes } ) => { + if ( ! classes ) { + return declarations; + } + + const presetByOrigin = get( blockPresets, path, [] ); + [ 'core', 'theme', 'user' ].forEach( ( origin ) => { + if ( presetByOrigin[ origin ] ) { + presetByOrigin[ origin ].forEach( ( { slug } ) => { + classes.forEach( ( { classSuffix, propertyName } ) => { + const classSelectorToUse = `.has-${ kebabCase( + slug + ) }-${ classSuffix }`; + const selectorToUse = blockSelector + .split( ',' ) // Selector can be "h1, h2, h3" + .map( + ( selector ) => + `${ selector }${ classSelectorToUse }` + ) + .join( ',' ); + const value = `var(--wp--preset--${ cssVarInfix }--${ kebabCase( + slug + ) })`; + declarations += `${ selectorToUse }{${ propertyName }: ${ value } !important;}`; + } ); + } ); + } + } ); + return declarations; + }, + '' + ); +} + +function flattenTree( input = {}, prefix, token ) { + let result = []; + Object.keys( input ).forEach( ( key ) => { + const newKey = prefix + kebabCase( key.replace( '/', '-' ) ); + const newLeaf = input[ key ]; + + if ( newLeaf instanceof Object ) { + const newPrefix = newKey + token; + result = [ ...result, ...flattenTree( newLeaf, newPrefix, token ) ]; + } else { + result.push( `${ newKey }: ${ newLeaf }` ); + } + } ); + return result; +} + +/** + * Transform given style tree into a set of style declarations. + * + * @param {Object} blockStyles Block styles. + * + * @return {Array} An array of style declarations. + */ +function getStylesDeclarations( blockStyles = {} ) { + return reduce( + STYLE_PROPERTY, + ( declarations, { value, properties }, key ) => { + const pathToValue = value; + if ( first( pathToValue ) === 'elements' ) { + return declarations; + } + + const styleValue = get( blockStyles, pathToValue ); + + if ( !! properties && ! isString( styleValue ) ) { + Object.entries( properties ).forEach( ( entry ) => { + const [ name, prop ] = entry; + + if ( ! get( styleValue, [ prop ], false ) ) { + // Do not create a declaration + // for sub-properties that don't have any value. + return; + } + + const cssProperty = kebabCase( name ); + declarations.push( + `${ cssProperty }: ${ compileStyleValue( + get( styleValue, [ prop ] ) + ) }` + ); + } ); + } else if ( get( blockStyles, pathToValue, false ) ) { + const cssProperty = key.startsWith( '--' ) + ? key + : kebabCase( key ); + declarations.push( + `${ cssProperty }: ${ compileStyleValue( + get( blockStyles, pathToValue ) + ) }` + ); + } + + return declarations; + }, + [] + ); +} + +export const getNodesWithStyles = ( tree, blockSelectors ) => { + const nodes = []; + + if ( ! tree?.styles ) { + return nodes; + } + + const pickStyleKeys = ( treeToPickFrom ) => + pickBy( treeToPickFrom, ( value, key ) => + [ 'border', 'color', 'spacing', 'typography' ].includes( key ) + ); + + // Top-level. + const styles = pickStyleKeys( tree.styles ); + if ( !! styles ) { + nodes.push( { + styles, + selector: ROOT_BLOCK_SELECTOR, + } ); + } + forEach( tree.styles?.elements, ( value, key ) => { + if ( !! value && !! ELEMENTS[ key ] ) { + nodes.push( { + styles: value, + selector: ELEMENTS[ key ], + } ); + } + } ); + + // Iterate over blocks: they can have styles & elements. + forEach( tree.styles?.blocks, ( node, blockName ) => { + const blockStyles = pickStyleKeys( node ); + if ( !! blockStyles && !! blockSelectors?.[ blockName ]?.selector ) { + nodes.push( { + styles: blockStyles, + selector: blockSelectors[ blockName ].selector, + } ); + } + + forEach( node?.elements, ( value, elementName ) => { + if ( + !! value && + !! blockSelectors?.[ blockName ]?.elements?.[ elementName ] + ) { + nodes.push( { + styles: value, + selector: + blockSelectors[ blockName ].elements[ elementName ], + } ); + } + } ); + } ); + + return nodes; +}; + +export const getNodesWithSettings = ( tree, blockSelectors ) => { + const nodes = []; + + if ( ! tree?.settings ) { + return nodes; + } + + const pickPresets = ( treeToPickFrom ) => { + const presets = {}; + PRESET_METADATA.forEach( ( { path } ) => { + const value = get( treeToPickFrom, path, false ); + if ( value !== false ) { + set( presets, path, value ); + } + } ); + return presets; + }; + + // Top-level. + const presets = pickPresets( tree.settings ); + const custom = tree.settings?.custom; + if ( ! isEmpty( presets ) || !! custom ) { + nodes.push( { + presets, + custom, + selector: ROOT_BLOCK_SELECTOR, + } ); + } + + // Blocks. + forEach( tree.settings?.blocks, ( node, blockName ) => { + const blockPresets = pickPresets( node ); + const blockCustom = node.custom; + if ( ! isEmpty( blockPresets ) || !! blockCustom ) { + nodes.push( { + presets: blockPresets, + custom: blockCustom, + selector: blockSelectors[ blockName ].selector, + } ); + } + } ); + + return nodes; +}; + +export const toCustomProperties = ( tree, blockSelectors ) => { + const settings = getNodesWithSettings( tree, blockSelectors ); + + let ruleset = ''; + settings.forEach( ( { presets, custom, selector } ) => { + const declarations = getPresetsDeclarations( presets ); + const customProps = flattenTree( custom, '--wp--custom--', '--' ); + if ( customProps.length > 0 ) { + declarations.push( ...customProps ); + } + + if ( declarations.length > 0 ) { + ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`; + } + } ); + + return ruleset; +}; + +export const toStyles = ( tree, blockSelectors ) => { + const nodesWithStyles = getNodesWithStyles( tree, blockSelectors ); + const nodesWithSettings = getNodesWithSettings( tree, blockSelectors ); + + let ruleset = + '.wp-site-blocks > * + * { margin-top: var( --wp--style--block-gap ); margin-bottom: 0; }'; + nodesWithStyles.forEach( ( { selector, styles } ) => { + const declarations = getStylesDeclarations( styles ); + if ( declarations.length === 0 ) { + return; + } + ruleset = ruleset + `${ selector }{${ declarations.join( ';' ) };}`; + } ); + + nodesWithSettings.forEach( ( { selector, presets } ) => { + if ( ROOT_BLOCK_SELECTOR === selector ) { + // Do not add extra specificity for top-level classes. + selector = ''; + } + + const classes = getPresetsClasses( selector, presets ); + if ( ! isEmpty( classes ) ) { + ruleset = ruleset + classes; + } + } ); + + return ruleset; +}; + +const getBlockSelectors = ( blockTypes ) => { + const result = {}; + blockTypes.forEach( ( blockType ) => { + const name = blockType.name; + const selector = + blockType?.supports?.__experimentalSelector ?? + '.wp-block-' + name.replace( 'core/', '' ).replace( '/', '-' ); + result[ name ] = { + name, + selector, + }; + } ); + + return result; +}; + +export function useGlobalStylesOutput() { + const [ stylesheets, setStylesheets ] = useState( [] ); + const [ settings, setSettings ] = useState( {} ); + const [ , , mergedConfig ] = useGlobalStylesConfig(); + + useEffect( () => { + if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) { + return; + } + + const blockSelectors = getBlockSelectors( getBlockTypes() ); + const customProperties = toCustomProperties( + mergedConfig, + blockSelectors + ); + const globalStyles = toStyles( mergedConfig, blockSelectors ); + setStylesheets( [ + { + css: customProperties, + isGlobalStyles: true, + __experimentalNoWrapper: true, + }, + { + css: globalStyles, + isGlobalStyles: true, + }, + ] ); + setSettings( mergedConfig.settings ); + }, [ mergedConfig ] ); + + return [ stylesheets, settings ]; +} diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/global-styles/utils.js similarity index 100% rename from packages/edit-site/src/components/editor/utils.js rename to packages/edit-site/src/components/global-styles/utils.js From 65bd583af851d448bed34971477464d0209ebdb0 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 5 Oct 2021 09:09:22 +0100 Subject: [PATCH 09/15] Add documentation --- .../src/components/global-styles/README.md | 91 +++++++++++++++++++ .../src/components/global-styles/index.js | 82 +---------------- .../src/components/global-styles/ui.js | 79 ++++++++++++++++ .../sidebar/global-styles-sidebar.js | 5 +- 4 files changed, 175 insertions(+), 82 deletions(-) create mode 100644 packages/edit-site/src/components/global-styles/README.md create mode 100644 packages/edit-site/src/components/global-styles/ui.js diff --git a/packages/edit-site/src/components/global-styles/README.md b/packages/edit-site/src/components/global-styles/README.md new file mode 100644 index 0000000000000..ac728f3eb8bf5 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/README.md @@ -0,0 +1,91 @@ +# Global Styles + +This folder contains all the necessary APIs to render the global styles UI and to manipulate the global styles data. It can be potentially extracted to its own package. + +# Available public APIs + +## GlobalStylesUI + +A component used to render the Global Styles UI. It's current used in the sidebar of the site editor. + +```js +import { GlobalStylesUI } from './global-styles'; + +function MyComponent() { + return ; +} +``` + +## useGlobalStylesReset + +A React hook used to retrieve whether the Global Styles have been edited and a callback to reset to the default theme values. + +```js +import { useGlobalStylesReset } from './global-styles'; + +function MyComponent() { + const [ canReset, reset ] = useGlobalStylesReset(); + + return canReset + ? + : null; +} +``` + +## useGlobalStylesOutput + +A React hook used to retrieve the styles array and settings to provide for block editor instances based on the current global styles. + +```js +import { useGlobalStylesOutput } from './global-styles'; +import { BlockEditorProvider, BlockList } from '@wordpress/block-editor'; + +function MyComponent() { + const [ styles, settings ] = useGlobalStylesOutput(); + + return + + +} +``` + +## useStyle + +A react hook used to retrieve the style applied to a given context. + +```js +import { useStyle } from './global-styles'; + +function MyComponent() { + // Text color for the site root. + const [ color, setColor ] = useStyle( 'color.text' ); + + // The user modified color for the core paragraph block. + const [ pColor, setPColor ] = useStyle( 'color.text', 'core/paragraph', 'user' ); + + return "Something"; +} +``` + +## useSetting + +A react hook used to retrieve the setting applied to a given context. + +```js +import { useSetting } from './global-styles'; + +function MyComponent() { + // The default color palette. + const [ colorPalette, setColorPalette ] = useSetting( 'color.palette' ); + + // The base (theme + core) color palette for the paragraph block, + // ignoring user provided palette. + // If the palette is not defined for the paragraph block, the root one is returned. + const [ pColor, setPColor ] = useSetting( 'color.palette', 'core/paragraph', 'base' ); + + return "Something"; +} +``` diff --git a/packages/edit-site/src/components/global-styles/index.js b/packages/edit-site/src/components/global-styles/index.js index 9d4ba618e21b0..026a4fd759f32 100644 --- a/packages/edit-site/src/components/global-styles/index.js +++ b/packages/edit-site/src/components/global-styles/index.js @@ -1,79 +1,3 @@ -/** - * WordPress dependencies - */ -import { - __experimentalNavigatorProvider as NavigatorProvider, - __experimentalNavigatorScreen as NavigatorScreen, -} from '@wordpress/components'; -import { getBlockTypes } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import ScreenRoot from './screen-root'; -import ScreenBlockList from './screen-block-list'; -import ScreenBlock from './screen-block'; -import ScreenTypography from './screen-typography'; -import ScreenColors from './screen-colors'; -import ScreenColorPalette from './screen-color-palette'; -import ScreenLayout from './screen-layout'; - -function ContextScreens( { name } ) { - const parentMenu = name === undefined ? '' : '/blocks/' + name; - - return ( - <> - - - - - - - - - - - - - - - - - ); -} - -function GlobalStyles() { - const blocks = getBlockTypes(); - - return ( - - - - - - - - - - { blocks.map( ( block ) => ( - - - - ) ) } - - - - { blocks.map( ( block ) => ( - - ) ) } - - ); -} - -export default GlobalStyles; +export { default as GlobalStylesUI } from './ui'; +export { useGlobalStylesReset, useStyle, useSetting } from './hooks'; +export { useGlobalStylesOutput } from './use-global-styles-output'; diff --git a/packages/edit-site/src/components/global-styles/ui.js b/packages/edit-site/src/components/global-styles/ui.js new file mode 100644 index 0000000000000..180bf343dd120 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/ui.js @@ -0,0 +1,79 @@ +/** + * WordPress dependencies + */ +import { + __experimentalNavigatorProvider as NavigatorProvider, + __experimentalNavigatorScreen as NavigatorScreen, +} from '@wordpress/components'; +import { getBlockTypes } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import ScreenRoot from './screen-root'; +import ScreenBlockList from './screen-block-list'; +import ScreenBlock from './screen-block'; +import ScreenTypography from './screen-typography'; +import ScreenColors from './screen-colors'; +import ScreenColorPalette from './screen-color-palette'; +import ScreenLayout from './screen-layout'; + +function ContextScreens( { name } ) { + const parentMenu = name === undefined ? '' : '/blocks/' + name; + + return ( + <> + + + + + + + + + + + + + + + + + ); +} + +function GlobalStylesUI() { + const blocks = getBlockTypes(); + + return ( + + + + + + + + + + { blocks.map( ( block ) => ( + + + + ) ) } + + + + { blocks.map( ( block ) => ( + + ) ) } + + ); +} + +export default GlobalStylesUI; diff --git a/packages/edit-site/src/components/sidebar/global-styles-sidebar.js b/packages/edit-site/src/components/sidebar/global-styles-sidebar.js index 1dfd29e883c2c..628932a314fa2 100644 --- a/packages/edit-site/src/components/sidebar/global-styles-sidebar.js +++ b/packages/edit-site/src/components/sidebar/global-styles-sidebar.js @@ -9,8 +9,7 @@ import { styles } from '@wordpress/icons'; * Internal dependencies */ import DefaultSidebar from './default-sidebar'; -import GlobalStyles from '../global-styles'; -import { useGlobalStylesReset } from '../global-styles/hooks'; +import { GlobalStylesUI, useGlobalStylesReset } from '../global-styles'; export default function GlobalStylesSidebar() { const [ canRestart, onReset ] = useGlobalStylesReset(); @@ -40,7 +39,7 @@ export default function GlobalStylesSidebar() { } > - + ); } From 0f3c757afe6f9d99ce5b0fd7b5f9f66aee165517 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Tue, 5 Oct 2021 09:19:30 +0100 Subject: [PATCH 10/15] Fix unit tests --- .../components/global-styles/test/utils.js | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/test/utils.js b/packages/edit-site/src/components/global-styles/test/utils.js index 0f067424687d5..40e434a935dbb 100644 --- a/packages/edit-site/src/components/global-styles/test/utils.js +++ b/packages/edit-site/src/components/global-styles/test/utils.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { getPresetVariable, getValueFromVariable } from '../utils'; +import { getPresetVariableFromValue, getValueFromVariable } from '../utils'; describe( 'editor utils', () => { const styles = { @@ -49,15 +49,15 @@ describe( 'editor utils', () => { isGlobalStylesUserThemeJSON: true, }; - describe( 'getPresetVariable', () => { + describe( 'getPresetVariableFromValue', () => { const context = 'root'; - const propertyName = 'color'; + const propertyName = 'color.text'; const value = '#007cba'; describe( 'when a provided global style (e.g. fontFamily, color,etc.) does not exist', () => { it( 'returns the originally provided value', () => { - const actual = getPresetVariable( - styles, + const actual = getPresetVariableFromValue( + styles.settings, context, 'fakePropertyName', value @@ -68,8 +68,8 @@ describe( 'editor utils', () => { describe( 'when a global style is cleared by the user', () => { it( 'returns an undefined preset variable', () => { - const actual = getPresetVariable( - styles, + const actual = getPresetVariableFromValue( + styles.settings, context, propertyName, undefined @@ -82,8 +82,8 @@ describe( 'editor utils', () => { describe( 'and it is not a preset value (e.g. custom color)', () => { it( 'returns the originally provided value', () => { const customValue = '#6e4545'; - const actual = getPresetVariable( - styles, + const actual = getPresetVariableFromValue( + styles.settings, context, propertyName, customValue @@ -94,8 +94,8 @@ describe( 'editor utils', () => { describe( 'and it is a preset value', () => { it( 'returns the preset variable', () => { - const actual = getPresetVariable( - styles, + const actual = getPresetVariableFromValue( + styles.settings, context, propertyName, value @@ -110,7 +110,7 @@ describe( 'editor utils', () => { describe( 'when provided an invalid variable', () => { it( 'returns the originally provided value', () => { const actual = getValueFromVariable( - styles, + styles.settings, 'root', undefined ); @@ -122,7 +122,7 @@ describe( 'editor utils', () => { describe( 'when provided a preset variable', () => { it( 'retrieves the correct preset value', () => { const actual = getValueFromVariable( - styles, + styles.settings, 'root', 'var:preset|color|primary' ); @@ -134,7 +134,7 @@ describe( 'editor utils', () => { describe( 'when provided a custom variable', () => { it( 'retrieves the correct custom value', () => { const actual = getValueFromVariable( - styles, + styles.settings, 'root', 'var(--wp--custom--color--secondary)' ); From 854e51c90e9d9671c2162e36f26a657053f5cbd2 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 6 Oct 2021 09:53:17 +0100 Subject: [PATCH 11/15] Fix settings updates --- .../src/components/global-styles/hooks.js | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index ea89b24423d16..8e807a53ecc53 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -41,25 +41,27 @@ function mergeBaseAndUserConfigs( base, user ) { } function addUserOriginToSettings( settingsToAdd ) { + const newSettings = cloneDeep( settingsToAdd ); PRESET_METADATA.forEach( ( { path } ) => { - const presetData = get( settingsToAdd, path ); + const presetData = get( newSettings, path ); if ( presetData ) { - set( settingsToAdd, path, { + set( newSettings, path, { user: presetData, } ); } } ); - return settingsToAdd; + return newSettings; } function removeUserOriginFromSettings( settingsToRemove ) { + const newSettings = cloneDeep( settingsToRemove ); PRESET_METADATA.forEach( ( { path } ) => { - const presetData = get( settingsToRemove, path ); + const presetData = get( newSettings, path ); if ( presetData ) { - set( settingsToRemove, path, ( presetData ?? {} ).user ); + set( newSettings, path, ( presetData ?? {} ).user ); } } ); - return settingsToRemove; + return newSettings; } function useGlobalStylesUserConfig() { @@ -175,7 +177,11 @@ export function useSetting( path, blockName, source = 'all' ) { const setSetting = ( newValue ) => { setUserConfig( ( currentConfig ) => { const newUserConfig = cloneDeep( currentConfig ); - set( newUserConfig, fullPath, newValue ); + const pathToSet = PATHS_WITH_MERGE[ path ] + ? fullPath + '.user' + : fullPath; + set( newUserConfig, pathToSet, newValue ); + return newUserConfig; } ); }; @@ -188,7 +194,7 @@ export function useSetting( path, blockName, source = 'all' ) { const getSettingValue = ( configToUse ) => { const result = get( configToUse, currentPath ); if ( PATHS_WITH_MERGE[ path ] ) { - return result?.theme ?? result?.core; + return result?.user ?? result?.theme ?? result?.core; } return result; }; From ead2bb38a326d6f5e1d28e361f825fea624ee09b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Wed, 6 Oct 2021 14:41:04 +0100 Subject: [PATCH 12/15] Fix useSetting calls --- .../src/components/global-styles/dimensions-panel.js | 6 +++--- .../src/components/global-styles/typography-panel.js | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/dimensions-panel.js b/packages/edit-site/src/components/global-styles/dimensions-panel.js index bd2dfc8d5f0bf..77c2e319ef3ef 100644 --- a/packages/edit-site/src/components/global-styles/dimensions-panel.js +++ b/packages/edit-site/src/components/global-styles/dimensions-panel.js @@ -28,21 +28,21 @@ export function useHasDimensionsPanel( name ) { function useHasPadding( name ) { const supports = getSupportedGlobalStylesPanels( name ); - const settings = useSetting( 'spacing.customPadding', name ); + const [ settings ] = useSetting( 'spacing.customPadding', name ); return settings && supports.includes( 'padding' ); } function useHasMargin( name ) { const supports = getSupportedGlobalStylesPanels( name ); - const settings = useSetting( 'spacing.customMargin', name ); + const [ settings ] = useSetting( 'spacing.customMargin', name ); return settings && supports.includes( 'margin' ); } function useHasGap( name ) { const supports = getSupportedGlobalStylesPanels( name ); - const settings = useSetting( 'spacing.blockGap', name ); + const [ settings ] = useSetting( 'spacing.blockGap', name ); return settings && supports.includes( '--wp--style--block-gap' ); } diff --git a/packages/edit-site/src/components/global-styles/typography-panel.js b/packages/edit-site/src/components/global-styles/typography-panel.js index 2450ef9b27a6a..723d2fee1c229 100644 --- a/packages/edit-site/src/components/global-styles/typography-panel.js +++ b/packages/edit-site/src/components/global-styles/typography-panel.js @@ -30,7 +30,7 @@ export function useHasTypographyPanel( name ) { function useHasLineHeightControl( name ) { const supports = getSupportedGlobalStylesPanels( name ); return ( - useSetting( 'typography.customLineHeight', name ) && + useSetting( 'typography.customLineHeight', name )[ 0 ] && supports.includes( 'lineHeight' ) ); } @@ -38,10 +38,10 @@ function useHasLineHeightControl( name ) { function useHasAppearanceControl( name ) { const supports = getSupportedGlobalStylesPanels( name ); const hasFontStyles = - useSetting( 'typography.customFontStyle', name ) && + useSetting( 'typography.customFontStyle', name )[ 0 ] && supports.includes( 'fontStyle' ); const hasFontWeights = - useSetting( 'typography.customFontWeight', name ) && + useSetting( 'typography.customFontWeight', name )[ 0 ] && supports.includes( 'fontWeight' ); return hasFontStyles || hasFontWeights; } @@ -49,7 +49,7 @@ function useHasAppearanceControl( name ) { function useHasLetterSpacingControl( name ) { const supports = getSupportedGlobalStylesPanels( name ); return ( - useSetting( 'typography.customLetterSpacing', name ) && + useSetting( 'typography.customLetterSpacing', name )[ 0 ] && supports.includes( 'letterSpacing' ) ); } From ebfa9ab20a701905d60814466c9f7d83f080735b Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 7 Oct 2021 08:58:08 +0100 Subject: [PATCH 13/15] Fix elements outputs --- .../components/global-styles/use-global-styles-output.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/use-global-styles-output.js index 88c14c85593b8..69e5bf5879ddd 100644 --- a/packages/edit-site/src/components/global-styles/use-global-styles-output.js +++ b/packages/edit-site/src/components/global-styles/use-global-styles-output.js @@ -232,12 +232,15 @@ export const getNodesWithStyles = ( tree, blockSelectors ) => { forEach( node?.elements, ( value, elementName ) => { if ( !! value && - !! blockSelectors?.[ blockName ]?.elements?.[ elementName ] + !! blockSelectors?.[ blockName ] && + !! ELEMENTS?.[ elementName ] ) { nodes.push( { styles: value, - selector: - blockSelectors[ blockName ].elements[ elementName ], + selector: blockSelectors[ blockName ].selector + .split( ',' ) + .map( ( sel ) => sel + ' ' + ELEMENTS[ elementName ] ) + .join( ',' ), } ); } } ); From 50bc2adfef2b4e608d8571129dfc9eba9601b437 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 7 Oct 2021 09:01:55 +0100 Subject: [PATCH 14/15] Fix wrong useSetting usage --- .../global-styles/color-palette-panel.js | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/color-palette-panel.js b/packages/edit-site/src/components/global-styles/color-palette-panel.js index d66ad3511f1af..76a9a951b3595 100644 --- a/packages/edit-site/src/components/global-styles/color-palette-panel.js +++ b/packages/edit-site/src/components/global-styles/color-palette-panel.js @@ -23,9 +23,17 @@ const EMPTY_ARRAY = []; export default function ColorPalettePanel( { name } ) { const [ colors, setColors ] = useSetting( 'color.palette', name ); - const userColors = useSetting( 'color.palette', name, 'user' ); - const baseGlobalPalette = useSetting( 'color.palette', undefined, 'base' ); - const baseContextualPalette = useSetting( 'color.palette', name, 'base' ); + const [ userColors ] = useSetting( 'color.palette', name, 'user' ); + const [ baseGlobalPalette ] = useSetting( + 'color.palette', + undefined, + 'base' + ); + const [ baseContextualPalette ] = useSetting( + 'color.palette', + name, + 'base' + ); const immutableColorSlugs = useMemo( () => { const basePalette = baseContextualPalette ?? baseGlobalPalette; if ( ! basePalette ) { From 0793abc645d0fbb76abaf9acca494c7d5409bb87 Mon Sep 17 00:00:00 2001 From: Riad Benguella Date: Thu, 7 Oct 2021 09:29:48 +0100 Subject: [PATCH 15/15] Fix unit tests --- .../test/use-global-styles-output.js | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js index 68555167ed72e..b5449b93a074f 100644 --- a/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js +++ b/packages/edit-site/src/components/global-styles/test/use-global-styles-output.js @@ -55,16 +55,7 @@ describe( 'global styles renderer', () => { }; const blockSelectors = { 'core/heading': { - selector: 'h1,h2,h3,h4,h5,h6', - elements: { - link: 'h1 a,h2 a,h3 a,h4 a,h5 a,h6 a', - h1: 'h1', - h2: 'h2', - h3: 'h3', - h4: 'h4', - h5: 'h5', - h6: 'h6', - }, + selector: '.my-heading1, .my-heading2', }, }; expect( getNodesWithStyles( tree, blockSelectors ) ).toEqual( [ @@ -93,7 +84,7 @@ describe( 'global styles renderer', () => { text: 'blue', }, }, - selector: 'h1,h2,h3,h4,h5,h6', + selector: '.my-heading1, .my-heading2', }, { styles: { @@ -101,7 +92,7 @@ describe( 'global styles renderer', () => { fontSize: '42px', }, }, - selector: 'h1', + selector: '.my-heading1 h1, .my-heading2 h1', }, { styles: { @@ -109,7 +100,7 @@ describe( 'global styles renderer', () => { fontSize: '23px', }, }, - selector: 'h2', + selector: '.my-heading1 h2, .my-heading2 h2', }, ] ); } );