"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.PaletteEdit = PaletteEdit;
exports.deduplicateElementSlugs = deduplicateElementSlugs;
exports.default = void 0;
exports.getNameAndSlugForPosition = getNameAndSlugForPosition;
var _clsx = _interopRequireDefault(require("clsx"));
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");
var _jsxRuntime = require("react/jsx-runtime");
/**
 * External dependencies
 */

/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

const DEFAULT_COLOR = '#000';
function NameInput({
  value,
  onChange,
  label
}) {
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.NameInputControl, {
    label: label,
    hideLabelFromVision: true,
    value: value,
    onChange: onChange
  });
}

/*
 * Deduplicates the slugs of the provided elements.
 */
function deduplicateElementSlugs(elements) {
  const slugCounts = {};
  return elements.map(element => {
    var _newSlug;
    let newSlug;
    const {
      slug
    } = element;
    slugCounts[slug] = (slugCounts[slug] || 0) + 1;
    if (slugCounts[slug] > 1) {
      newSlug = `${slug}-${slugCounts[slug] - 1}`;
    }
    return {
      ...element,
      slug: (_newSlug = newSlug) !== null && _newSlug !== void 0 ? _newSlug : slug
    };
  });
}

/**
 * Returns a name and slug for a palette item. The name takes 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 name and slug for the new palette item.
 */
