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,86 @@
import { createElement, Fragment } from "react";
/**
* WordPress dependencies
*/
import { useState } from '@wordpress/element';
import { swatch } from '@wordpress/icons';
import { __ } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import Button from '../../button';
import ColorPalette from '../../color-palette';
import ColorIndicator from '../../color-indicator';
import Icon from '../../icon';
import { HStack } from '../../h-stack';
import { useInstanceId } from '@wordpress/compose';
function ColorOption({
label,
value,
colors,
disableCustomColors,
enableAlpha,
onChange
}) {
const [isOpen, setIsOpen] = useState(false);
const idRoot = useInstanceId(ColorOption, 'color-list-picker-option');
const labelId = `${idRoot}__label`;
const contentId = `${idRoot}__content`;
return createElement(Fragment, null, createElement(Button, {
className: "components-color-list-picker__swatch-button",
onClick: () => setIsOpen(prev => !prev),
"aria-expanded": isOpen,
"aria-controls": contentId
}, createElement(HStack, {
justify: "flex-start",
spacing: 2
}, value ? createElement(ColorIndicator, {
colorValue: value,
className: "components-color-list-picker__swatch-color"
}) : createElement(Icon, {
icon: swatch
}), createElement("span", {
id: labelId
}, label))), createElement("div", {
role: "group",
id: contentId,
"aria-labelledby": labelId,
"aria-hidden": !isOpen
}, isOpen && createElement(ColorPalette, {
"aria-label": __('Color options'),
className: "components-color-list-picker__color-picker",
colors: colors,
value: value,
clearable: false,
onChange: onChange,
disableCustomColors: disableCustomColors,
enableAlpha: enableAlpha
})));
}
function ColorListPicker({
colors,
labels,
value = [],
disableCustomColors,
enableAlpha,
onChange
}) {
return createElement("div", {
className: "components-color-list-picker"
}, labels.map((label, index) => createElement(ColorOption, {
key: index,
label: label,
value: value[index],
colors: colors,
disableCustomColors: disableCustomColors,
enableAlpha: enableAlpha,
onChange: newColor => {
const newColors = value.slice();
newColors[index] = newColor;
onChange(newColors);
}
})));
}
export default ColorListPicker;
//# sourceMappingURL=index.js.map

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/duotone-picker/color-list-picker/types.ts"],"sourcesContent":["/**\n * External dependencies\n */\nimport type { CSSProperties } from 'react';\n\nexport type ColorListPickerProps = {\n\t/**\n\t * A list of predefined colors. Each color is an object with a `name` and a\n\t * `color` value.\n\t * The `name` is a string used to identify the color in the UI.\n\t * The `color` is a valid CSS color string.\n\t */\n\tcolors: Array< {\n\t\tname: string;\n\t\tcolor: NonNullable< CSSProperties[ 'color' ] >;\n\t} >;\n\t/**\n\t * A list of labels for each of the options displayed in the UI.\n\t */\n\tlabels: Array< string >;\n\t/**\n\t * An array containing the currently selected colors.\n\t */\n\tvalue?: Array< string >;\n\t/**\n\t * Controls whether the custom color picker is displayed.\n\t */\n\tdisableCustomColors?: boolean;\n\t/**\n\t * Controls whether the ColorPalette should show an alpha channel control.\n\t */\n\tenableAlpha?: boolean;\n\t/**\n\t * A function that receives the updated color value.\n\t */\n\tonChange: ( newValue: Array< string | undefined > ) => void;\n};\n\nexport type ColorOptionProps = Pick<\n\tColorListPickerProps,\n\t'colors' | 'disableCustomColors' | 'enableAlpha'\n> & {\n\tlabel: ColorListPickerProps[ 'labels' ][ number ];\n\tvalue: string | undefined;\n\tonChange: ( newValue: string | undefined ) => void;\n};\n"],"mappings":""}

View File

