diff --git a/lib/block-supports/border.php b/lib/block-supports/border.php new file mode 100644 index 0000000000000..085cdda187da0 --- /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 165589c0a569e..548134f3cd215 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 82c1bccf28e73..b2a1c31a639db 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 967c995ee413a..82686d85ca45f 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 0000000000000..f9d90b444b640 --- /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 0000000000000..228abd4083bfa --- /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 d533390250cb1..125a850836f10 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 533694be7282e..62c5a97b6e211 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 68a08552f16b0..8612244ff0984 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' ],