"use strict";

var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.DEFAULT_COLOR = void 0;
exports.PaletteEdit = PaletteEdit;
exports.default = void 0;
exports.getNameForPosition = getNameForPosition;
exports.isTemporaryElement = isTemporaryElement;
var _react = require("react");
var _classnames = _interopRequireDefault(require("classnames"));
var _changeCase = require("change-case");
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"));
/**
 * External dependencies
 */

/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

const DEFAULT_COLOR = '#000';
exports.DEFAULT_COLOR = DEFAULT_COLOR;
function NameInput({
  value,
  onChange,
  label
}) {
  return (0, _react.createElement)(_styles.NameInputControl, {
    label: label,
    hideLabelFromVision: true,
    value: value,
    onChange: onChange
  });
}

/**
 * Returns a temporary name for a palette item in the format "Color + id".
 * To ensure there are no duplicate ids, this function checks all slugs for temporary names.
 * 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 temporaryNameRegex = new RegExp(`^${slugPrefix}color-([\\d]+)$`);
  const position = elements.reduce((previousValue, currentValue) => {
    if (typeof currentValue?.slug === 'string') {
      const matches = currentValue?.slug.match(temporaryNameRegex);
      if (matches) {
        const id = parseInt(matches[1], 10);
        if (id >= previousValue) {
          return id + 1;
        }
      }
    }
    return previousValue;
  }, 1);
  return (0, _i18n.sprintf)( /* translators: %s: is a temporary 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, _changeCase.paramCase)(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, {
    isSmall: true,
    icon: _icons.lineSolid,
    label: (0, _i18n.__)('Remove color'),
    onClick: onRemove
  }))), isEditing && (0, _react.createElement)(ColorPickerPopover, {
    isGradient: isGradient,
    onChange: onChange,
    element: element,
    popoverProps: popoverProps
  }));
}

/**
 * Checks if a color or gradient is a temporary element by testing against default values.
 */
function isTemporaryElement(slugPrefix, {
  slug,
  color,
  gradient
}) {
  const regex = new RegExp(`^${slugPrefix}color-([\\d]+)$`);

  // If the slug matches the temporary name regex,
  // check if the color or gradient matches the default value.
  if (regex.test(slug)) {
    // The order is important as gradient elements
    // contain a color property.
    if (!!gradient) {
      return gradient === _constants.DEFAULT_GRADIENT;
    }
    if (!!color) {
      return color === DEFAULT_COLOR;
    }
  }
  return false;
}
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]);
  (0, _element.useEffect)(() => {
    return () => {
      if (elementsReference.current?.some(element => isTemporaryElement(slugPrefix, element))) {
        const newElements = elementsReference.current.filter(element => !isTemporaryElement(slugPrefix, element));
        onChange(newElements.length ? newElements : undefined);
      }
    };
    // Disable reason: adding the missing dependency here would cause breaking changes that will require
    // a heavier refactor to avoid. See https://github.com/WordPress/gutenberg/pull/43911
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
  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 (
 *     <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]);
  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, {
    isSmall: true,
    onClick: () => {
      setIsEditing(false);
      setEditingElement(null);
    }
  }, (0, _i18n.__)('Done')), !canOnlyChangeValues && (0, _react.createElement)(_button.default, {
    isSmall: true,
    isPressed: isAdding,
    icon: _icons.plus,
    label: isGradient ? (0, _i18n.__)('Add gradient') : (0, _i18n.__)('Add color'),
    onClick: () => {
      const tempOptionName = getNameForPosition(elements, slugPrefix);
      if (!!gradients) {
        onChange([...gradients, {
          gradient: _constants.DEFAULT_GRADIENT,
          name: tempOptionName,
          slug: slugPrefix + (0, _changeCase.paramCase)(tempOptionName)
        }]);
      } else {
        onChange([...colors, {
          color: DEFAULT_COLOR,
          name: tempOptionName,
          slug: slugPrefix + (0, _changeCase.paramCase)(tempOptionName)
        }]);
      }
      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