@@ -0,0 +1,27 @@
import { createElement } from "react";
/**
* Internal dependencies
*/
import CustomGradientBar from '../custom-gradient-picker/gradient-bar';
import { getColorStopsFromColors, getGradientFromCSSColors, getColorsFromColorStops } from './utils';
const PLACEHOLDER_VALUES = ['#333', '#CCC'];
export default function CustomDuotoneBar({
value,
onChange
}) {
const hasGradient = !!value;
const values = hasGradient ? value : PLACEHOLDER_VALUES;
const background = getGradientFromCSSColors(values);
const controlPoints = getColorStopsFromColors(values);
return createElement(CustomGradientBar, {
disableInserter: true,
background: background,
hasGradient: hasGradient,
value: controlPoints,
onChange: newColorStops => {
const newValue = getColorsFromColorStops(newColorStops);
onChange(newValue);
}
});
}
//# sourceMappingURL=custom-duotone-bar.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["CustomGradientBar","getColorStopsFromColors","getGradientFromCSSColors","getColorsFromColorStops","PLACEHOLDER_VALUES","CustomDuotoneBar","value","onChange","hasGradient","values","background","controlPoints","createElement","disableInserter","newColorStops","newValue"],"sources":["@wordpress/components/src/duotone-picker/custom-duotone-bar.tsx"],"sourcesContent":["/**\n * Internal dependencies\n */\nimport CustomGradientBar from '../custom-gradient-picker/gradient-bar';\n\nimport {\n\tgetColorStopsFromColors,\n\tgetGradientFromCSSColors,\n\tgetColorsFromColorStops,\n} from './utils';\n\nconst PLACEHOLDER_VALUES = [ '#333', '#CCC' ];\n\nexport default function CustomDuotoneBar( {\n\tvalue,\n\tonChange,\n}: {\n\tvalue?: string[];\n\tonChange: ( value?: string[] ) => void;\n} ) {\n\tconst hasGradient = !! value;\n\tconst values = hasGradient ? value : PLACEHOLDER_VALUES;\n\tconst background = getGradientFromCSSColors( values );\n\tconst controlPoints = getColorStopsFromColors( values );\n\treturn (\n\t\t<CustomGradientBar\n\t\t\tdisableInserter\n\t\t\tbackground={ background }\n\t\t\thasGradient={ hasGradient }\n\t\t\tvalue={ controlPoints }\n\t\t\tonChange={ ( newColorStops ) => {\n\t\t\t\tconst newValue = getColorsFromColorStops( newColorStops );\n\t\t\t\tonChange( newValue );\n\t\t\t} }\n\t\t/>\n\t);\n}\n"],"mappings":";AAAA;AACA;AACA;AACA,OAAOA,iBAAiB,MAAM,wCAAwC;AAEtE,SACCC,uBAAuB,EACvBC,wBAAwB,EACxBC,uBAAuB,QACjB,SAAS;AAEhB,MAAMC,kBAAkB,GAAG,CAAE,MAAM,EAAE,MAAM,CAAE;AAE7C,eAAe,SAASC,gBAAgBA,CAAE;EACzCC,KAAK;EACLC;AAID,CAAC,EAAG;EACH,MAAMC,WAAW,GAAG,CAAC,CAAEF,KAAK;EAC5B,MAAMG,MAAM,GAAGD,WAAW,GAAGF,KAAK,GAAGF,kBAAkB;EACvD,MAAMM,UAAU,GAAGR,wBAAwB,CAAEO,MAAO,CAAC;EACrD,MAAME,aAAa,GAAGV,uBAAuB,CAAEQ,MAAO,CAAC;EACvD,OACCG,aAAA,CAACZ,iBAAiB;IACjBa,eAAe;IACfH,UAAU,EAAGA,UAAY;IACzBF,WAAW,EAAGA,WAAa;IAC3BF,KAAK,EAAGK,aAAe;IACvBJ,QAAQ,EAAKO,aAAa,IAAM;MAC/B,MAAMC,QAAQ,GAAGZ,uBAAuB,CAAEW,aAAc,CAAC;MACzDP,QAAQ,CAAEQ,QAAS,CAAC;IACrB;EAAG,CACH,CAAC;AAEJ"}

View File

