From 37a48d2e2c3515a28335267189755a4c30ba1612 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Fri, 3 Mar 2023 16:08:54 +1000 Subject: [PATCH] Add text columns typography block support --- docs/how-to-guides/themes/theme-json.md | 2 + .../theme-json-reference/theme-json-living.md | 2 + .../theme-json-migrations.md | 2 + lib/block-supports/typography.php | 8 + lib/class-wp-theme-json-gutenberg.php | 3 + lib/compat/wordpress-6.3/theme.json | 428 ++++++++++++++++++ .../src/components/global-styles/hooks.js | 13 +- .../global-styles/typography-panel.js | 49 +- packages/block-editor/src/hooks/supports.js | 6 + packages/block-editor/src/hooks/test/style.js | 3 +- .../src/hooks/test/use-typography-props.js | 2 + .../block-editor/src/hooks/text-columns.js | 109 +++++ packages/block-editor/src/hooks/typography.js | 2 + packages/block-editor/src/hooks/utils.js | 3 + packages/blocks/src/api/constants.js | 5 + .../blocks/src/store/private-selectors.js | 5 + phpunit/class-wp-theme-json-test.php | 15 +- schemas/json/theme.json | 9 + 18 files changed, 658 insertions(+), 8 deletions(-) create mode 100644 lib/compat/wordpress-6.3/theme.json create mode 100644 packages/block-editor/src/hooks/text-columns.js diff --git a/docs/how-to-guides/themes/theme-json.md b/docs/how-to-guides/themes/theme-json.md index 8e2b60a7693eb6..be7489d78c05e0 100644 --- a/docs/how-to-guides/themes/theme-json.md +++ b/docs/how-to-guides/themes/theme-json.md @@ -289,6 +289,7 @@ The settings section has the following structure: "fontWeight": true, "letterSpacing": true, "lineHeight": false, + "textColumns": false, "textDecoration": true, "textTransform": true }, @@ -824,6 +825,7 @@ Each block declares which style properties it exposes via the [block supports me "fontWeight": "value", "letterSpacing": "value", "lineHeight": "value", + "textColumns": "value", "textDecoration": "value", "textTransform": "value" }, diff --git a/docs/reference-guides/theme-json-reference/theme-json-living.md b/docs/reference-guides/theme-json-reference/theme-json-living.md index acc0a499ec61b0..c8a59c5e10f0c0 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -134,6 +134,7 @@ Settings related to typography. | fluid | undefined | false | | | letterSpacing | boolean | true | | | lineHeight | boolean | false | | +| textColumns | boolean | false | | | textDecoration | boolean | true | | | textTransform | boolean | true | | | dropCap | boolean | true | | @@ -204,6 +205,7 @@ Typography styles. | fontWeight | string, object | | | letterSpacing | string, object | | | lineHeight | string, object | | +| textColumns | string | | | textDecoration | string, object | | | textTransform | string, object | | diff --git a/docs/reference-guides/theme-json-reference/theme-json-migrations.md b/docs/reference-guides/theme-json-reference/theme-json-migrations.md index cac253f64ffbe9..b043ca1fba52ac 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-migrations.md +++ b/docs/reference-guides/theme-json-reference/theme-json-migrations.md @@ -41,6 +41,7 @@ Additions to settings: - `settings.typography.fontStyle` - `settings.typography.fontWeight` - `settings.typography.letterSpacing` +- `settings.typography.textColumns` - `settings.typography.textDecoration` - `settings.typography.textTransform` @@ -55,6 +56,7 @@ Additions to styles: - `styles.typography.fontStyle` - `styles.typography.fontWeight` - `styles.typography.letterSpacing` +- `styles.typography.textColumns` - `styles.typography.textDecoration` - `styles.typography.textTransform` diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index c2e033eb176778..a7a26423e32d6a 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -26,6 +26,7 @@ function gutenberg_register_typography_support( $block_type ) { $has_font_weight_support = _wp_array_get( $typography_supports, array( '__experimentalFontWeight' ), false ); $has_letter_spacing_support = _wp_array_get( $typography_supports, array( '__experimentalLetterSpacing' ), false ); $has_line_height_support = _wp_array_get( $typography_supports, array( 'lineHeight' ), false ); + $has_text_columns_support = _wp_array_get( $typography_supports, array( 'textColumns' ), false ); $has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false ); $has_text_transform_support = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false ); @@ -35,6 +36,7 @@ function gutenberg_register_typography_support( $block_type ) { || $has_font_weight_support || $has_letter_spacing_support || $has_line_height_support + || $has_text_columns_support || $has_text_decoration_support || $has_text_transform_support; @@ -91,6 +93,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $has_font_weight_support = _wp_array_get( $typography_supports, array( '__experimentalFontWeight' ), false ); $has_letter_spacing_support = _wp_array_get( $typography_supports, array( '__experimentalLetterSpacing' ), false ); $has_line_height_support = _wp_array_get( $typography_supports, array( 'lineHeight' ), false ); + $has_text_columns_support = _wp_array_get( $typography_supports, array( 'textColumns' ), false ); $has_text_decoration_support = _wp_array_get( $typography_supports, array( '__experimentalTextDecoration' ), false ); $has_text_transform_support = _wp_array_get( $typography_supports, array( '__experimentalTextTransform' ), false ); @@ -100,6 +103,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $should_skip_font_style = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontStyle' ); $should_skip_font_weight = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'fontWeight' ); $should_skip_line_height = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'lineHeight' ); + $should_skip_text_columns = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textColumns' ); $should_skip_text_decoration = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textDecoration' ); $should_skip_text_transform = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textTransform' ); $should_skip_letter_spacing = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'letterSpacing' ); @@ -135,6 +139,10 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $typography_block_styles['lineHeight'] = _wp_array_get( $block_attributes, array( 'style', 'typography', 'lineHeight' ), null ); } + if ( $has_text_columns_support && ! $should_skip_text_columns && isset( $block_attributes['style']['typography']['textColumns'] ) ) { + $typography_block_styles['textColumns'] = _wp_array_get( $block_attributes, array( 'style', 'typography', 'textColumns' ), null ); + } + if ( $has_text_decoration_support && ! $should_skip_text_decoration && isset( $block_attributes['style']['typography']['textDecoration'] ) ) { $typography_block_styles['textDecoration'] = gutenberg_typography_get_preset_inline_style_value( $block_attributes['style']['typography']['textDecoration'], 'text-decoration' ); diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index 4597ae059f05af..b71a7c95457418 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -229,6 +229,7 @@ class WP_Theme_JSON_Gutenberg { 'border-left-width' => array( 'border', 'left', 'width' ), 'border-left-style' => array( 'border', 'left', 'style' ), 'color' => array( 'color', 'text' ), + 'column-count' => array( 'typography', 'textColumns' ), 'font-family' => array( 'typography', 'fontFamily' ), 'font-size' => array( 'typography', 'fontSize' ), 'font-style' => array( 'typography', 'fontStyle' ), @@ -396,6 +397,7 @@ class WP_Theme_JSON_Gutenberg { 'fontWeight' => null, 'letterSpacing' => null, 'lineHeight' => null, + 'textColumns' => null, 'textDecoration' => null, 'textTransform' => null, ), @@ -456,6 +458,7 @@ class WP_Theme_JSON_Gutenberg { 'fontWeight' => null, 'letterSpacing' => null, 'lineHeight' => null, + 'textColumns' => null, 'textDecoration' => null, 'textTransform' => null, ), diff --git a/lib/compat/wordpress-6.3/theme.json b/lib/compat/wordpress-6.3/theme.json new file mode 100644 index 00000000000000..9437225fde4195 --- /dev/null +++ b/lib/compat/wordpress-6.3/theme.json @@ -0,0 +1,428 @@ +{ + "version": 2, + "settings": { + "appearanceTools": false, + "useRootPaddingAwareAlignments": false, + "border": { + "color": false, + "radius": false, + "style": false, + "width": false + }, + "color": { + "background": true, + "custom": true, + "customDuotone": true, + "customGradient": true, + "defaultDuotone": true, + "defaultGradients": true, + "defaultPalette": true, + "duotone": [ + { + "name": "Dark grayscale", + "colors": [ "#000000", "#7f7f7f" ], + "slug": "dark-grayscale" + }, + { + "name": "Grayscale", + "colors": [ "#000000", "#ffffff" ], + "slug": "grayscale" + }, + { + "name": "Purple and yellow", + "colors": [ "#8c00b7", "#fcff41" ], + "slug": "purple-yellow" + }, + { + "name": "Blue and red", + "colors": [ "#000097", "#ff4747" ], + "slug": "blue-red" + }, + { + "name": "Midnight", + "colors": [ "#000000", "#00a5ff" ], + "slug": "midnight" + }, + { + "name": "Magenta and yellow", + "colors": [ "#c7005a", "#fff278" ], + "slug": "magenta-yellow" + }, + { + "name": "Purple and green", + "colors": [ "#a60072", "#67ff66" ], + "slug": "purple-green" + }, + { + "name": "Blue and orange", + "colors": [ "#1900d8", "#ffa96b" ], + "slug": "blue-orange" + } + ], + "gradients": [ + { + "name": "Vivid cyan blue to vivid purple", + "gradient": "linear-gradient(135deg,rgba(6,147,227,1) 0%,rgb(155,81,224) 100%)", + "slug": "vivid-cyan-blue-to-vivid-purple" + }, + { + "name": "Light green cyan to vivid green cyan", + "gradient": "linear-gradient(135deg,rgb(122,220,180) 0%,rgb(0,208,130) 100%)", + "slug": "light-green-cyan-to-vivid-green-cyan" + }, + { + "name": "Luminous vivid amber to luminous vivid orange", + "gradient": "linear-gradient(135deg,rgba(252,185,0,1) 0%,rgba(255,105,0,1) 100%)", + "slug": "luminous-vivid-amber-to-luminous-vivid-orange" + }, + { + "name": "Luminous vivid orange to vivid red", + "gradient": "linear-gradient(135deg,rgba(255,105,0,1) 0%,rgb(207,46,46) 100%)", + "slug": "luminous-vivid-orange-to-vivid-red" + }, + { + "name": "Very light gray to cyan bluish gray", + "gradient": "linear-gradient(135deg,rgb(238,238,238) 0%,rgb(169,184,195) 100%)", + "slug": "very-light-gray-to-cyan-bluish-gray" + }, + { + "name": "Cool to warm spectrum", + "gradient": "linear-gradient(135deg,rgb(74,234,220) 0%,rgb(151,120,209) 20%,rgb(207,42,186) 40%,rgb(238,44,130) 60%,rgb(251,105,98) 80%,rgb(254,248,76) 100%)", + "slug": "cool-to-warm-spectrum" + }, + { + "name": "Blush light purple", + "gradient": "linear-gradient(135deg,rgb(255,206,236) 0%,rgb(152,150,240) 100%)", + "slug": "blush-light-purple" + }, + { + "name": "Blush bordeaux", + "gradient": "linear-gradient(135deg,rgb(254,205,165) 0%,rgb(254,45,45) 50%,rgb(107,0,62) 100%)", + "slug": "blush-bordeaux" + }, + { + "name": "Luminous dusk", + "gradient": "linear-gradient(135deg,rgb(255,203,112) 0%,rgb(199,81,192) 50%,rgb(65,88,208) 100%)", + "slug": "luminous-dusk" + }, + { + "name": "Pale ocean", + "gradient": "linear-gradient(135deg,rgb(255,245,203) 0%,rgb(182,227,212) 50%,rgb(51,167,181) 100%)", + "slug": "pale-ocean" + }, + { + "name": "Electric grass", + "gradient": "linear-gradient(135deg,rgb(202,248,128) 0%,rgb(113,206,126) 100%)", + "slug": "electric-grass" + }, + { + "name": "Midnight", + "gradient": "linear-gradient(135deg,rgb(2,3,129) 0%,rgb(40,116,252) 100%)", + "slug": "midnight" + } + ], + "link": false, + "palette": [ + { + "name": "Black", + "slug": "black", + "color": "#000000" + }, + { + "name": "Cyan bluish gray", + "slug": "cyan-bluish-gray", + "color": "#abb8c3" + }, + { + "name": "White", + "slug": "white", + "color": "#ffffff" + }, + { + "name": "Pale pink", + "slug": "pale-pink", + "color": "#f78da7" + }, + { + "name": "Vivid red", + "slug": "vivid-red", + "color": "#cf2e2e" + }, + { + "name": "Luminous vivid orange", + "slug": "luminous-vivid-orange", + "color": "#ff6900" + }, + { + "name": "Luminous vivid amber", + "slug": "luminous-vivid-amber", + "color": "#fcb900" + }, + { + "name": "Light green cyan", + "slug": "light-green-cyan", + "color": "#7bdcb5" + }, + { + "name": "Vivid green cyan", + "slug": "vivid-green-cyan", + "color": "#00d084" + }, + { + "name": "Pale cyan blue", + "slug": "pale-cyan-blue", + "color": "#8ed1fc" + }, + { + "name": "Vivid cyan blue", + "slug": "vivid-cyan-blue", + "color": "#0693e3" + }, + { + "name": "Vivid purple", + "slug": "vivid-purple", + "color": "#9b51e0" + } + ], + "text": true + }, + "layout": { + "definitions": { + "default": { + "name": "default", + "slug": "flow", + "className": "is-layout-flow", + "baseStyles": [ + { + "selector": " > .alignleft", + "rules": { + "float": "left", + "margin-inline-start": "0", + "margin-inline-end": "2em" + } + }, + { + "selector": " > .alignright", + "rules": { + "float": "right", + "margin-inline-start": "2em", + "margin-inline-end": "0" + } + }, + { + "selector": " > .aligncenter", + "rules": { + "margin-left": "auto !important", + "margin-right": "auto !important" + } + } + ], + "spacingStyles": [ + { + "selector": " > *", + "rules": { + "margin-block-start": "0", + "margin-block-end": "0" + } + }, + { + "selector": " > * + *", + "rules": { + "margin-block-start": null, + "margin-block-end": "0" + } + } + ] + }, + "constrained": { + "name": "constrained", + "slug": "constrained", + "className": "is-layout-constrained", + "baseStyles": [ + { + "selector": " > .alignleft", + "rules": { + "float": "left", + "margin-inline-start": "0", + "margin-inline-end": "2em" + } + }, + { + "selector": " > .alignright", + "rules": { + "float": "right", + "margin-inline-start": "2em", + "margin-inline-end": "0" + } + }, + { + "selector": " > .aligncenter", + "rules": { + "margin-left": "auto !important", + "margin-right": "auto !important" + } + }, + { + "selector": " > :where(:not(.alignleft):not(.alignright):not(.alignfull))", + "rules": { + "max-width": "var(--wp--style--global--content-size)", + "margin-left": "auto !important", + "margin-right": "auto !important" + } + }, + { + "selector": " > .alignwide", + "rules": { + "max-width": "var(--wp--style--global--wide-size)" + } + } + ], + "spacingStyles": [ + { + "selector": " > *", + "rules": { + "margin-block-start": "0", + "margin-block-end": "0" + } + }, + { + "selector": " > * + *", + "rules": { + "margin-block-start": null, + "margin-block-end": "0" + } + } + ] + }, + "flex": { + "name": "flex", + "slug": "flex", + "className": "is-layout-flex", + "displayMode": "flex", + "baseStyles": [ + { + "selector": "", + "rules": { + "flex-wrap": "wrap", + "align-items": "center" + } + }, + { + "selector": " > *", + "rules": { + "margin": "0" + } + } + ], + "spacingStyles": [ + { + "selector": "", + "rules": { + "gap": null + } + } + ] + } + } + }, + "spacing": { + "blockGap": null, + "margin": false, + "minHeight": false, + "padding": false, + "customSpacingSize": true, + "units": [ "px", "em", "rem", "vh", "vw", "%" ], + "spacingScale": { + "operator": "*", + "increment": 1.5, + "steps": 7, + "mediumStep": 1.5, + "unit": "rem" + } + }, + "typography": { + "customFontSize": true, + "dropCap": true, + "fontSizes": [ + { + "name": "Small", + "slug": "small", + "size": "13px" + }, + { + "name": "Medium", + "slug": "medium", + "size": "20px" + }, + { + "name": "Large", + "slug": "large", + "size": "36px" + }, + { + "name": "Extra Large", + "slug": "x-large", + "size": "42px" + } + ], + "fontStyle": true, + "fontWeight": true, + "letterSpacing": true, + "lineHeight": false, + "textColumns": false, + "textDecoration": true, + "textTransform": true + }, + "blocks": { + "core/button": { + "border": { + "radius": true + } + }, + "core/pullquote": { + "border": { + "color": true, + "radius": true, + "style": true, + "width": true + } + } + } + }, + "styles": { + "elements": { + "button": { + "color": { + "text": "#fff", + "background": "#32373c" + }, + "spacing": { + "padding": "calc(0.667em + 2px) calc(1.333em + 2px)" + }, + "typography": { + "fontSize": "inherit", + "fontFamily": "inherit", + "lineHeight": "inherit", + "textDecoration": "none" + }, + "border": { + "width": "0" + } + }, + "link": { + "typography": { + "textDecoration": "underline" + } + } + }, + "spacing": { + "blockGap": "24px", + "padding": { + "top": "0px", + "right": "0px", + "bottom": "0px", + "left": "0px" + } + } + } +} diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js index 2414f3d4b4b9c7..c169cb03a60836 100644 --- a/packages/block-editor/src/components/global-styles/hooks.js +++ b/packages/block-editor/src/components/global-styles/hooks.js @@ -65,6 +65,7 @@ const VALID_SETTINGS = [ 'typography.fontWeight', 'typography.letterSpacing', 'typography.lineHeight', + 'typography.textColumns', 'typography.textDecoration', 'typography.textTransform', ]; @@ -178,7 +179,7 @@ export function useGlobalStyle( switch ( source ) { case 'all': rawResult = - // The stlyes.css path is allowed to be empty, so don't revert to base if undefined. + // The styles.css path is allowed to be empty, so don't revert to base if undefined. finalPath === 'styles.css' ? get( userConfig, finalPath ) : get( mergedConfig, finalPath ); @@ -266,6 +267,16 @@ export function useSettingsForBlockElement( } } ); + // The column-count style is named text column to reduce confusion with + // the columns block and manage expectations from the support. + // See: https://github.com/WordPress/gutenberg/pull/33587 + if ( ! supportedStyles.includes( 'columnCount' ) ) { + updatedSettings.typography = { + ...updatedSettings.typography, + textColumns: false, + }; + } + [ 'contentSize', 'wideSize' ].forEach( ( key ) => { if ( ! supportedStyles.includes( key ) ) { updatedSettings.layout = { diff --git a/packages/block-editor/src/components/global-styles/typography-panel.js b/packages/block-editor/src/components/global-styles/typography-panel.js index 9a928e30227f8f..1c3c253c7b69db 100644 --- a/packages/block-editor/src/components/global-styles/typography-panel.js +++ b/packages/block-editor/src/components/global-styles/typography-panel.js @@ -3,6 +3,7 @@ */ import { FontSizePicker, + __experimentalNumberControl as NumberControl, __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; @@ -20,6 +21,9 @@ import TextTransformControl from '../text-transform-control'; import TextDecorationControl from '../text-decoration-control'; import { getValueFromVariable } from './utils'; +const MIN_TEXT_COLUMNS = 1; +const MAX_TEXT_COLUMNS = 6; + export function useHasTypographyPanel( settings ) { const hasFontFamily = useHasFontFamilyControl( settings ); const hasLineHeight = useHasLineHeightControl( settings ); @@ -27,6 +31,7 @@ export function useHasTypographyPanel( settings ) { const hasLetterSpacing = useHasLetterSpacingControl( settings ); const hasTextTransform = useHasTextTransformControl( settings ); const hasTextDecoration = useHasTextDecorationControl( settings ); + const hasTextColumns = useHasTextColumnsControl( settings ); const hasFontSize = useHasFontSizeControl( settings ); return ( @@ -36,7 +41,8 @@ export function useHasTypographyPanel( settings ) { hasLetterSpacing || hasTextTransform || hasFontSize || - hasTextDecoration + hasTextDecoration || + hasTextColumns ); } @@ -93,6 +99,10 @@ function useHasTextDecorationControl( settings ) { return settings?.typography?.textDecoration; } +function useHasTextColumnsControl( settings ) { + return settings?.typography?.textColumns; +} + function TypographyToolsPanel( { resetAllFilter, onChange, @@ -124,6 +134,7 @@ const DEFAULT_CONTROLS = { letterSpacing: true, textTransform: true, textDecoration: true, + textColumns: true, }; export default function TypographyPanel( { @@ -246,6 +257,21 @@ export default function TypographyPanel( { const hasLetterSpacing = () => !! value?.typography?.letterSpacing; const resetLetterSpacing = () => setLetterSpacing( undefined ); + // Text Columns + const hasTextColumnsControl = useHasTextColumnsControl( settings ); + const textColumns = decodeValue( inheritedValue?.typography?.textColumns ); + const setTextColumns = ( newValue ) => { + onChange( { + ...value, + typography: { + ...value?.typography, + textColumns: newValue, + }, + } ); + }; + const hasTextColumns = () => !! value?.typography?.textColumns; + const resetTextColumns = () => setTextColumns( undefined ); + // Text Transform const hasTextTransformControl = useHasTextTransformControl( settings ); const textTransform = decodeValue( @@ -388,6 +414,27 @@ export default function TypographyPanel( { /> ) } + { hasTextColumnsControl && ( + + + + ) } { hasTextDecorationControl && ( { expect( getInlineStyles( { color: { text: 'red', background: 'black' }, - typography: { lineHeight: 1.5, fontSize: 10 }, + typography: { lineHeight: 1.5, fontSize: 10, textColumns: 2 }, border: { radius: '10px', width: '1em', @@ -44,6 +44,7 @@ describe( 'getInlineStyles', () => { borderStyle: 'dotted', borderWidth: '1em', color: 'red', + columnCount: 2, lineHeight: 1.5, fontSize: 10, marginBottom: '15px', diff --git a/packages/block-editor/src/hooks/test/use-typography-props.js b/packages/block-editor/src/hooks/test/use-typography-props.js index 12336eb2c44afd..9b43a074cfcbaf 100644 --- a/packages/block-editor/src/hooks/test/use-typography-props.js +++ b/packages/block-editor/src/hooks/test/use-typography-props.js @@ -12,6 +12,7 @@ describe( 'getTypographyClassesAndStyles', () => { typography: { letterSpacing: '22px', fontSize: '2rem', + textColumns: 3, textTransform: 'uppercase', }, }, @@ -19,6 +20,7 @@ describe( 'getTypographyClassesAndStyles', () => { expect( getTypographyClassesAndStyles( attributes ) ).toEqual( { className: 'has-tofu-font-family has-large-font-size', style: { + columnCount: 3, letterSpacing: '22px', fontSize: '2rem', textTransform: 'uppercase', diff --git a/packages/block-editor/src/hooks/text-columns.js b/packages/block-editor/src/hooks/text-columns.js new file mode 100644 index 00000000000000..17afd3a629f61d --- /dev/null +++ b/packages/block-editor/src/hooks/text-columns.js @@ -0,0 +1,109 @@ +/** + * WordPress dependencies + */ +import { hasBlockSupport } from '@wordpress/blocks'; +import { __experimentalNumberControl as NumberControl } from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import useSetting from '../components/use-setting'; +import { cleanEmptyObject } from './utils'; + +/** + * Key within block settings' supports array indicating support for text + * columns e.g. settings found in `block.json`. + */ +export const TEXT_COLUMNS_SUPPORT_KEY = 'typography.textColumns'; + +const MIN_COLUMNS = 1; +const MAX_COLUMNS = 6; + +/** + * Inspector control containing the text columns option. + * + * @param {Object} props Block properties. + * + * @return {WPElement} Text columns edit element. + */ +export function TextColumnsEdit( props ) { + const { + attributes: { style }, + setAttributes, + } = props; + + function onChange( newColumns ) { + setAttributes( { + style: cleanEmptyObject( { + ...style, + typography: { + ...style?.typography, + textColumns: newColumns, + }, + } ), + } ); + } + + return ( + + ); +} + +/** + * Checks if text columns setting has been disabled. + * + * @param {string} name Name of the block. + * + * @return {boolean} Whether or not the setting is disabled. + */ +export function useIsTextColumnsDisabled( { name: blockName } = {} ) { + const notSupported = ! hasBlockSupport( + blockName, + TEXT_COLUMNS_SUPPORT_KEY + ); + const hasTextColumns = useSetting( 'typography.textColumns' ); + return notSupported || ! hasTextColumns; +} + +/** + * Checks if there is a current value set for the text columns block support. + * + * @param {Object} props Block props. + * @return {boolean} Whether or not the block has a text columns set. + */ +export function hasTextColumnsValue( props ) { + return !! props.attributes.style?.typography?.textColumns; +} + +/** + * Resets the text columns block support attribute. This can be used when + * disabling the text columns 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 resetTextColumns( { attributes = {}, setAttributes } ) { + const { style } = attributes; + + setAttributes( { + style: cleanEmptyObject( { + ...style, + typography: { + ...style?.typography, + textColumns: undefined, + }, + } ), + } ); +} diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index c5d3c74852777f..00e1c348f57c80 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -27,6 +27,7 @@ function omit( object, keys ) { const LETTER_SPACING_SUPPORT_KEY = 'typography.__experimentalLetterSpacing'; const TEXT_TRANSFORM_SUPPORT_KEY = 'typography.__experimentalTextTransform'; const TEXT_DECORATION_SUPPORT_KEY = 'typography.__experimentalTextDecoration'; +const TEXT_COLUMNS_SUPPORT_KEY = 'typography.textColumns'; const FONT_STYLE_SUPPORT_KEY = 'typography.__experimentalFontStyle'; const FONT_WEIGHT_SUPPORT_KEY = 'typography.__experimentalFontWeight'; export const TYPOGRAPHY_SUPPORT_KEY = 'typography'; @@ -36,6 +37,7 @@ export const TYPOGRAPHY_SUPPORT_KEYS = [ FONT_STYLE_SUPPORT_KEY, FONT_WEIGHT_SUPPORT_KEY, FONT_FAMILY_SUPPORT_KEY, + TEXT_COLUMNS_SUPPORT_KEY, TEXT_DECORATION_SUPPORT_KEY, TEXT_TRANSFORM_SUPPORT_KEY, LETTER_SPACING_SUPPORT_KEY, diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index 3684598997a6a8..f324bd99bc6eac 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -202,6 +202,7 @@ export function useBlockSettings( name, parentLayout ) { const fontStyle = useSetting( 'typography.fontStyle' ); const fontWeight = useSetting( 'typography.fontWeight' ); const lineHeight = useSetting( 'typography.lineHeight' ); + const textColumns = useSetting( 'typography.textColumns' ); const textDecoration = useSetting( 'typography.textDecoration' ); const textTransform = useSetting( 'typography.textTransform' ); const letterSpacing = useSetting( 'typography.letterSpacing' ); @@ -244,6 +245,7 @@ export function useBlockSettings( name, parentLayout ) { fontStyle, fontWeight, lineHeight, + textColumns, textDecoration, textTransform, letterSpacing, @@ -276,6 +278,7 @@ export function useBlockSettings( name, parentLayout ) { fontStyle, fontWeight, lineHeight, + textColumns, textDecoration, textTransform, letterSpacing, diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index 28801b3cf4882e..1ab0b26448c1a5 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -123,6 +123,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { requiresOptOut: true, useEngine: true, }, + columnCount: { + value: [ 'typography', 'textColumns' ], + support: [ 'typography', 'textColumns' ], + useEngine: true, + }, filter: { value: [ 'filter', 'duotone' ], support: [ 'color', '__experimentalDuotone' ], diff --git a/packages/blocks/src/store/private-selectors.js b/packages/blocks/src/store/private-selectors.js index 2f4fb1dbea3866..9e38194cd11cb0 100644 --- a/packages/blocks/src/store/private-selectors.js +++ b/packages/blocks/src/store/private-selectors.js @@ -72,6 +72,11 @@ function filterElementBlockSupports( blockSupports, name, element ) { return false; } + // Text columns is only available for blocks. + if ( support === 'textColumns' && ! name ) { + return false; + } + return true; } ); } diff --git a/phpunit/class-wp-theme-json-test.php b/phpunit/class-wp-theme-json-test.php index abb7c26cba866a..742c2d948b4515 100644 --- a/phpunit/class-wp-theme-json-test.php +++ b/phpunit/class-wp-theme-json-test.php @@ -388,7 +388,7 @@ public function test_get_stylesheet() { ), ), 'blocks' => array( - 'core/group' => array( + 'core/group' => array( 'border' => array( 'radius' => '10px', ), @@ -406,7 +406,7 @@ public function test_get_stylesheet() { 'padding' => '24px', ), ), - 'core/heading' => array( + 'core/heading' => array( 'color' => array( 'text' => '#123456', ), @@ -422,7 +422,7 @@ public function test_get_stylesheet() { ), ), ), - 'core/post-date' => array( + 'core/post-date' => array( 'color' => array( 'text' => '#123456', ), @@ -435,7 +435,12 @@ public function test_get_stylesheet() { ), ), ), - 'core/image' => array( + 'core/post-excerpt' => array( + 'typography' => array( + 'textColumns' => 2, + ), + ), + 'core/image' => array( 'border' => array( 'radius' => array( 'topLeft' => '10px', @@ -455,7 +460,7 @@ public function test_get_stylesheet() { ); $variables = 'body{--wp--preset--color--grey: grey;--wp--preset--font-family--small: 14px;--wp--preset--font-family--big: 41px;}.wp-block-group{--wp--custom--base-font: 16;--wp--custom--line-height--small: 1.2;--wp--custom--line-height--medium: 1.4;--wp--custom--line-height--large: 1.8;}'; - $styles = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;min-height: 50vh;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}.wp-block-heading{color: #123456;}.wp-block-heading a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}'; + $styles = 'body { margin: 0;}.wp-site-blocks > .alignleft { float: left; margin-right: 2em; }.wp-site-blocks > .alignright { float: right; margin-left: 2em; }.wp-site-blocks > .aligncenter { justify-content: center; margin-left: auto; margin-right: auto; }body{color: var(--wp--preset--color--grey);}a:where(:not(.wp-element-button)){background-color: #333;color: #111;}.wp-block-group{border-radius: 10px;min-height: 50vh;padding: 24px;}.wp-block-group a:where(:not(.wp-element-button)){color: #111;}.wp-block-heading{color: #123456;}.wp-block-heading a:where(:not(.wp-element-button)){background-color: #333;color: #111;font-size: 60px;}.wp-block-post-date{color: #123456;}.wp-block-post-date a:where(:not(.wp-element-button)){background-color: #777;color: #555;}.wp-block-post-excerpt{column-count: 2;}.wp-block-image{margin-bottom: 30px;}.wp-block-image img, .wp-block-image .wp-block-image__crop-area{border-top-left-radius: 10px;border-bottom-right-radius: 1em;}'; $presets = '.has-grey-color{color: var(--wp--preset--color--grey) !important;}.has-grey-background-color{background-color: var(--wp--preset--color--grey) !important;}.has-grey-border-color{border-color: var(--wp--preset--color--grey) !important;}.has-small-font-family{font-family: var(--wp--preset--font-family--small) !important;}.has-big-font-family{font-family: var(--wp--preset--font-family--big) !important;}'; $all = $variables . $styles . $presets; diff --git a/schemas/json/theme.json b/schemas/json/theme.json index d2fb13b6f13b4c..39c4b3ac902000 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -424,6 +424,11 @@ "type": "boolean", "default": false }, + "textColumns": { + "description": "Allow users to set the number of text columns.", + "type": "boolean", + "default": false + }, "textDecoration": { "description": "Allow users to set custom text decorations.", "type": "boolean", @@ -1443,6 +1448,10 @@ } ] }, + "textColumns": { + "description": "Sets the `column-count` CSS property.", + "type": "string" + }, "textDecoration": { "description": "Sets the `text-decoration` CSS property.", "oneOf": [