"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.useToolsPanel = useToolsPanel;
var _element = require("@wordpress/element");
var styles = _interopRequireWildcard(require("../styles"));
var _context = require("../../context");
var _useCx = require("../../utils/hooks/use-cx");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
/**
 * WordPress dependencies
 */

/**
 * Internal dependencies
 */

const DEFAULT_COLUMNS = 2;
const generateMenuItems = ({
  panelItems,
  shouldReset,
  currentMenuItems,
  menuItemOrder
}) => {
  const newMenuItems = {
    default: {},
    optional: {}
  };
  const menuItems = {
    default: {},
    optional: {}
  };
  panelItems.forEach(({
    hasValue,
    isShownByDefault,
    label
  }) => {
    const group = isShownByDefault ? 'default' : 'optional';

    // If a menu item for this label has already been flagged as customized
    // (for default controls), or toggled on (for optional controls), do not
    // overwrite its value as those controls would lose that state.
    const existingItemValue = currentMenuItems?.[group]?.[label];
    const value = existingItemValue ? existingItemValue : hasValue();
    newMenuItems[group][label] = shouldReset ? false : value;
  });

  // Loop the known, previously registered items first to maintain menu order.
  menuItemOrder.forEach(key => {
    if (newMenuItems.default.hasOwnProperty(key)) {
      menuItems.default[key] = newMenuItems.default[key];
    }
    if (newMenuItems.optional.hasOwnProperty(key)) {
      menuItems.optional[key] = newMenuItems.optional[key];
    }
  });

  // Loop newMenuItems object adding any that aren't in the known items order.
  Object.keys(newMenuItems.default).forEach(key => {
    if (!menuItems.default.hasOwnProperty(key)) {
      menuItems.default[key] = newMenuItems.default[key];
    }
  });
  Object.keys(newMenuItems.optional).forEach(key => {
    if (!menuItems.optional.hasOwnProperty(key)) {
      menuItems.optional[key] = newMenuItems.optional[key];
    }
  });
  return menuItems;
};
const isMenuItemTypeEmpty = obj => obj && Object.keys(obj).length === 0;
function useToolsPanel(props) {
  const {
    className,
    headingLevel = 2,
    resetAll,
    panelId,
    hasInnerWrapper = false,
    shouldRenderPlaceholderItems = false,
    __experimentalFirstVisibleItemClass,
    __experimentalLastVisibleItemClass,
    ...otherProps
  } = (0, _context.useContextSystem)(props, 'ToolsPanel');
  const isResettingRef = (0, _element.useRef)(false);
  const wasResetting = isResettingRef.current;

  // `isResettingRef` is cleared via this hook to effectively batch together
  // the resetAll task. Without this, the flag is cleared after the first
  // control updates and forces a rerender with subsequent controls then
  // believing they need to reset, unfortunately using stale data.
  (0, _element.useEffect)(() => {
    if (wasResetting) {
      isResettingRef.current = false;
    }
  }, [wasResetting]);

  // Allow panel items to register themselves.
  const [panelItems, setPanelItems] = (0, _element.useState)([]);
  const [menuItemOrder, setMenuItemOrder] = (0, _element.useState)([]);
  const [resetAllFilters, setResetAllFilters] = (0, _element.useState)([]);
  const registerPanelItem = (0, _element.useCallback)(item => {
    // Add item to panel items.
    setPanelItems(items => {
      const newItems = [...items];
      // If an item with this label has already been registered, remove it
      // first. This can happen when an item is moved between the default
      // and optional groups.
      const existingIndex = newItems.findIndex(oldItem => oldItem.label === item.label);
      if (existingIndex !== -1) {
        newItems.splice(existingIndex, 1);
      }
      return [...newItems, item];
    });

    // Track the initial order of item registration. This is used for
    // maintaining menu item order later.
    setMenuItemOrder(items => {
      if (items.includes(item.label)) {
        return items;
      }
      return [...items, item.label];
    });
  }, [setPanelItems, setMenuItemOrder]);

  // Panels need to deregister on unmount to avoid orphans in menu state.
  // This is an issue when panel items are being injected via SlotFills.
  const deregisterPanelItem = (0, _element.useCallback)(label => {
    // When switching selections between components injecting matching
    // controls, e.g. both panels have a "padding" control, the
    // deregistration of the first panel doesn't occur until after the
    // registration of the next.
    setPanelItems(items => {
      const newItems = [...items];
      const index = newItems.findIndex(item => item.label === label);
      if (index !== -1) {
        newItems.splice(index, 1);
      }
      return newItems;
    });
  }, [setPanelItems]);
  const registerResetAllFilter = (0, _element.useCallback)(newFilter => {
    setResetAllFilters(filters => {
      return [...filters, newFilter];
    });
  }, [setResetAllFilters]);
  const deregisterResetAllFilter = (0, _element.useCallback)(filterToRemove => {
    setResetAllFilters(filters => {
      return filters.filter(filter => filter !== filterToRemove);
    });
  }, [setResetAllFilters]);

  // Manage and share display state of menu items representing child controls.
  const [menuItems, setMenuItems] = (0, _element.useState)({
    default: {},
    optional: {}
  });

  // Setup menuItems state as panel items register themselves.
  (0, _element.useEffect)(() => {
    setMenuItems(prevState => {
      const items = generateMenuItems({
        panelItems,
        shouldReset: false,
        currentMenuItems: prevState,
        menuItemOrder
      });
      return items;
    });
  }, [panelItems, setMenuItems, menuItemOrder]);

  // Updates the status of the panel’s menu items. For default items the
  // value represents whether it differs from the default and for optional
  // items whether the item is shown.
  const flagItemCustomization = (0, _element.useCallback)((value, label, group = 'default') => {
    setMenuItems(items => {
      const newState = {
        ...items,
        [group]: {
          ...items[group],
          [label]: value
        }
      };
      return newState;
    });
  }, [setMenuItems]);

  // Whether all optional menu items are hidden or not must be tracked
  // in order to later determine if the panel display is empty and handle
  // conditional display of a plus icon to indicate the presence of further
  // menu items.
  const [areAllOptionalControlsHidden, setAreAllOptionalControlsHidden] = (0, _element.useState)(false);
  (0, _element.useEffect)(() => {
    if (isMenuItemTypeEmpty(menuItems?.default) && !isMenuItemTypeEmpty(menuItems?.optional)) {
      const allControlsHidden = !Object.entries(menuItems.optional).some(([, isSelected]) => isSelected);
      setAreAllOptionalControlsHidden(allControlsHidden);
    }
  }, [menuItems, setAreAllOptionalControlsHidden]);
  const cx = (0, _useCx.useCx)();
  const classes = (0, _element.useMemo)(() => {
    const wrapperStyle = hasInnerWrapper && styles.ToolsPanelWithInnerWrapper(DEFAULT_COLUMNS);
    const emptyStyle = isMenuItemTypeEmpty(menuItems?.default) && areAllOptionalControlsHidden && styles.ToolsPanelHiddenInnerWrapper;
    return cx(styles.ToolsPanel(DEFAULT_COLUMNS), wrapperStyle, emptyStyle, className);
  }, [areAllOptionalControlsHidden, className, cx, hasInnerWrapper, menuItems]);

  // Toggle the checked state of a menu item which is then used to determine
  // display of the item within the panel.
  const toggleItem = (0, _element.useCallback)(label => {
    const currentItem = panelItems.find(item => item.label === label);
    if (!currentItem) {
      return;
    }
    const menuGroup = currentItem.isShownByDefault ? 'default' : 'optional';
    const newMenuItems = {
      ...menuItems,
      [menuGroup]: {
        ...menuItems[menuGroup],
        [label]: !menuItems[menuGroup][label]
      }
    };
    setMenuItems(newMenuItems);
  }, [menuItems, panelItems, setMenuItems]);

  // Resets display of children and executes resetAll callback if available.
  const resetAllItems = (0, _element.useCallback)(() => {
    if (typeof resetAll === 'function') {
      isResettingRef.current = true;
      resetAll(resetAllFilters);
    }

    // Turn off display of all non-default items.
    const resetMenuItems = generateMenuItems({
      panelItems,
      menuItemOrder,
      shouldReset: true
    });
    setMenuItems(resetMenuItems);
  }, [panelItems, resetAllFilters, resetAll, setMenuItems, menuItemOrder]);

  // Assist ItemGroup styling when there are potentially hidden placeholder
  // items by identifying first & last items that are toggled on for display.
  const getFirstVisibleItemLabel = items => {
    const optionalItems = menuItems.optional || {};
    const firstItem = items.find(item => item.isShownByDefault || !!optionalItems[item.label]);
    return firstItem?.label;
  };
  const firstDisplayedItem = getFirstVisibleItemLabel(panelItems);
  const lastDisplayedItem = getFirstVisibleItemLabel([...panelItems].reverse());
  const panelContext = (0, _element.useMemo)(() => ({
    areAllOptionalControlsHidden,
    deregisterPanelItem,
    deregisterResetAllFilter,
    firstDisplayedItem,
    flagItemCustomization,
    hasMenuItems: !!panelItems.length,
    isResetting: isResettingRef.current,
    lastDisplayedItem,
    menuItems,
    panelId,
    registerPanelItem,
    registerResetAllFilter,
    shouldRenderPlaceholderItems,
    __experimentalFirstVisibleItemClass,
    __experimentalLastVisibleItemClass
  }), [areAllOptionalControlsHidden, deregisterPanelItem, deregisterResetAllFilter, firstDisplayedItem, flagItemCustomization, lastDisplayedItem, menuItems, panelId, panelItems, registerResetAllFilter, registerPanelItem, shouldRenderPlaceholderItems, __experimentalFirstVisibleItemClass, __experimentalLastVisibleItemClass]);
  return {
    ...otherProps,
    headingLevel,
    panelContext,
    resetAllItems,
    toggleItem,
    className: classes
  };
}
//# sourceMappingURL=hook.js.map