From eb2e4b511eb64e932e1f4997741e63e2ae15733a Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Thu, 27 Jan 2022 18:31:21 +1000 Subject: [PATCH] Clean up border attribute hooks --- .../block-editor/src/hooks/border-color.js | 361 ------------------ .../block-editor/src/hooks/border-style.js | 64 ---- .../block-editor/src/hooks/border-width.js | 139 ------- packages/block-editor/src/hooks/border.js | 241 ++++++++++++ packages/block-editor/src/hooks/index.js | 2 +- .../src/hooks/use-border-props.js | 3 +- 6 files changed, 243 insertions(+), 567 deletions(-) delete mode 100644 packages/block-editor/src/hooks/border-color.js delete mode 100644 packages/block-editor/src/hooks/border-style.js delete mode 100644 packages/block-editor/src/hooks/border-width.js diff --git a/packages/block-editor/src/hooks/border-color.js b/packages/block-editor/src/hooks/border-color.js deleted file mode 100644 index a0524a2a35f75..0000000000000 --- a/packages/block-editor/src/hooks/border-color.js +++ /dev/null @@ -1,361 +0,0 @@ -/** - * External dependencies - */ -import classnames from 'classnames'; - -/** - * WordPress dependencies - */ -import { addFilter } from '@wordpress/hooks'; -import { __ } from '@wordpress/i18n'; -import { createHigherOrderComponent } from '@wordpress/compose'; -import { useEffect, useState } from '@wordpress/element'; - -/** - * Internal dependencies - */ -import ColorGradientSettingsDropdown from '../components/colors-gradients/dropdown'; -import useMultipleOriginColorsAndGradients from '../components/colors-gradients/use-multiple-origin-colors-and-gradients'; -import { - getColorClassName, - getColorObjectByColorValue, - getColorObjectByAttributeValues, -} from '../components/colors'; -import useSetting from '../components/use-setting'; -import { - hasBorderSupport, - removeBorderAttribute, - shouldSkipSerialization, -} from './border'; -import { cleanEmptyObject } from './utils'; - -// Defining empty array here instead of inline avoids unnecessary re-renders of -// color control. -const EMPTY_ARRAY = []; - -/** - * Inspector control panel containing the border color related configuration. - * - * There is deliberate overlap between the colors and borders block supports - * relating to border color. It can be argued the border color controls could - * be included within either, or both, the colors and borders panels in the - * inspector controls. If they share the same block attributes it should not - * matter. - * - * @param {Object} props Block properties. - * - * @return {WPElement} Border color edit element. - */ -export function BorderColorEdit( props ) { - const { - attributes: { borderColor, style }, - setAttributes, - } = props; - const colorGradientSettings = useMultipleOriginColorsAndGradients(); - const availableColors = colorGradientSettings.colors.reduce( - ( colors, origin ) => colors.concat( origin.colors ), - [] - ); - const { color: customBorderColor } = style?.border || {}; - const [ colorValue, setColorValue ] = useState( - () => - getColorObjectByAttributeValues( - availableColors, - borderColor, - customBorderColor - )?.color - ); - - // Detect changes in the color attributes and update the colorValue to keep the - // UI in sync. This is necessary for situations when border controls interact with - // each other: eg, setting the border width to zero causes the color and style - // selections to be cleared. - useEffect( () => { - setColorValue( - getColorObjectByAttributeValues( - availableColors, - borderColor, - customBorderColor - )?.color - ); - }, [ borderColor, customBorderColor, availableColors ] ); - - const onChangeColor = ( value ) => { - setColorValue( value ); - - const colorObject = getColorObjectByColorValue( - availableColors, - value - ); - const newStyle = { - ...style, - border: { - ...style?.border, - color: colorObject?.slug ? undefined : value, - }, - }; - - // If empty slug, ensure undefined to remove attribute. - const newNamedColor = colorObject?.slug ? colorObject.slug : undefined; - - setAttributes( { - style: cleanEmptyObject( newStyle ), - borderColor: newNamedColor, - } ); - }; - - const settings = [ - { - label: __( 'Color' ), - onColorChange: onChangeColor, - colorValue, - clearable: false, - }, - ]; - return ( - - ); -} - -/** - * Checks if there is a current value in the border color block support - * attributes. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a border color value set. - */ -export function hasBorderColorValue( props ) { - const { - attributes: { borderColor, style }, - } = props; - - return !! borderColor || !! style?.border?.color; -} - -/** - * Resets the border color block support attributes. This can be used when - * disabling the border color support controls for a block via a progressive - * discovery panel. - * - * @param {Object} props Block props. - * @param {Object} props.attributes Block's attributes. - * @param {Object} props.setAttributes Function to set block's attributes. - */ -export function resetBorderColor( { attributes = {}, setAttributes } ) { - const { style } = attributes; - - setAttributes( { - borderColor: undefined, - style: removeBorderAttribute( style, 'color' ), - } ); -} - -/** - * Filters registered block settings, extending attributes to include - * `borderColor` if needed. - * - * @param {Object} settings Original block settings. - * - * @return {Object} Updated block settings. - */ -function addAttributes( settings ) { - if ( ! hasBorderSupport( settings, 'color' ) ) { - return settings; - } - - // Allow blocks to specify border color values if needed. - const { attributes } = settings; - - // Skip any adjustments if block already defines both border color - // attributes to set defaults etc. - if ( attributes.borderColor && attributes.sideBorderColors ) { - return settings; - } - - // If we are missing border color attribute definition, add it. - if ( ! attributes.borderColor ) { - return { - ...settings, - attributes: { - ...attributes, - borderColor: { type: 'string' }, - }, - }; - } - - // We are missing attribute for side border colors, add it to existing - // attribute definitions. - return { - ...settings, - attributes: { - ...attributes, - sideBorderColors: { type: 'object', default: null }, - }, - }; -} - -/** - * Override props assigned to save component to inject border color. - * - * @param {Object} props Additional props applied to save element. - * @param {Object} blockType Block type definition. - * @param {Object} attributes Block's attributes. - * - * @return {Object} Filtered props to apply to save element. - */ -function addSaveProps( props, blockType, attributes ) { - if ( - ! hasBorderSupport( blockType, 'color' ) || - shouldSkipSerialization( blockType ) - ) { - return props; - } - - const borderClasses = getBorderClasses( attributes ); - const newClassName = classnames( props.className, borderClasses ); - - // If we are clearing the last of the previous classes in `className` - // set it to `undefined` to avoid rendering empty DOM attributes. - props.className = newClassName ? newClassName : undefined; - - return props; -} - -export function getBorderClasses( attributes ) { - const { borderColor, style } = attributes; - const borderColorClass = getColorClassName( 'border-color', borderColor ); - - return classnames( { - 'has-border-color': borderColor || style?.border?.color, - [ borderColorClass ]: !! borderColorClass, - ...getSideBorderClasses( attributes ), - } ); -} - -/** - * Generates a collection of CSS classes for the block's current border color - * selections. The results are intended to be further processed via a call - * through `classnames()`. - * - * @param {Object} attributes Block attributes. - * @return {Object} CSS classes for side border colors. - */ -function getSideBorderClasses( attributes ) { - const { sideBorderColors, style } = attributes; - const sides = [ 'top', 'right', 'bottom', 'left' ]; - - return sides.reduce( ( classes, side ) => { - const color = sideBorderColors?.[ side ]; - const hasColor = color || style?.border?.[ side ]?.color; - const baseClassName = `border-${ side }-color`; - const colorClass = getColorClassName( baseClassName, color ); - - return { - ...classes, - [ `has-${ baseClassName }` ]: hasColor, - [ colorClass ]: !! colorClass, - }; - }, {} ); -} - -/** - * Filters the registered block settings to apply border color styles and - * classnames to the block edit wrapper. - * - * @param {Object} settings Original block settings. - * - * @return {Object} Filtered block settings. - */ -function addEditProps( settings ) { - if ( - ! hasBorderSupport( settings, 'color' ) || - shouldSkipSerialization( settings ) - ) { - return settings; - } - - const existingGetEditWrapperProps = settings.getEditWrapperProps; - settings.getEditWrapperProps = ( attributes ) => { - let props = {}; - - if ( existingGetEditWrapperProps ) { - props = existingGetEditWrapperProps( attributes ); - } - - return addSaveProps( props, settings, attributes ); - }; - - return settings; -} - -/** - * This adds inline styles for color palette colors. - * Ideally, this is not needed and themes should load their palettes on the editor. - * - * @param {Function} BlockListBlock Original component. - * - * @return {Function} Wrapped component. - */ -export const withBorderColorPaletteStyles = createHigherOrderComponent( - ( BlockListBlock ) => ( props ) => { - const { name, attributes } = props; - const { borderColor } = attributes; - const colors = useSetting( 'color.palette' ) || EMPTY_ARRAY; - - if ( - ! hasBorderSupport( name, 'color' ) || - shouldSkipSerialization( name ) - ) { - return ; - } - - const extraStyles = { - borderColor: borderColor - ? getColorObjectByAttributeValues( colors, borderColor )?.color - : undefined, - }; - - let wrapperProps = props.wrapperProps; - wrapperProps = { - ...props.wrapperProps, - style: { - ...extraStyles, - ...props.wrapperProps?.style, - }, - }; - - return ; - } -); - -addFilter( - 'blocks.registerBlockType', - 'core/border/addAttributes', - addAttributes -); - -addFilter( - 'blocks.getSaveContent.extraProps', - 'core/border/addSaveProps', - addSaveProps -); - -addFilter( - 'blocks.registerBlockType', - 'core/border/addEditProps', - addEditProps -); - -addFilter( - 'editor.BlockListBlock', - 'core/border/with-border-color-palette-styles', - withBorderColorPaletteStyles -); diff --git a/packages/block-editor/src/hooks/border-style.js b/packages/block-editor/src/hooks/border-style.js deleted file mode 100644 index 1a3a5f923383c..0000000000000 --- a/packages/block-editor/src/hooks/border-style.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Internal dependencies - */ -import BorderStyleControl from '../components/border-style-control'; -import { cleanEmptyObject } from './utils'; -import { removeBorderAttribute } from './border'; - -/** - * Inspector control for configuring border style property. - * - * @param {Object} props Block properties. - * - * @return {WPElement} Border style edit element. - */ -export const BorderStyleEdit = ( props ) => { - const { - attributes: { style }, - setAttributes, - } = props; - - const onChange = ( newBorderStyle ) => { - const newStyleAttributes = { - ...style, - border: { - ...style?.border, - style: newBorderStyle, - }, - }; - - setAttributes( { style: cleanEmptyObject( newStyleAttributes ) } ); - }; - - return ( - - ); -}; - -/** - * Checks if there is a current value in the border style block support - * attributes. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a border style value set. - */ -export function hasBorderStyleValue( props ) { - return !! props.attributes.style?.border?.style; -} - -/** - * Resets the border style block support attribute. This can be used when - * disabling the border style support control for a block via a progressive - * discovery panel. - * - * @param {Object} props Block props. - * @param {Object} props.attributes Block's attributes. - * @param {Object} props.setAttributes Function to set block's attributes. - */ -export function resetBorderStyle( { attributes = {}, setAttributes } ) { - const { style } = attributes; - setAttributes( { style: removeBorderAttribute( style, 'style' ) } ); -} diff --git a/packages/block-editor/src/hooks/border-width.js b/packages/block-editor/src/hooks/border-width.js deleted file mode 100644 index f6ce67d781f34..0000000000000 --- a/packages/block-editor/src/hooks/border-width.js +++ /dev/null @@ -1,139 +0,0 @@ -/** - * WordPress dependencies - */ -import { - __experimentalUnitControl as UnitControl, - __experimentalUseCustomUnits as useCustomUnits, -} from '@wordpress/components'; -import { useState } from '@wordpress/element'; -import { __ } from '@wordpress/i18n'; - -/** - * Internal dependencies - */ -import { cleanEmptyObject } from './utils'; -import { removeBorderAttribute } from './border'; -import useSetting from '../components/use-setting'; - -const MIN_BORDER_WIDTH = 0; - -/** - * Inspector control for configuring border width property. - * - * @param {Object} props Block properties. - * - * @return {WPElement} Border width edit element. - */ -export const BorderWidthEdit = ( props ) => { - const { - attributes: { borderColor, style }, - setAttributes, - } = props; - - const { width, color: customBorderColor, style: borderStyle } = - style?.border || {}; - - // Used to temporarily track previous border color & style selections to be - // able to restore them when border width changes from zero value. - const [ styleSelection, setStyleSelection ] = useState(); - const [ colorSelection, setColorSelection ] = useState(); - const [ customColorSelection, setCustomColorSelection ] = useState(); - - const onChange = ( newWidth ) => { - let newStyle = { - ...style, - border: { - ...style?.border, - width: newWidth, - }, - }; - - // Used to clear named border color attribute. - let borderPaletteColor = borderColor; - - const hasZeroWidth = parseFloat( newWidth ) === 0; - const hadPreviousZeroWidth = parseFloat( width ) === 0; - - // Setting the border width explicitly to zero will also set the - // border style to `none` and clear border color attributes. - if ( hasZeroWidth && ! hadPreviousZeroWidth ) { - // Before clearing color and style selections, keep track of - // the current selections so they can be restored when the width - // changes to a non-zero value. - setColorSelection( borderColor ); - setCustomColorSelection( customBorderColor ); - setStyleSelection( borderStyle ); - - // Clear style and color attributes. - borderPaletteColor = undefined; - newStyle.border.color = undefined; - newStyle.border.style = 'none'; - } - - if ( ! hasZeroWidth && hadPreviousZeroWidth ) { - // Restore previous border style selection if width is now not zero and - // border style was 'none'. This is to support changes to the UI which - // change the border style UI to a segmented control without a "none" - // option. - if ( borderStyle === 'none' ) { - newStyle.border.style = styleSelection; - } - - // Restore previous border color selection if width is no longer zero - // and current border color is undefined. - if ( borderColor === undefined ) { - borderPaletteColor = colorSelection; - newStyle.border.color = customColorSelection; - } - } - - // If width was reset, clean out undefined styles. - if ( newWidth === undefined || newWidth === '' ) { - newStyle = cleanEmptyObject( newStyle ); - } - - setAttributes( { - borderColor: borderPaletteColor, - style: newStyle, - } ); - }; - - const units = useCustomUnits( { - availableUnits: useSetting( 'spacing.units' ) || [ 'px', 'em', 'rem' ], - } ); - - return ( - - ); -}; - -/** - * Checks if there is a current value in the border width block support - * attributes. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a border width value set. - */ -export function hasBorderWidthValue( props ) { - return !! props.attributes.style?.border?.width; -} - -/** - * Resets the border width block support attribute. This can be used when - * disabling the border width support control for a block via a progressive - * discovery panel. - * - * @param {Object} props Block props. - * @param {Object} props.attributes Block's attributes. - * @param {Object} props.setAttributes Function to set block's attributes. - */ -export function resetBorderWidth( { attributes = {}, setAttributes } ) { - const { style } = attributes; - setAttributes( { style: removeBorderAttribute( style, 'width' ) } ); -} diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js index b96c13a6cbee5..5a3057808bcb3 100644 --- a/packages/block-editor/src/hooks/border.js +++ b/packages/block-editor/src/hooks/border.js @@ -1,3 +1,8 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + /** * WordPress dependencies */ @@ -8,7 +13,9 @@ import { isDefinedBorder, hasSplitBorders, } from '@wordpress/components'; +import { createHigherOrderComponent } from '@wordpress/compose'; import { Platform } from '@wordpress/element'; +import { addFilter } from '@wordpress/hooks'; import { __ } from '@wordpress/i18n'; /** @@ -19,6 +26,7 @@ import { hasBorderRadiusValue, resetBorderRadius, } from './border-radius'; +import { getColorClassName } from '../components/colors'; import InspectorControls from '../components/inspector-controls'; import useMultipleOriginColorsAndGradients from '../components/colors-gradients/use-multiple-origin-colors-and-gradients'; import useSetting from '../components/use-setting'; @@ -352,3 +360,236 @@ export function removeBorderAttribute( style, attribute ) { }, } ); } + +/** + * Filters registered block settings, extending attributes to include + * `borderColor` if needed. + * + * @param {Object} settings Original block settings. + * + * @return {Object} Updated block settings. + */ +function addAttributes( settings ) { + if ( ! hasBorderSupport( settings, 'color' ) ) { + return settings; + } + + // Allow blocks to specify border color values if needed. + const { attributes } = settings; + + // Skip any adjustments if block already defines both border color + // attributes to set defaults etc. + if ( attributes.borderColor && attributes.sideBorderColors ) { + return settings; + } + + // If we are missing border color attribute definition, add it. + if ( ! attributes.borderColor ) { + return { + ...settings, + attributes: { + ...attributes, + borderColor: { type: 'string' }, + }, + }; + } + + // We are missing attribute for side border colors, add it to existing + // attribute definitions. + return { + ...settings, + attributes: { + ...attributes, + sideBorderColors: { type: 'object' }, + }, + }; +} + +/** + * Override props assigned to save component to inject border color. + * + * @param {Object} props Additional props applied to save element. + * @param {Object} blockType Block type definition. + * @param {Object} attributes Block's attributes. + * + * @return {Object} Filtered props to apply to save element. + */ +function addSaveProps( props, blockType, attributes ) { + if ( + ! hasBorderSupport( blockType, 'color' ) || + shouldSkipSerialization( blockType ) + ) { + return props; + } + + const borderClasses = getBorderClasses( attributes ); + const newClassName = classnames( props.className, borderClasses ); + + // If we are clearing the last of the previous classes in `className` + // set it to `undefined` to avoid rendering empty DOM attributes. + props.className = newClassName ? newClassName : undefined; + + return props; +} + +/** + * Generates a CSS class name consisting of all the applicable border color + * classes given the current block attributes. + * + * @param {Object} attributes Block's attributes. + * + * @return {string} CSS class name. + */ +export function getBorderClasses( attributes ) { + const { borderColor, style } = attributes; + const borderColorClass = getColorClassName( 'border-color', borderColor ); + + return classnames( { + 'has-border-color': borderColor || style?.border?.color, + [ borderColorClass ]: !! borderColorClass, + ...getSideBorderClasses( attributes ), + } ); +} + +/** + * Generates a collection of CSS classes for the block's current border color + * selections. The results are intended to be further processed via a call + * through `classnames()`. + * + * @param {Object} attributes Block attributes. + * @return {Object} CSS classes for side border colors. + */ +function getSideBorderClasses( attributes ) { + const { sideBorderColors, style } = attributes; + const sides = [ 'top', 'right', 'bottom', 'left' ]; + + return sides.reduce( ( classes, side ) => { + const color = sideBorderColors?.[ side ]; + const hasColor = color || style?.border?.[ side ]?.color; + const baseClassName = `border-${ side }-color`; + const colorClass = getColorClassName( baseClassName, color ); + + return { + ...classes, + [ `has-${ baseClassName }` ]: hasColor, + [ colorClass ]: !! colorClass, + }; + }, {} ); +} + +/** + * Filters the registered block settings to apply border color styles and + * classnames to the block edit wrapper. + * + * @param {Object} settings Original block settings. + * + * @return {Object} Filtered block settings. + */ +function addEditProps( settings ) { + if ( + ! hasBorderSupport( settings, 'color' ) || + shouldSkipSerialization( settings ) + ) { + return settings; + } + + const existingGetEditWrapperProps = settings.getEditWrapperProps; + settings.getEditWrapperProps = ( attributes ) => { + let props = {}; + + if ( existingGetEditWrapperProps ) { + props = existingGetEditWrapperProps( attributes ); + } + + return addSaveProps( props, settings, attributes ); + }; + + return settings; +} + +/** + * This adds inline styles for color palette colors. + * Ideally, this is not needed and themes should load their palettes on the editor. + * + * @param {Function} BlockListBlock Original component. + * + * @return {Function} Wrapped component. + */ +export const withBorderColorPaletteStyles = createHigherOrderComponent( + ( BlockListBlock ) => ( props ) => { + const { name, attributes } = props; + const { borderColor, sideBorderColors } = attributes; + const { colors } = useMultipleOriginColorsAndGradients(); + + if ( + ! hasBorderSupport( name, 'color' ) || + shouldSkipSerialization( name ) + ) { + return ; + } + + const { color: borderColorValue } = getMultiOriginColor( { + colors, + namedColor: borderColor, + } ); + const { color: borderTopColor } = getMultiOriginColor( { + colors, + namedColor: sideBorderColors?.top, + } ); + const { color: borderRightColor } = getMultiOriginColor( { + colors, + namedColor: sideBorderColors?.right, + } ); + const { color: borderBottomColor } = getMultiOriginColor( { + colors, + namedColor: sideBorderColors?.bottom, + } ); + const { color: borderLeftColor } = getMultiOriginColor( { + colors, + namedColor: sideBorderColors?.left, + } ); + + const extraStyles = { + borderColor: borderColorValue, + borderTopColor, + borderRightColor, + borderBottomColor, + borderLeftColor, + }; + + let wrapperProps = props.wrapperProps; + wrapperProps = { + ...props.wrapperProps, + style: { + ...extraStyles, + ...props.wrapperProps?.style, + }, + }; + + return ; + } +); + +addFilter( + 'blocks.registerBlockType', + 'core/border/addAttributes', + addAttributes +); + +addFilter( + 'blocks.getSaveContent.extraProps', + 'core/border/addSaveProps', + addSaveProps +); + +addFilter( + 'blocks.registerBlockType', + 'core/border/addEditProps', + addEditProps +); + +addFilter( + 'editor.BlockListBlock', + 'core/border/with-border-color-palette-styles', + withBorderColorPaletteStyles +); diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index 708dae62a3971..b18a175973c1d 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -11,7 +11,7 @@ import './style'; import './color'; import './duotone'; import './font-size'; -import './border-color'; +import './border'; import './layout'; export { useCustomSides } from './dimensions'; diff --git a/packages/block-editor/src/hooks/use-border-props.js b/packages/block-editor/src/hooks/use-border-props.js index cb78f0bfc148e..6a4ba6feddc69 100644 --- a/packages/block-editor/src/hooks/use-border-props.js +++ b/packages/block-editor/src/hooks/use-border-props.js @@ -2,8 +2,7 @@ * Internal dependencies */ import { getInlineStyles } from './style'; -import { getBorderClasses } from './border-color'; -import { getMultiOriginColor } from './border'; +import { getBorderClasses, getMultiOriginColor } from './border'; import useMultipleOriginColorsAndGradients from '../components/colors-gradients/use-multiple-origin-colors-and-gradients'; // This utility is intended to assist where the serialization of the border