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/theme.json b/lib/theme.json index 8e60c945456b1b..fa3a872518adc7 100644 --- a/lib/theme.json +++ b/lib/theme.json @@ -398,6 +398,7 @@ "fontWeight": true, "letterSpacing": true, "lineHeight": false, + "textColumns": false, "textDecoration": true, "textTransform": true }, 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/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": [