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 27988d56a60e6f..f046cf7d464094 100644 --- a/packages/edit-site/src/components/editor/global-styles-renderer.js +++ b/packages/edit-site/src/components/editor/global-styles-renderer.js @@ -14,7 +14,7 @@ import { store as editSiteStore } from '../../store'; */ import { useGlobalStylesOutput } from '../global-styles/use-global-styles-output'; -export function useGlobalStylesRenderer() { +function useGlobalStylesRenderer() { const [ styles, settings ] = useGlobalStylesOutput(); const { getSettings } = useSelect( editSiteStore ); const { updateSettings } = useDispatch( editSiteStore ); @@ -35,3 +35,9 @@ export function useGlobalStylesRenderer() { } ); }, [ styles, settings ] ); } + +export function GlobalStylesRenderer() { + useGlobalStylesRenderer(); + + return null; +} diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 525753c34f7bd3..b4a0d28056b9dc 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -41,7 +41,8 @@ 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'; +import { GlobalStylesRenderer } from './global-styles-renderer'; +import { GlobalStylesProvider } from '../global-styles/global-styles-provider'; const interfaceLabels = { secondarySidebar: __( 'Block Library' ), @@ -159,8 +160,6 @@ function Editor( { initialSettings, onError } ) { } }, [ isNavigationOpen ] ); - useGlobalStylesRenderer(); - // Don't render the Editor until the settings are set and loaded if ( ! settings?.siteUrl ) { return null; @@ -186,87 +185,94 @@ function Editor( { initialSettings, onError } ) { type={ templateType } id={ entityId } > - - - - - - - } - secondarySidebar={ secondarySidebar() } - sidebar={ - sidebarIsOpened && ( - - ) - } - header={ -
- } - notices={ } - content={ - <> - - { template && ( - - ) } - { templateResolved && - ! template && - settings?.siteUrl && - entityId && ( - - { __( - "You attempted to edit an item that doesn't exist. Perhaps it was deleted?" - ) } - + + + + + + + + + } + secondarySidebar={ secondarySidebar() } + sidebar={ + sidebarIsOpened && ( + + ) + } + header={ +
+ } + notices={ } + content={ + <> + + { template && ( + ) } - - - } - actions={ - <> - { isEntitiesSavedStatesOpen ? ( - - ) : ( -
- -
- ) } - - } - footer={ } - /> - - - - + /> + ) : ( +
+ +
+ ) } + + } + footer={ } + /> + + + + + diff --git a/packages/edit-site/src/components/global-styles/context.js b/packages/edit-site/src/components/global-styles/context.js new file mode 100644 index 00000000000000..630b5a2b9059d5 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/context.js @@ -0,0 +1,15 @@ +/** + * WordPress dependencies + */ +import { createContext } from '@wordpress/element'; + +export const DEFAULT_GLOBAL_STYLES_CONTEXT = { + user: {}, + base: {}, + merged: {}, + setUserConfig: () => {}, +}; + +export const GlobalStylesContext = createContext( + DEFAULT_GLOBAL_STYLES_CONTEXT +); diff --git a/packages/edit-site/src/components/global-styles/global-styles-provider.js b/packages/edit-site/src/components/global-styles/global-styles-provider.js new file mode 100644 index 00000000000000..f83e0804167e61 --- /dev/null +++ b/packages/edit-site/src/components/global-styles/global-styles-provider.js @@ -0,0 +1,162 @@ +/** + * External dependencies + */ +import { get, cloneDeep, set, mergeWith } from 'lodash'; + +/** + * WordPress dependencies + */ +import { useMemo, useCallback } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { store as coreStore } from '@wordpress/core-data'; + +/** + * Internal dependencies + */ +import { store as editSiteStore } from '../../store'; +import { PRESET_METADATA } from './utils'; +import { GlobalStylesContext } from './context'; + +function mergeTreesCustomizer( _, 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 ) { + const newSettings = cloneDeep( settingsToAdd ); + PRESET_METADATA.forEach( ( { path } ) => { + const presetData = get( newSettings, path ); + if ( presetData ) { + set( newSettings, path, { + user: presetData, + } ); + } + } ); + return newSettings; +} + +function removeUserOriginFromSettings( settingsToRemove ) { + const newSettings = cloneDeep( settingsToRemove ); + PRESET_METADATA.forEach( ( { path } ) => { + const presetData = get( newSettings, path ); + if ( presetData ) { + set( newSettings, path, ( presetData ?? {} ).user ); + } + } ); + return newSettings; +} + +function useGlobalStylesUserConfig() { + 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 parseContent = ( contentToParse ) => { + let parsedConfig; + try { + 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 ) { + parsedConfig = {}; + } else { + parsedConfig = { + ...parsedConfig, + settings: addUserOriginToSettings( parsedConfig.settings ), + }; + } + } 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; + }; + + const config = useMemo( () => { + return parseContent( content ); + }, [ content ] ); + + const setConfig = useCallback( + ( 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( + updatedConfig.settings + ), + } ), + } ); + }, + [ globalStylesId ] + ); + + return [ config, setConfig ]; +} + +function useGlobalStylesBaseConfig() { + const baseConfig = useSelect( ( select ) => { + return select( editSiteStore ).getSettings() + .__experimentalGlobalStylesBaseConfig; + }, [] ); + + return baseConfig; +} + +function useGlobalStylesContext() { + const [ userConfig, setUserConfig ] = useGlobalStylesUserConfig(); + const baseConfig = useGlobalStylesBaseConfig(); + const mergedConfig = useMemo( () => { + return mergeBaseAndUserConfigs( baseConfig, userConfig ); + }, [ userConfig, baseConfig ] ); + const context = useMemo( () => { + return { + user: userConfig, + base: baseConfig, + merged: mergedConfig, + setUserConfig, + }; + }, [ mergedConfig, userConfig, baseConfig, setUserConfig ] ); + + return context; +} + +export function GlobalStylesProvider( { children } ) { + const context = useGlobalStylesContext(); + + return ( + + { children } + + ); +} diff --git a/packages/edit-site/src/components/global-styles/hooks.js b/packages/edit-site/src/components/global-styles/hooks.js index f8ffdb40176e45..9f5f0617b47f16 100644 --- a/packages/edit-site/src/components/global-styles/hooks.js +++ b/packages/edit-site/src/components/global-styles/hooks.js @@ -1,14 +1,12 @@ /** * External dependencies */ -import { get, cloneDeep, set, isEqual, has, mergeWith } from 'lodash'; +import { get, cloneDeep, set, isEqual, has } from 'lodash'; /** * WordPress dependencies */ -import { useMemo, useCallback } from '@wordpress/element'; -import { useSelect, useDispatch } from '@wordpress/data'; -import { store as coreStore } from '@wordpress/core-data'; +import { useContext, useCallback } from '@wordpress/element'; import { getBlockType, __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE, @@ -18,157 +16,29 @@ import { /** * Internal dependencies */ -import { store as editSiteStore } from '../../store'; -import { - PRESET_METADATA, - getValueFromVariable, - getPresetVariableFromValue, -} from './utils'; +import { getValueFromVariable, getPresetVariableFromValue } from './utils'; +import { GlobalStylesContext } from './context'; 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 ) { - const newSettings = cloneDeep( settingsToAdd ); - PRESET_METADATA.forEach( ( { path } ) => { - const presetData = get( newSettings, path ); - if ( presetData ) { - set( newSettings, path, { - user: presetData, - } ); - } - } ); - return newSettings; -} - -function removeUserOriginFromSettings( settingsToRemove ) { - const newSettings = cloneDeep( settingsToRemove ); - PRESET_METADATA.forEach( ( { path } ) => { - const presetData = get( newSettings, path ); - if ( presetData ) { - set( newSettings, path, ( presetData ?? {} ).user ); - } - } ); - return newSettings; -} - -function useGlobalStylesUserConfig() { - 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 parseContent = ( contentToParse ) => { - let parsedConfig; - try { - 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 ) { - parsedConfig = {}; - } else { - parsedConfig = { - ...parsedConfig, - settings: addUserOriginToSettings( parsedConfig.settings ), - }; - } - } 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; - }; - - const config = useMemo( () => { - return parseContent( content ); - }, [ content ] ); - - const setConfig = useCallback( - ( 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( - updatedConfig.settings - ), - } ), - } ); - }, - [ globalStylesId ] - ); - - return [ config, setConfig ]; -} - -function useGlobalStylesBaseConfig() { - const baseConfig = useSelect( ( select ) => { - return select( editSiteStore ).getSettings() - .__experimentalGlobalStylesBaseConfig; - }, [] ); - - return baseConfig; -} - -export function useGlobalStylesConfig() { - const [ userConfig, setUserConfig ] = useGlobalStylesUserConfig(); - const baseConfig = useGlobalStylesBaseConfig(); - const mergedConfig = useMemo( () => { - return mergeBaseAndUserConfigs( baseConfig, userConfig ); - }, [ userConfig, baseConfig ] ); - - return [ baseConfig, userConfig, mergedConfig, setUserConfig ]; -} - export const useGlobalStylesReset = () => { - const [ config, setConfig ] = useGlobalStylesUserConfig(); + const { user: config, setUserConfig } = useContext( GlobalStylesContext ); const canReset = !! config && ! isEqual( config, EMPTY_CONFIG ); return [ canReset, - useCallback( () => setConfig( () => EMPTY_CONFIG ), [ setConfig ] ), + useCallback( () => setUserConfig( () => EMPTY_CONFIG ), [ + setUserConfig, + ] ), ]; }; export function useSetting( path, blockName, source = 'all' ) { - const [ - baseConfig, - userConfig, - mergedConfig, + const { + merged: mergedConfig, + base: baseConfig, + user: userConfig, setUserConfig, - ] = useGlobalStylesConfig(); + } = useContext( GlobalStylesContext ); const fullPath = ! blockName ? `settings.${ path }` @@ -225,12 +95,12 @@ export function useSetting( path, blockName, source = 'all' ) { } export function useStyle( path, blockName, source = 'all' ) { - const [ - baseConfig, - userConfig, - mergedConfig, + const { + merged: mergedConfig, + base: baseConfig, + user: userConfig, setUserConfig, - ] = useGlobalStylesConfig(); + } = useContext( GlobalStylesContext ); const finalPath = ! blockName ? `styles.${ path }` : `styles.blocks.${ blockName }.${ path }`; 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 35755d9523dd0f..a67c3e5fb3af42 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 @@ -22,17 +22,17 @@ import { __EXPERIMENTAL_ELEMENTS as ELEMENTS, getBlockTypes, } from '@wordpress/blocks'; -import { useEffect, useState } from '@wordpress/element'; +import { useEffect, useState, useContext } from '@wordpress/element'; /** * Internal dependencies */ -import { useGlobalStylesConfig } from './hooks'; /** * Internal dependencies */ import { PRESET_METADATA, ROOT_BLOCK_SELECTOR } from './utils'; +import { GlobalStylesContext } from './context'; function compileStyleValue( uncompiledValue ) { const VARIABLE_REFERENCE_PREFIX = 'var:'; @@ -361,7 +361,7 @@ const getBlockSelectors = ( blockTypes ) => { export function useGlobalStylesOutput() { const [ stylesheets, setStylesheets ] = useState( [] ); const [ settings, setSettings ] = useState( {} ); - const [ , , mergedConfig ] = useGlobalStylesConfig(); + const { merged: mergedConfig } = useContext( GlobalStylesContext ); useEffect( () => { if ( ! mergedConfig?.styles || ! mergedConfig?.settings ) {