@@ -0,0 +1,176 @@
import { createElement } from "react";
/**
* External dependencies
*/
import fastDeepEqual from 'fast-deep-equal/es6';
/**
* WordPress dependencies
*/
import { useMemo } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
/**
* Internal dependencies
*/
import ColorListPicker from './color-list-picker';
import CircularOptionPicker from '../circular-option-picker';
import { VStack } from '../v-stack';
import CustomDuotoneBar from './custom-duotone-bar';
import { getDefaultColors, getGradientFromCSSColors } from './utils';
import { Spacer } from '../spacer';
/**
* ```jsx
* import { DuotonePicker, DuotoneSwatch } from '@wordpress/components';
* import { useState } from '@wordpress/element';
*
* const DUOTONE_PALETTE = [
* { colors: [ '#8c00b7', '#fcff41' ], name: 'Purple and yellow', slug: 'purple-yellow' },
* { colors: [ '#000097', '#ff4747' ], name: 'Blue and red', slug: 'blue-red' },
* ];
*
* const COLOR_PALETTE = [
* { color: '#ff4747', name: 'Red', slug: 'red' },
* { color: '#fcff41', name: 'Yellow', slug: 'yellow' },
* { color: '#000097', name: 'Blue', slug: 'blue' },
* { color: '#8c00b7', name: 'Purple', slug: 'purple' },
* ];
*
* const Example = () => {
* const [ duotone, setDuotone ] = useState( [ '#000000', '#ffffff' ] );
* return (
* <>
* <DuotonePicker
* duotonePalette={ DUOTONE_PALETTE }
* colorPalette={ COLOR_PALETTE }
* value={ duotone }
* onChange={ setDuotone }
* />
* <DuotoneSwatch values={ duotone } />
* </>
* );
* };
* ```
*/
function DuotonePicker({
asButtons,
loop,
clearable = true,
unsetable = true,
colorPalette,
duotonePalette,
disableCustomColors,
disableCustomDuotone,
value,
onChange,
'aria-label': ariaLabel,
'aria-labelledby': ariaLabelledby,
...otherProps
}) {
const [defaultDark, defaultLight] = useMemo(() => getDefaultColors(colorPalette), [colorPalette]);
const isUnset = value === 'unset';
const unsetOptionLabel = __('Unset');
const unsetOption = createElement(CircularOptionPicker.Option, {
key: "unset",
value: "unset",
isSelected: isUnset,
tooltipText: unsetOptionLabel,
"aria-label": unsetOptionLabel,
className: "components-duotone-picker__color-indicator",
onClick: () => {
onChange(isUnset ? undefined : 'unset');
}
});
const duotoneOptions = duotonePalette.map(({
colors,
slug,
name
}) => {
const style = {
background: getGradientFromCSSColors(colors, '135deg'),
color: 'transparent'
};
const tooltipText = name !== null && name !== void 0 ? name : sprintf(
// translators: %s: duotone code e.g: "dark-grayscale" or "7f7f7f-ffffff".
__('Duotone code: %s'), slug);
const label = name ? sprintf(
// translators: %s: The name of the option e.g: "Dark grayscale".
__('Duotone: %s'), name) : tooltipText;
const isSelected = fastDeepEqual(colors, value);
return createElement(CircularOptionPicker.Option, {
key: slug,
value: colors,
isSelected: isSelected,
"aria-label": label,
tooltipText: tooltipText,
style: style,
onClick: () => {
onChange(isSelected ? undefined : colors);
}
});
});
let metaProps;
if (asButtons) {
metaProps = {
asButtons: true
};
} else {
const _metaProps = {
asButtons: false,
loop
};
if (ariaLabel) {
metaProps = {
..._metaProps,
'aria-label': ariaLabel
};
} else if (ariaLabelledby) {
metaProps = {
..._metaProps,
'aria-labelledby': ariaLabelledby
};
} else {
metaProps = {
..._metaProps,
'aria-label': __('Custom color picker.')
};
}
}
const options = unsetable ? [unsetOption, ...duotoneOptions] : duotoneOptions;
return createElement(CircularOptionPicker, {
...otherProps,
...metaProps,
options: options,
actions: !!clearable && createElement(CircularOptionPicker.ButtonAction, {
onClick: () => onChange(undefined)
}, __('Clear'))
}, createElement(Spacer, {
paddingTop: options.length === 0 ? 0 : 4
}, createElement(VStack, {
spacing: 3
}, !disableCustomColors && !disableCustomDuotone && createElement(CustomDuotoneBar, {
value: isUnset ? undefined : value,
onChange: onChange
}), !disableCustomDuotone && createElement(ColorListPicker, {
labels: [__('Shadows'), __('Highlights')],
colors: colorPalette,
value: isUnset ? undefined : value,
disableCustomColors: disableCustomColors,
enableAlpha: true,
onChange: newColors => {
if (!newColors[0]) {
newColors[0] = defaultDark;
}
if (!newColors[1]) {
newColors[1] = defaultLight;
}
const newValue = newColors.length >= 2 ? newColors : undefined;
// @ts-expect-error TODO: The color arrays for a DuotonePicker should be a tuple of two colors,
// but it's currently typed as a string[].
// See also https://github.com/WordPress/gutenberg/pull/49060#discussion_r1136951035
onChange(newValue);
}
}))));
}
export default DuotonePicker;
//# sourceMappingURL=duotone-picker.js.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
import { createElement } from "react";
/**
* WordPress dependencies
*/
import { swatch } from '@wordpress/icons';
/**
* Internal dependencies
*/
import ColorIndicator from '../color-indicator';
import Icon from '../icon';
import { getGradientFromCSSColors } from './utils';
function DuotoneSwatch({
values
}) {
return values ? createElement(ColorIndicator, {
colorValue: getGradientFromCSSColors(values, '135deg')
}) : createElement(Icon, {
icon: swatch
});
}
export default DuotoneSwatch;
//# sourceMappingURL=duotone-swatch.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["swatch","ColorIndicator","Icon","getGradientFromCSSColors","DuotoneSwatch","values","createElement","colorValue","icon"],"sources":["@wordpress/components/src/duotone-picker/duotone-swatch.tsx"],"sourcesContent":["/**\n * WordPress dependencies\n */\nimport { swatch } from '@wordpress/icons';\n\n/**\n * Internal dependencies\n */\nimport ColorIndicator from '../color-indicator';\nimport Icon from '../icon';\nimport { getGradientFromCSSColors } from './utils';\nimport type { DuotoneSwatchProps } from './types';\n\nfunction DuotoneSwatch( { values }: DuotoneSwatchProps ) {\n\treturn values ? (\n\t\t<ColorIndicator\n\t\t\tcolorValue={ getGradientFromCSSColors( values, '135deg' ) }\n\t\t/>\n\t) : (\n\t\t<Icon icon={ swatch } />\n\t);\n}\n\nexport default DuotoneSwatch;\n"],"mappings":";AAAA;AACA;AACA;AACA,SAASA,MAAM,QAAQ,kBAAkB;;AAEzC;AACA;AACA;AACA,OAAOC,cAAc,MAAM,oBAAoB;AAC/C,OAAOC,IAAI,MAAM,SAAS;AAC1B,SAASC,wBAAwB,QAAQ,SAAS;AAGlD,SAASC,aAAaA,CAAE;EAAEC;AAA2B,CAAC,EAAG;EACxD,OAAOA,MAAM,GACZC,aAAA,CAACL,cAAc;IACdM,UAAU,EAAGJ,wBAAwB,CAAEE,MAAM,EAAE,QAAS;EAAG,CAC3D,CAAC,GAEFC,aAAA,CAACJ,IAAI;IAACM,IAAI,EAAGR;EAAQ,CAAE,CACvB;AACF;AAEA,eAAeI,aAAa"}

