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 5e7a4de57ca34..120ade21c568e 100644 --- a/packages/block-editor/src/components/font-appearance-control/index.js +++ b/packages/block-editor/src/components/font-appearance-control/index.js @@ -63,7 +63,7 @@ const FONT_WEIGHTS = [ * @param {boolean} hasFontWeights Whether font weights are enabled and present. * @return {string} A label representing what font appearance is being edited. */ -export const getFontAppearanceLabel = ( hasFontStyles, hasFontWeights ) => { +const getFontAppearanceLabel = ( hasFontStyles, hasFontWeights ) => { if ( ! hasFontStyles ) { return __( 'Font weight' ); } diff --git a/packages/block-editor/src/components/global-styles/hooks.js b/packages/block-editor/src/components/global-styles/hooks.js index 9778cc4ee6420..5b2151dbfaba4 100644 --- a/packages/block-editor/src/components/global-styles/hooks.js +++ b/packages/block-editor/src/components/global-styles/hooks.js @@ -7,16 +7,67 @@ import { get, set } from 'lodash'; /** * WordPress dependencies */ -import { useContext, useCallback } from '@wordpress/element'; +import { useContext, useCallback, useMemo } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; +import { store as blocksStore } from '@wordpress/blocks'; /** * Internal dependencies */ import { getValueFromVariable, getPresetVariableFromValue } from './utils'; import { GlobalStylesContext } from './context'; +import { unlock } from '../../lock-unlock'; const EMPTY_CONFIG = { settings: {}, styles: {} }; +const VALID_SETTINGS = [ + 'appearanceTools', + 'useRootPaddingAwareAlignments', + 'border.color', + 'border.radius', + 'border.style', + 'border.width', + 'shadow.presets', + 'shadow.defaultPresets', + 'color.background', + 'color.custom', + 'color.customDuotone', + 'color.customGradient', + 'color.defaultDuotone', + 'color.defaultGradients', + 'color.defaultPalette', + 'color.duotone', + 'color.gradients', + 'color.link', + 'color.palette', + 'color.text', + 'custom', + 'dimensions.minHeight', + 'layout.contentSize', + 'layout.definitions', + 'layout.wideSize', + 'position.fixed', + 'position.sticky', + 'spacing.customSpacingSize', + 'spacing.spacingSizes', + 'spacing.spacingScale', + 'spacing.blockGap', + 'spacing.margin', + 'spacing.padding', + 'spacing.units', + 'typography.fuild', + 'typography.customFontSize', + 'typography.dropCap', + 'typography.fontFamilies', + 'typography.fontSizes', + 'typography.fontStyle', + 'typography.fontWeight', + 'typography.letterSpacing', + 'typography.lineHeight', + 'typography.textDecoration', + 'typography.textTransform', +]; + export const useGlobalStylesReset = () => { const { user: config, setUserConfig } = useContext( GlobalStylesContext ); const canReset = !! config && ! fastDeepEqual( config, EMPTY_CONFIG ); @@ -29,68 +80,78 @@ export const useGlobalStylesReset = () => { ]; }; -export function useGlobalSetting( path, blockName, source = 'all' ) { - const { - merged: mergedConfig, - base: baseConfig, - user: userConfig, - setUserConfig, - } = useContext( GlobalStylesContext ); +export function useGlobalSetting( propertyPath, blockName, source = 'all' ) { + const { setUserConfig, ...configs } = useContext( GlobalStylesContext ); - const fullPath = ! blockName - ? `settings.${ path }` - : `settings.blocks.${ blockName }.${ path }`; + const appendedBlockPath = blockName ? '.blocks.' + blockName : ''; + const appendedPropertyPath = propertyPath ? '.' + propertyPath : ''; + const contextualPath = `settings${ appendedBlockPath }${ appendedPropertyPath }`; + const globalPath = `settings${ appendedPropertyPath }`; + const sourceKey = source === 'all' ? 'merged' : source; + + const settingValue = useMemo( () => { + const configToUse = configs[ sourceKey ]; + if ( ! configToUse ) { + throw 'Unsupported source'; + } + + if ( propertyPath ) { + return ( + get( configToUse, contextualPath ) ?? + get( configToUse, globalPath ) + ); + } + + const result = {}; + VALID_SETTINGS.forEach( ( setting ) => { + const value = + get( + configToUse, + `settings${ appendedBlockPath }.${ setting }` + ) ?? get( configToUse, `settings.${ setting }` ); + if ( value ) { + set( result, setting, value ); + } + } ); + return result; + }, [ + configs, + sourceKey, + propertyPath, + contextualPath, + globalPath, + appendedBlockPath, + ] ); const setSetting = ( newValue ) => { setUserConfig( ( currentConfig ) => { // Deep clone `currentConfig` to avoid mutating it later. const newUserConfig = JSON.parse( JSON.stringify( currentConfig ) ); - set( newUserConfig, fullPath, newValue ); + set( newUserConfig, contextualPath, newValue ); return newUserConfig; } ); }; - const getSettingValueForContext = ( name ) => { - const currentPath = ! name - ? `settings.${ path }` - : `settings.blocks.${ name }.${ path }`; - - let result; - switch ( source ) { - case 'all': - result = get( mergedConfig, currentPath ); - break; - case 'user': - result = get( userConfig, currentPath ); - break; - case 'base': - result = get( baseConfig, currentPath ); - break; - default: - throw 'Unsupported source'; - } - - return result; - }; - - // Unlike styles settings get inherited from top level settings. - const resultWithFallback = - getSettingValueForContext( blockName ) ?? getSettingValueForContext(); - - return [ resultWithFallback, setSetting ]; + return [ settingValue, setSetting ]; } -export function useGlobalStyle( path, blockName, source = 'all' ) { +export function useGlobalStyle( + path, + blockName, + source = 'all', + { shouldDecodeEncode = true } = {} +) { const { merged: mergedConfig, base: baseConfig, user: userConfig, setUserConfig, } = useContext( GlobalStylesContext ); + const appendedPath = path ? '.' + path : ''; const finalPath = ! blockName - ? `styles.${ path }` - : `styles.blocks.${ blockName }.${ path }`; + ? `styles${ appendedPath }` + : `styles.blocks.${ blockName }${ appendedPath }`; const setStyle = ( newValue ) => { setUserConfig( ( currentConfig ) => { @@ -99,43 +160,42 @@ export function useGlobalStyle( path, blockName, source = 'all' ) { set( newUserConfig, finalPath, - getPresetVariableFromValue( - mergedConfig.settings, - blockName, - path, - newValue - ) + shouldDecodeEncode + ? getPresetVariableFromValue( + mergedConfig.settings, + blockName, + path, + newValue + ) + : newValue ); return newUserConfig; } ); }; - let result; + let rawResult, result; switch ( source ) { case 'all': - result = getValueFromVariable( - mergedConfig, - blockName, + rawResult = // The stlyes.css path is allowed to be empty, so don't revert to base if undefined. finalPath === 'styles.css' ? get( userConfig, finalPath ) - : get( userConfig, finalPath ) ?? - get( baseConfig, finalPath ) - ); + : get( mergedConfig, finalPath ); + result = shouldDecodeEncode + ? getValueFromVariable( mergedConfig, blockName, rawResult ) + : rawResult; break; case 'user': - result = getValueFromVariable( - mergedConfig, - blockName, - get( userConfig, finalPath ) - ); + rawResult = get( userConfig, finalPath ); + result = shouldDecodeEncode + ? getValueFromVariable( mergedConfig, blockName, rawResult ) + : rawResult; break; case 'base': - result = getValueFromVariable( - baseConfig, - blockName, - get( baseConfig, finalPath ) - ); + rawResult = get( baseConfig, finalPath ); + result = shouldDecodeEncode + ? getValueFromVariable( baseConfig, blockName, rawResult ) + : rawResult; break; default: throw 'Unsupported source'; @@ -143,3 +203,18 @@ export function useGlobalStyle( path, blockName, source = 'all' ) { return [ result, setStyle ]; } + +export function useSupportedStyles( name, element ) { + const { supportedPanels } = useSelect( + ( select ) => { + return { + supportedPanels: unlock( + select( blocksStore ) + ).getSupportedStyles( name, element ), + }; + }, + [ name, element ] + ); + + return supportedPanels; +} diff --git a/packages/block-editor/src/components/global-styles/index.js b/packages/block-editor/src/components/global-styles/index.js index cd0c8cdf487c5..6581f46254985 100644 --- a/packages/block-editor/src/components/global-styles/index.js +++ b/packages/block-editor/src/components/global-styles/index.js @@ -5,3 +5,7 @@ export { } from './hooks'; export { useGlobalStylesOutput } from './use-global-styles-output'; export { GlobalStylesContext } from './context'; +export { + default as TypographyPanel, + useHasTypographyPanel, +} from './typography-panel'; diff --git a/packages/block-editor/src/components/global-styles/typography-panel.js b/packages/block-editor/src/components/global-styles/typography-panel.js new file mode 100644 index 0000000000000..471055be391f7 --- /dev/null +++ b/packages/block-editor/src/components/global-styles/typography-panel.js @@ -0,0 +1,470 @@ +/** + * WordPress dependencies + */ +import { + FontSizePicker, + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, +} from '@wordpress/components'; +import { __ } from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import FontFamilyControl from '../font-family'; +import FontAppearanceControl from '../font-appearance-control'; +import LineHeightControl from '../line-height-control'; +import LetterSpacingControl from '../letter-spacing-control'; +import TextTransformControl from '../text-transform-control'; +import TextDecorationControl from '../text-decoration-control'; +import { useSupportedStyles } from './hooks'; +import { getValueFromVariable } from './utils'; + +export function useHasTypographyPanel( name, element, settings ) { + const hasFontFamily = useHasFontFamilyControl( name, element, settings ); + const hasLineHeight = useHasLineHeightControl( name, element, settings ); + const hasFontAppearance = useHasAppearanceControl( + name, + element, + settings + ); + const hasLetterSpacing = useHasLetterSpacingControl( + name, + element, + settings + ); + const hasTextTransform = useHasTextTransformControl( + name, + element, + settings + ); + const hasTextDecoration = useHasTextDecorationControl( name, element ); + const hasFontSize = useHasFontSizeControl( name, element, settings ); + + return ( + hasFontFamily || + hasLineHeight || + hasFontAppearance || + hasLetterSpacing || + hasTextTransform || + hasFontSize || + hasTextDecoration + ); +} + +function useHasFontSizeControl( name, element, settings ) { + const supports = useSupportedStyles( name, element ); + const disableCustomFontSizes = ! settings?.typography?.customFontSize; + const fontSizesPerOrigin = settings?.typography?.fontSizes ?? {}; + const fontSizes = + fontSizesPerOrigin?.custom ?? + fontSizesPerOrigin?.theme ?? + fontSizesPerOrigin.default; + return ( + supports.includes( 'fontSize' ) && + ( !! fontSizes?.length || ! disableCustomFontSizes ) + ); +} + +function useHasFontFamilyControl( name, element, settings ) { + const supports = useSupportedStyles( name, element ); + const fontFamiliesPerOrigin = settings?.typography?.fontFamilies; + const fontFamilies = + fontFamiliesPerOrigin?.custom ?? + fontFamiliesPerOrigin?.theme ?? + fontFamiliesPerOrigin?.default; + return supports.includes( 'fontFamily' ) && !! fontFamilies?.length; +} + +function useHasLineHeightControl( name, element, settings ) { + const supports = useSupportedStyles( name, element ); + return ( + settings?.typography?.lineHeight && supports.includes( 'lineHeight' ) + ); +} + +function useHasAppearanceControl( name, element, settings ) { + const supports = useSupportedStyles( name, element ); + const hasFontStyles = + settings?.typography?.fontStyle && supports.includes( 'fontStyle' ); + const hasFontWeights = + settings?.typography?.fontWeight && supports.includes( 'fontWeight' ); + return hasFontStyles || hasFontWeights; +} + +function useAppearanceControlLabel( name, element, settings ) { + const supports = useSupportedStyles( name, element ); + const hasFontStyles = + settings?.typography?.fontStyle && supports.includes( 'fontStyle' ); + const hasFontWeights = + settings?.typography?.fontWeight && supports.includes( 'fontWeight' ); + if ( ! hasFontStyles ) { + return __( 'Font weight' ); + } + if ( ! hasFontWeights ) { + return __( 'Font style' ); + } + return __( 'Appearance' ); +} + +function useHasLetterSpacingControl( name, element, settings ) { + const setting = settings?.typography?.letterSpacing; + const supports = useSupportedStyles( name, element ); + if ( ! setting ) { + return false; + } + return supports.includes( 'letterSpacing' ); +} + +function useHasTextTransformControl( name, element, settings ) { + const setting = settings?.typography?.textTransform; + const supports = useSupportedStyles( name, element ); + if ( ! setting ) { + return false; + } + return supports.includes( 'textTransform' ); +} + +function useHasTextDecorationControl( name, element ) { + const supports = useSupportedStyles( name, element ); + return supports.includes( 'textDecoration' ); +} + +function TypographyToolsPanel( { ...props } ) { + return ; +} + +const DEFAULT_CONTROLS = { + fontFamily: true, + fontSize: true, + fontAppearance: true, + lineHeight: true, + letterSpacing: true, + textTransform: true, + textDecoration: true, +}; + +export default function TypographyPanel( { + as: Wrapper = TypographyToolsPanel, + name, + element, + value, + onChange, + inheritedValue = value, + settings, + panelId, + defaultControls = DEFAULT_CONTROLS, +} ) { + const decodeValue = ( rawValue ) => + getValueFromVariable( { settings }, '', rawValue ); + + // Font Family + const hasFontFamilyEnabled = useHasFontFamilyControl( + name, + element, + settings + ); + const fontFamiliesPerOrigin = settings?.typography?.fontFamilies; + const fontFamilies = + fontFamiliesPerOrigin?.custom ?? + fontFamiliesPerOrigin?.theme ?? + fontFamiliesPerOrigin?.default; + const fontFamily = decodeValue( inheritedValue?.typography?.fontFamily ); + const setFontFamily = ( newValue ) => { + const slug = fontFamilies?.find( + ( { fontFamily: f } ) => f === newValue + )?.slug; + onChange( { + ...value, + typography: { + ...value?.typography, + fontFamily: slug + ? `var:preset|font-family|${ slug }` + : newValue, + }, + } ); + }; + const hasFontFamily = () => !! value?.typography?.fontFamily; + const resetFontFamily = () => setFontFamily( undefined ); + + // Font Size + const hasFontSizeEnabled = useHasFontSizeControl( name, element, settings ); + const disableCustomFontSizes = ! settings?.typography?.customFontSize; + const fontSizesPerOrigin = settings?.typography?.fontSizes ?? {}; + const fontSizes = + fontSizesPerOrigin?.custom ?? + fontSizesPerOrigin?.theme ?? + fontSizesPerOrigin.default; + const fontSize = decodeValue( inheritedValue?.typography?.fontSize ); + const setFontSize = ( newValue, metadata ) => { + const actualValue = !! metadata?.slug + ? `var:preset|font-size|${ metadata?.slug }` + : newValue; + + onChange( { + ...value, + typography: { + ...value?.typography, + fontSize: actualValue, + }, + } ); + }; + const hasFontSize = () => !! value?.typography?.fontSize; + const resetFontSize = () => setFontSize( undefined ); + + // Appearance + const hasAppearanceControl = useHasAppearanceControl( + name, + element, + settings + ); + const appearanceControlLabel = useAppearanceControlLabel( + name, + element, + settings + ); + const hasFontStyles = settings?.typography?.fontStyle; + const hasFontWeights = settings?.typography?.fontWeight; + const fontStyle = decodeValue( inheritedValue?.typography?.fontStyle ); + const fontWeight = decodeValue( inheritedValue?.typography?.fontWeight ); + const setFontAppearance = ( { + fontStyle: newFontStyle, + fontWeight: newFontWeight, + } ) => { + onChange( { + ...value, + typography: { + ...value?.typography, + fontStyle: newFontStyle, + fontWeight: newFontWeight, + }, + } ); + }; + const hasFontAppearance = () => + !! value?.typography?.fontStyle || !! value?.typography?.fontWeight; + const resetFontAppearance = () => { + setFontAppearance( {} ); + }; + + // Line Height + const hasLineHeightEnabled = useHasLineHeightControl( + name, + element, + settings + ); + const lineHeight = decodeValue( inheritedValue?.typography?.lineHeight ); + const setLineHeight = ( newValue ) => { + onChange( { + ...value, + typography: { + ...value?.typography, + lineHeight: newValue, + }, + } ); + }; + const hasLineHeight = () => !! value?.typography?.lineHeight; + const resetLineHeight = () => setLineHeight( undefined ); + + // Letter Spacing + const hasLetterSpacingControl = useHasLetterSpacingControl( + name, + element, + settings + ); + const letterSpacing = decodeValue( + inheritedValue?.typography?.letterSpacing + ); + const setLetterSpacing = ( newValue ) => { + onChange( { + ...value, + typography: { + ...value?.typography, + letterSpacing: newValue, + }, + } ); + }; + const hasLetterSpacing = () => !! value?.typography?.letterSpacing; + const resetLetterSpacing = () => setLetterSpacing( undefined ); + + // Text Transform + const hasTextTransformControl = useHasTextTransformControl( + name, + element, + settings + ); + const textTransform = decodeValue( + inheritedValue?.typography?.textTransform + ); + const setTextTransform = ( newValue ) => { + onChange( { + ...value, + typography: { + ...value?.typography, + textTransform: newValue, + }, + } ); + }; + const hasTextTransform = () => !! value?.typography?.textTransform; + const resetTextTransform = () => setTextTransform( undefined ); + + // Text Decoration + const hasTextDecorationControl = useHasTextDecorationControl( + name, + element + ); + const textDecoration = decodeValue( + inheritedValue?.typography?.textDecoration + ); + const setTextDecoration = ( newValue ) => { + onChange( { + ...value, + typography: { + ...value?.typography, + textDecoration: newValue, + }, + } ); + }; + const hasTextDecoration = () => !! value?.typography?.textDecoration; + const resetTextDecoration = () => setTextDecoration( undefined ); + + const resetAll = () => { + onChange( { + ...value, + typography: {}, + } ); + }; + + return ( + + { hasFontFamilyEnabled && ( + + + + ) } + { hasFontSizeEnabled && ( + + + + ) } + { hasAppearanceControl && ( + + + + ) } + { hasLineHeightEnabled && ( + + + + ) } + { hasLetterSpacingControl && ( + + + + ) } + { hasTextDecorationControl && ( + + + + ) } + { hasTextTransformControl && ( + + + + ) } + + ); +} diff --git a/packages/block-editor/src/hooks/font-appearance.js b/packages/block-editor/src/hooks/font-appearance.js deleted file mode 100644 index af25daed0d30b..0000000000000 --- a/packages/block-editor/src/hooks/font-appearance.js +++ /dev/null @@ -1,146 +0,0 @@ -/** - * WordPress dependencies - */ -import { hasBlockSupport } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import FontAppearanceControl from '../components/font-appearance-control'; -import useSetting from '../components/use-setting'; -import { cleanEmptyObject } from './utils'; - -/** - * Key within block settings' support array indicating support for font style. - */ -export const FONT_STYLE_SUPPORT_KEY = 'typography.__experimentalFontStyle'; - -/** - * Key within block settings' support array indicating support for font weight. - */ -export const FONT_WEIGHT_SUPPORT_KEY = 'typography.__experimentalFontWeight'; - -/** - * 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 hasFontStyles = ! useIsFontStyleDisabled( props ); - const hasFontWeights = ! useIsFontWeightDisabled( props ); - - const onChange = ( newStyles ) => { - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - fontStyle: newStyles.fontStyle, - fontWeight: newStyles.fontWeight, - }, - } ), - } ); - }; - - const fontStyle = style?.typography?.fontStyle; - const fontWeight = style?.typography?.fontWeight; - - return ( - - ); -} - -/** - * Checks if font style support has been disabled either by not opting in for - * support or by failing to provide preset styles. - * - * @param {Object} props Block properties. - * @param {string} props.name Name for the block type. - * - * @return {boolean} Whether font style support has been disabled. - */ -export function useIsFontStyleDisabled( { name: blockName } = {} ) { - const styleSupport = hasBlockSupport( blockName, FONT_STYLE_SUPPORT_KEY ); - const hasFontStyles = useSetting( 'typography.fontStyle' ); - - return ! styleSupport || ! hasFontStyles; -} - -/** - * Checks if font weight support has been disabled either by not opting in for - * support or by failing to provide preset weights. - * - * @param {Object} props Block properties. - * @param {string} props.name Name for the block type. - * - * @return {boolean} Whether font weight support has been disabled. - */ -export function useIsFontWeightDisabled( { name: blockName } = {} ) { - const weightSupport = hasBlockSupport( blockName, FONT_WEIGHT_SUPPORT_KEY ); - const hasFontWeights = useSetting( 'typography.fontWeight' ); - - return ! weightSupport || ! hasFontWeights; -} - -/** - * Checks if font appearance support has been disabled. - * - * @param {Object} props Block properties. - * - * @return {boolean} Whether font appearance support has been disabled. - */ -export function useIsFontAppearanceDisabled( props ) { - const stylesDisabled = useIsFontStyleDisabled( props ); - const weightsDisabled = useIsFontWeightDisabled( props ); - - return stylesDisabled && weightsDisabled; -} - -/** - * Checks if there is either a font style or weight value set within the - * typography styles. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a font style or weight. - */ -export function hasFontAppearanceValue( props ) { - const { fontStyle, fontWeight } = props.attributes.style?.typography || {}; - return !! fontStyle || !! fontWeight; -} - -/** - * Resets the font style and weight block support attributes. This can be used - * when disabling the font appearance 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 resetFontAppearance( { attributes = {}, setAttributes } ) { - const { style } = attributes; - - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - fontStyle: undefined, - fontWeight: undefined, - }, - } ), - } ); -} diff --git a/packages/block-editor/src/hooks/font-family.js b/packages/block-editor/src/hooks/font-family.js index 56335ced88707..754bcdd1b5bff 100644 --- a/packages/block-editor/src/hooks/font-family.js +++ b/packages/block-editor/src/hooks/font-family.js @@ -13,8 +13,6 @@ import TokenList from '@wordpress/token-list'; /** * Internal dependencies */ -import useSetting from '../components/use-setting'; -import FontFamilyControl from '../components/font-family'; import { shouldSkipSerialization } from './utils'; import { TYPOGRAPHY_SUPPORT_KEY } from './typography'; @@ -105,62 +103,6 @@ function addEditProps( settings ) { return settings; } -export function FontFamilyEdit( { - setAttributes, - attributes: { fontFamily }, -} ) { - const fontFamilies = useSetting( 'typography.fontFamilies' ); - - const value = fontFamilies?.find( - ( { slug } ) => fontFamily === slug - )?.fontFamily; - - function onChange( newValue ) { - const predefinedFontFamily = fontFamilies?.find( - ( { fontFamily: f } ) => f === newValue - ); - setAttributes( { - fontFamily: predefinedFontFamily?.slug, - } ); - } - - return ( - - ); -} - -/** - * Custom hook that checks if font-family functionality is disabled. - * - * @param {string} name The name of the block. - * @return {boolean} Whether setting is disabled. - */ -export function useIsFontFamilyDisabled( { name } ) { - const fontFamilies = useSetting( 'typography.fontFamilies' ); - return ( - ! fontFamilies || - fontFamilies.length === 0 || - ! hasBlockSupport( name, FONT_FAMILY_SUPPORT_KEY ) - ); -} - -/** - * Checks if there is a current value set for the font family block support. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a font family value set. - */ -export function hasFontFamilyValue( props ) { - return !! props.attributes.fontFamily; -} - /** * Resets the font family block support attribute. This can be used when * disabling the font family support controls for a block via a progressive diff --git a/packages/block-editor/src/hooks/font-size.js b/packages/block-editor/src/hooks/font-size.js index 0c7a71fd23d68..e03b73c56331d 100644 --- a/packages/block-editor/src/hooks/font-size.js +++ b/packages/block-editor/src/hooks/font-size.js @@ -157,41 +157,6 @@ export function FontSizeEdit( props ) { ); } -/** - * Checks if there is a current value set for the font size block support. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a font size value set. - */ -export function hasFontSizeValue( props ) { - const { fontSize, style } = props.attributes; - return !! fontSize || !! style?.typography?.fontSize; -} - -/** - * Resets the font size block support attribute. This can be used when - * disabling the font size 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 resetFontSize( { attributes = {}, setAttributes } ) { - const { style } = attributes; - - setAttributes( { - fontSize: undefined, - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - fontSize: undefined, - }, - } ), - } ); -} - /** * Custom hook that checks if font-size settings have been disabled. * @@ -268,7 +233,7 @@ const MIGRATION_PATHS = { fontSize: [ [ 'fontSize' ], [ 'style', 'typography', 'fontSize' ] ], }; -export function addTransforms( result, source, index, results ) { +function addTransforms( result, source, index, results ) { const destinationBlockType = result.name; const activeSupports = { fontSize: hasBlockSupport( diff --git a/packages/block-editor/src/hooks/index.js b/packages/block-editor/src/hooks/index.js index 84b3ba3a95a33..2077c143952f5 100644 --- a/packages/block-editor/src/hooks/index.js +++ b/packages/block-editor/src/hooks/index.js @@ -12,6 +12,7 @@ import './style'; import './settings'; import './color'; import './duotone'; +import './font-family'; import './font-size'; import './border'; import './position'; diff --git a/packages/block-editor/src/hooks/letter-spacing.js b/packages/block-editor/src/hooks/letter-spacing.js deleted file mode 100644 index 9e214fd07d792..0000000000000 --- a/packages/block-editor/src/hooks/letter-spacing.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * WordPress dependencies - */ -import { hasBlockSupport } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import LetterSpacingControl from '../components/letter-spacing-control'; -import useSetting from '../components/use-setting'; -import { cleanEmptyObject } from './utils'; - -/** - * Key within block settings' supports array indicating support for letter-spacing - * e.g. settings found in `block.json`. - */ -export const LETTER_SPACING_SUPPORT_KEY = - 'typography.__experimentalLetterSpacing'; - -/** - * Inspector control panel containing the letter-spacing options. - * - * @param {Object} props Block properties. - * @return {WPElement} Letter-spacing edit element. - */ -export function LetterSpacingEdit( props ) { - const { - attributes: { style }, - setAttributes, - } = props; - - function onChange( newSpacing ) { - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - letterSpacing: newSpacing, - }, - } ), - } ); - } - - return ( - - ); -} - -/** - * Checks if letter-spacing settings have been disabled. - * - * @param {string} name Name of the block. - * @return {boolean} Whether or not the setting is disabled. - */ -export function useIsLetterSpacingDisabled( { name: blockName } = {} ) { - const notSupported = ! hasBlockSupport( - blockName, - LETTER_SPACING_SUPPORT_KEY - ); - const hasLetterSpacing = useSetting( 'typography.letterSpacing' ); - - return notSupported || ! hasLetterSpacing; -} - -/** - * Checks if there is a current value set for the letter spacing block support. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a letter spacing set. - */ -export function hasLetterSpacingValue( props ) { - return !! props.attributes.style?.typography?.letterSpacing; -} - -/** - * Resets the letter spacing block support attribute. This can be used when - * disabling the letter spacing 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 resetLetterSpacing( { attributes = {}, setAttributes } ) { - const { style } = attributes; - - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - letterSpacing: undefined, - }, - } ), - } ); -} diff --git a/packages/block-editor/src/hooks/line-height.js b/packages/block-editor/src/hooks/line-height.js index c8397d850a1e5..363766e91b497 100644 --- a/packages/block-editor/src/hooks/line-height.js +++ b/packages/block-editor/src/hooks/line-height.js @@ -60,36 +60,3 @@ export function useIsLineHeightDisabled( { name: blockName } = {} ) { ! hasBlockSupport( blockName, LINE_HEIGHT_SUPPORT_KEY ) || isDisabled ); } - -/** - * Checks if there is a current value set for the line height block support. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a line height value set. - */ -export function hasLineHeightValue( props ) { - return !! props.attributes.style?.typography?.lineHeight; -} - -/** - * Resets the line height block support attribute. This can be used when - * disabling the line height 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 resetLineHeight( { attributes = {}, setAttributes } ) { - const { style } = attributes; - - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - lineHeight: undefined, - }, - } ), - } ); -} diff --git a/packages/block-editor/src/hooks/text-decoration.js b/packages/block-editor/src/hooks/text-decoration.js deleted file mode 100644 index 17ba9ee73f698..0000000000000 --- a/packages/block-editor/src/hooks/text-decoration.js +++ /dev/null @@ -1,102 +0,0 @@ -/** - * WordPress dependencies - */ -import { hasBlockSupport } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import TextDecorationControl from '../components/text-decoration-control'; -import useSetting from '../components/use-setting'; -import { cleanEmptyObject } from './utils'; - -/** - * Key within block settings' supports array indicating support for text - * decorations e.g. settings found in `block.json`. - */ -export const TEXT_DECORATION_SUPPORT_KEY = - 'typography.__experimentalTextDecoration'; - -/** - * Inspector control panel containing the text decoration options. - * - * @param {Object} props Block properties. - * - * @return {WPElement} Text decoration edit element. - */ -export function TextDecorationEdit( props ) { - const { - attributes: { style }, - setAttributes, - } = props; - - function onChange( newDecoration ) { - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - textDecoration: newDecoration, - }, - } ), - } ); - } - - return ( - - ); -} - -/** - * Checks if text-decoration settings have been disabled. - * - * @param {string} name Name of the block. - * - * @return {boolean} Whether or not the setting is disabled. - */ -export function useIsTextDecorationDisabled( { name: blockName } = {} ) { - const notSupported = ! hasBlockSupport( - blockName, - TEXT_DECORATION_SUPPORT_KEY - ); - const hasTextDecoration = useSetting( 'typography.textDecoration' ); - - return notSupported || ! hasTextDecoration; -} - -/** - * Checks if there is a current value set for the text decoration block support. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a text decoration set. - */ -export function hasTextDecorationValue( props ) { - return !! props.attributes.style?.typography?.textDecoration; -} - -/** - * Resets the text decoration block support attribute. This can be used when - * disabling the text decoration 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 resetTextDecoration( { attributes = {}, setAttributes } ) { - const { style } = attributes; - - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - textDecoration: undefined, - }, - } ), - } ); -} diff --git a/packages/block-editor/src/hooks/text-transform.js b/packages/block-editor/src/hooks/text-transform.js deleted file mode 100644 index 588327633ecb8..0000000000000 --- a/packages/block-editor/src/hooks/text-transform.js +++ /dev/null @@ -1,101 +0,0 @@ -/** - * WordPress dependencies - */ -import { hasBlockSupport } from '@wordpress/blocks'; - -/** - * Internal dependencies - */ -import TextTransformControl from '../components/text-transform-control'; -import useSetting from '../components/use-setting'; -import { cleanEmptyObject } from './utils'; - -/** - * Key within block settings' supports array indicating support for text - * transforms e.g. settings found in `block.json`. - */ -export const TEXT_TRANSFORM_SUPPORT_KEY = - 'typography.__experimentalTextTransform'; - -/** - * Inspector control panel containing the text transform options. - * - * @param {Object} props Block properties. - * - * @return {WPElement} Text transform edit element. - */ -export function TextTransformEdit( props ) { - const { - attributes: { style }, - setAttributes, - } = props; - - function onChange( newTransform ) { - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - textTransform: newTransform, - }, - } ), - } ); - } - - return ( - - ); -} - -/** - * Checks if text-transform settings have been disabled. - * - * @param {string} name Name of the block. - * - * @return {boolean} Whether or not the setting is disabled. - */ -export function useIsTextTransformDisabled( { name: blockName } = {} ) { - const notSupported = ! hasBlockSupport( - blockName, - TEXT_TRANSFORM_SUPPORT_KEY - ); - const hasTextTransforms = useSetting( 'typography.textTransform' ); - return notSupported || ! hasTextTransforms; -} - -/** - * Checks if there is a current value set for the text transform block support. - * - * @param {Object} props Block props. - * @return {boolean} Whether or not the block has a text transform set. - */ -export function hasTextTransformValue( props ) { - return !! props.attributes.style?.typography?.textTransform; -} - -/** - * Resets the text transform block support attribute. This can be used when - * disabling the text transform 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 resetTextTransform( { attributes = {}, setAttributes } ) { - const { style } = attributes; - - setAttributes( { - style: cleanEmptyObject( { - ...style, - typography: { - ...style?.typography, - textTransform: undefined, - }, - } ), - } ); -} diff --git a/packages/block-editor/src/hooks/typography.js b/packages/block-editor/src/hooks/typography.js index 43fb56ccc008e..87e734b00667c 100644 --- a/packages/block-editor/src/hooks/typography.js +++ b/packages/block-editor/src/hooks/typography.js @@ -2,68 +2,34 @@ * WordPress dependencies */ import { getBlockSupport, hasBlockSupport } from '@wordpress/blocks'; -import { __experimentalToolsPanelItem as ToolsPanelItem } from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { useMemo } from '@wordpress/element'; /** * Internal dependencies */ import InspectorControls from '../components/inspector-controls'; -import { getFontAppearanceLabel } from '../components/font-appearance-control'; - -import { - LINE_HEIGHT_SUPPORT_KEY, - LineHeightEdit, - hasLineHeightValue, - resetLineHeight, - useIsLineHeightDisabled, -} from './line-height'; -import { - FONT_STYLE_SUPPORT_KEY, - FONT_WEIGHT_SUPPORT_KEY, - FontAppearanceEdit, - hasFontAppearanceValue, - resetFontAppearance, - useIsFontAppearanceDisabled, - useIsFontStyleDisabled, - useIsFontWeightDisabled, -} from './font-appearance'; -import { - FONT_FAMILY_SUPPORT_KEY, - FontFamilyEdit, - hasFontFamilyValue, - resetFontFamily, - useIsFontFamilyDisabled, -} from './font-family'; -import { - FONT_SIZE_SUPPORT_KEY, - FontSizeEdit, - hasFontSizeValue, - resetFontSize, - useIsFontSizeDisabled, -} from './font-size'; -import { - TEXT_DECORATION_SUPPORT_KEY, - TextDecorationEdit, - hasTextDecorationValue, - resetTextDecoration, - useIsTextDecorationDisabled, -} from './text-decoration'; -import { - TEXT_TRANSFORM_SUPPORT_KEY, - TextTransformEdit, - hasTextTransformValue, - resetTextTransform, - useIsTextTransformDisabled, -} from './text-transform'; import { - LETTER_SPACING_SUPPORT_KEY, - LetterSpacingEdit, - hasLetterSpacingValue, - resetLetterSpacing, - useIsLetterSpacingDisabled, -} from './letter-spacing'; + default as StylesTypographyPanel, + useHasTypographyPanel, +} from '../components/global-styles/typography-panel'; + +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 { useSetting } from '../components'; +import { cleanEmptyObject } from './utils'; + +function omit( object, keys ) { + return Object.fromEntries( + Object.entries( object ).filter( ( [ key ] ) => ! keys.includes( key ) ) + ); +} +const LETTER_SPACING_SUPPORT_KEY = 'typography.__experimentalLetterSpacing'; +const TEXT_TRANSFORM_SUPPORT_KEY = 'typography.__experimentalTextTransform'; +const TEXT_DECORATION_SUPPORT_KEY = 'typography.__experimentalTextDecoration'; +const FONT_STYLE_SUPPORT_KEY = 'typography.__experimentalFontStyle'; +const FONT_WEIGHT_SUPPORT_KEY = 'typography.__experimentalFontWeight'; export const TYPOGRAPHY_SUPPORT_KEY = 'typography'; export const TYPOGRAPHY_SUPPORT_KEYS = [ LINE_HEIGHT_SUPPORT_KEY, @@ -76,159 +42,98 @@ export const TYPOGRAPHY_SUPPORT_KEYS = [ LETTER_SPACING_SUPPORT_KEY, ]; -export function TypographyPanel( props ) { - const { clientId } = props; - const isFontFamilyDisabled = useIsFontFamilyDisabled( props ); - const isFontSizeDisabled = useIsFontSizeDisabled( props ); - const isFontAppearanceDisabled = useIsFontAppearanceDisabled( props ); - const isLineHeightDisabled = useIsLineHeightDisabled( props ); - const isTextDecorationDisabled = useIsTextDecorationDisabled( props ); - const isTextTransformDisabled = useIsTextTransformDisabled( props ); - const isLetterSpacingDisabled = useIsLetterSpacingDisabled( props ); +function TypographyInspectorControl( { children } ) { + return ( + { children } + ); +} + +export function TypographyPanel( { + clientId, + name, + attributes, + setAttributes, +} ) { + const settings = { + typography: { + fontFamilies: { + custom: useSetting( 'typography.fontFamilies' ), + }, + fontSizes: { + custom: useSetting( 'typography.fontSizes' ), + }, + customFontSize: useSetting( 'typography.customFontSize' ), + fontStyle: useSetting( 'typography.fontStyle' ), + fontWeight: useSetting( 'typography.fontWeight' ), + lineHeight: useSetting( 'typography.lineHeight' ), + textDecoration: useSetting( 'typography.textDecoration' ), + textTransform: useSetting( 'typography.textTransform' ), + letterSpacing: useSetting( 'typography.letterSpacing' ), + }, + }; + + const isSupported = hasTypographySupport( name ); + const isEnabled = useHasTypographyPanel( name, null, settings ); - const hasFontStyles = ! useIsFontStyleDisabled( props ); - const hasFontWeights = ! useIsFontWeightDisabled( props ); + const value = useMemo( () => { + return { + ...attributes.style, + typography: { + ...attributes.style?.typography, + fontFamily: attributes.fontFamily + ? 'var:preset|font-family|' + attributes.fontFamily + : undefined, + fontSize: attributes.fontSize + ? 'var:preset|font-size|' + attributes.fontSize + : attributes.style?.typography?.fontSize, + }, + }; + }, [ attributes.style, attributes.fontSize, attributes.fontFamily ] ); - const isDisabled = useIsTypographyDisabled( props ); - const isSupported = hasTypographySupport( props.name ); + const onChange = ( newStyle ) => { + const updatedStyle = { ...omit( newStyle, [ 'fontFamily' ] ) }; + const fontSizeValue = newStyle?.typography?.fontSize; + const fontFamilyValue = newStyle?.typography?.fontFamily; + const fontSizeSlug = fontSizeValue?.startsWith( + 'var:preset|font-size|' + ) + ? fontSizeValue.substring( 'var:preset|font-size|'.length ) + : undefined; + const fontFamilySlug = fontFamilyValue?.startsWith( + 'var:preset|font-family|' + ) + ? fontFamilyValue.substring( 'var:preset|font-family|'.length ) + : undefined; + updatedStyle.typography = { + ...omit( updatedStyle.typography, [ 'fontFamily' ] ), + fontSize: fontSizeSlug ? undefined : fontSizeValue, + }; + setAttributes( { + style: cleanEmptyObject( updatedStyle ), + fontFamily: fontFamilySlug, + fontSize: fontSizeSlug, + } ); + }; - if ( isDisabled || ! isSupported ) return null; + if ( ! isEnabled || ! isSupported ) { + return null; + } - const defaultControls = getBlockSupport( props.name, [ + const defaultControls = getBlockSupport( name, [ TYPOGRAPHY_SUPPORT_KEY, '__experimentalDefaultControls', ] ); - const createResetAllFilter = ( attribute ) => ( newAttributes ) => ( { - ...newAttributes, - style: { - ...newAttributes.style, - typography: { - ...newAttributes.style?.typography, - [ attribute ]: undefined, - }, - }, - } ); - return ( - - { ! isFontFamilyDisabled && ( - hasFontFamilyValue( props ) } - label={ __( 'Font family' ) } - onDeselect={ () => resetFontFamily( props ) } - isShownByDefault={ defaultControls?.fontFamily } - resetAllFilter={ ( newAttributes ) => ( { - ...newAttributes, - fontFamily: undefined, - } ) } - panelId={ clientId } - > - - - ) } - { ! isFontSizeDisabled && ( - hasFontSizeValue( props ) } - /* translators: Ensure translation is distinct from "Letter case" */ - label={ __( 'Font size' ) } - onDeselect={ () => resetFontSize( props ) } - isShownByDefault={ defaultControls?.fontSize } - resetAllFilter={ ( newAttributes ) => ( { - ...newAttributes, - fontSize: undefined, - style: { - ...newAttributes.style, - typography: { - ...newAttributes.style?.typography, - fontSize: undefined, - }, - }, - } ) } - panelId={ clientId } - > - - - ) } - { ! isFontAppearanceDisabled && ( - hasFontAppearanceValue( props ) } - label={ getFontAppearanceLabel( - hasFontStyles, - hasFontWeights - ) } - onDeselect={ () => resetFontAppearance( props ) } - isShownByDefault={ defaultControls?.fontAppearance } - resetAllFilter={ ( newAttributes ) => ( { - ...newAttributes, - style: { - ...newAttributes.style, - typography: { - ...newAttributes.style?.typography, - fontStyle: undefined, - fontWeight: undefined, - }, - }, - } ) } - panelId={ clientId } - > - - - ) } - { ! isLineHeightDisabled && ( - hasLineHeightValue( props ) } - label={ __( 'Line height' ) } - onDeselect={ () => resetLineHeight( props ) } - isShownByDefault={ defaultControls?.lineHeight } - resetAllFilter={ createResetAllFilter( 'lineHeight' ) } - panelId={ clientId } - > - - - ) } - { ! isLetterSpacingDisabled && ( - hasLetterSpacingValue( props ) } - label={ __( 'Letter spacing' ) } - onDeselect={ () => resetLetterSpacing( props ) } - isShownByDefault={ defaultControls?.letterSpacing } - resetAllFilter={ createResetAllFilter( 'letterSpacing' ) } - panelId={ clientId } - > - - - ) } - { ! isTextDecorationDisabled && ( - hasTextDecorationValue( props ) } - label={ __( 'Decoration' ) } - onDeselect={ () => resetTextDecoration( props ) } - isShownByDefault={ defaultControls?.textDecoration } - resetAllFilter={ createResetAllFilter( 'textDecoration' ) } - panelId={ clientId } - > - - - ) } - { ! isTextTransformDisabled && ( - hasTextTransformValue( props ) } - /* translators: Ensure translation is distinct from "Font size" */ - label={ __( 'Letter case' ) } - onDeselect={ () => resetTextTransform( props ) } - isShownByDefault={ defaultControls?.textTransform } - resetAllFilter={ createResetAllFilter( 'textTransform' ) } - panelId={ clientId } - > - - - ) } - + ); } @@ -237,17 +142,3 @@ export const hasTypographySupport = ( blockName ) => { hasBlockSupport( blockName, key ) ); }; - -function useIsTypographyDisabled( props = {} ) { - const configs = [ - useIsFontAppearanceDisabled( props ), - useIsFontSizeDisabled( props ), - useIsLineHeightDisabled( props ), - useIsFontFamilyDisabled( props ), - useIsTextDecorationDisabled( props ), - useIsTextTransformDisabled( props ), - useIsLetterSpacingDisabled( props ), - ]; - - return configs.filter( Boolean ).length === configs.length; -} diff --git a/packages/edit-site/src/components/global-styles/color-utils.js b/packages/edit-site/src/components/global-styles/color-utils.js index a80684344e7a0..b022b8506b0a2 100644 --- a/packages/edit-site/src/components/global-styles/color-utils.js +++ b/packages/edit-site/src/components/global-styles/color-utils.js @@ -1,7 +1,6 @@ /** * Internal dependencies */ - import { useSupportedStyles } from './hooks'; export function useHasColorPanel( name ) { diff --git a/packages/edit-site/src/components/global-styles/context-menu.js b/packages/edit-site/src/components/global-styles/context-menu.js index 96251c94d8c08..0e0cab1027650 100644 --- a/packages/edit-site/src/components/global-styles/context-menu.js +++ b/packages/edit-site/src/components/global-styles/context-menu.js @@ -21,6 +21,7 @@ import { import { isRTL, __ } from '@wordpress/i18n'; import { useSelect } from '@wordpress/data'; import { store as coreStore } from '@wordpress/core-data'; +import { experiments as blockEditorExperiments } from '@wordpress/block-editor'; /** * Internal dependencies @@ -28,15 +29,20 @@ import { store as coreStore } from '@wordpress/core-data'; import { useHasBorderPanel } from './border-panel'; import { useHasColorPanel } from './color-utils'; import { useHasDimensionsPanel } from './dimensions-panel'; -import { useHasTypographyPanel } from './typography-panel'; import { useHasVariationsPanel } from './variations-panel'; import { NavigationButtonAsItem } from './navigation-button'; import { IconWithCurrentColor } from './icon-with-current-color'; import { ScreenVariations } from './screen-variations'; import { useHasShadowControl } from './shadow-panel'; +import { unlock } from '../../experiments'; + +const { useHasTypographyPanel, useGlobalSetting } = unlock( + blockEditorExperiments +); function ContextMenu( { name, parentMenu = '' } ) { - const hasTypographyPanel = useHasTypographyPanel( name ); + const [ settings ] = useGlobalSetting( '', name ); + const hasTypographyPanel = useHasTypographyPanel( name, null, settings ); const hasColorPanel = useHasColorPanel( name ); const hasBorderPanel = useHasBorderPanel( name ); const hasEffectsPanel = useHasShadowControl( name ); diff --git a/packages/edit-site/src/components/global-styles/screen-block-list.js b/packages/edit-site/src/components/global-styles/screen-block-list.js index 144bfba265f37..7b2e931bfcb00 100644 --- a/packages/edit-site/src/components/global-styles/screen-block-list.js +++ b/packages/edit-site/src/components/global-styles/screen-block-list.js @@ -10,7 +10,10 @@ import { } from '@wordpress/components'; import { useSelect } from '@wordpress/data'; import { useState, useMemo, useEffect, useRef } from '@wordpress/element'; -import { BlockIcon } from '@wordpress/block-editor'; +import { + BlockIcon, + experiments as blockEditorExperiments, +} from '@wordpress/block-editor'; import { useDebounce } from '@wordpress/compose'; import { speak } from '@wordpress/a11y'; @@ -20,10 +23,14 @@ import { speak } from '@wordpress/a11y'; import { useHasBorderPanel } from './border-panel'; import { useHasColorPanel } from './color-utils'; import { useHasDimensionsPanel } from './dimensions-panel'; -import { useHasTypographyPanel } from './typography-panel'; import { useHasVariationsPanel } from './variations-panel'; import ScreenHeader from './header'; import { NavigationButtonAsItem } from './navigation-button'; +import { unlock } from '../../experiments'; + +const { useHasTypographyPanel, useGlobalSetting } = unlock( + blockEditorExperiments +); function useSortedBlockTypes() { const blockItems = useSelect( @@ -49,7 +56,12 @@ function useSortedBlockTypes() { } function BlockMenuItem( { block } ) { - const hasTypographyPanel = useHasTypographyPanel( block.name ); + const [ settings ] = useGlobalSetting( '', block.name ); + const hasTypographyPanel = useHasTypographyPanel( + block.name, + null, + settings + ); const hasColorPanel = useHasColorPanel( block.name ); const hasBorderPanel = useHasBorderPanel( block.name ); const hasDimensionsPanel = useHasDimensionsPanel( block.name ); diff --git a/packages/edit-site/src/components/global-styles/typography-panel.js b/packages/edit-site/src/components/global-styles/typography-panel.js index 114ed4fb3ba67..214a08cf6e9f3 100644 --- a/packages/edit-site/src/components/global-styles/typography-panel.js +++ b/packages/edit-site/src/components/global-styles/typography-panel.js @@ -1,178 +1,18 @@ /** * WordPress dependencies */ -import { - LineHeightControl, - __experimentalFontFamilyControl as FontFamilyControl, - __experimentalFontAppearanceControl as FontAppearanceControl, - __experimentalLetterSpacingControl as LetterSpacingControl, - __experimentalTextTransformControl as TextTransformControl, - __experimentalTextDecorationControl as TextDecorationControl, - experiments as blockEditorExperiments, -} from '@wordpress/block-editor'; -import { - FontSizePicker, - __experimentalToolsPanel as ToolsPanel, - __experimentalToolsPanelItem as ToolsPanelItem, -} from '@wordpress/components'; -import { __ } from '@wordpress/i18n'; +import { experiments as blockEditorExperiments } from '@wordpress/block-editor'; /** * Internal dependencies */ -import { useSupportedStyles } from './hooks'; import { unlock } from '../../experiments'; -const { useGlobalSetting, useGlobalStyle } = unlock( blockEditorExperiments ); - -export function useHasTypographyPanel( name ) { - const hasFontFamily = useHasFontFamilyControl( name ); - const hasLineHeight = useHasLineHeightControl( name ); - const hasFontAppearance = useHasAppearanceControl( name ); - const hasLetterSpacing = useHasLetterSpacingControl( name ); - const supports = useSupportedStyles( name ); - return ( - hasFontFamily || - hasLineHeight || - hasFontAppearance || - hasLetterSpacing || - supports.includes( 'fontSize' ) - ); -} - -function useHasFontFamilyControl( name ) { - const supports = useSupportedStyles( name ); - const [ fontFamiliesPerOrigin ] = useGlobalSetting( - 'typography.fontFamilies', - name - ); - const fontFamilies = - fontFamiliesPerOrigin?.custom || - fontFamiliesPerOrigin?.theme || - fontFamiliesPerOrigin?.default; - return supports.includes( 'fontFamily' ) && !! fontFamilies?.length; -} - -function useHasLineHeightControl( name ) { - const supports = useSupportedStyles( name ); - return ( - useGlobalSetting( 'typography.lineHeight', name )[ 0 ] && - supports.includes( 'lineHeight' ) - ); -} - -function useHasAppearanceControl( name ) { - const supports = useSupportedStyles( name ); - const hasFontStyles = - useGlobalSetting( 'typography.fontStyle', name )[ 0 ] && - supports.includes( 'fontStyle' ); - const hasFontWeights = - useGlobalSetting( 'typography.fontWeight', name )[ 0 ] && - supports.includes( 'fontWeight' ); - return hasFontStyles || hasFontWeights; -} - -function useAppearanceControlLabel( name ) { - const supports = useSupportedStyles( name ); - const hasFontStyles = - useGlobalSetting( 'typography.fontStyle', name )[ 0 ] && - supports.includes( 'fontStyle' ); - const hasFontWeights = - useGlobalSetting( 'typography.fontWeight', name )[ 0 ] && - supports.includes( 'fontWeight' ); - if ( ! hasFontStyles ) { - return __( 'Font weight' ); - } - if ( ! hasFontWeights ) { - return __( 'Font style' ); - } - return __( 'Appearance' ); -} - -function useHasLetterSpacingControl( name, element ) { - const supports = useSupportedStyles( name, element ); - return ( - useGlobalSetting( 'typography.letterSpacing', name )[ 0 ] && - supports.includes( 'letterSpacing' ) - ); -} - -function useHasTextTransformControl( name, element ) { - const supports = useSupportedStyles( name, element ); - return ( - useGlobalSetting( 'typography.textTransform', name )[ 0 ] && - supports.includes( 'textTransform' ) - ); -} - -function useHasTextDecorationControl( name, element ) { - // This is an exception for link elements. - // We shouldn't allow other blocks or elements to set textDecoration - // because this will be inherited by their children. - return ! name && element === 'link'; -} - -function useStyleWithReset( path, blockName ) { - const [ style, setStyle ] = useGlobalStyle( path, blockName ); - const [ userStyle ] = useGlobalStyle( path, blockName, 'user' ); - const hasStyle = () => !! userStyle; - const resetStyle = () => setStyle( undefined ); - return [ style, setStyle, hasStyle, resetStyle ]; -} - -function useFontSizeWithReset( path, blockName ) { - const [ fontSize, setStyleCallback ] = useGlobalStyle( path, blockName ); - const [ userStyle ] = useGlobalStyle( path, blockName, 'user' ); - const hasFontSize = () => !! userStyle; - const resetFontSize = () => setStyleCallback( undefined ); - const setFontSize = ( newValue, metadata ) => { - if ( !! metadata?.slug ) { - newValue = `var:preset|font-size|${ metadata?.slug }`; - } - setStyleCallback( newValue ); - }; - - return { - fontSize, - setFontSize, - hasFontSize, - resetFontSize, - }; -} - -function useFontAppearance( prefix, name ) { - const [ fontStyle, setFontStyle ] = useGlobalStyle( - prefix + 'typography.fontStyle', - name - ); - const [ userFontStyle ] = useGlobalStyle( - prefix + 'typography.fontStyle', - name, - 'user' - ); - const [ fontWeight, setFontWeight ] = useGlobalStyle( - prefix + 'typography.fontWeight', - name - ); - const [ userFontWeight ] = useGlobalStyle( - prefix + 'typography.fontWeight', - name, - 'user' - ); - const hasFontAppearance = () => !! userFontStyle || !! userFontWeight; - const resetFontAppearance = () => { - setFontStyle( undefined ); - setFontWeight( undefined ); - }; - return { - fontStyle, - setFontStyle, - fontWeight, - setFontWeight, - hasFontAppearance, - resetFontAppearance, - }; -} +const { + useGlobalStyle, + useGlobalSetting, + TypographyPanel: StylesTypographyPanel, +} = unlock( blockEditorExperiments ); export default function TypographyPanel( { name, @@ -180,234 +20,31 @@ export default function TypographyPanel( { headingLevel, variation = '', } ) { - const supports = useSupportedStyles( name ); - let prefix = ''; + let prefixParts = []; if ( element === 'heading' ) { - prefix = `elements.${ headingLevel }.`; + prefixParts = prefixParts.concat( [ 'elements', headingLevel ] ); } else if ( element && element !== 'text' ) { - prefix = `elements.${ element }.`; + prefixParts = prefixParts.concat( [ 'elements', element ] ); } if ( variation ) { - prefix = prefix - ? `variations.${ variation }.${ prefix }` - : `variations.${ variation }`; + prefixParts = [ 'variations', variation ].concat( prefixParts ); } - const [ fontSizesPerOrigin ] = useGlobalSetting( - 'typography.fontSizes', - name - ); - const fontSizes = - fontSizesPerOrigin?.custom || - fontSizesPerOrigin?.theme || - fontSizesPerOrigin?.default; - - const disableCustomFontSizes = ! useGlobalSetting( - 'typography.customFontSize', - name - )[ 0 ]; - const [ fontFamiliesPerOrigin ] = useGlobalSetting( - 'typography.fontFamilies', - name - ); - const fontFamilies = - fontFamiliesPerOrigin?.custom || - fontFamiliesPerOrigin?.theme || - fontFamiliesPerOrigin?.default; - const hasFontStyles = - useGlobalSetting( 'typography.fontStyle', name )[ 0 ] && - supports.includes( 'fontStyle' ); - const hasFontWeights = - useGlobalSetting( 'typography.fontWeight', name )[ 0 ] && - supports.includes( 'fontWeight' ); - const hasFontFamilyEnabled = useHasFontFamilyControl( name ); - const hasLineHeightEnabled = useHasLineHeightControl( name ); - const hasAppearanceControl = useHasAppearanceControl( name ); - const appearanceControlLabel = useAppearanceControlLabel( name ); - const hasLetterSpacingControl = useHasLetterSpacingControl( name, element ); - const hasTextTransformControl = useHasTextTransformControl( name, element ); - const hasTextDecorationControl = useHasTextDecorationControl( - name, - element - ); - - /* Disable font size controls when the option to style all headings is selected. */ - let hasFontSizeEnabled = supports.includes( 'fontSize' ); - if ( element === 'heading' && headingLevel === 'heading' ) { - hasFontSizeEnabled = false; - } - - const [ fontFamily, setFontFamily, hasFontFamily, resetFontFamily ] = - useStyleWithReset( prefix + 'typography.fontFamily', name ); - const { fontSize, setFontSize, hasFontSize, resetFontSize } = - useFontSizeWithReset( prefix + 'typography.fontSize', name ); - const { - fontStyle, - setFontStyle, - fontWeight, - setFontWeight, - hasFontAppearance, - resetFontAppearance, - } = useFontAppearance( prefix, name ); - const [ lineHeight, setLineHeight, hasLineHeight, resetLineHeight ] = - useStyleWithReset( prefix + 'typography.lineHeight', name ); - const [ - letterSpacing, - setLetterSpacing, - hasLetterSpacing, - resetLetterSpacing, - ] = useStyleWithReset( prefix + 'typography.letterSpacing', name ); - const [ - textTransform, - setTextTransform, - hasTextTransform, - resetTextTransform, - ] = useStyleWithReset( prefix + 'typography.textTransform', name ); - const [ - textDecoration, - setTextDecoration, - hasTextDecoration, - resetTextDecoration, - ] = useStyleWithReset( prefix + 'typography.textDecoration', name ); + const prefix = prefixParts.join( '.' ); - const resetAll = () => { - resetFontFamily(); - resetFontSize(); - resetFontAppearance(); - resetLineHeight(); - resetLetterSpacing(); - resetTextTransform(); - }; + const [ style ] = useGlobalStyle( prefix, name, 'user', false ); + const [ inheritedStyle, setStyle ] = useGlobalStyle( prefix, name, 'all', { + shouldDecodeEncode: false, + } ); + const [ settings ] = useGlobalSetting( '', name ); return ( - - { hasFontFamilyEnabled && ( - - - - ) } - { hasFontSizeEnabled && ( - - - - ) } - { hasAppearanceControl && ( - - { - setFontStyle( newFontStyle ); - setFontWeight( newFontWeight ); - } } - hasFontStyles={ hasFontStyles } - hasFontWeights={ hasFontWeights } - size="__unstable-large" - __nextHasNoMarginBottom - /> - - ) } - { hasLineHeightEnabled && ( - - - - ) } - { hasLetterSpacingControl && ( - - - - ) } - { hasTextTransformControl && ( - - - - ) } - { hasTextDecorationControl && ( - - - - ) } - + ); }