UNPKG

@wordpress/block-editor

Version:
228 lines (201 loc) 5.91 kB
/** * External dependencies */ import { capitalize, get, has, omit, omitBy, startsWith } from 'lodash'; /** * WordPress dependencies */ import { addFilter } from '@wordpress/hooks'; import { getBlockSupport, hasBlockSupport, __EXPERIMENTAL_STYLE_PROPERTY as STYLE_PROPERTY, } from '@wordpress/blocks'; import { createHigherOrderComponent } from '@wordpress/compose'; /** * Internal dependencies */ import { BORDER_SUPPORT_KEY, BorderPanel } from './border'; import { COLOR_SUPPORT_KEY, ColorEdit } from './color'; import { FONT_SIZE_SUPPORT_KEY } from './font-size'; import { TypographyPanel, TYPOGRAPHY_SUPPORT_KEYS } from './typography'; import { SPACING_SUPPORT_KEY, SpacingPanel } from './spacing'; import useDisplayBlockControls from '../components/use-display-block-controls'; const styleSupportKeys = [ ...TYPOGRAPHY_SUPPORT_KEYS, BORDER_SUPPORT_KEY, COLOR_SUPPORT_KEY, SPACING_SUPPORT_KEY, ]; const hasStyleSupport = ( blockType ) => styleSupportKeys.some( ( key ) => hasBlockSupport( blockType, key ) ); const VARIABLE_REFERENCE_PREFIX = 'var:'; const VARIABLE_PATH_SEPARATOR_TOKEN_ATTRIBUTE = '|'; const VARIABLE_PATH_SEPARATOR_TOKEN_STYLE = '--'; function compileStyleValue( uncompiledValue ) { 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; } /** * Returns the inline styles to add depending on the style object * * @param {Object} styles Styles configuration * @return {Object} Flattened CSS variables declaration */ export function getInlineStyles( styles = {} ) { const output = {}; Object.keys( STYLE_PROPERTY ).forEach( ( propKey ) => { const path = STYLE_PROPERTY[ propKey ].value; const subPaths = STYLE_PROPERTY[ propKey ].properties; if ( has( styles, path ) ) { if ( !! subPaths ) { subPaths.forEach( ( suffix ) => { output[ propKey + capitalize( suffix ) ] = compileStyleValue( get( styles, [ ...path, suffix ] ) ); } ); } else { output[ propKey ] = compileStyleValue( get( styles, path ) ); } } } ); return output; } /** * Filters registered block settings, extending attributes to include `style` attribute. * * @param {Object} settings Original block settings * @return {Object} Filtered block settings */ function addAttribute( settings ) { if ( ! hasStyleSupport( settings ) ) { return settings; } // allow blocks to specify their own attribute definition with default values if needed. if ( ! settings.attributes.style ) { Object.assign( settings.attributes, { style: { type: 'object', }, } ); } return settings; } /** * Filters a style object returning only the keys * that are serializable for a given block. * * @param {Object} style Input style object to filter. * @param {Object} blockSupports Info about block supports. * @return {Object} Filtered style. */ export function omitKeysNotToSerialize( style, blockSupports ) { return omitBy( style, ( value, key ) => !! blockSupports[ key ]?.__experimentalSkipSerialization ); } /** * Override props assigned to save component to inject the CSS variables definition. * * @param {Object} props Additional props applied to save element * @param {Object} blockType Block type * @param {Object} attributes Block attributes * @return {Object} Filtered props applied to save element */ export function addSaveProps( props, blockType, attributes ) { if ( ! hasStyleSupport( blockType ) ) { return props; } const { style } = attributes; let filteredStyle = omitKeysNotToSerialize( style, { border: getBlockSupport( blockType, BORDER_SUPPORT_KEY ), [ COLOR_SUPPORT_KEY ]: getBlockSupport( blockType, COLOR_SUPPORT_KEY ), } ); if ( getBlockSupport( blockType, '__experimentalSkipFontSizeSerialization' ) ) { filteredStyle = omit( filteredStyle, [ [ 'typography', FONT_SIZE_SUPPORT_KEY ], ] ); } props.style = { ...getInlineStyles( filteredStyle ), ...props.style, }; return props; } /** * Filters registered block settings to extend the block edit wrapper * to apply the desired styles and classnames properly. * * @param {Object} settings Original block settings * @return {Object} Filtered block settings */ export function addEditProps( settings ) { if ( ! hasStyleSupport( settings ) ) { return settings; } const existingGetEditWrapperProps = settings.getEditWrapperProps; settings.getEditWrapperProps = ( attributes ) => { let props = {}; if ( existingGetEditWrapperProps ) { props = existingGetEditWrapperProps( attributes ); } return addSaveProps( props, settings, attributes ); }; return settings; } /** * Override the default edit UI to include new inspector controls for * all the custom styles configs. * * @param {Function} BlockEdit Original component * @return {Function} Wrapped component */ export const withBlockControls = createHigherOrderComponent( ( BlockEdit ) => ( props ) => { const shouldDisplayControls = useDisplayBlockControls(); return ( <> { shouldDisplayControls && ( <> <TypographyPanel { ...props } /> <BorderPanel { ...props } /> <ColorEdit { ...props } /> <SpacingPanel { ...props } /> </> ) } <BlockEdit { ...props } /> </> ); }, 'withToolbarControls' ); addFilter( 'blocks.registerBlockType', 'core/style/addAttribute', addAttribute ); addFilter( 'blocks.getSaveContent.extraProps', 'core/style/addSaveProps', addSaveProps ); addFilter( 'blocks.registerBlockType', 'core/style/addEditProps', addEditProps ); addFilter( 'editor.BlockEdit', 'core/style/with-block-controls', withBlockControls );