diff --git a/packages/block-editor/src/components/block-inspector/index.js b/packages/block-editor/src/components/block-inspector/index.js index 425b527e96eac8..46bcd7baba5232 100644 --- a/packages/block-editor/src/components/block-inspector/index.js +++ b/packages/block-editor/src/components/block-inspector/index.js @@ -138,6 +138,11 @@ const BlockInspectorSingleBlock = ( { bubblesVirtually={ bubblesVirtually } label={ __( 'Typography' ) } /> + ); } + +/** + * Checks if there is a current value in the border radius block support + * attributes. + * + * @param {Object} props Block props. + * @return {boolean} Whether or not the block has a border radius value set. + */ +export function hasBorderRadiusValue( props ) { + const borderRadius = props.attributes.style?.border?.radius; + + if ( typeof borderRadius === 'object' ) { + return Object.entries( borderRadius ).some( Boolean ); + } + + return !! borderRadius; +} + +/** + * Resets the border radius block support attributes. This can be used when + * disabling the border radius 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 resetBorderRadius( { attributes = {}, setAttributes } ) { + const { style } = attributes; + setAttributes( { style: removeBorderAttribute( style, 'radius' ) } ); +} diff --git a/packages/block-editor/src/hooks/border-style.js b/packages/block-editor/src/hooks/border-style.js index feaac4c694389a..1a3a5f923383c7 100644 --- a/packages/block-editor/src/hooks/border-style.js +++ b/packages/block-editor/src/hooks/border-style.js @@ -3,6 +3,7 @@ */ import BorderStyleControl from '../components/border-style-control'; import { cleanEmptyObject } from './utils'; +import { removeBorderAttribute } from './border'; /** * Inspector control for configuring border style property. @@ -36,3 +37,28 @@ export const BorderStyleEdit = ( props ) => { /> ); }; + +/** + * 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 index 4a08e240cef607..29c4ddbda28fc4 100644 --- a/packages/block-editor/src/hooks/border-width.js +++ b/packages/block-editor/src/hooks/border-width.js @@ -12,6 +12,7 @@ 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; @@ -113,3 +114,28 @@ export const BorderWidthEdit = ( props ) => { /> ); }; + +/** + * 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 761c04200a5319..30c16339c27fe8 100644 --- a/packages/block-editor/src/hooks/border.js +++ b/packages/block-editor/src/hooks/border.js @@ -2,23 +2,41 @@ * WordPress dependencies */ import { getBlockSupport } from '@wordpress/blocks'; -import { PanelBody } from '@wordpress/components'; +import { __experimentalToolsPanelItem as ToolsPanelItem } from '@wordpress/components'; import { Platform } from '@wordpress/element'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ +import { + BorderColorEdit, + hasBorderColorValue, + resetBorderColor, +} from './border-color'; +import { + BorderRadiusEdit, + hasBorderRadiusValue, + resetBorderRadius, +} from './border-radius'; +import { + BorderStyleEdit, + hasBorderStyleValue, + resetBorderStyle, +} from './border-style'; +import { + BorderWidthEdit, + hasBorderWidthValue, + resetBorderWidth, +} from './border-width'; import InspectorControls from '../components/inspector-controls'; import useSetting from '../components/use-setting'; -import { BorderColorEdit } from './border-color'; -import { BorderRadiusEdit } from './border-radius'; -import { BorderStyleEdit } from './border-style'; -import { BorderWidthEdit } from './border-width'; +import { cleanEmptyObject } from './utils'; export const BORDER_SUPPORT_KEY = '__experimentalBorder'; export function BorderPanel( props ) { + const { clientId } = props; const isDisabled = useIsBorderDisabled( props ); const isSupported = hasBorderSupport( props.name ); @@ -39,22 +57,80 @@ export function BorderPanel( props ) { return null; } + const defaultBorderControls = getBlockSupport( props.name, [ + BORDER_SUPPORT_KEY, + '__experimentalDefaultControls', + ] ); + + const createResetAllFilter = ( + borderAttribute, + topLevelAttributes = {} + ) => ( newAttributes ) => ( { + ...newAttributes, + ...topLevelAttributes, + style: { + ...newAttributes.style, + border: { + ...newAttributes.style?.border, + [ borderAttribute ]: undefined, + }, + }, + } ); + return ( - - - { ( isWidthSupported || isStyleSupported ) && ( -
- { isWidthSupported && } - { isStyleSupported && } -
- ) } - { isColorSupported && } - { isRadiusSupported && } -
+ + { isWidthSupported && ( + hasBorderWidthValue( props ) } + label={ __( 'Width' ) } + onDeselect={ () => resetBorderWidth( props ) } + isShownByDefault={ defaultBorderControls?.width } + resetAllFilter={ createResetAllFilter( 'width' ) } + panelId={ clientId } + > + + + ) } + { isStyleSupported && ( + hasBorderStyleValue( props ) } + label={ __( 'Style' ) } + onDeselect={ () => resetBorderStyle( props ) } + isShownByDefault={ defaultBorderControls?.style } + resetAllFilter={ createResetAllFilter( 'style' ) } + panelId={ clientId } + > + + + ) } + { isColorSupported && ( + hasBorderColorValue( props ) } + label={ __( 'Color' ) } + onDeselect={ () => resetBorderColor( props ) } + isShownByDefault={ defaultBorderControls?.color } + resetAllFilter={ createResetAllFilter( 'color', { + borderColor: undefined, + } ) } + panelId={ clientId } + > + + + ) } + { isRadiusSupported && ( + hasBorderRadiusValue( props ) } + label={ __( 'Radius' ) } + onDeselect={ () => resetBorderRadius( props ) } + isShownByDefault={ defaultBorderControls?.radius } + resetAllFilter={ createResetAllFilter( 'radius' ) } + panelId={ clientId } + > + + + ) } ); } @@ -118,3 +194,22 @@ const useIsBorderDisabled = () => { return configs.every( Boolean ); }; + +/** + * Returns a new style object where the specified border attribute has been + * removed. + * + * @param {Object} style Styles from block attributes. + * @param {string} attribute The border style attribute to clear. + * + * @return {Object} Style object with the specified attribute removed. + */ +export function removeBorderAttribute( style, attribute ) { + return cleanEmptyObject( { + ...style, + border: { + ...style?.border, + [ attribute ]: undefined, + }, + } ); +} diff --git a/packages/block-editor/src/hooks/border.scss b/packages/block-editor/src/hooks/border.scss index 6c859252afc34e..c86c046309d886 100644 --- a/packages/block-editor/src/hooks/border.scss +++ b/packages/block-editor/src/hooks/border.scss @@ -1,19 +1,5 @@ -.block-editor-hooks__border-controls { - .block-editor-hooks__border-controls-row { - display: flex; - justify-content: space-between; - - > * { - width: calc(50% - #{ $grid-unit-10 }); - } - } - - .components-unit-control-wrapper { - margin-bottom: $grid-unit-30; - - &:last-child { - margin-bottom: $grid-unit-10; - } +.border-block-support-panel { + .single-column { + grid-column: span 1; } } - diff --git a/packages/block-library/src/common.scss b/packages/block-library/src/common.scss index b76682ccf6b347..4111181e551133 100644 --- a/packages/block-library/src/common.scss +++ b/packages/block-library/src/common.scss @@ -89,3 +89,22 @@ width: auto; z-index: 100000; } + +/** + * The following provide a simple means of applying a default border style when + * a user first makes a selection in the border block support panel. + * This prevents issues such as where the user could set a border width + * and see no border due there being no border style set. + * + * This is intended to be removed once intelligent defaults can be set while + * making border selections via the block support. + * + * See: https://github.com/WordPress/gutenberg/pull/33743 + */ +html :where(.has-border-color) { + border-style: solid; +} + +html :where([style*="border-width"]) { + border-style: solid; +} diff --git a/packages/edit-site/src/components/global-styles/border-panel.js b/packages/edit-site/src/components/global-styles/border-panel.js index 14ce8e798cdc74..f8dafd5584368f 100644 --- a/packages/edit-site/src/components/global-styles/border-panel.js +++ b/packages/edit-site/src/components/global-styles/border-panel.js @@ -7,7 +7,8 @@ import { __experimentalColorGradientControl as ColorGradientControl, } from '@wordpress/block-editor'; import { - PanelBody, + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, __experimentalUnitControl as UnitControl, __experimentalUseCustomUnits as useCustomUnits, } from '@wordpress/components'; @@ -68,6 +69,18 @@ function useHasBorderWidthControl( name ) { } export default function BorderPanel( { name } ) { + // To better reflect if the user has customized a value we need to + // ensure the style value being checked is from the `user` origin. + const [ userBorderStyles ] = useStyle( 'border', name, 'user' ); + const createHasValueCallback = ( feature ) => () => + !! userBorderStyles?.[ feature ]; + + const createResetCallback = ( setStyle ) => () => setStyle( undefined ); + + const handleOnChange = ( setStyle ) => ( value ) => { + setStyle( value || undefined ); + }; + const units = useCustomUnits( { availableUnits: useSetting( 'spacing.units' )[ 0 ] || [ 'px', @@ -77,70 +90,121 @@ export default function BorderPanel( { name } ) { } ); // Border width. - const hasBorderWidth = useHasBorderWidthControl( name ); + const showBorderWidth = useHasBorderWidthControl( name ); const [ borderWidthValue, setBorderWidth ] = useStyle( 'border.width', name ); // Border style. - const hasBorderStyle = useHasBorderStyleControl( name ); + const showBorderStyle = useHasBorderStyleControl( name ); const [ borderStyle, setBorderStyle ] = useStyle( 'border.style', name ); // Border color. + const showBorderColor = useHasBorderColorControl( name ); + const [ borderColor, setBorderColor ] = useStyle( 'border.color', name ); const [ colors = EMPTY_ARRAY ] = useSetting( 'color.palette' ); const disableCustomColors = ! useSetting( 'color.custom' )[ 0 ]; const disableCustomGradients = ! useSetting( 'color.customGradient' )[ 0 ]; - const hasBorderColor = useHasBorderColorControl( name ); - const [ borderColor, setBorderColor ] = useStyle( 'border.color', name ); // Border radius. - const hasBorderRadius = useHasBorderRadiusControl( name ); + const showBorderRadius = useHasBorderRadiusControl( name ); const [ borderRadiusValues, setBorderRadius ] = useStyle( 'border.radius', name ); + const hasBorderRadius = () => { + const borderValues = userBorderStyles?.radius; + if ( typeof borderValues === 'object' ) { + return Object.entries( borderValues ).some( Boolean ); + } + return !! borderValues; + }; + + const resetAll = () => { + setBorderColor( undefined ); + setBorderRadius( undefined ); + setBorderStyle( undefined ); + setBorderWidth( undefined ); + }; + + // When we set a border color or width ensure we have a style so the user + // can see a visible border. + const handleOnChangeWithStyle = ( setStyle ) => ( value ) => { + if ( !! value && ! borderStyle ) { + setBorderStyle( 'solid' ); + } + + setStyle( value || undefined ); + }; return ( - - { ( hasBorderWidth || hasBorderStyle ) && ( -
- { hasBorderWidth && ( - { - setBorderWidth( value || undefined ); - } } - units={ units } - /> - ) } - { hasBorderStyle && ( - - ) } -
+ + { showBorderWidth && ( + + + + ) } + { showBorderStyle && ( + + + ) } - { hasBorderColor && ( - + onDeselect={ createResetCallback( setBorderColor ) } + isShownByDefault={ true } + > + + ) } - { hasBorderRadius && ( - + { showBorderRadius && ( + + + ) } -
+ ); } diff --git a/packages/edit-site/src/components/sidebar/style.scss b/packages/edit-site/src/components/sidebar/style.scss index 9744b269a073e5..b6189e4d914417 100644 --- a/packages/edit-site/src/components/sidebar/style.scss +++ b/packages/edit-site/src/components/sidebar/style.scss @@ -25,20 +25,6 @@ margin-left: auto; } -.edit-site-global-styles-sidebar__border-controls-row { - display: flex; - justify-content: space-between; - margin-bottom: $grid-unit-15; - - > * { - width: calc(50% - #{ $grid-unit-10 }); - } - - .components-border-style-control__buttons { - margin-bottom: 0; - } -} - .edit-site-global-styles-sidebar .components-navigation__menu-title-heading { font-size: $default-font-size * 1.2; font-weight: 500; @@ -53,6 +39,10 @@ border: 0; } +.edit-site-global-styles-sidebar .components-tools-panel-item.single-column { + grid-column: span 1; +} + .edit-site-global-styles-sidebar__blocks-group { padding-top: $grid-unit-30; border-top: $border-width solid $gray-200;