From 014d10bde0bb4acc8f6dd4ee0dc6fd39c8bc4a79 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Mon, 26 Oct 2020 16:17:36 +1000 Subject: [PATCH 1/2] Add combined font style and weight block support Adds both font style and font weight block support options. The UI for both are combined into a single dropdown. The inline styles generated via this feature leverage CSS variables. --- lib/block-supports/typography.php | 61 ++++++++------ lib/experimental-default-theme.json | 48 +++++++++++ lib/global-styles.php | 61 ++++++++++++++ .../font-appearance-control/index.js | 77 +++++++++++++++++ .../font-appearance-control/style.scss | 3 + .../block-editor/src/hooks/font-appearance.js | 82 +++++++++++++++++++ packages/block-editor/src/hooks/typography.js | 8 ++ packages/block-editor/src/style.scss | 1 + .../block-library/src/navigation/block.json | 1 + .../block-library/src/navigation/index.php | 4 +- packages/blocks/src/api/constants.js | 2 + .../edit-site/src/components/editor/utils.js | 2 + 12 files changed, 322 insertions(+), 28 deletions(-) create mode 100644 packages/block-editor/src/components/font-appearance-control/index.js create mode 100644 packages/block-editor/src/components/font-appearance-control/style.scss create mode 100644 packages/block-editor/src/hooks/font-appearance.js diff --git a/lib/block-supports/typography.php b/lib/block-supports/typography.php index 925071767182dc..63f7f292a88bc9 100644 --- a/lib/block-supports/typography.php +++ b/lib/block-supports/typography.php @@ -11,31 +11,22 @@ * @param WP_Block_Type $block_type Block Type. */ function gutenberg_register_typography_support( $block_type ) { - $has_font_size_support = false; - if ( property_exists( $block_type, 'supports' ) ) { - $has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false ); - } - - $has_line_height_support = false; - if ( property_exists( $block_type, 'supports' ) ) { - $has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false ); - } - - $has_text_transform_support = false; - if ( property_exists( $block_type, 'supports' ) ) { - $has_text_transform_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextTransform' ), false ); + if ( ! property_exists( $block_type, 'supports' ) ) { + return; } - $has_text_decoration_support = false; - if ( property_exists( $block_type, 'supports' ) ) { - $has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false ); - } + $has_font_appearance_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontAppearance' ), false ); + $has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false ); + $has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false ); + $has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false ); + $has_text_transform_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextTransform' ), false ); + $has_typography_support = $has_font_appearance_support || $has_font_size_support || $has_line_height_support || $has_text_transform_support || $has_text_decoration_support; if ( ! $block_type->attributes ) { $block_type->attributes = array(); } - if ( ( $has_font_size_support || $has_line_height_support || $has_text_transform_support || $has_text_decoration_support ) && ! array_key_exists( 'style', $block_type->attributes ) ) { + if ( $has_typography_support && ! array_key_exists( 'style', $block_type->attributes ) ) { $block_type->attributes['style'] = array( 'type' => 'object', ); @@ -65,6 +56,7 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { $classes = array(); $styles = array(); + $has_font_appearance_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalFontAppearance' ), false ); $has_font_size_support = gutenberg_experimental_get( $block_type->supports, array( 'fontSize' ), false ); $has_line_height_support = gutenberg_experimental_get( $block_type->supports, array( 'lineHeight' ), false ); $has_text_decoration_support = gutenberg_experimental_get( $block_type->supports, array( '__experimentalTextDecoration' ), false ); @@ -105,6 +97,21 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { } } + // Font appearance - style and weight. + if ( $has_font_appearance_support ) { + // Apply font style. + $font_style = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontStyle', 'font-style' ); + if ( $font_style ) { + $styles[] = $font_style; + } + + // Apply font weight. + $font_weight = gutenberg_typography_get_css_variable_inline_style( $block_attributes, 'fontWeight', 'font-weight' ); + if ( $font_weight ) { + $styles[] = $font_weight; + } + } + // Line Height. if ( $has_line_height_support ) { $has_line_height = isset( $block_attributes['style']['typography']['lineHeight'] ); @@ -141,15 +148,6 @@ function gutenberg_apply_typography_support( $block_type, $block_attributes ) { return $attributes; } -// Register the block support. -WP_Block_Supports::get_instance()->register( - 'typography', - array( - 'register_attribute' => 'gutenberg_register_typography_support', - 'apply' => 'gutenberg_apply_typography_support', - ) -); - /** * Generates an inline style for a typography feature e.g. text decoration, * text transform, and font style. @@ -180,3 +178,12 @@ function gutenberg_typography_get_css_variable_inline_style( $attributes, $featu // Return the actual CSS inline style e.g. `text-decoration:var(--wp--preset--text-decoration--underline);`. return sprintf( '%s:var(--wp--preset--%s--%s);', $css_property, $css_property, $slug ); } + +// Register the block support. +WP_Block_Supports::get_instance()->register( + 'typography', + array( + 'register_attribute' => 'gutenberg_register_typography_support', + 'apply' => 'gutenberg_apply_typography_support', + ) +); diff --git a/lib/experimental-default-theme.json b/lib/experimental-default-theme.json index 8448b0c05f24b4..d2cb060b67d9c4 100644 --- a/lib/experimental-default-theme.json +++ b/lib/experimental-default-theme.json @@ -161,6 +161,54 @@ "size": 42 } ], + "fontStyles": [ + { + "name": "Regular", + "slug": "normal" + }, + { + "name": "Italic", + "slug": "italic" + } + ], + "fontWeights": [ + { + "name": "Ultralight", + "slug": "100" + }, + { + "name": "Thin", + "slug": "200" + }, + { + "name": "Light", + "slug": "300" + }, + { + "name": "Regular", + "slug": "400" + }, + { + "name": "Medium", + "slug": "500" + }, + { + "name": "Semibold", + "slug": "600" + }, + { + "name": "Bold", + "slug": "700" + }, + { + "name": "Heavy", + "slug": "800" + }, + { + "name": "Black", + "slug": "900" + } + ], "textTransforms": [ { "name": "AB", diff --git a/lib/global-styles.php b/lib/global-styles.php index d8c3c3e72cc4ea..04babf8b3c73af 100644 --- a/lib/global-styles.php +++ b/lib/global-styles.php @@ -250,6 +250,40 @@ function gutenberg_experimental_global_styles_get_core() { $font_size['name'] = $default_font_sizes_i18n[ $font_size['slug'] ]; } } + + $default_font_styles_i18n = array( + 'normal' => __( 'Regular', 'gutenberg' ), + 'italic' => __( 'Italic', 'gutenberg' ), + 'initial' => __( 'Initial', 'gutenberg' ), + 'inherit' => __( 'Inherit', 'gutenberg' ), + ); + + if ( ! empty( $config['global']['settings']['typography']['fontStyles'] ) ) { + foreach ( $config['global']['settings']['typography']['fontStyles'] as &$font_style ) { + $font_style['name'] = $default_font_styles_i18n[ $font_style['slug'] ]; + } + } + + $default_font_weights_i18n = array( + '100' => __( 'Ultralight', 'gutenberg' ), + '200' => __( 'Thin', 'gutenberg' ), + '300' => __( 'Light', 'gutenberg' ), + '400' => __( 'Regular', 'gutenberg' ), + '500' => __( 'Medium', 'gutenberg' ), + '600' => __( 'Semibold', 'gutenberg' ), + '700' => __( 'Bold', 'gutenberg' ), + '800' => __( 'Heavy', 'gutenberg' ), + '900' => __( 'Black', 'gutenberg' ), + 'initial' => __( 'Initial', 'gutenberg' ), + 'inherit' => __( 'Inherit', 'gutenberg' ), + ); + + if ( ! empty( $config['global']['settings']['typography']['fontWeights'] ) ) { + foreach ( $config['global']['settings']['typography']['fontWeights'] as &$font_weight ) { + $font_weight['name'] = $default_font_weights_i18n[ $font_weight['slug'] ]; + } + } + // End i18n logic to remove when JSON i18 strings are extracted. return $config; } @@ -393,6 +427,10 @@ function gutenberg_experimental_global_styles_get_css_property( $style_property return 'background-color'; case 'fontSize': return 'font-size'; + case 'fontStyle': + return 'font-style'; + case 'fontWeight': + return 'font-weight'; case 'lineHeight': return 'line-height'; case 'fontFamily': @@ -419,6 +457,8 @@ function gutenberg_experimental_global_styles_get_style_property() { 'color' => array( 'color', 'text' ), 'fontSize' => array( 'typography', 'fontSize' ), 'fontFamily' => array( 'typography', 'fontFamily' ), + 'fontStyle' => array( 'typography', 'fontStyle' ), + 'fontWeight' => array( 'typography', 'fontWeight' ), 'lineHeight' => array( 'typography', 'lineHeight' ), 'textDecoration' => array( 'typography', 'textDecoration' ), 'textTransform' => array( 'typography', 'textTransform' ), @@ -437,6 +477,8 @@ function gutenberg_experimental_global_styles_get_support_keys() { 'backgroundColor' => array( 'color' ), 'color' => array( 'color' ), 'fontSize' => array( 'fontSize' ), + 'fontStyle' => array( '__experimentalFontAppearance' ), + 'fontWeight' => array( '__experimentalFontAppearance' ), 'lineHeight' => array( 'lineHeight' ), 'fontFamily' => array( '__experimentalFontFamily' ), 'textDecoration' => array( '__experimentalTextDecoration' ), @@ -467,6 +509,14 @@ function gutenberg_experimental_global_styles_get_presets_structure() { 'path' => array( 'typography', 'fontFamilies' ), 'key' => 'fontFamily', ), + 'fontStyle' => array( + 'path' => array( 'typography', 'fontStyles' ), + 'key' => 'slug', + ), + 'fontWeight' => array( + 'path' => array( 'typography', 'fontWeights' ), + 'key' => 'slug', + ), 'textDecoration' => array( 'path' => array( 'typography', 'textDecorations' ), 'key' => 'value', @@ -514,6 +564,7 @@ function gutenberg_experimental_global_styles_get_block_data() { array( 'supports' => array( '__experimentalSelector' => ':root', + '__experimentalFontAppearance' => false, '__experimentalFontFamily' => true, 'fontSize' => true, '__experimentalTextDecoration' => true, @@ -653,6 +704,16 @@ function gutenberg_experimental_global_styles_get_preset_classes( $selector, $se 'key' => 'size', 'property' => 'font-size', ), + 'font-style' => array( + 'path' => array( 'typography', 'fontStyles' ), + 'key' => 'slug', + 'property' => 'font-style', + ), + 'font-weight' => array( + 'path' => array( 'typography', 'fontWeights' ), + 'key' => 'slug', + 'property' => 'font-weight', + ), 'text-decoration' => array( 'path' => array( 'typography', 'textDecorations' ), 'key' => 'value', diff --git a/packages/block-editor/src/components/font-appearance-control/index.js b/packages/block-editor/src/components/font-appearance-control/index.js new file mode 100644 index 00000000000000..6e7dea0cbd126d --- /dev/null +++ b/packages/block-editor/src/components/font-appearance-control/index.js @@ -0,0 +1,77 @@ +/** + * WordPress dependencies + */ +import { CustomSelectControl } from '@wordpress/components'; +import { useMemo } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; + +/** + * Control to display unified font style and weight options. + * + * @param {Object} props Component props. + * @param {Object} props.value Currently selected combination of font style and weight. + * @param {Object} props.options Object containing weight and style options. + * @param {Function} props.onChange Handles selection change. + * @return {WPElement} Font appearance control. + */ +export default function FontAppearanceControl( { value, options, onChange } ) { + const { fontStyle, fontWeight } = value; + const { fontStyles = [], fontWeights = [] } = options; + const hasStylesOrWeights = fontStyles.length > 0 || fontWeights.length > 0; + + // Map font styles and weights to select options. + const selectOptions = useMemo( () => { + const defaultCombo = { fontStyle: undefined, fontWeight: undefined }; + const combinedOptions = [ + { + key: 'default', + name: __( 'Default' ), + style: defaultCombo, + presetStyle: defaultCombo, + }, + ]; + + fontStyles.forEach( ( { name: styleName, slug: styleSlug } ) => { + fontWeights.forEach( ( { name: weightName, slug: weightSlug } ) => { + combinedOptions.push( { + key: `${ weightSlug }-${ styleSlug }`, + name: + styleSlug === 'normal' + ? weightName + : `${ weightName } - ${ styleName }`, + // style applies font appearance to the individual select option. + style: { fontStyle: styleSlug, fontWeight: weightSlug }, + // presetStyle are the actual typography styles that should be given to onChange. + presetStyle: { + fontStyle: `var:preset|font-style|${ styleSlug }`, + fontWeight: `var:preset|font-weight|${ weightSlug }`, + }, + } ); + } ); + } ); + + return combinedOptions; + }, [ options ] ); + + const currentSelection = selectOptions.find( + ( option ) => + option.presetStyle.fontStyle === fontStyle && + option.presetStyle.fontWeight === fontWeight + ); + + return ( +
+ { hasStylesOrWeights && ( + + onChange( selectedItem.presetStyle ) + } + /> + ) } +
+ ); +} diff --git a/packages/block-editor/src/components/font-appearance-control/style.scss b/packages/block-editor/src/components/font-appearance-control/style.scss new file mode 100644 index 00000000000000..8ea03e8edddcc4 --- /dev/null +++ b/packages/block-editor/src/components/font-appearance-control/style.scss @@ -0,0 +1,3 @@ +.components-font-appearance-control__select { + margin-bottom: 24px; +} diff --git a/packages/block-editor/src/hooks/font-appearance.js b/packages/block-editor/src/hooks/font-appearance.js new file mode 100644 index 00000000000000..4e51e86edfef8d --- /dev/null +++ b/packages/block-editor/src/hooks/font-appearance.js @@ -0,0 +1,82 @@ +/** + * WordPress dependencies + */ +import { hasBlockSupport } from '@wordpress/blocks'; + +/** + * Internal dependencies + */ +import FontAppearanceControl from '../components/font-appearance-control'; +import useEditorFeature from '../components/use-editor-feature'; +import { cleanEmptyObject } from './utils'; + +/** + * Key within block settings' support array indicating support for font + * appearance options e.g. font weight and style. + */ +export const FONT_APPEARANCE_SUPPORT_KEY = '__experimentalFontAppearance'; + +/** + * Inspector control panel containing the font appearance options. + * + * @param {Object} props Block properties. + * @return {WPElement} Font appearance edit element. + */ +export function FontAppearanceEdit( props ) { + const { + attributes: { style }, + setAttributes, + } = props; + + const fontStyles = useEditorFeature( 'typography.fontStyles' ); + const fontWeights = useEditorFeature( 'typography.fontWeights' ); + const isDisabled = useIsFontAppearanceDisabled( props ); + + if ( isDisabled ) { + return null; + } + + const onChange = ( newStyles ) => { + setAttributes( { + style: cleanEmptyObject( { + ...style, + typography: { + ...style?.typography, + ...newStyles, + }, + } ), + } ); + }; + + const currentSelection = { + fontStyle: style?.typography?.fontStyle, + fontWeight: style?.typography?.fontWeight, + }; + + return ( + + ); +} + +/** + * Checks if font appearance support has been disabled. + * + * @param {Object} props Block properties. + * @param {string} props.name Name for the block type. + * @return {boolean} Whether font appearance support has been disabled. + */ +export function useIsFontAppearanceDisabled( { name: blockName } = {} ) { + const notSupported = ! hasBlockSupport( + blockName, + FONT_APPEARANCE_SUPPORT_KEY + ); + const fontStyles = useEditorFeature( 'typography.fontStyles' ); + const fontWeights = useEditorFeature( 'typography.fontWeights' ); + const hasFontAppearance = !! fontStyles.length && !! fontWeights.length; + + return notSupported || ! hasFontAppearance; +} diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index 56a03b349615d8..db17bd98aaca9f 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -17,6 +17,11 @@ import { LineHeightEdit, useIsLineHeightDisabled, } from './line-height'; +import { + FONT_APPEARANCE_SUPPORT_KEY, + FontAppearanceEdit, + useIsFontAppearanceDisabled, +} from './font-appearance'; import { FONT_FAMILY_SUPPORT_KEY, FontFamilyEdit, @@ -38,6 +43,7 @@ import { export const TYPOGRAPHY_SUPPORT_KEYS = [ LINE_HEIGHT_SUPPORT_KEY, + FONT_APPEARANCE_SUPPORT_KEY, FONT_SIZE_SUPPORT_KEY, FONT_FAMILY_SUPPORT_KEY, TEXT_DECORATION_SUPPORT_KEY, @@ -55,6 +61,7 @@ export function TypographyPanel( props ) { + @@ -73,6 +80,7 @@ const hasTypographySupport = ( blockName ) => { function useIsTypographyDisabled( props = {} ) { const configs = [ + useIsFontAppearanceDisabled( props ), useIsFontSizeDisabled( props ), useIsLineHeightDisabled( props ), useIsFontFamilyDisabled( props ), diff --git a/packages/block-editor/src/style.scss b/packages/block-editor/src/style.scss index e7126ae14662dd..bc117ec4307bba 100644 --- a/packages/block-editor/src/style.scss +++ b/packages/block-editor/src/style.scss @@ -30,6 +30,7 @@ @import "./components/colors-gradients/style.scss"; @import "./components/contrast-checker/style.scss"; @import "./components/default-block-appender/style.scss"; +@import "./components/font-appearance-control/style.scss"; @import "./components/link-control/style.scss"; @import "./components/line-height-control/style.scss"; @import "./components/image-size-control/style.scss"; diff --git a/packages/block-library/src/navigation/block.json b/packages/block-library/src/navigation/block.json index 0b7262b4d2f41d..8268fabdffe48b 100644 --- a/packages/block-library/src/navigation/block.json +++ b/packages/block-library/src/navigation/block.json @@ -50,6 +50,7 @@ "html": false, "inserter": true, "fontSize": true, + "__experimentalFontAppearance": true, "__experimentalTextTransform": true, "color": { "textColor": true, diff --git a/packages/block-library/src/navigation/index.php b/packages/block-library/src/navigation/index.php index b56ea6023be975..b9046b529d9dcd 100644 --- a/packages/block-library/src/navigation/index.php +++ b/packages/block-library/src/navigation/index.php @@ -139,10 +139,12 @@ function render_block_core_navigation( $attributes, $content, $block ) { $inner_blocks_html .= $inner_block->render(); } + $block_styles = isset( $attributes['styles'] ) ? $attributes['styles'] : ''; + $wrapper_attributes = get_block_wrapper_attributes( array( 'class' => implode( ' ', $classes ), - 'style' => $colors['inline_styles'] . $font_sizes['inline_styles'], + 'style' => $block_styles . $colors['inline_styles'] . $font_sizes['inline_styles'], ) ); diff --git a/packages/blocks/src/api/constants.js b/packages/blocks/src/api/constants.js index d37e67f5341bbd..0d919995ef28bd 100644 --- a/packages/blocks/src/api/constants.js +++ b/packages/blocks/src/api/constants.js @@ -18,6 +18,8 @@ export const __EXPERIMENTAL_STYLE_PROPERTY = { backgroundColor: [ 'color', 'background' ], color: [ 'color', 'text' ], fontSize: [ 'typography', 'fontSize' ], + fontStyle: [ 'typography', 'fontStyle' ], + fontWeight: [ 'typography', 'fontWeight' ], lineHeight: [ 'typography', 'lineHeight' ], textDecoration: [ 'typography', 'textDecoration' ], textTransform: [ 'typography', 'textTransform' ], diff --git a/packages/edit-site/src/components/editor/utils.js b/packages/edit-site/src/components/editor/utils.js index cd44cf4cf7bb5b..9f4838a700be42 100644 --- a/packages/edit-site/src/components/editor/utils.js +++ b/packages/edit-site/src/components/editor/utils.js @@ -14,6 +14,8 @@ export const PRESET_CATEGORIES = { gradient: { path: [ 'color', 'gradients' ], key: 'gradient' }, fontSize: { path: [ 'typography', 'fontSizes' ], key: 'size' }, fontFamily: { path: [ 'typography', 'fontFamilies' ], key: 'fontFamily' }, + fontStyle: { path: [ 'typography', 'fontStyles' ], key: 'slug' }, + fontWeight: { path: [ 'typography', 'fontWeights' ], key: 'slug' }, textDecoration: { path: [ 'typography', 'textDecorations' ], key: 'value' }, textTransform: { path: [ 'typography', 'textTransforms' ], key: 'slug' }, }; From 5eaf4fce16ed7a6c6dacbad1a482a4287a7e5369 Mon Sep 17 00:00:00 2001 From: Aaron Robertshaw <60436221+aaronrobertshaw@users.noreply.github.com> Date: Tue, 27 Oct 2020 11:57:16 +1000 Subject: [PATCH 2/2] Update styling of font appearance list items --- .../src/components/font-appearance-control/index.js | 2 +- .../src/components/font-appearance-control/style.scss | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/block-editor/src/components/font-appearance-control/index.js b/packages/block-editor/src/components/font-appearance-control/index.js index 6e7dea0cbd126d..29a6036d4a385a 100644 --- a/packages/block-editor/src/components/font-appearance-control/index.js +++ b/packages/block-editor/src/components/font-appearance-control/index.js @@ -38,7 +38,7 @@ export default function FontAppearanceControl( { value, options, onChange } ) { name: styleSlug === 'normal' ? weightName - : `${ weightName } - ${ styleName }`, + : `${ weightName } ${ styleName }`, // style applies font appearance to the individual select option. style: { fontStyle: styleSlug, fontWeight: weightSlug }, // presetStyle are the actual typography styles that should be given to onChange. diff --git a/packages/block-editor/src/components/font-appearance-control/style.scss b/packages/block-editor/src/components/font-appearance-control/style.scss index 8ea03e8edddcc4..6669d2c452d1a4 100644 --- a/packages/block-editor/src/components/font-appearance-control/style.scss +++ b/packages/block-editor/src/components/font-appearance-control/style.scss @@ -1,3 +1,10 @@ .components-font-appearance-control__select { margin-bottom: 24px; + + ul { + li { + color: $gray-900; + text-transform: capitalize; + } + } }