fix: prevent asset conflicts between React and Grid.js versions

Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

View File

@@ -0,0 +1,184 @@
import { createElement } from "react";
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import deprecated from '@wordpress/deprecated';
import { forwardRef, useMemo, useRef, useEffect } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import { ValueInput } from './styles/unit-control-styles';
import UnitSelectControl from './unit-select-control';
import { CSS_UNITS, getParsedQuantityAndUnit, getUnitsWithCurrentUnit, getValidParsedQuantityAndUnit } from './utils';
import { useControlledState } from '../utils/hooks';
import { escapeRegExp } from '../utils/strings';
import { useDeprecated36pxDefaultSizeProp } from '../utils/use-deprecated-props';
function UnforwardedUnitControl(unitControlProps, forwardedRef) {
const {
__unstableStateReducer,
autoComplete = 'off',
// @ts-expect-error Ensure that children is omitted from restProps
children,
className,
disabled = false,
disableUnits = false,
isPressEnterToChange = false,
isResetValueOnUnitChange = false,
isUnitSelectTabbable = true,
label,
onChange: onChangeProp,
onUnitChange,
size = 'default',
unit: unitProp,
units: unitsProp = CSS_UNITS,
value: valueProp,
onFocus: onFocusProp,
...props
} = useDeprecated36pxDefaultSizeProp(unitControlProps, 'wp.components.UnitControl', '6.4');
if ('unit' in unitControlProps) {
deprecated('UnitControl unit prop', {
since: '5.6',
hint: 'The unit should be provided within the `value` prop.',
version: '6.2'
});
}
// The `value` prop, in theory, should not be `null`, but the following line
// ensures it fallback to `undefined` in case a consumer of `UnitControl`
// still passes `null` as a `value`.
const nonNullValueProp = valueProp !== null && valueProp !== void 0 ? valueProp : undefined;
const [units, reFirstCharacterOfUnits] = useMemo(() => {
const list = getUnitsWithCurrentUnit(nonNullValueProp, unitProp, unitsProp);
const [{
value: firstUnitValue = ''
} = {}, ...rest] = list;
const firstCharacters = rest.reduce((carry, {
value
}) => {
const first = escapeRegExp(value?.substring(0, 1) || '');
return carry.includes(first) ? carry : `${carry}|${first}`;
}, escapeRegExp(firstUnitValue.substring(0, 1)));
return [list, new RegExp(`^(?:${firstCharacters})$`, 'i')];
}, [nonNullValueProp, unitProp, unitsProp]);
const [parsedQuantity, parsedUnit] = getParsedQuantityAndUnit(nonNullValueProp, unitProp, units);
const [unit, setUnit] = useControlledState(units.length === 1 ? units[0].value : unitProp, {
initial: parsedUnit,
fallback: ''
});
useEffect(() => {
if (parsedUnit !== undefined) {
setUnit(parsedUnit);
}
}, [parsedUnit, setUnit]);
const classes = classnames('components-unit-control',
// This class is added for legacy purposes to maintain it on the outer
// wrapper. See: https://github.com/WordPress/gutenberg/pull/45139
'components-unit-control-wrapper', className);
const handleOnQuantityChange = (nextQuantityValue, changeProps) => {
if (nextQuantityValue === '' || typeof nextQuantityValue === 'undefined' || nextQuantityValue === null) {
onChangeProp?.('', changeProps);
return;
}
/*
* Customizing the onChange callback.
* This allows as to broadcast a combined value+unit to onChange.
*/
const onChangeValue = getValidParsedQuantityAndUnit(nextQuantityValue, units, parsedQuantity, unit).join('');
onChangeProp?.(onChangeValue, changeProps);
};
const handleOnUnitChange = (nextUnitValue, changeProps) => {
const {
data
} = changeProps;
let nextValue = `${parsedQuantity !== null && parsedQuantity !== void 0 ? parsedQuantity : ''}${nextUnitValue}`;
if (isResetValueOnUnitChange && data?.default !== undefined) {
nextValue = `${data.default}${nextUnitValue}`;
}
onChangeProp?.(nextValue, changeProps);
onUnitChange?.(nextUnitValue, changeProps);
setUnit(nextUnitValue);
};
let handleOnKeyDown;
if (!disableUnits && isUnitSelectTabbable && units.length) {
handleOnKeyDown = event => {
props.onKeyDown?.(event);
// Unless the meta key was pressed (to avoid interfering with
// shortcuts, e.g. pastes), moves focus to the unit select if a key
// matches the first character of a unit.
if (!event.metaKey && reFirstCharacterOfUnits.test(event.key)) refInputSuffix.current?.focus();
};
}
const refInputSuffix = useRef(null);
const inputSuffix = !disableUnits ? createElement(UnitSelectControl, {
ref: refInputSuffix,
"aria-label": __('Select unit'),
disabled: disabled,
isUnitSelectTabbable: isUnitSelectTabbable,
onChange: handleOnUnitChange,
size: ['small', 'compact'].includes(size) || size === 'default' && !props.__next40pxDefaultSize ? 'small' : 'default',
unit: unit,
units: units,
onFocus: onFocusProp,
onBlur: unitControlProps.onBlur
}) : null;
let step = props.step;
/*
* If no step prop has been passed, lookup the active unit and
* try to get step from `units`, or default to a value of `1`
*/
if (!step && units) {
var _activeUnit$step;
const activeUnit = units.find(option => option.value === unit);
step = (_activeUnit$step = activeUnit?.step) !== null && _activeUnit$step !== void 0 ? _activeUnit$step : 1;
}
return createElement(ValueInput, {
...props,
autoComplete: autoComplete,
className: classes,
disabled: disabled,
spinControls: "none",
isPressEnterToChange: isPressEnterToChange,
label: label,
onKeyDown: handleOnKeyDown,
onChange: handleOnQuantityChange,
ref: forwardedRef,
size: size,
suffix: inputSuffix,
type: isPressEnterToChange ? 'text' : 'number',
value: parsedQuantity !== null && parsedQuantity !== void 0 ? parsedQuantity : '',
step: step,
onFocus: onFocusProp,
__unstableStateReducer: __unstableStateReducer
});
}
/**
* `UnitControl` allows the user to set a numeric quantity as well as a unit (e.g. `px`).
*
*
* ```jsx
* import { __experimentalUnitControl as UnitControl } from '@wordpress/components';
* import { useState } from '@wordpress/element';
*
* const Example = () => {
* const [ value, setValue ] = useState( '10px' );
*
* return <UnitControl onChange={ setValue } value={ value } />;
* };
* ```
*/
export const UnitControl = forwardRef(UnforwardedUnitControl);
export { parseQuantityAndUnitFromRawValue, useCustomUnits } from './utils';
export default UnitControl;
//# sourceMappingURL=index.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,148 @@
import { createElement, Fragment } from "react";
/**
* External dependencies
*/
import { Text, View, TouchableWithoutFeedback, Platform, findNodeHandle } from 'react-native';
/**
* Internal dependencies
*/
import RangeCell from '../mobile/bottom-sheet/range-cell';
import StepperCell from '../mobile/bottom-sheet/stepper-cell';
import Picker from '../mobile/picker';
import styles from './style.scss';
import { CSS_UNITS, hasUnits, getAccessibleLabelForUnit } from './utils';
/**
* WordPress dependencies
*/
import { useRef, useCallback, useMemo, memo } from '@wordpress/element';
import { withPreferredColorScheme } from '@wordpress/compose';
import { __, sprintf } from '@wordpress/i18n';
function UnitControl({
currentInput,
label,
value,
onChange,
onUnitChange,
initialPosition,
min,
max,
separatorType,
units = CSS_UNITS,
unit,
getStylesFromColorScheme,
...props
}) {
const pickerRef = useRef();
const anchorNodeRef = useRef();
const onPickerPresent = useCallback(() => {
if (pickerRef?.current) {
pickerRef.current.presentPicker();
}
// Disable reason: this should be fixed by the native team.
// It would be great if this could be done in the context of
// https://github.com/WordPress/gutenberg/pull/39218
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pickerRef?.current]);
const currentInputValue = currentInput === null ? value : currentInput;
const initialControlValue = isFinite(currentInputValue) ? currentInputValue : initialPosition;
const unitButtonTextStyle = getStylesFromColorScheme(styles.unitButtonText, styles.unitButtonTextDark);
/* translators: accessibility text. Inform about current unit value. %s: Current unit value. */
const accessibilityLabel = sprintf(__('Current unit is %s'), unit);
const accessibilityHint = Platform.OS === 'ios' ? __('Double tap to open Action Sheet with available options') : __('Double tap to open Bottom Sheet with available options');
const renderUnitButton = useMemo(() => {
const unitButton = createElement(View, {
style: styles.unitButton
}, createElement(Text, {
style: unitButtonTextStyle
}, unit));
if (hasUnits(units) && units?.length > 1) {
return createElement(TouchableWithoutFeedback, {
onPress: onPickerPresent,
accessibilityLabel: accessibilityLabel,
accessibilityRole: "button",
accessibilityHint: accessibilityHint
}, unitButton);
}
return unitButton;
}, [onPickerPresent, accessibilityLabel, accessibilityHint, unitButtonTextStyle, unit, units]);
const getAnchor = useCallback(() => anchorNodeRef?.current ? findNodeHandle(anchorNodeRef?.current) : undefined,
// Disable reason: this should be fixed by the native team.
// It would be great if this could be done in the context of
// https://github.com/WordPress/gutenberg/pull/39218
// eslint-disable-next-line react-hooks/exhaustive-deps
[anchorNodeRef?.current]);
const getDecimal = step => {
// Return the decimal offset based on the step size.
// if step size is 0.1 we expect the offset to be 1.
// for example 12 + 0.1 we would expect the see 12.1 (not 12.10 or 12 );
// steps are defined in the CSS_UNITS and they vary from unit to unit.
const stepToString = step;
const splitStep = stepToString.toString().split('.');
return splitStep[1] ? splitStep[1].length : 0;
};
const renderUnitPicker = useCallback(() => {
// Keeping for legacy reasons, although `false` should not be a valid
// value for the `units` prop anymore.
if (units === false) {
return null;
}
return createElement(View, {
style: styles.unitMenu,
ref: anchorNodeRef
}, renderUnitButton, hasUnits(units) && units?.length > 1 ? createElement(Picker, {
ref: pickerRef,
options: units,
onChange: onUnitChange,
hideCancelButton: true,
leftAlign: true,
getAnchor: getAnchor
}) : null);
}, [pickerRef, units, onUnitChange, getAnchor, renderUnitButton]);
let step = props.step;
/*
* If no step prop has been passed, lookup the active unit and
* try to get step from `units`, or default to a value of `1`
*/
if (!step && units) {
var _activeUnit$step;
const activeUnit = units.find(option => option.value === unit);
step = (_activeUnit$step = activeUnit?.step) !== null && _activeUnit$step !== void 0 ? _activeUnit$step : 1;
}
const decimalNum = getDecimal(step);
return createElement(Fragment, null, unit !== '%' ? createElement(StepperCell, {
label: label,
max: max,
min: min,
onChange: onChange,
separatorType: separatorType,
value: value,
step: step,
defaultValue: initialControlValue,
shouldDisplayTextInput: true,
decimalNum: decimalNum,
openUnitPicker: onPickerPresent,
unitLabel: getAccessibleLabelForUnit(unit),
...props
}, renderUnitPicker()) : createElement(RangeCell, {
label: label,
onChange: onChange,
minimumValue: min,
maximumValue: max,
value: value,
step: step,
unit: unit,
defaultValue: initialControlValue,
separatorType: separatorType,
decimalNum: decimalNum,
openUnitPicker: onPickerPresent,
unitLabel: getAccessibleLabelForUnit(unit),
...props
}, renderUnitPicker()));
}
export { useCustomUnits } from './utils';
export default memo(withPreferredColorScheme(UnitControl));
//# sourceMappingURL=index.native.js.map

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=types.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":[],"sources":["@wordpress/components/src/unit-control/types.ts"],"sourcesContent":["/**\n * External dependencies\n */\nimport type { FocusEventHandler } from 'react';\n\n/**\n * Internal dependencies\n */\nimport type {\n\tInputChangeCallback,\n\tInputControlProps,\n} from '../input-control/types';\nimport type { NumberControlProps } from '../number-control/types';\n\nexport type SelectSize = 'default' | 'small';\n\nexport type WPUnitControlUnit = {\n\t/**\n\t * The value for the unit, used in a CSS value (e.g `px`).\n\t */\n\tvalue: string;\n\t/**\n\t * The label used in a dropdown selector for the unit.\n\t */\n\tlabel: string;\n\t/**\n\t * Default value (quantity) for the unit, used when switching units.\n\t */\n\tdefault?: number;\n\t/**\n\t * An accessible label used by screen readers.\n\t */\n\ta11yLabel?: string;\n\t/**\n\t * A step value used when incrementing/decrementing the value.\n\t */\n\tstep?: number;\n};\n\nexport type UnitControlOnChangeCallback = InputChangeCallback< {\n\tdata?: WPUnitControlUnit;\n} >;\n\nexport type UnitSelectControlProps = {\n\t/**\n\t * Whether the control can be focused via keyboard navigation.\n\t *\n\t * @default true\n\t */\n\tisUnitSelectTabbable?: boolean;\n\t/**\n\t * A callback function invoked when the value is changed.\n\t */\n\tonChange?: UnitControlOnChangeCallback;\n\t/**\n\t * The size of the unit select.\n\t */\n\tsize?: SelectSize;\n\t/**\n\t * Current unit.\n\t */\n\tunit?: string;\n\t/**\n\t * Available units to select from.\n\t *\n\t * @default CSS_UNITS\n\t */\n\tunits?: WPUnitControlUnit[];\n};\n\nexport type UnitControlProps = Pick< InputControlProps, 'size' > &\n\tOmit< UnitSelectControlProps, 'size' | 'unit' > &\n\tOmit< NumberControlProps, 'spinControls' | 'suffix' | 'type' > & {\n\t\t/**\n\t\t * If `true`, the unit `<select>` is hidden.\n\t\t *\n\t\t * @default false\n\t\t */\n\t\tdisableUnits?: boolean;\n\t\t/**\n\t\t * If `true`, and the selected unit provides a `default` value, this value is set\n\t\t * when changing units.\n\t\t *\n\t\t * @default false\n\t\t */\n\t\tisResetValueOnUnitChange?: boolean;\n\t\t/**\n\t\t * Callback when the `unit` changes.\n\t\t */\n\t\tonUnitChange?: UnitControlOnChangeCallback;\n\t\t/**\n\t\t * Current unit. _Note: this prop is deprecated. Instead, provide a unit with a value through the `value` prop._\n\t\t *\n\t\t * @deprecated\n\t\t */\n\t\tunit?: string;\n\t\t/**\n\t\t * Current value. If passed as a string, the current unit will be inferred from this value.\n\t\t * For example, a `value` of \"50%\" will set the current unit to `%`.\n\t\t */\n\t\tvalue?: string | number;\n\t\t/**\n\t\t * Callback when either the quantity or the unit inputs lose focus.\n\t\t */\n\t\tonBlur?: FocusEventHandler< HTMLInputElement | HTMLSelectElement >;\n\t\t/**\n\t\t * Callback when either the quantity or the unit inputs gains focus.\n\t\t */\n\t\tonFocus?: FocusEventHandler< HTMLInputElement | HTMLSelectElement >;\n\t};\n"],"mappings":""}

