Files
formipay/node_modules/@wordpress/components/build-module/palette-edit/index.js
dwindown e8fbfb14c1 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>
2026-04-18 17:02:14 +07:00

395 lines
12 KiB
JavaScript

import { createElement, Fragment } from "react";
/**
* External dependencies
*/
import classnames from 'classnames';
/**
* WordPress dependencies
*/
import { useState, useRef, useEffect, useCallback, useMemo } from '@wordpress/element';
import { __, sprintf } from '@wordpress/i18n';
import { lineSolid, moreVertical, plus } from '@wordpress/icons';
import { __experimentalUseFocusOutside as useFocusOutside, useDebounce } from '@wordpress/compose';
/**
* Internal dependencies
*/
import Button from '../button';
import { ColorPicker } from '../color-picker';
import { FlexItem } from '../flex';
import { HStack } from '../h-stack';
import { ItemGroup } from '../item-group';
import { VStack } from '../v-stack';
import GradientPicker from '../gradient-picker';
import ColorPalette from '../color-palette';
import DropdownMenu from '../dropdown-menu';
import Popover from '../popover';
import { PaletteActionsContainer, PaletteEditStyles, PaletteHeading, PaletteHStackHeader, IndicatorStyled, PaletteItem, NameContainer, NameInputControl, DoneButton, RemoveButton } from './styles';
import { NavigableMenu } from '../navigable-container';
import { DEFAULT_GRADIENT } from '../custom-gradient-picker/constants';
import CustomGradientPicker from '../custom-gradient-picker';
import { kebabCase } from '../utils/strings';
const DEFAULT_COLOR = '#000';
function NameInput({
value,
onChange,
label
}) {
return createElement(NameInputControl, {
label: label,
hideLabelFromVision: true,
value: value,
onChange: onChange
});
}
/**
* Returns a name for a palette item in the format "Color + id".
* To ensure there are no duplicate ids, this function checks all slugs.
* It expects slugs to be in the format: slugPrefix + color- + number.
* It then sets the id component of the new name based on the incremented id of the highest existing slug id.
*
* @param elements An array of color palette items.
* @param slugPrefix The slug prefix used to match the element slug.
*
* @return A unique name for a palette item.
*/
export function getNameForPosition(elements, slugPrefix) {
const nameRegex = new RegExp(`^${slugPrefix}color-([\\d]+)$`);
const position = elements.reduce((previousValue, currentValue) => {
if (typeof currentValue?.slug === 'string') {
const matches = currentValue?.slug.match(nameRegex);
if (matches) {
const id = parseInt(matches[1], 10);
if (id >= previousValue) {
return id + 1;
}
}
}
return previousValue;
}, 1);
return sprintf( /* translators: %s: is an id for a custom color */
__('Color %s'), position);
}
function ColorPickerPopover({
isGradient,
element,
onChange,
popoverProps: receivedPopoverProps,
onClose = () => {}
}) {
const popoverProps = useMemo(() => ({
shift: true,
offset: 20,
// Disabling resize as it would otherwise cause the popover to show
// scrollbars while dragging the color picker's handle close to the
// popover edge.
resize: false,
placement: 'left-start',
...receivedPopoverProps,
className: classnames('components-palette-edit__popover', receivedPopoverProps?.className)
}), [receivedPopoverProps]);
return createElement(Popover, {
...popoverProps,
onClose: onClose
}, !isGradient && createElement(ColorPicker, {
color: element.color,
enableAlpha: true,
onChange: newColor => {
onChange({
...element,
color: newColor
});
}
}), isGradient && createElement("div", {
className: "components-palette-edit__popover-gradient-picker"
}, createElement(CustomGradientPicker, {
__nextHasNoMargin: true,
__experimentalIsRenderedInSidebar: true,
value: element.gradient,
onChange: newGradient => {
onChange({
...element,
gradient: newGradient
});
}
})));
}
function Option({
canOnlyChangeValues,
element,
onChange,
isEditing,
onStartEditing,
onRemove,
onStopEditing,
popoverProps: receivedPopoverProps,
slugPrefix,
isGradient
}) {
const focusOutsideProps = useFocusOutside(onStopEditing);
const value = isGradient ? element.gradient : element.color;
// Use internal state instead of a ref to make sure that the component
// re-renders when the popover's anchor updates.
const [popoverAnchor, setPopoverAnchor] = useState(null);
const popoverProps = useMemo(() => ({
...receivedPopoverProps,
// Use the custom palette color item as the popover anchor.
anchor: popoverAnchor
}), [popoverAnchor, receivedPopoverProps]);
return createElement(PaletteItem, {
className: isEditing ? 'is-selected' : undefined,
as: "div",
onClick: onStartEditing,
ref: setPopoverAnchor,
...(isEditing ? {
...focusOutsideProps
} : {
style: {
cursor: 'pointer'
}
})
}, createElement(HStack, {
justify: "flex-start"
}, createElement(FlexItem, null, createElement(IndicatorStyled, {
style: {
background: value,
color: 'transparent'
}
})), createElement(FlexItem, null, isEditing && !canOnlyChangeValues ? createElement(NameInput, {
label: isGradient ? __('Gradient name') : __('Color name'),
value: element.name,
onChange: nextName => onChange({
...element,
name: nextName,
slug: slugPrefix + kebabCase(nextName !== null && nextName !== void 0 ? nextName : '')
})
}) : createElement(NameContainer, null, element.name)), isEditing && !canOnlyChangeValues && createElement(FlexItem, null, createElement(RemoveButton, {
size: "small",
icon: lineSolid,
label: __('Remove color'),
onClick: onRemove
}))), isEditing && createElement(ColorPickerPopover, {
isGradient: isGradient,
onChange: onChange,
element: element,
popoverProps: popoverProps
}));
}
function PaletteEditListView({
elements,
onChange,
editingElement,
setEditingElement,
canOnlyChangeValues,
slugPrefix,
isGradient,
popoverProps
}) {
// When unmounting the component if there are empty elements (the user did not complete the insertion) clean them.
const elementsReference = useRef();
useEffect(() => {
elementsReference.current = elements;
}, [elements]);
const debounceOnChange = useDebounce(onChange, 100);
return createElement(VStack, {
spacing: 3
}, createElement(ItemGroup, {
isRounded: true
}, elements.map((element, index) => createElement(Option, {
isGradient: isGradient,
canOnlyChangeValues: canOnlyChangeValues,
key: index,
element: element,
onStartEditing: () => {
if (editingElement !== index) {
setEditingElement(index);
}
},
onChange: newElement => {
debounceOnChange(elements.map((currentElement, currentIndex) => {
if (currentIndex === index) {
return newElement;
}
return currentElement;
}));
},
onRemove: () => {
setEditingElement(null);
const newElements = elements.filter((_currentElement, currentIndex) => {
if (currentIndex === index) {
return false;
}
return true;
});
onChange(newElements.length ? newElements : undefined);
},
isEditing: index === editingElement,
onStopEditing: () => {
if (index === editingElement) {
setEditingElement(null);
}
},
slugPrefix: slugPrefix,
popoverProps: popoverProps
}))));
}
const EMPTY_ARRAY = [];
/**
* Allows editing a palette of colors or gradients.
*
* ```jsx
* import { PaletteEdit } from '@wordpress/components';
* const MyPaletteEdit = () => {
* const [ controlledColors, setControlledColors ] = useState( colors );
*
* return (
* <PaletteEdit
* colors={ controlledColors }
* onChange={ ( newColors?: Color[] ) => {
* setControlledColors( newColors );
* } }
* paletteLabel="Here is a label"
* />
* );
* };
* ```
*/
export function PaletteEdit({
gradients,
colors = EMPTY_ARRAY,
onChange,
paletteLabel,
paletteLabelHeadingLevel = 2,
emptyMessage,
canOnlyChangeValues,
canReset,
slugPrefix = '',
popoverProps
}) {
const isGradient = !!gradients;
const elements = isGradient ? gradients : colors;
const [isEditing, setIsEditing] = useState(false);
const [editingElement, setEditingElement] = useState(null);
const isAdding = isEditing && !!editingElement && elements[editingElement] && !elements[editingElement].slug;
const elementsLength = elements.length;
const hasElements = elementsLength > 0;
const debounceOnChange = useDebounce(onChange, 100);
const onSelectPaletteItem = useCallback((value, newEditingElementIndex) => {
const selectedElement = newEditingElementIndex === undefined ? undefined : elements[newEditingElementIndex];
const key = isGradient ? 'gradient' : 'color';
// Ensures that the index returned matches a known element value.
if (!!selectedElement && selectedElement[key] === value) {
setEditingElement(newEditingElementIndex);
} else {
setIsEditing(true);
}
}, [isGradient, elements]);
return createElement(PaletteEditStyles, null, createElement(PaletteHStackHeader, null, createElement(PaletteHeading, {
level: paletteLabelHeadingLevel
}, paletteLabel), createElement(PaletteActionsContainer, null, hasElements && isEditing && createElement(DoneButton, {
size: "small",
onClick: () => {
setIsEditing(false);
setEditingElement(null);
}
}, __('Done')), !canOnlyChangeValues && createElement(Button, {
size: "small",
isPressed: isAdding,
icon: plus,
label: isGradient ? __('Add gradient') : __('Add color'),
onClick: () => {
const optionName = getNameForPosition(elements, slugPrefix);
if (!!gradients) {
onChange([...gradients, {
gradient: DEFAULT_GRADIENT,
name: optionName,
slug: slugPrefix + kebabCase(optionName)
}]);
} else {
onChange([...colors, {
color: DEFAULT_COLOR,
name: optionName,
slug: slugPrefix + kebabCase(optionName)
}]);
}
setIsEditing(true);
setEditingElement(elements.length);
}
}), hasElements && (!isEditing || !canOnlyChangeValues || canReset) && createElement(DropdownMenu, {
icon: moreVertical,
label: isGradient ? __('Gradient options') : __('Color options'),
toggleProps: {
isSmall: true
}
}, ({
onClose
}) => createElement(Fragment, null, createElement(NavigableMenu, {
role: "menu"
}, !isEditing && createElement(Button, {
variant: "tertiary",
onClick: () => {
setIsEditing(true);
onClose();
},
className: "components-palette-edit__menu-button"
}, __('Show details')), !canOnlyChangeValues && createElement(Button, {
variant: "tertiary",
onClick: () => {
setEditingElement(null);
setIsEditing(false);
onChange();
onClose();
},
className: "components-palette-edit__menu-button"
}, isGradient ? __('Remove all gradients') : __('Remove all colors')), canReset && createElement(Button, {
variant: "tertiary",
onClick: () => {
setEditingElement(null);
onChange();
onClose();
}
}, isGradient ? __('Reset gradient') : __('Reset colors'))))))), hasElements && createElement(Fragment, null, isEditing && createElement(PaletteEditListView, {
canOnlyChangeValues: canOnlyChangeValues,
elements: elements
// @ts-expect-error TODO: Don't know how to resolve
,
onChange: onChange,
editingElement: editingElement,
setEditingElement: setEditingElement,
slugPrefix: slugPrefix,
isGradient: isGradient,
popoverProps: popoverProps
}), !isEditing && editingElement !== null && createElement(ColorPickerPopover, {
isGradient: isGradient,
onClose: () => setEditingElement(null),
onChange: newElement => {
debounceOnChange(
// @ts-expect-error TODO: Don't know how to resolve
elements.map((currentElement, currentIndex) => {
if (currentIndex === editingElement) {
return newElement;
}
return currentElement;
}));
},
element: elements[editingElement !== null && editingElement !== void 0 ? editingElement : -1],
popoverProps: popoverProps
}), !isEditing && (isGradient ? createElement(GradientPicker, {
__nextHasNoMargin: true,
gradients: gradients,
onChange: onSelectPaletteItem,
clearable: false,
disableCustomGradients: true
}) : createElement(ColorPalette, {
colors: colors,
onChange: onSelectPaletteItem,
clearable: false,
disableCustomColors: true
}))), !hasElements && emptyMessage);
}
export default PaletteEdit;
//# sourceMappingURL=index.js.map