diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index bb4dc1725bc3fa..1ac284320078a6 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -762,8 +762,10 @@ _Parameters_ ### useSetting -Hook that retrieves the editor setting. -It works with nested objects using by finding the value at path. +Hook that retrieves the given setting for the block instance in use. + +It looks up the settings first in the block instance hierarchy. +If none is found, it'll look it up in the block editor store. _Usage_ diff --git a/packages/block-editor/src/components/use-setting/index.js b/packages/block-editor/src/components/use-setting/index.js index db6f1e79507af7..bc8a496c9536df 100644 --- a/packages/block-editor/src/components/use-setting/index.js +++ b/packages/block-editor/src/components/use-setting/index.js @@ -7,7 +7,10 @@ import { get } from 'lodash'; * WordPress dependencies */ import { useSelect } from '@wordpress/data'; -import { __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE } from '@wordpress/blocks'; +import { + __EXPERIMENTAL_PATHS_WITH_MERGE as PATHS_WITH_MERGE, + hasBlockSupport, +} from '@wordpress/blocks'; /** * Internal dependencies @@ -91,8 +94,10 @@ const removeCustomPrefixes = ( path ) => { }; /** - * Hook that retrieves the editor setting. - * It works with nested objects using by finding the value at path. + * Hook that retrieves the given setting for the block instance in use. + * + * It looks up the settings first in the block instance hierarchy. + * If none is found, it'll look it up in the block editor store. * * @param {string} path The path to the setting. * @return {any} Returns the value defined for the setting. @@ -113,41 +118,55 @@ export default function useSetting( path ) { ); return undefined; } - const settings = select( blockEditorStore ).getSettings(); - // 0 - Use settings for this block instance, if there's any. - // Also, look up in the block hierarchy. - // todo: only check if the block type has support for settings to minimize the queries we make - // todo: how do we provide i18n for the presets defined by block instances? In PHP, they already do i18n. - // todo: the presets defined in a block instance are "custom" ones; - // remove the need for users to provide the custom key. - const blockAtts = select( blockEditorStore ).getBlockAttributes( - clientId - ); - - // 1 - Use __experimental features, if available. - // We cascade to the all value if the block one is not available. + let result; const normalizedPath = removeCustomPrefixes( path ); - const defaultsPath = `__experimentalFeatures.${ normalizedPath }`; - const blockPath = `__experimentalFeatures.blocks.${ blockName }.${ normalizedPath }`; - const blockInstancePath = `settings.${ normalizedPath }`; - const experimentalFeaturesResult = - get( blockAtts, blockInstancePath ) ?? - get( settings, blockPath ) ?? - get( settings, defaultsPath ); - - if ( experimentalFeaturesResult !== undefined ) { + + // 1. Take settings from the block instance or its ancestors. + const candidates = [ + ...select( blockEditorStore ).getBlockParents( clientId ), + clientId, // The current block is added last, so it overwrites any ancestor. + ]; + candidates.forEach( ( candidateClientId ) => { + const candidateBlockName = select( + blockEditorStore + ).getBlockName( candidateClientId ); + if ( + hasBlockSupport( candidateBlockName, 'settings', false ) + ) { + const candidateAtts = select( + blockEditorStore + ).getBlockAttributes( candidateClientId ); + const candidateResult = + get( + candidateAtts, + `settings.blocks.${ blockName }.${ normalizedPath }` + ) ?? + get( candidateAtts, `settings.${ normalizedPath }` ); + if ( candidateResult !== undefined ) { + result = candidateResult; + } + } + } ); + + // 2. Fall back to the settings from the block editor store (__experimentalFeatures). + const settings = select( blockEditorStore ).getSettings(); + if ( result === undefined ) { + const defaultsPath = `__experimentalFeatures.${ normalizedPath }`; + const blockPath = `__experimentalFeatures.blocks.${ blockName }.${ normalizedPath }`; + result = + get( settings, blockPath ) ?? get( settings, defaultsPath ); + } + + // Return if the setting was found in either the block instance or the store. + if ( result !== undefined ) { if ( PATHS_WITH_MERGE[ normalizedPath ] ) { - return ( - experimentalFeaturesResult.custom ?? - experimentalFeaturesResult.theme ?? - experimentalFeaturesResult.default - ); + return result.custom ?? result.theme ?? result.default; } - return experimentalFeaturesResult; + return result; } - // 2 - Use deprecated settings, otherwise. + // 3. Otherwise, use deprecated settings. const deprecatedSettingsValue = deprecatedFlags[ normalizedPath ] ? deprecatedFlags[ normalizedPath ]( settings ) : undefined; @@ -155,13 +174,13 @@ export default function useSetting( path ) { return deprecatedSettingsValue; } - // 3 - Fall back for typography.dropCap: + // 4. Fallback for typography.dropCap: // This is only necessary to support typography.dropCap. // when __experimentalFeatures are not present (core without plugin). // To remove when __experimentalFeatures are ported to core. return normalizedPath === 'typography.dropCap' ? true : undefined; }, - [ blockName, path ] + [ blockName, clientId, path ] ); return setting;