UNPKG

@wordpress/block-editor

Version:
300 lines (267 loc) 9.81 kB
/** * The fluid utilities must match the backend equivalent. * See: gutenberg_get_typography_font_size_value() in lib/block-supports/typography.php * --------------------------------------------------------------- */ /** * Internal dependencies */ import { getComputedFluidTypographyValue, getTypographyValueAndUnit, } from '../font-sizes/fluid-utils'; import { getFontStylesAndWeights } from '../../utils/get-font-styles-and-weights'; /** * @typedef {Object} FluidPreset * @property {string|undefined} max A maximum font size value. * @property {?string|undefined} min A minimum font size value. */ /** * @typedef {Object} Preset * @property {?string|?number} size A default font size. * @property {string} name A font size name, displayed in the UI. * @property {string} slug A font size slug * @property {boolean|FluidPreset|undefined} fluid Specifies the minimum and maximum font size value of a fluid font size. */ /** * @typedef {Object} TypographySettings * @property {?string} minViewportWidth Minimum viewport size from which type will have fluidity. Optional if size is specified. * @property {?string} maxViewportWidth Maximum size up to which type will have fluidity. Optional if size is specified. * @property {?number} scaleFactor A scale factor to determine how fast a font scales within boundaries. Optional. * @property {?number} minFontSizeFactor How much to scale defaultFontSize by to derive minimumFontSize. Optional. * @property {?string} minFontSize The smallest a calculated font size may be. Optional. */ /** * Returns a font-size value based on a given font-size preset. * Takes into account fluid typography parameters and attempts to return a css formula depending on available, valid values. * * The Core PHP equivalent is wp_get_typography_font_size_value(). * * @param {Preset} preset * @param {Object} settings * @param {boolean|TypographySettings} settings.typography.fluid Whether fluid typography is enabled, and, optionally, fluid font size options. * @param {?Object} settings.typography.layout Layout options. * * @return {string|*} A font-size value or the value of preset.size. */ export function getTypographyFontSizeValue( preset, settings ) { const { size: defaultSize } = preset; /* * Catch falsy values and 0/'0'. Fluid calculations cannot be performed on `0`. * Also return early when a preset font size explicitly disables fluid typography with `false`. */ if ( ! defaultSize || '0' === defaultSize || false === preset?.fluid ) { return defaultSize; } /* * Return early when fluid typography is disabled in the settings, and there * are no local settings to enable it for the individual preset. * * If this condition isn't met, either the settings or individual preset settings * have enabled fluid typography. */ if ( ! isFluidTypographyEnabled( settings?.typography ) && ! isFluidTypographyEnabled( preset ) ) { return defaultSize; } let fluidTypographySettings = getFluidTypographyOptionsFromSettings( settings ); fluidTypographySettings = typeof fluidTypographySettings?.fluid === 'object' ? fluidTypographySettings?.fluid : {}; const fluidFontSizeValue = getComputedFluidTypographyValue( { minimumFontSize: preset?.fluid?.min, maximumFontSize: preset?.fluid?.max, fontSize: defaultSize, minimumFontSizeLimit: fluidTypographySettings?.minFontSize, maximumViewportWidth: fluidTypographySettings?.maxViewportWidth, minimumViewportWidth: fluidTypographySettings?.minViewportWidth, } ); if ( !! fluidFontSizeValue ) { return fluidFontSizeValue; } return defaultSize; } function isFluidTypographyEnabled( typographySettings ) { const fluidSettings = typographySettings?.fluid; return ( true === fluidSettings || ( fluidSettings && typeof fluidSettings === 'object' && Object.keys( fluidSettings ).length > 0 ) ); } /** * Returns fluid typography settings from theme.json setting object. * * @param {Object} settings Theme.json settings * @param {Object} settings.typography Theme.json typography settings * @param {Object} settings.layout Theme.json layout settings * @return {TypographySettings} Fluid typography settings */ export function getFluidTypographyOptionsFromSettings( settings ) { const typographySettings = settings?.typography; const layoutSettings = settings?.layout; const defaultMaxViewportWidth = getTypographyValueAndUnit( layoutSettings?.wideSize ) ? layoutSettings?.wideSize : null; return isFluidTypographyEnabled( typographySettings ) && defaultMaxViewportWidth ? { fluid: { maxViewportWidth: defaultMaxViewportWidth, ...typographySettings.fluid, }, } : { fluid: typographySettings?.fluid, }; } /** * Returns an object of merged font families and the font faces from the selected font family * based on the theme.json settings object and the currently selected font family. * * @param {Object} settings Theme.json settings. * @param {string} selectedFontFamily Decoded font family string. * @return {Object} Merged font families and font faces from the selected font family. */ export function getMergedFontFamiliesAndFontFamilyFaces( settings, selectedFontFamily ) { const fontFamiliesFromSettings = settings?.typography?.fontFamilies; const fontFamilies = [ 'default', 'theme', 'custom' ].flatMap( ( key ) => fontFamiliesFromSettings?.[ key ] ?? [] ); const fontFamilyFaces = fontFamilies.find( ( family ) => family.fontFamily === selectedFontFamily )?.fontFace ?? []; return { fontFamilies, fontFamilyFaces }; } /** * Returns the nearest font weight value from the available font weight list based on the new font weight. * The nearest font weight is the one with the smallest difference from the new font weight. * * @param {Array} availableFontWeights Array of available font weights. * @param {string} newFontWeightValue New font weight value. * @return {string} Nearest font weight. */ export function findNearestFontWeight( availableFontWeights, newFontWeightValue ) { newFontWeightValue = 'number' === typeof newFontWeightValue ? newFontWeightValue.toString() : newFontWeightValue; if ( ! newFontWeightValue || typeof newFontWeightValue !== 'string' ) { return ''; } if ( ! availableFontWeights || availableFontWeights.length === 0 ) { return newFontWeightValue; } const nearestFontWeight = availableFontWeights?.reduce( ( nearest, { value: fw } ) => { const currentDiff = Math.abs( parseInt( fw ) - parseInt( newFontWeightValue ) ); const nearestDiff = Math.abs( parseInt( nearest ) - parseInt( newFontWeightValue ) ); return currentDiff < nearestDiff ? fw : nearest; }, availableFontWeights[ 0 ]?.value ); return nearestFontWeight; } /** * Returns the nearest font style based on the new font style. * Defaults to an empty string if the new font style is not valid or available. * * @param {Array} availableFontStyles Array of available font weights. * @param {string} newFontStyleValue New font style value. * @return {string} Nearest font style or an empty string. */ export function findNearestFontStyle( availableFontStyles, newFontStyleValue ) { if ( typeof newFontStyleValue !== 'string' || ! newFontStyleValue ) { return ''; } const validStyles = [ 'normal', 'italic', 'oblique' ]; if ( ! validStyles.includes( newFontStyleValue ) ) { return ''; } if ( ! availableFontStyles || availableFontStyles.length === 0 || availableFontStyles.find( ( style ) => style.value === newFontStyleValue ) ) { return newFontStyleValue; } if ( newFontStyleValue === 'oblique' && ! availableFontStyles.find( ( style ) => style.value === 'oblique' ) ) { return 'italic'; } return ''; } /** * Returns the nearest font style and weight based on the available font family faces and the new font style and weight. * * @param {Array} fontFamilyFaces Array of available font family faces. * @param {string} fontStyle New font style. Defaults to previous value. * @param {string} fontWeight New font weight. Defaults to previous value. * @return {Object} Nearest font style and font weight. */ export function findNearestStyleAndWeight( fontFamilyFaces, fontStyle, fontWeight ) { let nearestFontStyle = fontStyle; let nearestFontWeight = fontWeight; const { fontStyles, fontWeights, combinedStyleAndWeightOptions } = getFontStylesAndWeights( fontFamilyFaces ); // Check if the new font style and weight are available in the font family faces. const hasFontStyle = fontStyles?.some( ( { value: fs } ) => fs === fontStyle ); const hasFontWeight = fontWeights?.some( ( { value: fw } ) => fw?.toString() === fontWeight?.toString() ); if ( ! hasFontStyle ) { /* * Default to italic if oblique is not available. * Or find the nearest font style based on the nearest font weight. */ nearestFontStyle = fontStyle ? findNearestFontStyle( fontStyles, fontStyle ) : combinedStyleAndWeightOptions?.find( ( option ) => option.style.fontWeight === findNearestFontWeight( fontWeights, fontWeight ) )?.style?.fontStyle; } if ( ! hasFontWeight ) { /* * Find the nearest font weight based on available weights. * Or find the nearest font weight based on the nearest font style. */ nearestFontWeight = fontWeight ? findNearestFontWeight( fontWeights, fontWeight ) : combinedStyleAndWeightOptions?.find( ( option ) => option.style.fontStyle === ( nearestFontStyle || fontStyle ) )?.style?.fontWeight; } return { nearestFontStyle, nearestFontWeight }; }