"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.PaletteEdit = PaletteEdit; exports.default = void 0; exports.getNameForPosition = getNameForPosition; var _react = require("react"); var _classnames = _interopRequireDefault(require("classnames")); var _element = require("@wordpress/element"); var _i18n = require("@wordpress/i18n"); var _icons = require("@wordpress/icons"); var _compose = require("@wordpress/compose"); var _button = _interopRequireDefault(require("../button")); var _colorPicker = require("../color-picker"); var _flex = require("../flex"); var _hStack = require("../h-stack"); var _itemGroup = require("../item-group"); var _vStack = require("../v-stack"); var _gradientPicker = _interopRequireDefault(require("../gradient-picker")); var _colorPalette = _interopRequireDefault(require("../color-palette")); var _dropdownMenu = _interopRequireDefault(require("../dropdown-menu")); var _popover = _interopRequireDefault(require("../popover")); var _styles = require("./styles"); var _navigableContainer = require("../navigable-container"); var _constants = require("../custom-gradient-picker/constants"); var _customGradientPicker = _interopRequireDefault(require("../custom-gradient-picker")); var _strings = require("../utils/strings"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const DEFAULT_COLOR = '#000'; function NameInput({ value, onChange, label }) { return (0, _react.createElement)(_styles.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. */ 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 (0, _i18n.sprintf)( /* translators: %s: is an id for a custom color */ (0, _i18n.__)('Color %s'), position); } function ColorPickerPopover({ isGradient, element, onChange, popoverProps: receivedPopoverProps, onClose = () => {} }) { const popoverProps = (0, _element.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: (0, _classnames.default)('components-palette-edit__popover', receivedPopoverProps?.className) }), [receivedPopoverProps]); return (0, _react.createElement)(_popover.default, { ...popoverProps, onClose: onClose }, !isGradient && (0, _react.createElement)(_colorPicker.ColorPicker, { color: element.color, enableAlpha: true, onChange: newColor => { onChange({ ...element, color: newColor }); } }), isGradient && (0, _react.createElement)("div", { className: "components-palette-edit__popover-gradient-picker" }, (0, _react.createElement)(_customGradientPicker.default, { __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 = (0, _compose.__experimentalUseFocusOutside)(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] = (0, _element.useState)(null); const popoverProps = (0, _element.useMemo)(() => ({ ...receivedPopoverProps, // Use the custom palette color item as the popover anchor. anchor: popoverAnchor }), [popoverAnchor, receivedPopoverProps]); return (0, _react.createElement)(_styles.PaletteItem, { className: isEditing ? 'is-selected' : undefined, as: "div", onClick: onStartEditing, ref: setPopoverAnchor, ...(isEditing ? { ...focusOutsideProps } : { style: { cursor: 'pointer' } }) }, (0, _react.createElement)(_hStack.HStack, { justify: "flex-start" }, (0, _react.createElement)(_flex.FlexItem, null, (0, _react.createElement)(_styles.IndicatorStyled, { style: { background: value, color: 'transparent' } })), (0, _react.createElement)(_flex.FlexItem, null, isEditing && !canOnlyChangeValues ? (0, _react.createElement)(NameInput, { label: isGradient ? (0, _i18n.__)('Gradient name') : (0, _i18n.__)('Color name'), value: element.name, onChange: nextName => onChange({ ...element, name: nextName, slug: slugPrefix + (0, _strings.kebabCase)(nextName !== null && nextName !== void 0 ? nextName : '') }) }) : (0, _react.createElement)(_styles.NameContainer, null, element.name)), isEditing && !canOnlyChangeValues && (0, _react.createElement)(_flex.FlexItem, null, (0, _react.createElement)(_styles.RemoveButton, { size: "small", icon: _icons.lineSolid, label: (0, _i18n.__)('Remove color'), onClick: onRemove }))), isEditing && (0, _react.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 = (0, _element.useRef)(); (0, _element.useEffect)(() => { elementsReference.current = elements; }, [elements]); const debounceOnChange = (0, _compose.useDebounce)(onChange, 100); return (0, _react.createElement)(_vStack.VStack, { spacing: 3 }, (0, _react.createElement)(_itemGroup.ItemGroup, { isRounded: true }, elements.map((element, index) => (0, _react.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 ( * { * setControlledColors( newColors ); * } } * paletteLabel="Here is a label" * /> * ); * }; * ``` */ 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] = (0, _element.useState)(false); const [editingElement, setEditingElement] = (0, _element.useState)(null); const isAdding = isEditing && !!editingElement && elements[editingElement] && !elements[editingElement].slug; const elementsLength = elements.length; const hasElements = elementsLength > 0; const debounceOnChange = (0, _compose.useDebounce)(onChange, 100); const onSelectPaletteItem = (0, _element.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 (0, _react.createElement)(_styles.PaletteEditStyles, null, (0, _react.createElement)(_styles.PaletteHStackHeader, null, (0, _react.createElement)(_styles.PaletteHeading, { level: paletteLabelHeadingLevel }, paletteLabel), (0, _react.createElement)(_styles.PaletteActionsContainer, null, hasElements && isEditing && (0, _react.createElement)(_styles.DoneButton, { size: "small", onClick: () => { setIsEditing(false); setEditingElement(null); } }, (0, _i18n.__)('Done')), !canOnlyChangeValues && (0, _react.createElement)(_button.default, { size: "small", isPressed: isAdding, icon: _icons.plus, label: isGradient ? (0, _i18n.__)('Add gradient') : (0, _i18n.__)('Add color'), onClick: () => { const optionName = getNameForPosition(elements, slugPrefix); if (!!gradients) { onChange([...gradients, { gradient: _constants.DEFAULT_GRADIENT, name: optionName, slug: slugPrefix + (0, _strings.kebabCase)(optionName) }]); } else { onChange([...colors, { color: DEFAULT_COLOR, name: optionName, slug: slugPrefix + (0, _strings.kebabCase)(optionName) }]); } setIsEditing(true); setEditingElement(elements.length); } }), hasElements && (!isEditing || !canOnlyChangeValues || canReset) && (0, _react.createElement)(_dropdownMenu.default, { icon: _icons.moreVertical, label: isGradient ? (0, _i18n.__)('Gradient options') : (0, _i18n.__)('Color options'), toggleProps: { isSmall: true } }, ({ onClose }) => (0, _react.createElement)(_react.Fragment, null, (0, _react.createElement)(_navigableContainer.NavigableMenu, { role: "menu" }, !isEditing && (0, _react.createElement)(_button.default, { variant: "tertiary", onClick: () => { setIsEditing(true); onClose(); }, className: "components-palette-edit__menu-button" }, (0, _i18n.__)('Show details')), !canOnlyChangeValues && (0, _react.createElement)(_button.default, { variant: "tertiary", onClick: () => { setEditingElement(null); setIsEditing(false); onChange(); onClose(); }, className: "components-palette-edit__menu-button" }, isGradient ? (0, _i18n.__)('Remove all gradients') : (0, _i18n.__)('Remove all colors')), canReset && (0, _react.createElement)(_button.default, { variant: "tertiary", onClick: () => { setEditingElement(null); onChange(); onClose(); } }, isGradient ? (0, _i18n.__)('Reset gradient') : (0, _i18n.__)('Reset colors'))))))), hasElements && (0, _react.createElement)(_react.Fragment, null, isEditing && (0, _react.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 && (0, _react.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 ? (0, _react.createElement)(_gradientPicker.default, { __nextHasNoMargin: true, gradients: gradients, onChange: onSelectPaletteItem, clearable: false, disableCustomGradients: true }) : (0, _react.createElement)(_colorPalette.default, { colors: colors, onChange: onSelectPaletteItem, clearable: false, disableCustomColors: true }))), !hasElements && emptyMessage); } var _default = PaletteEdit; exports.default = _default; //# sourceMappingURL=index.js.map