function getNameAndSlugForPosition(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 {
    name: (0, _i18n.sprintf)( /* translators: %s: is an id for a custom color */
    (0, _i18n.__)('Color %s'), position),
    slug: `${slugPrefix}color-${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, _clsx.default)('components-palette-edit__popover', receivedPopoverProps?.className)
  }), [receivedPopoverProps]);
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_popover.default, {
    ...popoverProps,
    onClose: onClose,
    children: [!isGradient && /*#__PURE__*/(0, _jsxRuntime.jsx)(_colorPicker.ColorPicker, {
      color: element.color,
      enableAlpha: true,
      onChange: newColor => {
        onChange({
          ...element,
          color: newColor
        });
      }
    }), isGradient && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
      className: "components-palette-edit__popover-gradient-picker",
      children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_customGradientPicker.default, {
        __experimentalIsRenderedInSidebar: true,
        value: element.gradient,
        onChange: newGradient => {
          onChange({
            ...element,
            gradient: newGradient
          });
        }
      })
    })]
  });
}
function Option({
  canOnlyChangeValues,
  element,
  onChange,
  onRemove,
  popoverProps: receivedPopoverProps,
  slugPrefix,
  isGradient
}) {
  const value = isGradient ? element.gradient : element.color;
  const [isEditingColor, setIsEditingColor] = (0, _element.useState)(false);

  // 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 /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.PaletteItem, {
    ref: setPopoverAnchor,
    as: "div",
    children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_hStack.HStack, {
      justify: "flex-start",
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_button.default, {
        onClick: () => {
          setIsEditingColor(true);
        },
        "aria-label": (0, _i18n.sprintf)(
        // translators: %s is a color or gradient name, e.g. "Red".
        (0, _i18n.__)('Edit: %s'), element.name.trim().length ? element.name : value),
        style: {
          padding: 0
        },
        children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.IndicatorStyled, {
          colorValue: value
        })
      }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_flex.FlexItem, {
        children: !canOnlyChangeValues ? /*#__PURE__*/(0, _jsxRuntime.jsx)(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 : '')
          })
        }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.NameContainer, {
          children: element.name.trim().length ? element.name : /* Fall back to non-breaking space to maintain height */
          '\u00A0'
        })
      }), !canOnlyChangeValues && /*#__PURE__*/(0, _jsxRuntime.jsx)(_flex.FlexItem, {
        children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.RemoveButton, {
          size: "small",
          icon: _icons.lineSolid,
          label: (0, _i18n.sprintf)(
          // translators: %s is a color or gradient name, e.g. "Red".
          (0, _i18n.__)('Remove color: %s'), element.name.trim().length ? element.name : value),
          onClick: onRemove
        })
      })]
    }), isEditingColor && /*#__PURE__*/(0, _jsxRuntime.jsx)(ColorPickerPopover, {
      isGradient: isGradient,
      onChange: onChange,
      element: element,
      popoverProps: popoverProps,
      onClose: () => setIsEditingColor(false)
    })]
  });
}
function PaletteEditListView({
  elements,
  onChange,
  canOnlyChangeValues,
  slugPrefix,
  isGradient,
  popoverProps,
  addColorRef
}) {
  // When unmounting the component if there are empty elements (the user did not complete the insertion) clean them.
  const elementsReferenceRef = (0, _element.useRef)();
  (0, _element.useEffect)(() => {
    elementsReferenceRef.current = elements;
  }, [elements]);
  const debounceOnChange = (0, _compose.useDebounce)(updatedElements => onChange(deduplicateElementSlugs(updatedElements)), 100);
  return /*#__PURE__*/(0, _jsxRuntime.jsx)(_vStack.VStack, {
    spacing: 3,
    children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_itemGroup.ItemGroup, {
      isRounded: true,
      children: elements.map((element, index) => /*#__PURE__*/(0, _jsxRuntime.jsx)(Option, {
        isGradient: isGradient,
        canOnlyChangeValues: canOnlyChangeValues,
        element: element,
        onChange: newElement => {
          debounceOnChange(elements.map((currentElement, currentIndex) => {
            if (currentIndex === index) {
              return newElement;
            }
            return currentElement;
          }));
        },
        onRemove: () => {
          const newElements = elements.filter((_currentElement, currentIndex) => {
            if (currentIndex === index) {
              return false;
            }
            return true;
          });
          onChange(newElements.length ? newElements : undefined);
          addColorRef.current?.focus();
        },
        slugPrefix: slugPrefix,
        popoverProps: popoverProps
      }, index))
    })
  });
}
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"
 *     />
 *   );
 * };
 * ```
 */
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]);
  const addColorRef = (0, _element.useRef)(null);
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.PaletteEditStyles, {
    children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_hStack.HStack, {
      children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.PaletteHeading, {
        level: paletteLabelHeadingLevel,
        children: paletteLabel
      }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.PaletteActionsContainer, {
        children: [hasElements && isEditing && /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.DoneButton, {
          size: "small",
          onClick: () => {
            setIsEditing(false);
            setEditingElement(null);
          },
          children: (0, _i18n.__)('Done')
        }), !canOnlyChangeValues && /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.default, {
          ref: addColorRef,
          size: "small",
          isPressed: isAdding,
          icon: _icons.plus,
          label: isGradient ? (0, _i18n.__)('Add gradient') : (0, _i18n.__)('Add color'),
          onClick: () => {
            const {
              name,
              slug
            } = getNameAndSlugForPosition(elements, slugPrefix);
            if (!!gradients) {
              onChange([...gradients, {
                gradient: _constants.DEFAULT_GRADIENT,
                name,
                slug
              }]);
            } else {
              onChange([...colors, {
                color: DEFAULT_COLOR,
                name,
                slug
              }]);
            }
            setIsEditing(true);
            setEditingElement(elements.length);
          }
        }), hasElements && (!isEditing || !canOnlyChangeValues || canReset) && /*#__PURE__*/(0, _jsxRuntime.jsx)(_dropdownMenu.default, {
          icon: _icons.moreVertical,
          label: isGradient ? (0, _i18n.__)('Gradient options') : (0, _i18n.__)('Color options'),
          toggleProps: {
            size: 'small'
          },
          children: ({
            onClose
          }) => /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
            children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_navigableContainer.NavigableMenu, {
              role: "menu",
              children: [!isEditing && /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.default, {
                variant: "tertiary",
                onClick: () => {
                  setIsEditing(true);
                  onClose();
                },
                className: "components-palette-edit__menu-button",
                children: (0, _i18n.__)('Show details')
              }), !canOnlyChangeValues && /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.default, {
                variant: "tertiary",
                onClick: () => {
                  setEditingElement(null);
                  setIsEditing(false);
                  onChange();
                  onClose();
                },
                className: "components-palette-edit__menu-button",
                children: isGradient ? (0, _i18n.__)('Remove all gradients') : (0, _i18n.__)('Remove all colors')
              }), canReset && /*#__PURE__*/(0, _jsxRuntime.jsx)(_button.default, {
                variant: "tertiary",
                onClick: () => {
                  setEditingElement(null);
                  onChange();
                  onClose();
                },
                children: isGradient ? (0, _i18n.__)('Reset gradient') : (0, _i18n.__)('Reset colors')
              })]
            })
          })
        })]
      })]
    }), hasElements && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_styles.PaletteEditContents, {
      children: [isEditing && /*#__PURE__*/(0, _jsxRuntime.jsx)(PaletteEditListView, {
        canOnlyChangeValues: canOnlyChangeValues,
        elements: elements
        // @ts-expect-error TODO: Don't know how to resolve
        ,
        onChange: onChange,
        slugPrefix: slugPrefix,
        isGradient: isGradient,
        popoverProps: popoverProps,
        addColorRef: addColorRef
      }), !isEditing && editingElement !== null && /*#__PURE__*/(0, _jsxRuntime.jsx)(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 ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_gradientPicker.default, {
        gradients: gradients,
        onChange: onSelectPaletteItem,
        clearable: false,
        disableCustomGradients: true
      }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_colorPalette.default, {
        colors: colors,
        onChange: onSelectPaletteItem,
        clearable: false,
        disableCustomColors: true
      }))]
    }), !hasElements && emptyMessage && /*#__PURE__*/(0, _jsxRuntime.jsx)(_styles.PaletteEditContents, {
      children: emptyMessage
    })]
  });
}
var _default = exports.default = PaletteEdit;
//# sourceMappingURL=index.js.map