View File

@@ -0,0 +1,3 @@
export { default as DuotonePicker } from './duotone-picker';
export { default as DuotoneSwatch } from './duotone-swatch';
//# sourceMappingURL=index.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["default","DuotonePicker","DuotoneSwatch"],"sources":["@wordpress/components/src/duotone-picker/index.ts"],"sourcesContent":["export { default as DuotonePicker } from './duotone-picker';\nexport { default as DuotoneSwatch } from './duotone-swatch';\n"],"mappings":"AAAA,SAASA,OAAO,IAAIC,aAAa,QAAQ,kBAAkB;AAC3D,SAASD,OAAO,IAAIE,aAAa,QAAQ,kBAAkB"}

View File

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

View File

@@ -0,0 +1 @@
{"version":3,"names":[],"sources":["@wordpress/components/src/duotone-picker/types.ts"],"sourcesContent":["export type DuotonePickerProps = {\n\t/**\n\t * Whether there should be a button to clear the duotone value.\n\t *\n\t * @default true\n\t */\n\tclearable?: boolean;\n\t/**\n\t * Whether there should be an `unset` option.\n\t *\n\t * @default true\n\t */\n\tunsetable?: boolean;\n\t/**\n\t * Array of color presets of the form `{ color: '#000000', name: 'Black', slug: 'black' }`.\n\t */\n\tcolorPalette: Color[];\n\t/**\n\t * Array of duotone presets of the form `{ colors: [ '#000000', '#ffffff' ], name: 'Grayscale', slug: 'grayscale' }`.\n\t */\n\tduotonePalette: DuotoneColor[];\n\t/**\n\t * Whether custom colors should be disabled.\n\t *\n\t * @default false\n\t */\n\tdisableCustomColors?: boolean;\n\t/**\n\t * Whether custom duotone values should be disabled.\n\t *\n\t * @default false\n\t */\n\tdisableCustomDuotone?: boolean;\n\t/**\n\t * An array of colors for the duotone effect.\n\t */\n\tvalue?: string[] | 'unset';\n\t/**\n\t * Callback which is called when the duotone colors change.\n\t */\n\tonChange: ( value: DuotonePickerProps[ 'value' ] | undefined ) => void;\n\t/**\n\t * Whether the control should present as a set of buttons,\n\t * each with its own tab stop.\n\t *\n\t * @default false\n\t */\n\tasButtons?: boolean;\n\t/**\n\t * Prevents keyboard interaction from wrapping around.\n\t * Only used when `asButtons` is not true.\n\t *\n\t * @default true\n\t */\n\tloop?: boolean;\n} & (\n\t| {\n\t\t\t/**\n\t\t\t * A label to identify the purpose of the control.\n\t\t\t *\n\t\t\t * @todo [#54055] Either this or `aria-labelledby` should be required\n\t\t\t */\n\t\t\t'aria-label'?: string;\n\t\t\t'aria-labelledby'?: never;\n\t }\n\t| {\n\t\t\t/**\n\t\t\t * An ID of an element to provide a label for the control.\n\t\t\t *\n\t\t\t * @todo [#54055] Either this or `aria-label` should be required\n\t\t\t */\n\t\t\t'aria-labelledby'?: string;\n\t\t\t'aria-label'?: never;\n\t }\n);\n\ntype Color = {\n\tcolor: string;\n\tname: string;\n\tslug: string;\n};\n\ntype DuotoneColor = {\n\tcolors: string[];\n\tname: string;\n\tslug: string;\n};\n\nexport type DuotoneSwatchProps = {\n\t/**\n\t * An array of colors to show or `null` to show the placeholder swatch icon.\n\t */\n\tvalues?: string[] | null;\n};\n"],"mappings":""}

