@wordpress/block-editor
Version:
8 lines (7 loc) • 14 kB
Source Map (JSON)
{
"version": 3,
"sources": ["../../../src/components/font-sizes/fluid-utils.js"],
"sourcesContent": ["/**\n * The fluid utilities must match the backend equivalent.\n * See: gutenberg_get_typography_font_size_value() in lib/block-supports/typography.php\n * ---------------------------------------------------------------\n */\n\n// Defaults.\nconst DEFAULT_MAXIMUM_VIEWPORT_WIDTH = '1600px';\nconst DEFAULT_MINIMUM_VIEWPORT_WIDTH = '320px';\nconst DEFAULT_SCALE_FACTOR = 1;\nconst DEFAULT_MINIMUM_FONT_SIZE_FACTOR_MIN = 0.25;\nconst DEFAULT_MINIMUM_FONT_SIZE_FACTOR_MAX = 0.75;\nconst DEFAULT_MINIMUM_FONT_SIZE_LIMIT = '14px';\n\n/**\n * Computes a fluid font-size value that uses clamp(). A minimum and maximum\n * font size OR a single font size can be specified.\n *\n * If a single font size is specified, it is scaled up and down using a logarithmic scale.\n *\n * @example\n * ```js\n * // Calculate fluid font-size value from a minimum and maximum value.\n * const fontSize = getComputedFluidTypographyValue( {\n * minimumFontSize: '20px',\n * maximumFontSize: '45px'\n * } );\n * // Calculate fluid font-size value from a single font size.\n * const fontSize = getComputedFluidTypographyValue( {\n * fontSize: '30px',\n * } );\n * ```\n *\n * @param {Object} args\n * @param {?string} args.minimumViewportWidth Minimum viewport size from which type will have fluidity. Optional if fontSize is specified.\n * @param {?string} args.maximumViewportWidth Maximum size up to which type will have fluidity. Optional if fontSize is specified.\n * @param {string|number} [args.fontSize] Size to derive maximumFontSize and minimumFontSize from, if necessary. Optional if minimumFontSize and maximumFontSize are specified.\n * @param {?string} args.maximumFontSize Maximum font size for any clamp() calculation. Optional.\n * @param {?string} args.minimumFontSize Minimum font size for any clamp() calculation. Optional.\n * @param {?number} args.scaleFactor A scale factor to determine how fast a font scales within boundaries. Optional.\n * @param {?string} args.minimumFontSizeLimit The smallest a calculated font size may be. Optional.\n *\n * @return {string|null} A font-size value using clamp().\n */\nexport function getComputedFluidTypographyValue( {\n\tminimumFontSize,\n\tmaximumFontSize,\n\tfontSize,\n\tminimumViewportWidth = DEFAULT_MINIMUM_VIEWPORT_WIDTH,\n\tmaximumViewportWidth = DEFAULT_MAXIMUM_VIEWPORT_WIDTH,\n\tscaleFactor = DEFAULT_SCALE_FACTOR,\n\tminimumFontSizeLimit,\n} ) {\n\t// Validate incoming settings and set defaults.\n\tminimumFontSizeLimit = !! getTypographyValueAndUnit( minimumFontSizeLimit )\n\t\t? minimumFontSizeLimit\n\t\t: DEFAULT_MINIMUM_FONT_SIZE_LIMIT;\n\n\t/*\n\t * Calculates missing minimumFontSize and maximumFontSize from\n\t * defaultFontSize if provided.\n\t */\n\tif ( fontSize ) {\n\t\t// Parses default font size.\n\t\tconst fontSizeParsed = getTypographyValueAndUnit( fontSize );\n\n\t\t// Protect against invalid units.\n\t\tif ( ! fontSizeParsed?.unit ) {\n\t\t\treturn null;\n\t\t}\n\n\t\t// Parses the minimum font size limit, so we can perform checks using it.\n\t\tconst minimumFontSizeLimitParsed = getTypographyValueAndUnit(\n\t\t\tminimumFontSizeLimit,\n\t\t\t{\n\t\t\t\tcoerceTo: fontSizeParsed.unit,\n\t\t\t}\n\t\t);\n\n\t\t// Don't enforce minimum font size if a font size has explicitly set a min and max value.\n\t\tif (\n\t\t\t!! minimumFontSizeLimitParsed?.value &&\n\t\t\t! minimumFontSize &&\n\t\t\t! maximumFontSize\n\t\t) {\n\t\t\t/*\n\t\t\t * If a minimum size was not passed to this function\n\t\t\t * and the user-defined font size is lower than $minimum_font_size_limit,\n\t\t\t * do not calculate a fluid value.\n\t\t\t */\n\t\t\tif ( fontSizeParsed?.value <= minimumFontSizeLimitParsed?.value ) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\n\t\t// If no fluid max font size is available use the incoming value.\n\t\tif ( ! maximumFontSize ) {\n\t\t\tmaximumFontSize = `${ fontSizeParsed.value }${ fontSizeParsed.unit }`;\n\t\t}\n\n\t\t/*\n\t\t * If no minimumFontSize is provided, create one using\n\t\t * the given font size multiplied by the min font size scale factor.\n\t\t */\n\t\tif ( ! minimumFontSize ) {\n\t\t\tconst fontSizeValueInPx =\n\t\t\t\tfontSizeParsed.unit === 'px'\n\t\t\t\t\t? fontSizeParsed.value\n\t\t\t\t\t: fontSizeParsed.value * 16;\n\n\t\t\t/*\n\t\t\t * The scale factor is a multiplier that affects how quickly the curve will move towards the minimum,\n\t\t\t * that is, how quickly the size factor reaches 0 given increasing font size values.\n\t\t\t * For a - b * log2(), lower values of b will make the curve move towards the minimum faster.\n\t\t\t * The scale factor is constrained between min and max values.\n\t\t\t */\n\t\t\tconst minimumFontSizeFactor = Math.min(\n\t\t\t\tMath.max(\n\t\t\t\t\t1 - 0.075 * Math.log2( fontSizeValueInPx ),\n\t\t\t\t\tDEFAULT_MINIMUM_FONT_SIZE_FACTOR_MIN\n\t\t\t\t),\n\t\t\t\tDEFAULT_MINIMUM_FONT_SIZE_FACTOR_MAX\n\t\t\t);\n\n\t\t\t// Calculates the minimum font size.\n\t\t\tconst calculatedMinimumFontSize = roundToPrecision(\n\t\t\t\tfontSizeParsed.value * minimumFontSizeFactor,\n\t\t\t\t3\n\t\t\t);\n\n\t\t\t// Only use calculated min font size if it's > $minimum_font_size_limit value.\n\t\t\tif (\n\t\t\t\t!! minimumFontSizeLimitParsed?.value &&\n\t\t\t\tcalculatedMinimumFontSize < minimumFontSizeLimitParsed?.value\n\t\t\t) {\n\t\t\t\tminimumFontSize = `${ minimumFontSizeLimitParsed.value }${ minimumFontSizeLimitParsed.unit }`;\n\t\t\t} else {\n\t\t\t\tminimumFontSize = `${ calculatedMinimumFontSize }${ fontSizeParsed.unit }`;\n\t\t\t}\n\t\t}\n\t}\n\n\t// Grab the minimum font size and normalize it in order to use the value for calculations.\n\tconst minimumFontSizeParsed = getTypographyValueAndUnit( minimumFontSize );\n\n\t// We get a 'preferred' unit to keep units consistent when calculating,\n\t// otherwise the result will not be accurate.\n\tconst fontSizeUnit = minimumFontSizeParsed?.unit || 'rem';\n\n\t// Grabs the maximum font size and normalize it in order to use the value for calculations.\n\tconst maximumFontSizeParsed = getTypographyValueAndUnit( maximumFontSize, {\n\t\tcoerceTo: fontSizeUnit,\n\t} );\n\n\t// Checks for mandatory min and max sizes, and protects against unsupported units.\n\tif ( ! minimumFontSizeParsed || ! maximumFontSizeParsed ) {\n\t\treturn null;\n\t}\n\n\t// Uses rem for accessible fluid target font scaling.\n\tconst minimumFontSizeRem = getTypographyValueAndUnit( minimumFontSize, {\n\t\tcoerceTo: 'rem',\n\t} );\n\n\t// Viewport widths defined for fluid typography. Normalize units\n\tconst maximumViewportWidthParsed = getTypographyValueAndUnit(\n\t\tmaximumViewportWidth,\n\t\t{ coerceTo: fontSizeUnit }\n\t);\n\tconst minimumViewportWidthParsed = getTypographyValueAndUnit(\n\t\tminimumViewportWidth,\n\t\t{ coerceTo: fontSizeUnit }\n\t);\n\n\t// Protect against unsupported units.\n\tif (\n\t\t! maximumViewportWidthParsed ||\n\t\t! minimumViewportWidthParsed ||\n\t\t! minimumFontSizeRem\n\t) {\n\t\treturn null;\n\t}\n\n\t// Calculates the linear factor denominator. If it's 0, we cannot calculate a fluid value.\n\tconst linearDenominator =\n\t\tmaximumViewportWidthParsed.value - minimumViewportWidthParsed.value;\n\tif ( ! linearDenominator ) {\n\t\treturn null;\n\t}\n\n\t// Build CSS rule.\n\t// Borrowed from https://websemantics.uk/tools/responsive-font-calculator/.\n\tconst minViewportWidthOffsetValue = roundToPrecision(\n\t\tminimumViewportWidthParsed.value / 100,\n\t\t3\n\t);\n\n\tconst viewportWidthOffset =\n\t\troundToPrecision( minViewportWidthOffsetValue, 3 ) + fontSizeUnit;\n\tconst linearFactor =\n\t\t100 *\n\t\t( ( maximumFontSizeParsed.value - minimumFontSizeParsed.value ) /\n\t\t\tlinearDenominator );\n\tconst linearFactorScaled = roundToPrecision(\n\t\t( linearFactor || 1 ) * scaleFactor,\n\t\t3\n\t);\n\tconst fluidTargetFontSize = `${ minimumFontSizeRem.value }${ minimumFontSizeRem.unit } + ((1vw - ${ viewportWidthOffset }) * ${ linearFactorScaled })`;\n\n\treturn `clamp(${ minimumFontSize }, ${ fluidTargetFontSize }, ${ maximumFontSize })`;\n}\n\n/**\n * Internal method that checks a string for a unit and value and returns an array consisting of `'value'` and `'unit'`, e.g., [ '42', 'rem' ].\n * A raw font size of `value + unit` is expected. If the value is an integer, it will convert to `value + 'px'`.\n *\n * @param {string|number} rawValue Raw size value from theme.json.\n * @param {Object|undefined} options Calculation options.\n *\n * @return {{ unit: string, value: number }|null} An object consisting of `'value'` and `'unit'` properties.\n */\nexport function getTypographyValueAndUnit( rawValue, options = {} ) {\n\tif ( typeof rawValue !== 'string' && typeof rawValue !== 'number' ) {\n\t\treturn null;\n\t}\n\n\t// Converts numeric values to pixel values by default.\n\tif ( isFinite( rawValue ) ) {\n\t\trawValue = `${ rawValue }px`;\n\t}\n\n\tconst { coerceTo, rootSizeValue, acceptableUnits } = {\n\t\tcoerceTo: '',\n\t\t// Default browser font size. Later we could inject some JS to compute this `getComputedStyle( document.querySelector( \"html\" ) ).fontSize`.\n\t\trootSizeValue: 16,\n\t\tacceptableUnits: [ 'rem', 'px', 'em' ],\n\t\t...options,\n\t};\n\n\tconst acceptableUnitsGroup = acceptableUnits?.join( '|' );\n\tconst regexUnits = new RegExp(\n\t\t`^(\\\\d*\\\\.?\\\\d+)(${ acceptableUnitsGroup }){1,1}$`\n\t);\n\n\tconst matches = rawValue.match( regexUnits );\n\n\t// We need a number value and a unit.\n\tif ( ! matches || matches.length < 3 ) {\n\t\treturn null;\n\t}\n\n\tlet [ , value, unit ] = matches;\n\n\tlet returnValue = parseFloat( value );\n\n\tif ( 'px' === coerceTo && ( 'em' === unit || 'rem' === unit ) ) {\n\t\treturnValue = returnValue * rootSizeValue;\n\t\tunit = coerceTo;\n\t}\n\n\tif ( 'px' === unit && ( 'em' === coerceTo || 'rem' === coerceTo ) ) {\n\t\treturnValue = returnValue / rootSizeValue;\n\t\tunit = coerceTo;\n\t}\n\n\t/*\n\t * No calculation is required if swapping between em and rem yet,\n\t * since we assume a root size value. Later we might like to differentiate between\n\t * :root font size (rem) and parent element font size (em) relativity.\n\t */\n\tif (\n\t\t( 'em' === coerceTo || 'rem' === coerceTo ) &&\n\t\t( 'em' === unit || 'rem' === unit )\n\t) {\n\t\tunit = coerceTo;\n\t}\n\n\treturn {\n\t\tvalue: roundToPrecision( returnValue, 3 ),\n\t\tunit,\n\t};\n}\n\n/**\n * Returns a value rounded to defined precision.\n * Returns `undefined` if the value is not a valid finite number.\n *\n * @param {number} value Raw value.\n * @param {number} digits The number of digits to appear after the decimal point\n *\n * @return {number|undefined} Value rounded to standard precision.\n */\nexport function roundToPrecision( value, digits = 3 ) {\n\tconst base = Math.pow( 10, digits );\n\treturn Number.isFinite( value )\n\t\t? parseFloat( Math.round( value * base ) / base )\n\t\t: undefined;\n}\n"],
"mappings": "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",
"names": []
}