From 229beaa59a49bde1db909bd00e37ac98d8bb5842 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 11 Dec 2020 15:25:13 +1000 Subject: [PATCH] Add block support for borders (#25791) Border block support has been added following the examples set by the Typography tools and Colors support. This is intended to be extended later to include border width as an option as well. --- lib/block-supports/border.php | 89 +++++++++++++++++++ lib/class-wp-theme-json.php | 8 ++ lib/experimental-default-theme.json | 3 + lib/load.php | 1 + .../block-editor/src/hooks/border-radius.js | 79 ++++++++++++++++ packages/block-editor/src/hooks/border.js | 67 ++++++++++++++ packages/block-editor/src/hooks/style.js | 3 + packages/block-editor/src/hooks/test/style.js | 2 + packages/blocks/src/api/constants.js | 4 + 9 files changed, 256 insertions(+) create mode 100644 lib/block-supports/border.php create mode 100644 packages/block-editor/src/hooks/border-radius.js create mode 100644 packages/block-editor/src/hooks/border.js diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php new file mode 100644 index 00000000000000..085cdda187da0c --- /dev/null +++ b/lib/block-supports/border.php @@ -0,0 +1,89 @@ +attributes ) { + $block_type->attributes = array(); + } + + if ( $has_border_radius_support && ! array_key_exists( 'style', $block_type->attributes ) ) { + $block_type->attributes['style'] = array( + 'type' => 'object', + ); + } +} + +/** + * Adds CSS classes and inline styles for border styles to the incoming + * attributes array. This will be applied to the block markup in the front-end. + * + * @param WP_Block_type $block_type Block type. + * @param array $block_attributes Block attributes. + * + * @return array Border CSS classes and inline styles. + */ +function gutenberg_apply_border_support( $block_type, $block_attributes ) { + // Arrays used to ease addition of further border related features in future. + $styles = array(); + + // Border Radius. + if ( gutenberg_has_border_support( $block_type, 'radius' ) ) { + if ( isset( $block_attributes['style']['border']['radius'] ) ) { + $border_radius = intval( $block_attributes['style']['border']['radius'] ); + $styles[] = sprintf( 'border-radius: %dpx;', $border_radius ); + } + } + + // Border width, style etc can be added here. + + // Collect classes and styles. + $attributes = array(); + + if ( ! empty( $styles ) ) { + $attributes['style'] = implode( ' ', $styles ); + } + + return $attributes; +} + +/** + * Checks whether the current block type supports the feature requested. + * + * @param WP_Block_Type $block_type Block type to check for support. + * @param string $feature Name of the feature to check support for. + * @param mixed $default Fallback value for feature support, defaults to false. + * + * @return boolean Whether or not the feature is supported. + */ +function gutenberg_has_border_support( $block_type, $feature, $default = false ) { + $block_support = false; + if ( property_exists( $block_type, 'supports' ) ) { + $block_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalBorder' ), $default ); + } + + return true === $block_support || ( is_array( $block_support ) && gutenberg_experimental_get( $block_support, array( $feature ), false ) ); +} + +// Register the block support. +WP_Block_Supports::get_instance()->register( + 'border', + array( + 'register_attribute' => 'gutenberg_register_border_support', + 'apply' => 'gutenberg_apply_border_support', + ) +); diff --git a/lib/class-wp-theme-json.php b/lib/class-wp-theme-json.php index 165589c0a569ef..548134f3cd2157 100644 --- a/lib/class-wp-theme-json.php +++ b/lib/class-wp-theme-json.php @@ -50,6 +50,7 @@ class WP_Theme_JSON { '--wp--style--color--link', 'background', 'backgroundColor', + 'border', 'color', 'fontFamily', 'fontSize', @@ -114,6 +115,9 @@ class WP_Theme_JSON { ), ), 'settings' => array( + 'border' => array( + 'customRadius' => null, + ), 'color' => array( 'custom' => null, 'customGradient' => null, @@ -284,6 +288,10 @@ class WP_Theme_JSON { 'value' => array( 'color', 'background' ), 'support' => array( 'color' ), ), + 'borderRadius' => array( + 'value' => array( 'border', 'radius' ), + 'support' => array( '__experimentalBorder' ), + ), 'color' => array( 'value' => array( 'color', 'text' ), 'support' => array( 'color' ), diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json index 82c1bccf28e738..b2a1c31a639dbe 100644 --- a/lib/experimental-default-theme.json +++ b/lib/experimental-default-theme.json @@ -250,6 +250,9 @@ "spacing": { "customPadding": false, "units": [ "px", "em", "rem", "vh", "vw" ] + }, + "border": { + "customRadius": true } } } diff --git a/lib/load.php b/lib/load.php index 967c995ee413a8..82686d85ca45f8 100644 --- a/lib/load.php +++ b/lib/load.php @@ -135,3 +135,4 @@ function gutenberg_is_experiment_enabled( $name ) { require __DIR__ . '/block-supports/align.php'; require __DIR__ . '/block-supports/typography.php'; require __DIR__ . '/block-supports/custom-classname.php'; +require __DIR__ . '/block-supports/border.php'; diff --git a/packages/block-editor/src/hooks/border-radius.js b/packages/block-editor/src/hooks/border-radius.js new file mode 100644 index 00000000000000..f9d90b444b640c --- /dev/null +++ b/packages/block-editor/src/hooks/border-radius.js @@ -0,0 +1,79 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; +import { RangeControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import useEditorFeature from '../components/use-editor-feature'; +import { BORDER_SUPPORT_KEY } from './border'; +import { cleanEmptyObject } from './utils'; + +const MIN_BORDER_RADIUS_VALUE = 0; +const MAX_BORDER_RADIUS_VALUE = 50; + +/** + * Inspector control panel containing the border radius related configuration. + * + * @param {Object} props Block properties. + * @return {WPElement} Border radius edit element. + */ +export function BorderRadiusEdit( props ) { + const { + attributes: { style }, + setAttributes, + } = props; + + if ( useIsBorderRadiusDisabled( props ) ) { + return null; + } + + const onChange = ( newRadius ) => { + const newStyle = { + ...style, + border: { + ...style?.border, + radius: newRadius, + }, + }; + + setAttributes( { style: cleanEmptyObject( newStyle ) } ); + }; + + return ( + + ); +} + +/** + * Determines if there is border radius support. + * + * @param {string|Object} blockType Block name or Block Type object. + * @return {boolean} Whether there is support. + */ +export function hasBorderRadiusSupport( blockType ) { + const support = getBlockSupport( blockType, BORDER_SUPPORT_KEY ); + return true === support || ( support && !! support.radius ); +} + +/** + * Custom hook that checks if border radius settings have been disabled. + * + * @param {string} name The name of the block. + * @return {boolean} Whether border radius setting is disabled. + */ +export function useIsBorderRadiusDisabled( { name: blockName } = {} ) { + const isDisabled = ! useEditorFeature( 'border.customRadius' ); + return ! hasBorderRadiusSupport( blockName ) || isDisabled; +} diff --git a/packages/block-editor/src/hooks/border.js b/packages/block-editor/src/hooks/border.js new file mode 100644 index 00000000000000..228abd4083bfa5 --- /dev/null +++ b/packages/block-editor/src/hooks/border.js @@ -0,0 +1,67 @@ +/** + * WordPress dependencies + */ +import { getBlockSupport } from '@wordpress/blocks'; +import { PanelBody } from '@wordpress/components'; +import { Platform } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import InspectorControls from '../components/inspector-controls'; +import { BorderRadiusEdit, useIsBorderRadiusDisabled } from './border-radius'; + +export const BORDER_SUPPORT_KEY = '__experimentalBorder'; + +export function BorderPanel( props ) { + const isDisabled = useIsBorderDisabled( props ); + const isSupported = hasBorderSupport( props.name ); + + if ( isDisabled || ! isSupported ) { + return null; + } + + return ( + + + + + + ); +} + +/** + * Determine whether there is block support for borders. + * + * @param {string} blockName Block name. + * @return {boolean} Whether there is support. + */ +export function hasBorderSupport( blockName ) { + if ( Platform.OS !== 'web' ) { + return false; + } + + const support = getBlockSupport( blockName, BORDER_SUPPORT_KEY ); + + // Further border properties to be added in future iterations. + // e.g. support && ( support.radius || support.width || support.style ) + return true === support || ( support && support.radius ); +} + +/** + * Determines whether there is any block support for borders e.g. border radius, + * style, width etc. + * + * @param {Object} props Block properties. + * @return {boolean} If border support is completely disabled. + */ +const useIsBorderDisabled = ( props = {} ) => { + // Further border properties to be added in future iterations. + // e.g. const configs = [ + // useIsBorderRadiusDisabled( props ), + // useIsBorderWidthDisabled( props ), + // ]; + const configs = [ useIsBorderRadiusDisabled( props ) ]; + return configs.filter( Boolean ).length === configs.length; +}; diff --git a/packages/block-editor/src/hooks/style.js b/packages/block-editor/src/hooks/style.js index d533390250cb1b..125a850836f103 100644 --- a/packages/block-editor/src/hooks/style.js +++ b/packages/block-editor/src/hooks/style.js @@ -16,6 +16,7 @@ import { createHigherOrderComponent } from '@wordpress/compose'; /** * Internal dependencies */ +import { BORDER_SUPPORT_KEY, BorderPanel } from './border'; import { COLOR_SUPPORT_KEY, ColorEdit } from './color'; import { TypographyPanel, TYPOGRAPHY_SUPPORT_KEYS } from './typography'; import { SPACING_SUPPORT_KEY, PaddingEdit } from './padding'; @@ -23,6 +24,7 @@ import SpacingPanelControl from '../components/spacing-panel-control'; const styleSupportKeys = [ ...TYPOGRAPHY_SUPPORT_KEYS, + BORDER_SUPPORT_KEY, COLOR_SUPPORT_KEY, SPACING_SUPPORT_KEY, ]; @@ -156,6 +158,7 @@ export const withBlockControls = createHigherOrderComponent( return [ , + , , , hasSpacingSupport && ( diff --git a/packages/block-editor/src/hooks/test/style.js b/packages/block-editor/src/hooks/test/style.js index 533694be7282ed..62c5a97b6e211e 100644 --- a/packages/block-editor/src/hooks/test/style.js +++ b/packages/block-editor/src/hooks/test/style.js @@ -17,9 +17,11 @@ describe( 'getInlineStyles', () => { getInlineStyles( { color: { text: 'red', background: 'black' }, typography: { lineHeight: 1.5, fontSize: 10 }, + border: { radius: 10 }, } ) ).toEqual( { backgroundColor: 'black', + borderRadius: 10, color: 'red', lineHeight: 1.5, fontSize: 10, diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index 68a08552f16b09..8612244ff09840 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -25,6 +25,10 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { value: [ 'color', 'background' ], support: [ 'color' ], }, + borderRadius: { + value: [ 'border', 'radius' ], + support: [ '__experimentalBorder', 'radius' ], + }, color: { value: [ 'color', 'text' ], support: [ 'color' ],