diff --git a/docs/how-to-guides/themes/global-settings-and-styles.md b/docs/how-to-guides/themes/global-settings-and-styles.md index cd5557d40e55c6..37f0ee8951c3c9 100644 --- a/docs/how-to-guides/themes/global-settings-and-styles.md +++ b/docs/how-to-guides/themes/global-settings-and-styles.md @@ -265,6 +265,7 @@ The settings section has the following structure: "fontWeight": true, "letterSpacing": true, "lineHeight": false, + "textAlign": true, "textColumns": false, "textDecoration": true, "textTransform": true diff --git a/docs/reference-guides/block-api/block-supports.md b/docs/reference-guides/block-api/block-supports.md index 057047434eb69d..b327cd0c03a9ee 100644 --- a/docs/reference-guides/block-api/block-supports.md +++ b/docs/reference-guides/block-api/block-supports.md @@ -149,13 +149,13 @@ When a background image is selected, the image data is stored in the `style.back When a background images is selected and its position or size are changed, the background-position is stored in the `style.background.backgroundPosition` and its background-size in `style.background.backgroundSize` attribute. - `style`: an attribute of `object` type with no default assigned. This is added when `backgroundImage` or `backgroundSize` support is declared. It stores the custom values set by the user. - - `background`: an attribute of `object` type. - - `backgroundImage`: an attribute of `object` type, containing information about the selected image + - `background`: an attribute of `object` type. + - `backgroundImage`: an attribute of `object` type, containing information about the selected image - `url`: type `string`, URL to the image - `id`: type `int`, media attachment ID - `source`: type `string`, at the moment the only value is `file` - - `title`: type `string`, title of the media attachment - - `backgroundPosition`: an attribute of `string` type, defining the background images position, selected by FocalPointPicker and used in CSS as the [`background-position`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-position) value. + - `title`: type `string`, title of the media attachment + - `backgroundPosition`: an attribute of `string` type, defining the background images position, selected by FocalPointPicker and used in CSS as the [`background-position`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-position) value. - `backgroundSize`: an attribute of `string` type. defining the CSS [`background-size`](https://developer.mozilla.org/en-US/docs/Web/CSS/background-size) value. The block can apply a default background image, position and size by specifying its own attribute with a default. For example: @@ -484,10 +484,10 @@ When the block declares support for `color.link`, the attributes definition is e color: { text: 'var:preset|color|contrast', }, - ":hover": { - color: { - text: "#000000" - } + ":hover": { + color: { + text: "#000000" + } } } } @@ -973,6 +973,7 @@ supports: { - Subproperties: - `fontSize`: type `boolean`, default value `false` - `lineHeight`: type `boolean`, default value `false` + - `textAlign`: type `boolean` or `array`, default value `false` The presence of this object signals that a block supports some typography related properties. When it does, the block editor will show a typography UI allowing the user to control their values. @@ -983,6 +984,8 @@ supports: { fontSize: true, // Enable support and UI control for line-height. lineHeight: true, + // Enable support and UI control for text alignment. + textAlign: true, }, } ``` @@ -1063,3 +1066,47 @@ attributes: { } } ``` + +### typography.textAlign + +_**Note:** Since WordPress 6.6._ + +- Type: `boolean` or `array` +- Default value: `false` + +This property adds block toolbar controls which allow to change block's text alignment. + +```js +supports: { + typography: { + // Declare support for block's text alignment. + // This adds support for all the options: + // left, center, right. + textAlign: true + } +} +``` + +```js +supports: { + typography: { + // Declare support for specific text alignment options. + textAlign: [ 'left', 'right' ] + } +} +``` + +When the block declares support for `textAlign`, the attributes definition is extended to include a new attribute `style` of `object` type with no default assigned. It stores the custom value set by the user. The block can apply a default style by specifying its own `style` attribute with a default. For example: + +```js +attributes: { + style: { + type: 'object', + default: { + typography: { + textAlign: '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 00b1f7971f3d91..0377f6a3c3e05d 100644 --- a/docs/reference-guides/theme-json-reference/theme-json-living.md +++ b/docs/reference-guides/theme-json-reference/theme-json-living.md @@ -185,6 +185,7 @@ Settings related to typography. | fluid | undefined | false | | | letterSpacing | boolean | true | | | lineHeight | boolean | false | | +| textAlign | boolean | true | | | textColumns | boolean | false | | | textDecoration | boolean | true | | | writingMode | boolean | false | | @@ -268,6 +269,7 @@ Typography styles. | fontWeight | string, object | | | letterSpacing | string, object | | | lineHeight | string, object | | +| textAlign | string | | | textColumns | string | | | textDecoration | string, object | | | writingMode | string, object | | diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 8fceda4d5daba0..fa2a7b81e94e21 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 = $typography_supports['__experimentalFontWeight'] ?? false; $has_letter_spacing_support = $typography_supports['__experimentalLetterSpacing'] ?? false; $has_line_height_support = $typography_supports['lineHeight'] ?? false; + $has_text_align_support = $typography_supports['textAlign'] ?? false; $has_text_columns_support = $typography_supports['textColumns'] ?? false; $has_text_decoration_support = $typography_supports['__experimentalTextDecoration'] ?? false; $has_text_transform_support = $typography_supports['__experimentalTextTransform'] ?? false; @@ -37,6 +38,7 @@ function gutenberg_register_typography_support( $block_type ) { || $has_font_weight_support || $has_letter_spacing_support || $has_line_height_support + || $has_text_align_support || $has_text_columns_support || $has_text_decoration_support || $has_text_transform_support @@ -95,6 +97,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $has_font_weight_support = $typography_supports['__experimentalFontWeight'] ?? false; $has_letter_spacing_support = $typography_supports['__experimentalLetterSpacing'] ?? false; $has_line_height_support = $typography_supports['lineHeight'] ?? false; + $has_text_align_support = $typography_supports['textAlign'] ?? false; $has_text_columns_support = $typography_supports['textColumns'] ?? false; $has_text_decoration_support = $typography_supports['__experimentalTextDecoration'] ?? false; $has_text_transform_support = $typography_supports['__experimentalTextTransform'] ?? false; @@ -106,6 +109,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_align = wp_should_skip_block_supports_serialization( $block_type, 'typography', 'textAlign' ); $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' ); @@ -143,6 +147,10 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $typography_block_styles['lineHeight'] = $block_attributes['style']['typography']['lineHeight'] ?? null; } + if ( $has_text_align_support && ! $should_skip_text_align ) { + $typography_block_styles['textAlign'] = $block_attributes['style']['typography']['textAlign'] ?? null; + } + if ( $has_text_columns_support && ! $should_skip_text_columns && isset( $block_attributes['style']['typography']['textColumns'] ) ) { $typography_block_styles['textColumns'] = $block_attributes['style']['typography']['textColumns'] ?? null; } @@ -167,13 +175,22 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { } $attributes = array(); + $classnames = array(); $styles = gutenberg_style_engine_get_styles( array( 'typography' => $typography_block_styles ), array( 'convert_vars_to_classnames' => true ) ); if ( ! empty( $styles['classnames'] ) ) { - $attributes['class'] = $styles['classnames']; + $classnames[] = $styles['classnames']; + } + + if ( $has_text_align_support && ! $should_skip_text_align && isset( $block_attributes['style']['typography']['textAlign'] ) ) { + $classnames[] = 'has-text-align-' . $block_attributes['style']['typography']['textAlign']; + } + + if ( ! empty( $classnames ) ) { + $attributes['class'] = implode( ' ', $classnames ); } if ( ! empty( $styles['css'] ) ) { diff --git a/lib/class-wp-theme-json-gutenberg.php b/lib/class-wp-theme-json-gutenberg.php index aafc20a2debd51..a665235d7e774e 100644 --- a/lib/class-wp-theme-json-gutenberg.php +++ b/lib/class-wp-theme-json-gutenberg.php @@ -244,6 +244,7 @@ class WP_Theme_JSON_Gutenberg { 'border-left-width' => array( 'border', 'left', 'width' ), 'border-left-style' => array( 'border', 'left', 'style' ), 'color' => array( 'color', 'text' ), + 'text-align' => array( 'typography', 'textAlign' ), 'column-count' => array( 'typography', 'textColumns' ), 'font-family' => array( 'typography', 'fontFamily' ), 'font-size' => array( 'typography', 'fontSize' ), @@ -436,6 +437,7 @@ class WP_Theme_JSON_Gutenberg { 'fontWeight' => null, 'letterSpacing' => null, 'lineHeight' => null, + 'textAlign' => null, 'textColumns' => null, 'textDecoration' => null, 'textTransform' => null, @@ -531,6 +533,7 @@ class WP_Theme_JSON_Gutenberg { 'fontWeight' => null, 'letterSpacing' => null, 'lineHeight' => null, + 'textAlign' => null, 'textColumns' => null, 'textDecoration' => null, 'textTransform' => null, diff --git a/lib/theme.json b/lib/theme.json index 2e40ccadffef85..ece76b5f63cb29 100644 --- a/lib/theme.json +++ b/lib/theme.json @@ -263,6 +263,7 @@ "fontWeight": true, "letterSpacing": true, "lineHeight": false, + "textAlign": false, "textColumns": false, "textDecoration": true, "textTransform": true, diff --git a/packages/block-editor/src/components/use-paste-styles/index.js b/packages/block-editor/src/components/use-paste-styles/index.js index 9150776ad1158b..1c2a844488b862 100644 --- a/packages/block-editor/src/components/use-paste-styles/index.js +++ b/packages/block-editor/src/components/use-paste-styles/index.js @@ -15,6 +15,7 @@ import { hasAlignSupport, hasBorderSupport, hasBackgroundColorSupport, + hasTextAlignSupport, hasTextColorSupport, hasGradientSupport, hasCustomClassNameSupport, @@ -59,6 +60,7 @@ const STYLE_ATTRIBUTES = { align: hasAlignSupport, borderColor: ( nameOrType ) => hasBorderSupport( nameOrType, 'color' ), backgroundColor: hasBackgroundColorSupport, + textAlign: hasTextAlignSupport, textColor: hasTextColorSupport, gradient: hasGradientSupport, className: hasCustomClassNameSupport, diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index 0c7c59b20ec674..ec5cf29b49c5a6 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -21,6 +21,7 @@ import dimensions from './dimensions'; import duotone from './duotone'; import fontFamily from './font-family'; import fontSize from './font-size'; +import textAlign from './text-align'; import border from './border'; import position from './position'; import layout from './layout'; @@ -34,6 +35,7 @@ import './use-bindings-attributes'; createBlockEditFilter( [ align, + textAlign, anchor, customClassName, style, @@ -47,6 +49,7 @@ createBlockEditFilter( ); createBlockListBlockFilter( [ align, + textAlign, background, style, color, @@ -60,6 +63,7 @@ createBlockListBlockFilter( [ ] ); createBlockSaveFilter( [ align, + textAlign, anchor, ariaLabel, customClassName, diff --git a/packages/block-editor/src/hooks/supports.js b/packages/block-editor/src/hooks/supports.js index 4e116494029bf1..75f2bdf2dc219e 100644 --- a/packages/block-editor/src/hooks/supports.js +++ b/packages/block-editor/src/hooks/supports.js @@ -20,6 +20,11 @@ const FONT_STYLE_SUPPORT_KEY = 'typography.__experimentalFontStyle'; * Key within block settings' support array indicating support for font weight. */ const FONT_WEIGHT_SUPPORT_KEY = 'typography.__experimentalFontWeight'; +/** + * Key within block settings' supports array indicating support for text + * align e.g. settings found in `block.json`. + */ +const TEXT_ALIGN_SUPPORT_KEY = 'typography.textAlign'; /** * Key within block settings' supports array indicating support for text * columns e.g. settings found in `block.json`. @@ -53,6 +58,7 @@ const TYPOGRAPHY_SUPPORT_KEYS = [ FONT_STYLE_SUPPORT_KEY, FONT_WEIGHT_SUPPORT_KEY, FONT_FAMILY_SUPPORT_KEY, + TEXT_ALIGN_SUPPORT_KEY, TEXT_COLUMNS_SUPPORT_KEY, TEXT_DECORATION_SUPPORT_KEY, TEXT_TRANSFORM_SUPPORT_KEY, @@ -212,6 +218,24 @@ export const hasBackgroundColorSupport = ( nameOrType ) => { return colorSupport && colorSupport.background !== false; }; +/** + * Returns true if the block defines support for text-align. + * + * @param {string|Object} nameOrType Block name or type object. + * @return {boolean} Whether the block supports the feature. + */ +export const hasTextAlignSupport = ( nameOrType ) => + hasBlockSupport( nameOrType, TEXT_ALIGN_SUPPORT_KEY ); + +/** + * Returns the block support value for text-align, if defined. + * + * @param {string|Object} nameOrType Block name or type object. + * @return {unknown} The block support value. + */ +export const getTextAlignSupport = ( nameOrType ) => + getBlockSupport( nameOrType, TEXT_ALIGN_SUPPORT_KEY ); + /** * Returns true if the block defines support for background color. * diff --git a/packages/block-editor/src/hooks/test/text-align.js b/packages/block-editor/src/hooks/test/text-align.js new file mode 100644 index 00000000000000..3b7d338193b30b --- /dev/null +++ b/packages/block-editor/src/hooks/test/text-align.js @@ -0,0 +1,114 @@ +/** + * WordPress dependencies + */ +import { + getBlockTypes, + registerBlockType, + unregisterBlockType, +} from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import { getValidTextAlignments, addAssignedTextAlign } from '../text-align'; + +const noop = () => {}; + +describe( 'textAlign', () => { + const blockSettings = { + save: noop, + category: 'text', + title: 'block title', + edit: ( { children } ) => <>{ children }, + }; + + afterEach( () => { + getBlockTypes().forEach( ( block ) => { + unregisterBlockType( block.name ); + } ); + } ); + + describe( 'getValidTextAlignments()', () => { + it( 'should return an empty array if block does not define align support', () => { + expect( getValidTextAlignments() ).toEqual( [] ); + } ); + + it( 'should return all custom text aligns set', () => { + expect( getValidTextAlignments( [ 'left', 'right' ] ) ).toEqual( [ + 'left', + 'right', + ] ); + } ); + + it( 'should return all text aligns sorted when provided in the random order', () => { + expect( + getValidTextAlignments( [ 'right', 'center', 'left' ] ) + ).toEqual( [ 'left', 'center', 'right' ] ); + } ); + + it( 'should return all text aligns if block defines text align support as true', () => { + expect( getValidTextAlignments( true ) ).toEqual( [ + 'left', + 'center', + 'right', + ] ); + } ); + + it( 'should remove incorrect text aligns', () => { + expect( + getValidTextAlignments( [ 'left', 'right', 'justify' ] ) + ).toEqual( [ 'left', 'right' ] ); + } ); + } ); + + describe( 'addAssignedTextAlign', () => { + it( 'should do nothing if block does not support text align', () => { + registerBlockType( 'core/foo', blockSettings ); + + const props = addAssignedTextAlign( + { + className: 'foo', + }, + 'core/foo', + { + typography: { + textAlign: 'center', + }, + } + ); + + expect( props ).toEqual( { + className: 'foo', + } ); + } ); + + it( 'should do add text align classname if block supports text align', () => { + registerBlockType( 'core/foo', { + ...blockSettings, + supports: { + typography: { + textAlign: true, + }, + }, + } ); + + const props = addAssignedTextAlign( + { + className: 'foo', + }, + 'core/foo', + { + style: { + typography: { + textAlign: 'center', + }, + }, + } + ); + + expect( props ).toEqual( { + className: 'has-text-align-center foo', + } ); + } ); + } ); +} ); 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 77b2e0cdf33cb7..c226b7f8199b9f 100644 --- a/packages/block-editor/src/hooks/test/use-typography-props.js +++ b/packages/block-editor/src/hooks/test/use-typography-props.js @@ -12,13 +12,15 @@ describe( 'getTypographyClassesAndStyles', () => { typography: { letterSpacing: '22px', fontSize: '2rem', + textAlign: 'center', textColumns: 3, textTransform: 'uppercase', }, }, }; expect( getTypographyClassesAndStyles( attributes ) ).toEqual( { - className: 'has-tofu-font-family has-large-font-size', + className: + 'has-tofu-font-family has-text-align-center has-large-font-size', style: { columnCount: 3, letterSpacing: '22px', diff --git a/packages/block-editor/src/hooks/text-align.js b/packages/block-editor/src/hooks/text-align.js new file mode 100644 index 00000000000000..daa8df6cc82106 --- /dev/null +++ b/packages/block-editor/src/hooks/text-align.js @@ -0,0 +1,178 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; + +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; +import { alignLeft, alignRight, alignCenter } from '@wordpress/icons'; + +/** + * Internal dependencies + */ +import { AlignmentControl, BlockControls } from '../components'; +import { useBlockEditingMode } from '../components/block-editing-mode'; +import { cleanEmptyObject, shouldSkipSerialization } from './utils'; +import { TYPOGRAPHY_SUPPORT_KEY } from './typography'; + +export const TEXT_ALIGN_SUPPORT_KEY = 'typography.textAlign'; + +const TEXT_ALIGNMENT_OPTIONS = [ + { + icon: alignLeft, + title: __( 'Align text left' ), + align: 'left', + }, + { + icon: alignCenter, + title: __( 'Align text center' ), + align: 'center', + }, + { + icon: alignRight, + title: __( 'Align text right' ), + align: 'right', + }, +]; + +const VALID_TEXT_ALIGNMENTS = [ 'left', 'center', 'right' ]; + +/** + * Returns the valid text alignments. + * Takes into consideration the text aligns supported by a block. + * Exported just for testing purposes, not exported outside the module. + * + * @param {?boolean|string[]} blockTextAlign Text aligns supported by the block. + * + * @return {string[]} Valid text alignments. + */ +export function getValidTextAlignments( blockTextAlign ) { + let validTextAlignments; + if ( Array.isArray( blockTextAlign ) ) { + validTextAlignments = VALID_TEXT_ALIGNMENTS.filter( ( textAlign ) => + blockTextAlign.includes( textAlign ) + ); + } else if ( blockTextAlign === true ) { + // `true` includes all alignments... + validTextAlignments = [ ...VALID_TEXT_ALIGNMENTS ]; + } else { + validTextAlignments = []; + } + + return validTextAlignments; +} + +function BlockEditTextAlignmentToolbarControlsPure( { + style, + name: blockName, + setAttributes, +} ) { + const validTextAlignments = getValidTextAlignments( + getBlockSupport( blockName, TEXT_ALIGN_SUPPORT_KEY ) + ); + const blockEditingMode = useBlockEditingMode(); + if ( ! validTextAlignments.length || blockEditingMode !== 'default' ) { + return null; + } + + const textAlignmentControls = TEXT_ALIGNMENT_OPTIONS.filter( ( control ) => + validTextAlignments.includes( control.align ) + ); + + const onChange = ( newTextAlignValue ) => { + const newStyle = { + ...style, + typography: { + ...style?.typography, + textAlign: newTextAlignValue, + }, + }; + + setAttributes( { style: cleanEmptyObject( newStyle ) } ); + }; + + return ( + + + + ); +} + +export default { + edit: BlockEditTextAlignmentToolbarControlsPure, + useBlockProps, + addSaveProps: addAssignedTextAlign, + attributeKeys: [ 'style' ], + hasSupport( name ) { + return hasBlockSupport( name, TEXT_ALIGN_SUPPORT_KEY, false ); + }, +}; + +function useBlockProps( { name, style } ) { + if ( ! style?.typography?.textAlign ) { + return null; + } + + const validTextAlignments = getValidTextAlignments( + getBlockSupport( name, TEXT_ALIGN_SUPPORT_KEY ) + ); + + if ( ! validTextAlignments.length ) { + return null; + } + + if ( + shouldSkipSerialization( name, TYPOGRAPHY_SUPPORT_KEY, 'textAlign' ) + ) { + return null; + } + + const textAlign = style.typography.textAlign; + + const className = classnames( { + [ `has-text-align-${ textAlign }` ]: textAlign, + } ); + return { className }; +} + +/** + * Override props assigned to save component to inject text alignment class + * name if block supports it. + * + * @param {Object} props Additional props applied to save element. + * @param {Object} blockType Block type. + * @param {Object} attributes Block attributes. + * + * @return {Object} Filtered props applied to save element. + */ +export function addAssignedTextAlign( props, blockType, attributes ) { + if ( ! attributes?.style?.typography?.textAlign ) { + return props; + } + + const { textAlign } = attributes.style.typography; + const blockTextAlign = getBlockSupport( blockType, TEXT_ALIGN_SUPPORT_KEY ); + const isTextAlignValid = + getValidTextAlignments( blockTextAlign ).includes( textAlign ); + if ( + isTextAlignValid && + ! shouldSkipSerialization( + blockType, + TYPOGRAPHY_SUPPORT_KEY, + 'textAlign' + ) + ) { + props.className = classnames( + `has-text-align-${ textAlign }`, + props.className + ); + } + return props; +} diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index 12d0075527bec5..cf3f4327c8f034 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -17,6 +17,7 @@ import { import { LINE_HEIGHT_SUPPORT_KEY } from './line-height'; import { FONT_FAMILY_SUPPORT_KEY } from './font-family'; import { FONT_SIZE_SUPPORT_KEY } from './font-size'; +import { TEXT_ALIGN_SUPPORT_KEY } from './text-align'; import { cleanEmptyObject } from './utils'; import { store as blockEditorStore } from '../store'; @@ -40,6 +41,7 @@ export const TYPOGRAPHY_SUPPORT_KEYS = [ FONT_STYLE_SUPPORT_KEY, FONT_WEIGHT_SUPPORT_KEY, FONT_FAMILY_SUPPORT_KEY, + TEXT_ALIGN_SUPPORT_KEY, TEXT_COLUMNS_SUPPORT_KEY, TEXT_DECORATION_SUPPORT_KEY, WRITING_MODE_SUPPORT_KEY, diff --git a/packages/block-editor/src/hooks/use-typography-props.js b/packages/block-editor/src/hooks/use-typography-props.js index a63f77468c7233..3986bbf7cfe17e 100644 --- a/packages/block-editor/src/hooks/use-typography-props.js +++ b/packages/block-editor/src/hooks/use-typography-props.js @@ -45,9 +45,12 @@ export function getTypographyClassesAndStyles( attributes, settings ) { const fontFamilyClassName = !! attributes?.fontFamily ? `has-${ kebabCase( attributes.fontFamily ) }-font-family` : ''; - + const textAlignClassName = !! attributes?.style?.typography?.textAlign + ? `has-text-align-${ attributes?.style?.typography?.textAlign }` + : ''; const className = classnames( fontFamilyClassName, + textAlignClassName, getFontSizeClass( attributes?.fontSize ) ); diff --git a/packages/block-editor/src/hooks/utils.js b/packages/block-editor/src/hooks/utils.js index 6d75f2c2a686d2..2148b2bb8e5ce7 100644 --- a/packages/block-editor/src/hooks/utils.js +++ b/packages/block-editor/src/hooks/utils.js @@ -220,6 +220,7 @@ export function useBlockSettings( name, parentLayout ) { fontStyle, fontWeight, lineHeight, + textAlign, textColumns, textDecoration, writingMode, @@ -271,6 +272,7 @@ export function useBlockSettings( name, parentLayout ) { 'typography.fontStyle', 'typography.fontWeight', 'typography.lineHeight', + 'typography.textAlign', 'typography.textColumns', 'typography.textDecoration', 'typography.writingMode', @@ -360,6 +362,7 @@ export function useBlockSettings( name, parentLayout ) { fontStyle, fontWeight, lineHeight, + textAlign, textColumns, textDecoration, textTransform, @@ -402,6 +405,7 @@ export function useBlockSettings( name, parentLayout ) { fontStyle, fontWeight, lineHeight, + textAlign, textColumns, textDecoration, textTransform, diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index 62933d69d764f4..08b4e084e2ec9b 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -228,6 +228,11 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { }, useEngine: true, }, + textAlign: { + value: [ 'typography', 'textAlign' ], + support: [ 'typography', 'textAlign' ], + useEngine: false, + }, textDecoration: { value: [ 'typography', 'textDecoration' ], support: [ 'typography', '__experimentalTextDecoration' ], diff --git a/schemas/json/block.json b/schemas/json/block.json index 672de711be79f3..92bcecf90b9b60 100644 --- a/schemas/json/block.json +++ b/schemas/json/block.json @@ -597,6 +597,22 @@ "type": "boolean", "description": "This value signals that a block supports the line-height CSS style property. When it does, the block editor will show an UI control for the user to set its value if the theme declares support.\n\nWhen the block declares support for lineHeight, its attributes definition is extended to include a new attribute style of object type with no default assigned. It stores the custom value set by the user. The block can apply a default style by specifying its own style attribute with a default", "default": false + }, + "textAlign": { + "description": "This property adds block toolbar controls which allow to change block's text alignment.", + "oneOf": [ + { + "type": "boolean" + }, + { + "type": "array", + "items": { + "type": "string", + "enum": [ "left", "center", "right" ] + } + } + ], + "default": false } } }, diff --git a/schemas/json/theme.json b/schemas/json/theme.json index 63889366b7bd40..7cb34945b56810 100644 --- a/schemas/json/theme.json +++ b/schemas/json/theme.json @@ -551,6 +551,11 @@ "type": "boolean", "default": false }, + "textAlign": { + "description": "Allow users to set the text align.", + "type": "boolean", + "default": true + }, "textColumns": { "description": "Allow users to set the number of text columns.", "type": "boolean", @@ -1629,6 +1634,10 @@ } ] }, + "textAlign": { + "description": "Sets the `text-align` CSS property.", + "type": "string" + }, "textColumns": { "description": "Sets the `column-count` CSS property.", "type": "string"