UNPKG

@gechiui/block-editor

Version:
288 lines (233 loc) 7.49 kB
/** * Converts string to object { value, unit }. * * @param {string} cssUnit * @return {Object} parsedUnit */ function parseUnit(cssUnit) { const match = cssUnit === null || cssUnit === void 0 ? void 0 : cssUnit.trim().match(/^(0?[-.]?\d+)(r?e[m|x]|v[h|w|min|max]+|p[x|t|c]|[c|m]m|%|in|ch|Q|lh)$/); if (!isNaN(cssUnit) && !isNaN(parseFloat(cssUnit))) { return { value: parseFloat(cssUnit), unit: 'px' }; } return match ? { value: parseFloat(match[1]) || match[1], unit: match[2] } : { value: cssUnit, unit: undefined }; } /** * Evaluate a math expression. * * @param {string} expression * @return {number} evaluated expression. */ function calculate(expression) { return Function(`'use strict'; return (${expression})`)(); } /** * Calculates the css function value for the supported css functions such as max, min, clamp and calc. * * @param {string} functionUnitValue string should be in a particular format (for example min(12px,12px) ) no nested loops. * @param {Object} options * @return {string} unit containing the unit in PX. */ function getFunctionUnitValue(functionUnitValue, options) { const functionUnit = functionUnitValue.split(/[(),]/g).filter(Boolean); const units = functionUnit.slice(1).map(unit => parseUnit(getPxFromCssUnit(unit, options)).value).filter(Boolean); switch (functionUnit[0]) { case 'min': return Math.min(...units) + 'px'; case 'max': return Math.max(...units) + 'px'; case 'clamp': if (units.length !== 3) { return null; } if (units[1] < units[0]) { return units[0] + 'px'; } if (units[1] > units[2]) { return units[2] + 'px'; } return units[1] + 'px'; case 'calc': return units[0] + 'px'; } } /** * Take a css function such as min, max, calc, clamp and returns parsedUnit * * How this works for the nested function is that it first replaces the inner function call. * Then it tackles the outer onces. * So for example: min( max(25px, 35px), 40px ) * in the first pass we would replace max(25px, 35px) with 35px. * then we would try to evaluate min( 35px, 40px ) * and then finally return 35px. * * @param {string} cssUnit * @return {Object} parsedUnit object. */ function parseUnitFunction(cssUnit) { while (true) { const currentCssUnit = cssUnit; const regExp = /(max|min|calc|clamp)\(([^()]*)\)/g; const matches = regExp.exec(cssUnit) || []; if (matches[0]) { const functionUnitValue = getFunctionUnitValue(matches[0]); cssUnit = cssUnit.replace(matches[0], functionUnitValue); } // if the unit hasn't been modified or we have a single value break free. if (cssUnit === currentCssUnit || parseFloat(cssUnit)) { break; } } return parseUnit(cssUnit); } /** * Return true if we think this is a math expression. * * @param {string} cssUnit the cssUnit value being evaluted. * @return {boolean} Whether the cssUnit is a math expression. */ function isMathExpression(cssUnit) { for (let i = 0; i < cssUnit.length; i++) { if (['+', '-', '/', '*'].includes(cssUnit[i])) { return true; } } return false; } /** * Evaluates the math expression and return a px value. * * @param {string} cssUnit the cssUnit value being evaluted. * @return {string} return a converfted value to px. */ function evalMathExpression(cssUnit) { let errorFound = false; // Convert every part of the expression to px values. const cssUnitsBits = cssUnit.split(/[+-/*/]/g).filter(Boolean); for (const unit of cssUnitsBits) { // Standardize the unit to px and extract the value. const parsedUnit = parseUnit(getPxFromCssUnit(unit)); if (!parseFloat(parsedUnit.value)) { errorFound = true; // end early since we are dealing with a null value. break; } cssUnit = cssUnit.replace(unit, parsedUnit.value); } return errorFound ? null : calculate(cssUnit).toFixed(0) + 'px'; } /** * Convert a parsedUnit object to px value. * * @param {Object} parsedUnit * @param {Object} options * @return {string} or {null} returns the converted with in a px value format. */ function convertParsedUnitToPx(parsedUnit, options) { const PIXELS_PER_INCH = 96; const ONE_PERCENT = 0.01; const defaultProperties = { fontSize: 16, lineHeight: 16, width: 375, height: 812, type: 'font' }; const setOptions = Object.assign({}, defaultProperties, options); const relativeUnits = { em: setOptions.fontSize, rem: setOptions.fontSize, vh: setOptions.height * ONE_PERCENT, vw: setOptions.width * ONE_PERCENT, vmin: (setOptions.width < setOptions.height ? setOptions.width : setOptions.height) * ONE_PERCENT, vmax: (setOptions.width > setOptions.height ? setOptions.width : setOptions.height) * ONE_PERCENT, '%': (setOptions.type === 'font' ? setOptions.fontSize : setOptions.width) * ONE_PERCENT, ch: 8, // The advance measure (width) of the glyph "0" of the element's font. Approximate ex: 7.15625, // x-height of the element's font. Approximate lh: setOptions.lineHeight }; const absoluteUnits = { in: PIXELS_PER_INCH, cm: PIXELS_PER_INCH / 2.54, mm: PIXELS_PER_INCH / 25.4, pt: PIXELS_PER_INCH / 72, pc: PIXELS_PER_INCH / 6, px: 1, Q: PIXELS_PER_INCH / 2.54 / 40 }; if (relativeUnits[parsedUnit.unit]) { return (relativeUnits[parsedUnit.unit] * parsedUnit.value).toFixed(0) + 'px'; } if (absoluteUnits[parsedUnit.unit]) { return (absoluteUnits[parsedUnit.unit] * parsedUnit.value).toFixed(0) + 'px'; } return null; } /** * Returns the px value of a cssUnit. * * @param {string} cssUnit * @param {Object} options * @return {string} returns the cssUnit value in a simple px format. */ export function getPxFromCssUnit(cssUnit) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (Number.isFinite(cssUnit)) { return cssUnit.toFixed(0) + 'px'; } if (cssUnit === undefined) { return null; } let parsedUnit = parseUnit(cssUnit); if (!parsedUnit.unit) { parsedUnit = parseUnitFunction(cssUnit, options); } if (isMathExpression(cssUnit) && !parsedUnit.unit) { return evalMathExpression(cssUnit); } return convertParsedUnitToPx(parsedUnit, options); } // Use simple cache. const cache = {}; /** * Returns the px value of a cssUnit. The memoized version of getPxFromCssUnit; * * @param {string} cssUnit * @param {Object} options * @return {string} returns the cssUnit value in a simple px format. */ function memoizedGetPxFromCssUnit(cssUnit) { let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; const hash = cssUnit + hashOptions(options); if (!cache[hash]) { cache[hash] = getPxFromCssUnit(cssUnit, options); } return cache[hash]; } function hashOptions(options) { let hash = ''; if (options.hasOwnProperty('fontSize')) { hash = ':' + options.width; } if (options.hasOwnProperty('lineHeight')) { hash = ':' + options.lineHeight; } if (options.hasOwnProperty('width')) { hash = ':' + options.width; } if (options.hasOwnProperty('height')) { hash = ':' + options.height; } if (options.hasOwnProperty('type')) { hash = ':' + options.type; } return hash; } export default memoizedGetPxFromCssUnit; //# sourceMappingURL=parse-css-unit-to-px.js.map