View File

@@ -0,0 +1,90 @@
/**
* External dependencies
*/
import { colord, extend } from 'colord';
import namesPlugin from 'colord/plugins/names';
/**
* Internal dependencies
*/
extend([namesPlugin]);
/**
* Object representation for a color.
*
* @typedef {Object} RGBColor
* @property {number} r Red component of the color in the range [0,1].
* @property {number} g Green component of the color in the range [0,1].
* @property {number} b Blue component of the color in the range [0,1].
*/
/**
* Calculate the brightest and darkest values from a color palette.
*
* @param palette Color palette for the theme.
*
* @return Tuple of the darkest color and brightest color.
*/
export function getDefaultColors(palette) {
// A default dark and light color are required.
if (!palette || palette.length < 2) return ['#000', '#fff'];
return palette.map(({
color
}) => ({
color,
brightness: colord(color).brightness()
})).reduce(([min, max], current) => {
return [current.brightness <= min.brightness ? current : min, current.brightness >= max.brightness ? current : max];
}, [{
brightness: 1,
color: ''
}, {
brightness: 0,
color: ''
}]).map(({
color
}) => color);
}
/**
* Generate a duotone gradient from a list of colors.
*
* @param colors CSS color strings.
* @param angle CSS gradient angle.
*
* @return CSS gradient string for the duotone swatch.
*/
export function getGradientFromCSSColors(colors = [], angle = '90deg') {
const l = 100 / colors.length;
const stops = colors.map((c, i) => `${c} ${i * l}%, ${c} ${(i + 1) * l}%`).join(', ');
return `linear-gradient( ${angle}, ${stops} )`;
}
/**
* Convert a color array to an array of color stops.
*
* @param colors CSS colors array
*
* @return Color stop information.
*/
export function getColorStopsFromColors(colors) {
return colors.map((color, i) => ({
position: i * 100 / (colors.length - 1),
color
}));
}
/**
* Convert a color stop array to an array colors.
*
* @param colorStops Color stop information.
*
* @return CSS colors array.
*/
export function getColorsFromColorStops(colorStops = []) {
return colorStops.map(({
color
}) => color);
}
//# sourceMappingURL=utils.js.map

