@wordpress/editor
Version:
Enhanced block editor for WordPress posts.
224 lines (200 loc) • 5.46 kB
JavaScript
/**
* External dependencies
*/
import deepmerge from 'deepmerge';
import { isPlainObject } from 'is-plain-object';
/**
* WordPress dependencies
*/
import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor';
import { store as coreStore } from '@wordpress/core-data';
import { useSelect, useDispatch } from '@wordpress/data';
import { useMemo, useCallback } from '@wordpress/element';
/**
* Internal dependencies
*/
import { unlock } from '../../lock-unlock';
const { GlobalStylesContext, cleanEmptyObject } = unlock(
blockEditorPrivateApis
);
export function mergeBaseAndUserConfigs( base, user ) {
return deepmerge( base, user, {
/*
* We only pass as arrays the presets,
* in which case we want the new array of values
* to override the old array (no merging).
*/
isMergeableObject: isPlainObject,
/*
* Exceptions to the above rule.
* Background images should be replaced, not merged,
* as they themselves are specific object definitions for the style.
*/
customMerge: ( key ) => {
if ( key === 'backgroundImage' ) {
return ( baseConfig, userConfig ) => userConfig;
}
return undefined;
},
} );
}
function useGlobalStylesUserConfig() {
const { globalStylesId, isReady, settings, styles, _links } = useSelect(
( select ) => {
const {
getEntityRecord,
getEditedEntityRecord,
hasFinishedResolution,
canUser,
} = select( coreStore );
const _globalStylesId =
select( coreStore ).__experimentalGetCurrentGlobalStylesId();
let record;
const userCanEditGlobalStyles = canUser( 'update', {
kind: 'root',
name: 'globalStyles',
id: _globalStylesId,
} );
if ( _globalStylesId ) {
if ( userCanEditGlobalStyles ) {
record = getEditedEntityRecord(
'root',
'globalStyles',
_globalStylesId
);
} else {
record = getEntityRecord(
'root',
'globalStyles',
_globalStylesId,
{ context: 'view' }
);
}
}
let hasResolved = false;
if (
hasFinishedResolution(
'__experimentalGetCurrentGlobalStylesId'
)
) {
if ( _globalStylesId ) {
hasResolved = userCanEditGlobalStyles
? hasFinishedResolution( 'getEditedEntityRecord', [
'root',
'globalStyles',
_globalStylesId,
] )
: hasFinishedResolution( 'getEntityRecord', [
'root',
'globalStyles',
_globalStylesId,
{ context: 'view' },
] );
} else {
hasResolved = true;
}
}
return {
globalStylesId: _globalStylesId,
isReady: hasResolved,
settings: record?.settings,
styles: record?.styles,
_links: record?._links,
};
},
[]
);
const { getEditedEntityRecord } = useSelect( coreStore );
const { editEntityRecord } = useDispatch( coreStore );
const config = useMemo( () => {
return {
settings: settings ?? {},
styles: styles ?? {},
_links: _links ?? {},
};
}, [ settings, styles, _links ] );
const setConfig = useCallback(
/**
* Set the global styles config.
* @param {Function|Object} callbackOrObject If the callbackOrObject is a function, pass the current config to the callback so the consumer can merge values.
* Otherwise, overwrite the current config with the incoming object.
* @param {Object} options Options for editEntityRecord Core selector.
*/
( callbackOrObject, options = {} ) => {
const record = getEditedEntityRecord(
'root',
'globalStyles',
globalStylesId
);
const currentConfig = {
styles: record?.styles ?? {},
settings: record?.settings ?? {},
_links: record?._links ?? {},
};
const updatedConfig =
typeof callbackOrObject === 'function'
? callbackOrObject( currentConfig )
: callbackOrObject;
editEntityRecord(
'root',
'globalStyles',
globalStylesId,
{
styles: cleanEmptyObject( updatedConfig.styles ) || {},
settings: cleanEmptyObject( updatedConfig.settings ) || {},
_links: cleanEmptyObject( updatedConfig._links ) || {},
},
options
);
},
[ globalStylesId, editEntityRecord, getEditedEntityRecord ]
);
return [ isReady, config, setConfig ];
}
function useGlobalStylesBaseConfig() {
const baseConfig = useSelect(
( select ) =>
select( coreStore ).__experimentalGetCurrentThemeBaseGlobalStyles(),
[]
);
return [ !! baseConfig, baseConfig ];
}
export function useGlobalStylesContext() {
const [ isUserConfigReady, userConfig, setUserConfig ] =
useGlobalStylesUserConfig();
const [ isBaseConfigReady, baseConfig ] = useGlobalStylesBaseConfig();
const mergedConfig = useMemo( () => {
if ( ! baseConfig || ! userConfig ) {
return {};
}
return mergeBaseAndUserConfigs( baseConfig, userConfig );
}, [ userConfig, baseConfig ] );
const context = useMemo( () => {
return {
isReady: isUserConfigReady && isBaseConfigReady,
user: userConfig,
base: baseConfig,
merged: mergedConfig,
setUserConfig,
};
}, [
mergedConfig,
userConfig,
baseConfig,
setUserConfig,
isUserConfigReady,
isBaseConfigReady,
] );
return context;
}
export function GlobalStylesProvider( { children } ) {
const context = useGlobalStylesContext();
if ( ! context.isReady ) {
return null;
}
return (
<GlobalStylesContext.Provider value={ context }>
{ children }
</GlobalStylesContext.Provider>
);
}