View File

@@ -0,0 +1,57 @@
import { createElement } from "react";
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { forwardRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import { UnitSelect, UnitLabel } from './styles/unit-control-styles';
import { CSS_UNITS, hasUnits } from './utils';
function UnitSelectControl({
className,
isUnitSelectTabbable: isTabbable = true,
onChange,
size = 'default',
unit = 'px',
units = CSS_UNITS,
...props
}, ref) {
if (!hasUnits(units) || units?.length === 1) {
return createElement(UnitLabel, {
className: "components-unit-control__unit-label",
selectSize: size
}, unit);
}
const handleOnChange = event => {
const {
value: unitValue
} = event.target;
const data = units.find(option => option.value === unitValue);
onChange?.(unitValue, {
event,
data
});
};
const classes = classnames('components-unit-control__select', className);
return createElement(UnitSelect, {
ref: ref,
className: classes,
onChange: handleOnChange,
selectSize: size,
tabIndex: isTabbable ? undefined : -1,
value: unit,
...props
}, units.map(option => createElement("option", {
value: option.value,
key: option.value
}, option.label)));
}
export default forwardRef(UnitSelectControl);
//# sourceMappingURL=unit-select-control.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["classnames","forwardRef","UnitSelect","UnitLabel","CSS_UNITS","hasUnits","UnitSelectControl","className","isUnitSelectTabbable","isTabbable","onChange","size","unit","units","props","ref","length","createElement","selectSize","handleOnChange","event","value","unitValue","target","data","find","option","classes","tabIndex","undefined","map","key","label"],"sources":["@wordpress/components/src/unit-control/unit-select-control.tsx"],"sourcesContent":["/**\n * External dependencies\n */\nimport classnames from 'classnames';\nimport type { ChangeEvent, ForwardedRef } from 'react';\n\n/**\n * WordPress dependencies\n */\nimport { forwardRef } from '@wordpress/element';\n\n/**\n * Internal dependencies\n */\nimport type { WordPressComponentProps } from '../context';\nimport { UnitSelect, UnitLabel } from './styles/unit-control-styles';\nimport { CSS_UNITS, hasUnits } from './utils';\nimport type { UnitSelectControlProps } from './types';\n\nfunction UnitSelectControl(\n\t{\n\t\tclassName,\n\t\tisUnitSelectTabbable: isTabbable = true,\n\t\tonChange,\n\t\tsize = 'default',\n\t\tunit = 'px',\n\t\tunits = CSS_UNITS,\n\t\t...props\n\t}: WordPressComponentProps< UnitSelectControlProps, 'select', false >,\n\tref: ForwardedRef< any >\n) {\n\tif ( ! hasUnits( units ) || units?.length === 1 ) {\n\t\treturn (\n\t\t\t<UnitLabel\n\t\t\t\tclassName=\"components-unit-control__unit-label\"\n\t\t\t\tselectSize={ size }\n\t\t\t>\n\t\t\t\t{ unit }\n\t\t\t</UnitLabel>\n\t\t);\n\t}\n\n\tconst handleOnChange = ( event: ChangeEvent< HTMLSelectElement > ) => {\n\t\tconst { value: unitValue } = event.target;\n\t\tconst data = units.find( ( option ) => option.value === unitValue );\n\n\t\tonChange?.( unitValue, { event, data } );\n\t};\n\n\tconst classes = classnames( 'components-unit-control__select', className );\n\n\treturn (\n\t\t<UnitSelect\n\t\t\tref={ ref }\n\t\t\tclassName={ classes }\n\t\t\tonChange={ handleOnChange }\n\t\t\tselectSize={ size }\n\t\t\ttabIndex={ isTabbable ? undefined : -1 }\n\t\t\tvalue={ unit }\n\t\t\t{ ...props }\n\t\t>\n\t\t\t{ units.map( ( option ) => (\n\t\t\t\t<option value={ option.value } key={ option.value }>\n\t\t\t\t\t{ option.label }\n\t\t\t\t</option>\n\t\t\t) ) }\n\t\t</UnitSelect>\n\t);\n}\nexport default forwardRef( UnitSelectControl );\n"],"mappings":";AAAA;AACA;AACA;AACA,OAAOA,UAAU,MAAM,YAAY;AAGnC;AACA;AACA;AACA,SAASC,UAAU,QAAQ,oBAAoB;;AAE/C;AACA;AACA;;AAEA,SAASC,UAAU,EAAEC,SAAS,QAAQ,8BAA8B;AACpE,SAASC,SAAS,EAAEC,QAAQ,QAAQ,SAAS;AAG7C,SAASC,iBAAiBA,CACzB;EACCC,SAAS;EACTC,oBAAoB,EAAEC,UAAU,GAAG,IAAI;EACvCC,QAAQ;EACRC,IAAI,GAAG,SAAS;EAChBC,IAAI,GAAG,IAAI;EACXC,KAAK,GAAGT,SAAS;EACjB,GAAGU;AACgE,CAAC,EACrEC,GAAwB,EACvB;EACD,IAAK,CAAEV,QAAQ,CAAEQ,KAAM,CAAC,IAAIA,KAAK,EAAEG,MAAM,KAAK,CAAC,EAAG;IACjD,OACCC,aAAA,CAACd,SAAS;MACTI,SAAS,EAAC,qCAAqC;MAC/CW,UAAU,EAAGP;IAAM,GAEjBC,IACQ,CAAC;EAEd;EAEA,MAAMO,cAAc,GAAKC,KAAuC,IAAM;IACrE,MAAM;MAAEC,KAAK,EAAEC;IAAU,CAAC,GAAGF,KAAK,CAACG,MAAM;IACzC,MAAMC,IAAI,GAAGX,KAAK,CAACY,IAAI,CAAIC,MAAM,IAAMA,MAAM,CAACL,KAAK,KAAKC,SAAU,CAAC;IAEnEZ,QAAQ,GAAIY,SAAS,EAAE;MAAEF,KAAK;MAAEI;IAAK,CAAE,CAAC;EACzC,CAAC;EAED,MAAMG,OAAO,GAAG3B,UAAU,CAAE,iCAAiC,EAAEO,SAAU,CAAC;EAE1E,OACCU,aAAA,CAACf,UAAU;IACVa,GAAG,EAAGA,GAAK;IACXR,SAAS,EAAGoB,OAAS;IACrBjB,QAAQ,EAAGS,cAAgB;IAC3BD,UAAU,EAAGP,IAAM;IACnBiB,QAAQ,EAAGnB,UAAU,GAAGoB,SAAS,GAAG,CAAC,CAAG;IACxCR,KAAK,EAAGT,IAAM;IAAA,GACTE;EAAK,GAERD,KAAK,CAACiB,GAAG,CAAIJ,MAAM,IACpBT,aAAA;IAAQI,KAAK,EAAGK,MAAM,CAACL,KAAO;IAACU,GAAG,EAAGL,MAAM,CAACL;EAAO,GAChDK,MAAM,CAACM,KACF,CACP,CACS,CAAC;AAEf;AACA,eAAe/B,UAAU,CAAEK,iBAAkB,CAAC"}

View File

@@ -0,0 +1,396 @@
/**
* WordPress dependencies
*/
import { __, _x } from '@wordpress/i18n';
import { Platform } from '@wordpress/element';
/**
* Internal dependencies
*/
const isWeb = Platform.OS === 'web';
const allUnits = {
px: {
value: 'px',
label: isWeb ? 'px' : __('Pixels (px)'),
a11yLabel: __('Pixels (px)'),
step: 1
},
'%': {
value: '%',
label: isWeb ? '%' : __('Percentage (%)'),
a11yLabel: __('Percent (%)'),
step: 0.1
},
em: {
value: 'em',
label: isWeb ? 'em' : __('Relative to parent font size (em)'),
a11yLabel: _x('ems', 'Relative to parent font size (em)'),
step: 0.01
},
rem: {
value: 'rem',
label: isWeb ? 'rem' : __('Relative to root font size (rem)'),
a11yLabel: _x('rems', 'Relative to root font size (rem)'),
step: 0.01
},
vw: {
value: 'vw',
label: isWeb ? 'vw' : __('Viewport width (vw)'),
a11yLabel: __('Viewport width (vw)'),
step: 0.1
},
vh: {
value: 'vh',
label: isWeb ? 'vh' : __('Viewport height (vh)'),
a11yLabel: __('Viewport height (vh)'),
step: 0.1
},
vmin: {
value: 'vmin',
label: isWeb ? 'vmin' : __('Viewport smallest dimension (vmin)'),
a11yLabel: __('Viewport smallest dimension (vmin)'),
step: 0.1
},
vmax: {
value: 'vmax',
label: isWeb ? 'vmax' : __('Viewport largest dimension (vmax)'),
a11yLabel: __('Viewport largest dimension (vmax)'),
step: 0.1
},
ch: {
value: 'ch',
label: isWeb ? 'ch' : __('Width of the zero (0) character (ch)'),
a11yLabel: __('Width of the zero (0) character (ch)'),
step: 0.01
},
ex: {
value: 'ex',
label: isWeb ? 'ex' : __('x-height of the font (ex)'),
a11yLabel: __('x-height of the font (ex)'),
step: 0.01
},
cm: {
value: 'cm',
label: isWeb ? 'cm' : __('Centimeters (cm)'),
a11yLabel: __('Centimeters (cm)'),
step: 0.001
},
mm: {
value: 'mm',
label: isWeb ? 'mm' : __('Millimeters (mm)'),
a11yLabel: __('Millimeters (mm)'),
step: 0.1
},
in: {
value: 'in',
label: isWeb ? 'in' : __('Inches (in)'),
a11yLabel: __('Inches (in)'),
step: 0.001
},
pc: {
value: 'pc',
label: isWeb ? 'pc' : __('Picas (pc)'),
a11yLabel: __('Picas (pc)'),
step: 1
},
pt: {
value: 'pt',
label: isWeb ? 'pt' : __('Points (pt)'),
a11yLabel: __('Points (pt)'),
step: 1
},
svw: {
value: 'svw',
label: isWeb ? 'svw' : __('Small viewport width (svw)'),
a11yLabel: __('Small viewport width (svw)'),
step: 0.1
},
svh: {
value: 'svh',
label: isWeb ? 'svh' : __('Small viewport height (svh)'),
a11yLabel: __('Small viewport height (svh)'),
step: 0.1
},
svi: {
value: 'svi',
label: isWeb ? 'svi' : __('Viewport smallest size in the inline direction (svi)'),
a11yLabel: __('Small viewport width or height (svi)'),
step: 0.1
},
svb: {
value: 'svb',
label: isWeb ? 'svb' : __('Viewport smallest size in the block direction (svb)'),
a11yLabel: __('Small viewport width or height (svb)'),
step: 0.1
},
svmin: {
value: 'svmin',
label: isWeb ? 'svmin' : __('Small viewport smallest dimension (svmin)'),
a11yLabel: __('Small viewport smallest dimension (svmin)'),
step: 0.1
},
lvw: {
value: 'lvw',
label: isWeb ? 'lvw' : __('Large viewport width (lvw)'),
a11yLabel: __('Large viewport width (lvw)'),
step: 0.1
},
lvh: {
value: 'lvh',
label: isWeb ? 'lvh' : __('Large viewport height (lvh)'),
a11yLabel: __('Large viewport height (lvh)'),
step: 0.1
},
lvi: {
value: 'lvi',
label: isWeb ? 'lvi' : __('Large viewport width or height (lvi)'),
a11yLabel: __('Large viewport width or height (lvi)'),
step: 0.1
},
lvb: {
value: 'lvb',
label: isWeb ? 'lvb' : __('Large viewport width or height (lvb)'),
a11yLabel: __('Large viewport width or height (lvb)'),
step: 0.1
},
lvmin: {
value: 'lvmin',
label: isWeb ? 'lvmin' : __('Large viewport smallest dimension (lvmin)'),
a11yLabel: __('Large viewport smallest dimension (lvmin)'),
step: 0.1
},
dvw: {
value: 'dvw',
label: isWeb ? 'dvw' : __('Dynamic viewport width (dvw)'),
a11yLabel: __('Dynamic viewport width (dvw)'),
step: 0.1
},
dvh: {
value: 'dvh',
label: isWeb ? 'dvh' : __('Dynamic viewport height (dvh)'),
a11yLabel: __('Dynamic viewport height (dvh)'),
step: 0.1
},
dvi: {
value: 'dvi',
label: isWeb ? 'dvi' : __('Dynamic viewport width or height (dvi)'),
a11yLabel: __('Dynamic viewport width or height (dvi)'),
step: 0.1
},
dvb: {
value: 'dvb',
label: isWeb ? 'dvb' : __('Dynamic viewport width or height (dvb)'),
a11yLabel: __('Dynamic viewport width or height (dvb)'),
step: 0.1
},
dvmin: {
value: 'dvmin',
label: isWeb ? 'dvmin' : __('Dynamic viewport smallest dimension (dvmin)'),
a11yLabel: __('Dynamic viewport smallest dimension (dvmin)'),
step: 0.1
},
dvmax: {
value: 'dvmax',
label: isWeb ? 'dvmax' : __('Dynamic viewport largest dimension (dvmax)'),
a11yLabel: __('Dynamic viewport largest dimension (dvmax)'),
step: 0.1
},
svmax: {
value: 'svmax',
label: isWeb ? 'svmax' : __('Small viewport largest dimension (svmax)'),
a11yLabel: __('Small viewport largest dimension (svmax)'),
step: 0.1
},
lvmax: {
value: 'lvmax',
label: isWeb ? 'lvmax' : __('Large viewport largest dimension (lvmax)'),
a11yLabel: __('Large viewport largest dimension (lvmax)'),
step: 0.1
}
};
/**
* An array of all available CSS length units.
*/
export const ALL_CSS_UNITS = Object.values(allUnits);
/**
* Units of measurements. `a11yLabel` is used by screenreaders.
*/
export const CSS_UNITS = [allUnits.px, allUnits['%'], allUnits.em, allUnits.rem, allUnits.vw, allUnits.vh];
export const DEFAULT_UNIT = allUnits.px;
/**
* Handles legacy value + unit handling.
* This component use to manage both incoming value and units separately.
*
* Moving forward, ideally the value should be a string that contains both
* the value and unit, example: '10px'
*
* @param rawValue The raw value as a string (may or may not contain the unit)
* @param fallbackUnit The unit used as a fallback, if not unit is detected in the `value`
* @param allowedUnits Units to derive from.
* @return The extracted quantity and unit. The quantity can be `undefined` in case the raw value
* could not be parsed to a number correctly. The unit can be `undefined` in case the unit parse
* from the raw value could not be matched against the list of allowed units.
*/
export function getParsedQuantityAndUnit(rawValue, fallbackUnit, allowedUnits) {
const initialValue = fallbackUnit ? `${rawValue !== null && rawValue !== void 0 ? rawValue : ''}${fallbackUnit}` : rawValue;
return parseQuantityAndUnitFromRawValue(initialValue, allowedUnits);
}
/**
* Checks if units are defined.
*
* @param units List of units.
* @return Whether the list actually contains any units.
*/
export function hasUnits(units) {
// Although the `isArray` check shouldn't be necessary (given the signature of
// this typed function), it's better to stay on the side of caution, since
// this function may be called from un-typed environments.
return Array.isArray(units) && !!units.length;
}
/**
* Parses a quantity and unit from a raw string value, given a list of allowed
* units and otherwise falling back to the default unit.
*
* @param rawValue The raw value as a string (may or may not contain the unit)
* @param allowedUnits Units to derive from.
* @return The extracted quantity and unit. The quantity can be `undefined` in case the raw value
* could not be parsed to a number correctly. The unit can be `undefined` in case the unit parsed
* from the raw value could not be matched against the list of allowed units.
*/
export function parseQuantityAndUnitFromRawValue(rawValue, allowedUnits = ALL_CSS_UNITS) {
let trimmedValue;
let quantityToReturn;
if (typeof rawValue !== 'undefined' || rawValue === null) {
trimmedValue = `${rawValue}`.trim();
const parsedQuantity = parseFloat(trimmedValue);
quantityToReturn = !isFinite(parsedQuantity) ? undefined : parsedQuantity;
}
const unitMatch = trimmedValue?.match(/[\d.\-\+]*\s*(.*)/);
const matchedUnit = unitMatch?.[1]?.toLowerCase();
let unitToReturn;
if (hasUnits(allowedUnits)) {
const match = allowedUnits.find(item => item.value === matchedUnit);
unitToReturn = match?.value;
} else {
unitToReturn = DEFAULT_UNIT.value;
}
return [quantityToReturn, unitToReturn];
}
/**
* Parses quantity and unit from a raw value. Validates parsed value, using fallback
* value if invalid.
*
* @param rawValue The next value.
* @param allowedUnits Units to derive from.
* @param fallbackQuantity The fallback quantity, used in case it's not possible to parse a valid quantity from the raw value.
* @param fallbackUnit The fallback unit, used in case it's not possible to parse a valid unit from the raw value.
* @return The extracted quantity and unit. The quantity can be `undefined` in case the raw value
* could not be parsed to a number correctly, and the `fallbackQuantity` was also `undefined`. The
* unit can be `undefined` only if the unit parsed from the raw value could not be matched against
* the list of allowed units, the `fallbackQuantity` is also `undefined` and the list of
* `allowedUnits` is passed empty.
*/
export function getValidParsedQuantityAndUnit(rawValue, allowedUnits, fallbackQuantity, fallbackUnit) {
const [parsedQuantity, parsedUnit] = parseQuantityAndUnitFromRawValue(rawValue, allowedUnits);
// The parsed value from `parseQuantityAndUnitFromRawValue` should now be
// either a real number or undefined. If undefined, use the fallback value.
const quantityToReturn = parsedQuantity !== null && parsedQuantity !== void 0 ? parsedQuantity : fallbackQuantity;
// If no unit is parsed from the raw value, or if the fallback unit is not
// defined, use the first value from the list of allowed units as fallback.
let unitToReturn = parsedUnit || fallbackUnit;
if (!unitToReturn && hasUnits(allowedUnits)) {
unitToReturn = allowedUnits[0].value;
}
return [quantityToReturn, unitToReturn];
}
/**
* Takes a unit value and finds the matching accessibility label for the
* unit abbreviation.
*
* @param unit Unit value (example: `px`)
* @return a11y label for the unit abbreviation
*/
export function getAccessibleLabelForUnit(unit) {
const match = ALL_CSS_UNITS.find(item => item.value === unit);
return match?.a11yLabel ? match?.a11yLabel : match?.value;
}
/**
* Filters available units based on values defined a list of allowed unit values.
*
* @param allowedUnitValues Collection of allowed unit value strings.
* @param availableUnits Collection of available unit objects.
* @return Filtered units.
*/
export function filterUnitsWithSettings(allowedUnitValues = [], availableUnits) {
// Although the `isArray` check shouldn't be necessary (given the signature of
// this typed function), it's better to stay on the side of caution, since
// this function may be called from un-typed environments.
return Array.isArray(availableUnits) ? availableUnits.filter(unit => allowedUnitValues.includes(unit.value)) : [];
}
/**
* Custom hook to retrieve and consolidate units setting from add_theme_support().
* TODO: ideally this hook shouldn't be needed
* https://github.com/WordPress/gutenberg/pull/31822#discussion_r633280823
*
* @param args An object containing units, settingPath & defaultUnits.
* @param args.units Collection of all potentially available units.
* @param args.availableUnits Collection of unit value strings for filtering available units.
* @param args.defaultValues Collection of default values for defined units. Example: `{ px: 350, em: 15 }`.
*
* @return Filtered list of units, with their default values updated following the `defaultValues`
* argument's property.
*/
export const useCustomUnits = ({
units = ALL_CSS_UNITS,
availableUnits = [],
defaultValues
}) => {
const customUnitsToReturn = filterUnitsWithSettings(availableUnits, units);
if (defaultValues) {
customUnitsToReturn.forEach((unit, i) => {
if (defaultValues[unit.value]) {
const [parsedDefaultValue] = parseQuantityAndUnitFromRawValue(defaultValues[unit.value]);
customUnitsToReturn[i].default = parsedDefaultValue;
}
});
}
return customUnitsToReturn;
};
/**
* Get available units with the unit for the currently selected value
* prepended if it is not available in the list of units.
*
* This is useful to ensure that the current value's unit is always
* accurately displayed in the UI, even if the intention is to hide
* the availability of that unit.
*
* @param rawValue Selected value to parse.
* @param legacyUnit Legacy unit value, if rawValue needs it appended.
* @param units List of available units.
*
* @return A collection of units containing the unit for the current value.
*/
export function getUnitsWithCurrentUnit(rawValue, legacyUnit, units = ALL_CSS_UNITS) {
const unitsToReturn = Array.isArray(units) ? [...units] : [];
const [, currentUnit] = getParsedQuantityAndUnit(rawValue, legacyUnit, ALL_CSS_UNITS);
if (currentUnit && !unitsToReturn.some(unit => unit.value === currentUnit)) {
if (allUnits[currentUnit]) {
unitsToReturn.unshift(allUnits[currentUnit]);
}
}
return unitsToReturn;
}
//# sourceMappingURL=utils.js.map

File diff suppressed because one or more lines are too long