View File

@@ -0,0 +1 @@
{"version":3,"names":["colord","extend","namesPlugin","getDefaultColors","palette","length","map","color","brightness","reduce","min","max","current","getGradientFromCSSColors","colors","angle","l","stops","c","i","join","getColorStopsFromColors","position","getColorsFromColorStops","colorStops"],"sources":["@wordpress/components/src/duotone-picker/utils.ts"],"sourcesContent":["/**\n * External dependencies\n */\nimport { colord, extend } from 'colord';\nimport namesPlugin from 'colord/plugins/names';\n\n/**\n * Internal dependencies\n */\nimport type { DuotonePickerProps } from './types';\n\nextend( [ namesPlugin ] );\n\n/**\n * Object representation for a color.\n *\n * @typedef {Object} RGBColor\n * @property {number} r Red component of the color in the range [0,1].\n * @property {number} g Green component of the color in the range [0,1].\n * @property {number} b Blue component of the color in the range [0,1].\n */\n\n/**\n * Calculate the brightest and darkest values from a color palette.\n *\n * @param palette Color palette for the theme.\n *\n * @return Tuple of the darkest color and brightest color.\n */\nexport function getDefaultColors(\n\tpalette: DuotonePickerProps[ 'colorPalette' ]\n) {\n\t// A default dark and light color are required.\n\tif ( ! palette || palette.length < 2 ) return [ '#000', '#fff' ];\n\n\treturn palette\n\t\t.map( ( { color } ) => ( {\n\t\t\tcolor,\n\t\t\tbrightness: colord( color ).brightness(),\n\t\t} ) )\n\t\t.reduce(\n\t\t\t( [ min, max ], current ) => {\n\t\t\t\treturn [\n\t\t\t\t\tcurrent.brightness <= min.brightness ? current : min,\n\t\t\t\t\tcurrent.brightness >= max.brightness ? current : max,\n\t\t\t\t];\n\t\t\t},\n\t\t\t[\n\t\t\t\t{ brightness: 1, color: '' },\n\t\t\t\t{ brightness: 0, color: '' },\n\t\t\t]\n\t\t)\n\t\t.map( ( { color } ) => color );\n}\n\n/**\n * Generate a duotone gradient from a list of colors.\n *\n * @param colors CSS color strings.\n * @param angle CSS gradient angle.\n *\n * @return CSS gradient string for the duotone swatch.\n */\nexport function getGradientFromCSSColors(\n\tcolors: string[] = [],\n\tangle = '90deg'\n) {\n\tconst l = 100 / colors.length;\n\n\tconst stops = colors\n\t\t.map( ( c, i ) => `${ c } ${ i * l }%, ${ c } ${ ( i + 1 ) * l }%` )\n\t\t.join( ', ' );\n\n\treturn `linear-gradient( ${ angle }, ${ stops } )`;\n}\n\n/**\n * Convert a color array to an array of color stops.\n *\n * @param colors CSS colors array\n *\n * @return Color stop information.\n */\nexport function getColorStopsFromColors( colors: string[] ) {\n\treturn colors.map( ( color, i ) => ( {\n\t\tposition: ( i * 100 ) / ( colors.length - 1 ),\n\t\tcolor,\n\t} ) );\n}\n\n/**\n * Convert a color stop array to an array colors.\n *\n * @param colorStops Color stop information.\n *\n * @return CSS colors array.\n */\nexport function getColorsFromColorStops(\n\tcolorStops: { position: number; color: string }[] = []\n) {\n\treturn colorStops.map( ( { color } ) => color );\n}\n"],"mappings":"AAAA;AACA;AACA;AACA,SAASA,MAAM,EAAEC,MAAM,QAAQ,QAAQ;AACvC,OAAOC,WAAW,MAAM,sBAAsB;;AAE9C;AACA;AACA;;AAGAD,MAAM,CAAE,CAAEC,WAAW,CAAG,CAAC;;AAEzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAC/BC,OAA6C,EAC5C;EACD;EACA,IAAK,CAAEA,OAAO,IAAIA,OAAO,CAACC,MAAM,GAAG,CAAC,EAAG,OAAO,CAAE,MAAM,EAAE,MAAM,CAAE;EAEhE,OAAOD,OAAO,CACZE,GAAG,CAAE,CAAE;IAAEC;EAAM,CAAC,MAAQ;IACxBA,KAAK;IACLC,UAAU,EAAER,MAAM,CAAEO,KAAM,CAAC,CAACC,UAAU,CAAC;EACxC,CAAC,CAAG,CAAC,CACJC,MAAM,CACN,CAAE,CAAEC,GAAG,EAAEC,GAAG,CAAE,EAAEC,OAAO,KAAM;IAC5B,OAAO,CACNA,OAAO,CAACJ,UAAU,IAAIE,GAAG,CAACF,UAAU,GAAGI,OAAO,GAAGF,GAAG,EACpDE,OAAO,CAACJ,UAAU,IAAIG,GAAG,CAACH,UAAU,GAAGI,OAAO,GAAGD,GAAG,CACpD;EACF,CAAC,EACD,CACC;IAAEH,UAAU,EAAE,CAAC;IAAED,KAAK,EAAE;EAAG,CAAC,EAC5B;IAAEC,UAAU,EAAE,CAAC;IAAED,KAAK,EAAE;EAAG,CAAC,CAE9B,CAAC,CACAD,GAAG,CAAE,CAAE;IAAEC;EAAM,CAAC,KAAMA,KAAM,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASM,wBAAwBA,CACvCC,MAAgB,GAAG,EAAE,EACrBC,KAAK,GAAG,OAAO,EACd;EACD,MAAMC,CAAC,GAAG,GAAG,GAAGF,MAAM,CAACT,MAAM;EAE7B,MAAMY,KAAK,GAAGH,MAAM,CAClBR,GAAG,CAAE,CAAEY,CAAC,EAAEC,CAAC,KAAO,GAAGD,CAAG,IAAIC,CAAC,GAAGH,CAAG,MAAME,CAAG,IAAI,CAAEC,CAAC,GAAG,CAAC,IAAKH,CAAG,GAAG,CAAC,CACnEI,IAAI,CAAE,IAAK,CAAC;EAEd,OAAQ,oBAAoBL,KAAO,KAAKE,KAAO,IAAG;AACnD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,uBAAuBA,CAAEP,MAAgB,EAAG;EAC3D,OAAOA,MAAM,CAACR,GAAG,CAAE,CAAEC,KAAK,EAAEY,CAAC,MAAQ;IACpCG,QAAQ,EAAIH,CAAC,GAAG,GAAG,IAAOL,MAAM,CAACT,MAAM,GAAG,CAAC,CAAE;IAC7CE;EACD,CAAC,CAAG,CAAC;AACN;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASgB,uBAAuBA,CACtCC,UAAiD,GAAG,EAAE,EACrD;EACD,OAAOA,UAAU,CAAClB,GAAG,CAAE,CAAE;IAAEC;EAAM,CAAC,KAAMA,KAAM,CAAC;AAChD"}