UNPKG

@wordpress/block-editor

Version:
406 lines (342 loc) 10.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VIEWS = exports.LABELS = exports.ICONS = exports.DEFAULT_VALUES = exports.ALL_SIDES = void 0; exports.getAllRawValue = getAllRawValue; exports.getCustomValueFromPreset = getCustomValueFromPreset; exports.getInitialView = getInitialView; exports.getPresetValueFromCustomValue = getPresetValueFromCustomValue; exports.getSliderValueFromPreset = getSliderValueFromPreset; exports.getSpacingPresetCssVar = getSpacingPresetCssVar; exports.getSpacingPresetSlug = getSpacingPresetSlug; exports.getSupportedMenuItems = getSupportedMenuItems; exports.hasAxisSupport = hasAxisSupport; exports.hasBalancedSidesSupport = hasBalancedSidesSupport; exports.isValueSpacingPreset = isValueSpacingPreset; exports.isValuesDefined = isValuesDefined; exports.isValuesMixed = isValuesMixed; var _i18n = require("@wordpress/i18n"); var _icons = require("@wordpress/icons"); /** * WordPress dependencies */ const ALL_SIDES = ['top', 'right', 'bottom', 'left']; exports.ALL_SIDES = ALL_SIDES; const DEFAULT_VALUES = { top: undefined, right: undefined, bottom: undefined, left: undefined }; exports.DEFAULT_VALUES = DEFAULT_VALUES; const ICONS = { custom: _icons.sidesAll, axial: _icons.sidesAxial, horizontal: _icons.sidesHorizontal, vertical: _icons.sidesVertical, top: _icons.sidesTop, right: _icons.sidesRight, bottom: _icons.sidesBottom, left: _icons.sidesLeft }; exports.ICONS = ICONS; const LABELS = { default: (0, _i18n.__)('Spacing control'), top: (0, _i18n.__)('Top'), bottom: (0, _i18n.__)('Bottom'), left: (0, _i18n.__)('Left'), right: (0, _i18n.__)('Right'), mixed: (0, _i18n.__)('Mixed'), vertical: (0, _i18n.__)('Vertical'), horizontal: (0, _i18n.__)('Horizontal'), axial: (0, _i18n.__)('Horizontal & vertical'), custom: (0, _i18n.__)('Custom') }; exports.LABELS = LABELS; const VIEWS = { axial: 'axial', top: 'top', right: 'right', bottom: 'bottom', left: 'left', custom: 'custom' }; /** * Checks is given value is a spacing preset. * * @param {string} value Value to check * * @return {boolean} Return true if value is string in format var:preset|spacing|. */ exports.VIEWS = VIEWS; function isValueSpacingPreset(value) { if (!value?.includes) { return false; } return value === '0' || value.includes('var:preset|spacing|'); } /** * Converts a spacing preset into a custom value. * * @param {string} value Value to convert * @param {Array} spacingSizes Array of the current spacing preset objects * * @return {string} Mapping of the spacing preset to its equivalent custom value. */ function getCustomValueFromPreset(value, spacingSizes) { if (!isValueSpacingPreset(value)) { return value; } const slug = getSpacingPresetSlug(value); const spacingSize = spacingSizes.find(size => String(size.slug) === slug); return spacingSize?.size; } /** * Converts a custom value to preset value if one can be found. * * Returns value as-is if no match is found. * * @param {string} value Value to convert * @param {Array} spacingSizes Array of the current spacing preset objects * * @return {string} The preset value if it can be found. */ function getPresetValueFromCustomValue(value, spacingSizes) { // Return value as-is if it is already a preset; if (isValueSpacingPreset(value)) { return value; } const spacingMatch = spacingSizes.find(size => String(size.size) === String(value)); if (spacingMatch?.slug) { return `var:preset|spacing|${spacingMatch.slug}`; } return value; } /** * Converts a spacing preset into a custom value. * * @param {string} value Value to convert. * * @return {string | undefined} CSS var string for given spacing preset value. */ function getSpacingPresetCssVar(value) { if (!value) { return; } const slug = value.match(/var:preset\|spacing\|(.+)/); if (!slug) { return value; } return `var(--wp--preset--spacing--${slug[1]})`; } /** * Returns the slug section of the given spacing preset string. * * @param {string} value Value to extract slug from. * * @return {string|undefined} The int value of the slug from given spacing preset. */ function getSpacingPresetSlug(value) { if (!value) { return; } if (value === '0' || value === 'default') { return value; } const slug = value.match(/var:preset\|spacing\|(.+)/); return slug ? slug[1] : undefined; } /** * Converts spacing preset value into a Range component value . * * @param {string} presetValue Value to convert to Range value. * @param {Array} spacingSizes Array of current spacing preset value objects. * * @return {number} The int value for use in Range control. */ function getSliderValueFromPreset(presetValue, spacingSizes) { if (presetValue === undefined) { return 0; } const slug = parseFloat(presetValue, 10) === 0 ? '0' : getSpacingPresetSlug(presetValue); const sliderValue = spacingSizes.findIndex(spacingSize => { return String(spacingSize.slug) === slug; }); // Returning NaN rather than undefined as undefined makes range control thumb sit in center return sliderValue !== -1 ? sliderValue : NaN; } /** * Gets an items with the most occurrence within an array * https://stackoverflow.com/a/20762713 * * @param {Array<any>} arr Array of items to check. * @return {any} The item with the most occurrences. */ function mode(arr) { return arr.sort((a, b) => arr.filter(v => v === a).length - arr.filter(v => v === b).length).pop(); } /** * Gets the 'all' input value from values data. * * @param {Object} values Box spacing values * * @return {string} The most common value from all sides of box. */ function getAllRawValue(values = {}) { return mode(Object.values(values)); } /** * Checks to determine if values are mixed. * * @param {Object} values Box values. * @param {Array} sides Sides that values relate to. * * @return {boolean} Whether values are mixed. */ function isValuesMixed(values = {}, sides = ALL_SIDES) { return Object.values(values).length >= 1 && Object.values(values).length < sides.length || new Set(Object.values(values)).size > 1; } /** * Checks to determine if values are defined. * * @param {Object} values Box values. * * @return {boolean} Whether values are defined. */ function isValuesDefined(values) { if (values === undefined || values === null) { return false; } return Object.values(values).filter(value => !!value).length > 0; } /** * Determines whether a particular axis has support. If no axis is * specified, this function checks if either axis is supported. * * @param {Array} sides Supported sides. * @param {string} axis Which axis to check. * * @return {boolean} Whether there is support for the specified axis or both axes. */ function hasAxisSupport(sides, axis) { if (!sides || !sides.length) { return false; } const hasHorizontalSupport = sides.includes('horizontal') || sides.includes('left') && sides.includes('right'); const hasVerticalSupport = sides.includes('vertical') || sides.includes('top') && sides.includes('bottom'); if (axis === 'horizontal') { return hasHorizontalSupport; } if (axis === 'vertical') { return hasVerticalSupport; } return hasHorizontalSupport || hasVerticalSupport; } /** * Determines which menu options should be included in the SidePicker. * * @param {Array} sides Supported sides. * * @return {Object} Menu options with each option containing label & icon. */ function getSupportedMenuItems(sides) { if (!sides || !sides.length) { return {}; } const menuItems = {}; // Determine the primary "side" menu options. const hasHorizontalSupport = hasAxisSupport(sides, 'horizontal'); const hasVerticalSupport = hasAxisSupport(sides, 'vertical'); if (hasHorizontalSupport && hasVerticalSupport) { menuItems.axial = { label: LABELS.axial, icon: ICONS.axial }; } else if (hasHorizontalSupport) { menuItems.axial = { label: LABELS.horizontal, icon: ICONS.horizontal }; } else if (hasVerticalSupport) { menuItems.axial = { label: LABELS.vertical, icon: ICONS.vertical }; } // Track whether we have any individual sides so we can omit the custom // option if required. let numberOfIndividualSides = 0; ALL_SIDES.forEach(side => { if (sides.includes(side)) { numberOfIndividualSides += 1; menuItems[side] = { label: LABELS[side], icon: ICONS[side] }; } }); // Add custom item if there are enough sides to warrant a separated view. if (numberOfIndividualSides > 1) { menuItems.custom = { label: LABELS.custom, icon: ICONS.custom }; } return menuItems; } /** * Checks if the supported sides are balanced for each axis. * - Horizontal - both left and right sides are supported. * - Vertical - both top and bottom are supported. * * @param {Array} sides The supported sides which may be axes as well. * * @return {boolean} Whether or not the supported sides are balanced. */ function hasBalancedSidesSupport(sides = []) { const counts = { top: 0, right: 0, bottom: 0, left: 0 }; sides.forEach(side => counts[side] += 1); return (counts.top + counts.bottom) % 2 === 0 && (counts.left + counts.right) % 2 === 0; } /** * Determines which view the SpacingSizesControl should default to on its * first render; Axial, Custom, or Single side. * * @param {Object} values Current side values. * @param {Array} sides Supported sides. * * @return {string} View to display. */ function getInitialView(values = {}, sides) { const { top, right, bottom, left } = values; const sideValues = [top, right, bottom, left].filter(Boolean); // Axial ( Horizontal & vertical ). // - Has axial side support // - Has axial side values which match // - Has no values and the supported sides are balanced const hasMatchingAxialValues = top === bottom && left === right && (!!top || !!left); const hasNoValuesAndBalancedSides = !sideValues.length && hasBalancedSidesSupport(sides); if (hasAxisSupport(sides) && (hasMatchingAxialValues || hasNoValuesAndBalancedSides)) { return VIEWS.axial; } // Single side. // - Ensure the side returned is the first side that has a value. if (sideValues.length === 1) { let side; Object.entries(values).some(([key, value]) => { side = key; return value !== undefined; }); return side; } // Only single side supported and no value defined. if (sides?.length === 1 && !sideValues.length) { return sides[0]; } // Default to the Custom (separated sides) view. return VIEWS.custom; } //# sourceMappingURL=utils.js.map