import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { forwardRef, useRef, useId, useMemo, useEffect, useState, useCallback } from 'react';
import { u as useBulkActionRunner, a as useToggleSelection, r as resolveBulkActionCheckState, b as resolveBulkActionLabel, M as MultiSelectOptions } from './bulkActionTypes-CTKwMpO7.js';
import { O as OptionsDialog, a as OptionsPopover } from './useAdaptiveView-AvsDkChF.js';
import { u as useSelectOrchestration, a as useProcessedOptions, b as buildSelectItems, s as shouldShowAddNewItem, A as AddNewItemButton, d as defaultSyncFilter, t as toSyncFilterFn } from './syncFilterUtils-BVclVovw.js';
import { M as MenuFooter } from './MenuFooter-Ck9L599M.js';
import { u as useMenuInteraction } from './useMenuInteraction-NEJXUD4I.js';
import { r as resolveConfirmation, u as useConfirmationDraft, a as arrayIdsEqual } from './confirmationTypes-CG7xl50f.js';
import { u as useDebouncedCallback } from './useDebouncedCallback-DLkapjcP.js';
import { S as SearchField } from './SearchField-B7fIOJf-.js';
import { w as warnOnce } from './warnOnce-Y9PRHcU4.js';

const MultiSelectMenu = forwardRef((props, ref) => {
  const triggerRef = useRef(null);
  const searchRef = useRef(null);
  const {
    id: idProp,
    value: selectedOptions,
    initialLoad: initialLoadProp,
    displayMenuAs: displayMenuAsProp,
    lazy,
    loadOptions: loadOptionsProp,
    label,
    searchPlaceholder,
    debounceMs = 200,
    onSelectedOptionsChange,
    cache,
    pinned,
    selectAll: selectAllProp,
    selectFiltered: selectFilteredProp,
    virtualize,
    disableSearch,
    popoverWidth,
    trigger,
    onSearchChange,
    onMenuKeyDown,
    onImplicitClose,
    onExplicitClose,
    addItemLabel,
    onAddNewItem,
    confirmation: confirmationProp,
    clear
  } = props;
  const confirmation = resolveConfirmation(confirmationProp);
  const groupToString = "groupToString" in props ? props.groupToString : void 0;
  const groupSorter = "groupSorter" in props ? props.groupSorter : void 0;
  const autoId = useId();
  const id = idProp ?? autoId;
  if (disableSearch && selectFilteredProp) {
    warnOnce(
      "MultiSelectMenu: `selectFiltered` has no effect when `disableSearch` is true because there is no search input to produce filtered results."
    );
  }
  const {
    displayAs,
    initialLoad,
    initialLoadPerformed,
    setInitialLoadPerformed,
    options,
    loading,
    loadingMore,
    hasMore,
    loadOptions,
    loadMore
  } = useSelectOrchestration({
    lazy,
    loadOptions: loadOptionsProp,
    cache,
    lazyOptions: "lazyOptions" in props ? props.lazyOptions : void 0,
    initialLoad: initialLoadProp,
    displayMenuAs: displayMenuAsProp,
    ref
  });
  const selectAll = useMemo(
    () => selectAllProp ? {
      label: selectAllProp.label,
      onClick: selectAllProp.onClick,
      compute: selectAllProp.compute,
      checkState: selectAllProp.checkState
    } : void 0,
    [selectAllProp]
  );
  const selectFiltered = useMemo(
    () => selectFilteredProp ? (searchValue2) => {
      const config = selectFilteredProp(searchValue2);
      return {
        label: config.label,
        onClick: config.onClick,
        compute: config.compute,
        checkState: config.checkState
      };
    } : void 0,
    [selectFilteredProp]
  );
  useEffect(() => {
    if (!confirmation.enabled) return;
    if (selectAllProp && !selectAllProp.compute) {
      warnOnce(
        "MultiSelectMenu: `selectAll.onClick` bypasses confirmation. Use `selectAll.compute` to integrate the bulk action with the draft."
      );
    }
  }, [confirmation.enabled, selectAllProp]);
  const [inputValue, setInputValue] = useState("");
  const searchValue = disableSearch ? "" : inputValue;
  const { pinnedSections, ungroupedItems, groupSections } = useProcessedOptions(
    {
      options,
      pinned,
      searchValue,
      groupToString,
      groupSorter
    }
  );
  const [isOpen, setIsOpen] = useState(false);
  const {
    displayValue: draftSelectedOptions,
    setDraft,
    apply,
    cancel
  } = useConfirmationDraft({
    value: selectedOptions,
    onCommit: onSelectedOptionsChange,
    isOpen,
    enabled: confirmation.enabled,
    isEqual: arrayIdsEqual
  });
  const {
    pending: bulkPending,
    runBulk,
    setDraftAndBump,
    reset: resetBulk
  } = useBulkActionRunner({ setDraft });
  const { handleItemClick } = useToggleSelection({
    options,
    pinnedSections,
    selectedOptions: draftSelectedOptions,
    // Route per-option toggles through setDraftAndBump so that any in-flight
    // bulk-action promise's resolution is discarded if it arrives after this
    // click.
    onSelectedOptionsChange: setDraftAndBump
  });
  const runBulkAction = useCallback(
    (config, kind) => {
      if (config.compute) {
        runBulk(config.compute, draftSelectedOptions);
      } else if (config.onClick) {
        if (confirmation.enabled && kind === "selectFiltered") {
          warnOnce(
            "MultiSelectMenu: `selectFiltered.onClick` bypasses confirmation. Use `selectFiltered.compute` to integrate the bulk action with the draft."
          );
        }
        config.onClick();
      }
    },
    [runBulk, draftSelectedOptions, confirmation.enabled]
  );
  const bulkActionItems = useMemo(() => {
    const items2 = [];
    if (selectAll && !searchValue) {
      items2.push({
        id: "select-all",
        type: "select-all",
        original: {
          id: "select-all",
          label: resolveBulkActionLabel(selectAll.label, draftSelectedOptions)
        },
        checkState: bulkPending ? "loading" : resolveBulkActionCheckState(
          selectAll.checkState,
          draftSelectedOptions
        )
      });
    }
    if (selectFiltered && searchValue && !loading) {
      const config = selectFiltered(searchValue);
      items2.push({
        id: "select-filtered",
        type: "select-filtered",
        original: {
          id: "select-filtered",
          label: resolveBulkActionLabel(
            config.label,
            draftSelectedOptions,
            `Select items matching "${searchValue}"`
          )
        },
        checkState: bulkPending ? "loading" : resolveBulkActionCheckState(
          config.checkState,
          draftSelectedOptions
        )
      });
    }
    return items2;
  }, [
    selectAll,
    selectFiltered,
    searchValue,
    loading,
    bulkPending,
    draftSelectedOptions
  ]);
  const { items, sectionsMeta } = useMemo(
    () => buildSelectItems({
      prefixItems: bulkActionItems,
      pinnedSections,
      groupSections,
      ungroupedItems
    }),
    [bulkActionItems, pinnedSections, groupSections, ungroupedItems]
  );
  const selectedItemIds = useMemo(
    () => new Set(draftSelectedOptions.map((o) => o.id)),
    [draftSelectedOptions]
  );
  const handleItemActivate = useCallback(
    (item) => {
      if (item.type === "select-all") {
        if (selectAll) runBulkAction(selectAll, "selectAll");
      } else if (item.type === "select-filtered" && selectFiltered) {
        runBulkAction(selectFiltered(searchValue), "selectFiltered");
      } else if (item.type === "option" || item.type === "pinned-option" || item.type === "grouped-option") {
        handleItemClick(item.original);
      }
    },
    [selectAll, selectFiltered, searchValue, handleItemClick, runBulkAction]
  );
  const pendingApplyRef = useRef(false);
  const {
    closeMenu,
    highlightedIndex,
    getTriggerProps,
    getMenuProps,
    getItemProps,
    listRef,
    menuContainerRef,
    handleKeyDown
  } = useMenuInteraction({
    items,
    sectionsMeta,
    onItemActivate: handleItemActivate,
    selectedItemIds,
    displayAs,
    closeOnActivate: false,
    // While a bulk-action promise is pending, disable all rows.
    disabled: bulkPending,
    id,
    triggerRef,
    label,
    isOpen,
    setIsOpen,
    onImplicitClose: () => {
      if (confirmation.enabled && !pendingApplyRef.current) cancel();
      pendingApplyRef.current = false;
      resetBulk();
      onImplicitClose?.();
    },
    onExplicitClose: () => {
      if (confirmation.enabled && !pendingApplyRef.current) cancel();
      pendingApplyRef.current = false;
      resetBulk();
      onExplicitClose?.();
    }
  });
  const debouncedOptionLoader = useDebouncedCallback((value) => {
    loadOptions(value, { initial: true });
  }, debounceMs);
  const handleInputChange = useCallback(
    (e) => {
      const value = e.target.value;
      setInputValue(value);
      debouncedOptionLoader(value);
      onSearchChange?.(value);
    },
    [debouncedOptionLoader, onSearchChange]
  );
  const handleInputKeyDown = useCallback(
    (e) => {
      onMenuKeyDown?.(e);
      handleKeyDown(e);
    },
    [handleKeyDown, onMenuKeyDown]
  );
  const getMenuPropsWithKeyDown = useCallback(
    (opts) => {
      const menuProps = getMenuProps(opts);
      if (!onMenuKeyDown) return menuProps;
      return {
        ...menuProps,
        onKeyDown: (e) => {
          onMenuKeyDown(e);
          menuProps.onKeyDown(e);
        }
      };
    },
    [getMenuProps, onMenuKeyDown]
  );
  useEffect(() => {
    const performInitialLoad = async () => {
      if (initialLoad === "immediate" && !initialLoadPerformed) {
        await loadOptions("", { initial: true });
        setInitialLoadPerformed(true);
      }
    };
    performInitialLoad();
  }, [loadOptions, initialLoadPerformed, initialLoad, setInitialLoadPerformed]);
  const prevIsOpenRef = useRef(false);
  useEffect(() => {
    const wasOpen = prevIsOpenRef.current;
    prevIsOpenRef.current = isOpen;
    if (isOpen && !wasOpen) {
      if (initialLoad === "open" && !initialLoadPerformed) {
        loadOptions("", { initial: true }).then(() => {
          setInitialLoadPerformed(true);
        });
      }
    }
    if (!isOpen && wasOpen) {
      setInputValue("");
      loadOptions("", { initial: true });
    }
  }, [
    isOpen,
    initialLoad,
    initialLoadPerformed,
    loadOptions,
    setInitialLoadPerformed
  ]);
  useEffect(() => {
    if (!isOpen || displayAs !== "popover") return;
    const frameId = requestAnimationFrame(() => {
      if (!disableSearch) {
        searchRef.current?.focus();
      } else {
        menuContainerRef.current?.focus();
      }
    });
    return () => cancelAnimationFrame(frameId);
  }, [isOpen, displayAs, disableSearch, menuContainerRef]);
  const handleLoadMore = async () => {
    await loadMore(searchValue);
  };
  const handleClickOutside = useCallback(() => {
    closeMenu({ restoreFocus: false });
  }, [closeMenu]);
  const triggerProps = getTriggerProps();
  const menuPropsForAria = getMenuProps();
  const activeDescendant = menuPropsForAria["aria-activedescendant"];
  const handleSearchClear = useCallback(() => {
    setInputValue("");
    debouncedOptionLoader("");
    onSearchChange?.("");
  }, [debouncedOptionLoader, onSearchChange]);
  const handleApply = useCallback(() => {
    apply();
    pendingApplyRef.current = true;
    closeMenu();
  }, [apply, closeMenu]);
  const handleCancel = useCallback(() => {
    closeMenu();
  }, [closeMenu]);
  const searchInput = !disableSearch ? /* @__PURE__ */ jsx(
    SearchField,
    {
      ref: searchRef,
      value: inputValue,
      onChange: handleInputChange,
      onKeyDown: handleInputKeyDown,
      onClear: handleSearchClear,
      "aria-activedescendant": activeDescendant,
      "aria-controls": menuPropsForAria.id,
      placeholder: searchPlaceholder,
      type: "search"
    }
  ) : null;
  const addNewItemButton = shouldShowAddNewItem(
    onAddNewItem,
    addItemLabel,
    inputValue
  ) ? /* @__PURE__ */ jsx(
    AddNewItemButton,
    {
      searchText: inputValue,
      addItemLabel,
      onAddNewItem,
      onCloseMenu: () => {
        pendingApplyRef.current = true;
        closeMenu({ restoreFocus: false });
      }
    }
  ) : null;
  const confirmationConfig = confirmation.enabled ? {
    applyLabel: confirmation.applyLabel,
    cancelLabel: confirmation.cancelLabel,
    onApply: handleApply,
    onCancel: handleCancel
  } : void 0;
  const wrappedClear = clear ? {
    ...clear,
    onClick: () => {
      clear.onClick();
      if (confirmation.enabled) pendingApplyRef.current = true;
      closeMenu();
    }
  } : void 0;
  const hasFooter = !!wrappedClear || !!confirmationConfig || !!addNewItemButton;
  const footer = hasFooter ? /* @__PURE__ */ jsx(
    MenuFooter,
    {
      clear: wrappedClear,
      confirmation: confirmationConfig,
      addNew: addNewItemButton
    }
  ) : void 0;
  if (displayAs === "dialog") {
    return /* @__PURE__ */ jsxs(Fragment, { children: [
      trigger(triggerProps),
      /* @__PURE__ */ jsx(
        OptionsDialog,
        {
          id: `${id}-dialog`,
          isOpen,
          onClose: closeMenu,
          title: label,
          field: searchInput ?? void 0,
          footer: footer ?? void 0,
          initialFocusResolver: !disableSearch ? (elements) => elements.find((el) => el.tagName === "INPUT") || elements[0] : void 0,
          children: /* @__PURE__ */ jsx(
            MultiSelectOptions,
            {
              ref: listRef,
              isOpen: true,
              items,
              sectionsMeta,
              getMenuProps: (opts) => {
                const menuProps = getMenuPropsWithKeyDown(opts);
                if (disableSearch) {
                  return menuProps;
                }
                return { ...menuProps, tabIndex: -1 };
              },
              getItemProps,
              highlightedIndex,
              selectedOptions: draftSelectedOptions,
              hasMore,
              onLoadMore: handleLoadMore,
              loading,
              loadingMore,
              virtualize
            }
          )
        }
      )
    ] });
  }
  return /* @__PURE__ */ jsxs(Fragment, { children: [
    trigger(triggerProps),
    /* @__PURE__ */ jsxs(
      OptionsPopover,
      {
        id: `${id}-popover`,
        referenceElement: triggerRef,
        open: isOpen && displayAs === "popover",
        onClose: handleClickOutside,
        width: popoverWidth ?? 320,
        footer: footer ?? void 0,
        children: [
          searchInput,
          /* @__PURE__ */ jsx(
            MultiSelectOptions,
            {
              ref: listRef,
              isOpen,
              items,
              sectionsMeta,
              getMenuProps: (opts) => {
                const menuProps = getMenuPropsWithKeyDown(opts);
                if (disableSearch) {
                  return menuProps;
                }
                return { ...menuProps, tabIndex: -1 };
              },
              getItemProps,
              highlightedIndex,
              selectedOptions: draftSelectedOptions,
              hasMore,
              onLoadMore: handleLoadMore,
              loading,
              loadingMore,
              virtualize
            }
          )
        ]
      }
    )
  ] });
});
MultiSelectMenu.displayName = "MultiSelectMenu";

const getEnabledSelectionStats = (options, current) => {
  const enabledOptions = options.filter((opt) => !opt.disabled);
  const enabledIds = new Set(enabledOptions.map((opt) => opt.id));
  const selectedCount = current.filter((v) => enabledIds.has(v.id)).length;
  const totalCount = enabledOptions.length;
  const allSelected = totalCount > 0 && selectedCount === totalCount;
  return { enabledOptions, enabledIds, selectedCount, allSelected };
};
const MultiSelectMenuSync = (props) => {
  const {
    options,
    filter: filterProp = defaultSyncFilter,
    selectAll: selectAllProp,
    selectFiltered: selectFilteredProp,
    value,
    onSelectedOptionsChange,
    ...rest
  } = props;
  const filter = useMemo(() => toSyncFilterFn(filterProp), [filterProp]);
  const selectAllConfig = useMemo(() => {
    if (!selectAllProp) return void 0;
    const labelProp = typeof selectAllProp === "object" ? selectAllProp.label : void 0;
    const checkState = (current) => {
      const { selectedCount, allSelected } = getEnabledSelectionStats(
        options,
        current
      );
      return allSelected ? "checked" : selectedCount > 0 ? "indeterminate" : "unchecked";
    };
    const label = typeof labelProp === "function" ? (current) => labelProp(getEnabledSelectionStats(options, current).allSelected) : labelProp;
    const compute = (current) => {
      const { enabledOptions, enabledIds } = getEnabledSelectionStats(
        options,
        current
      );
      const currentIds = new Set(current.map((v) => v.id));
      const allEnabledSelected = enabledOptions.length > 0 && enabledOptions.every((o) => currentIds.has(o.id));
      if (allEnabledSelected) {
        return current.filter((v) => !enabledIds.has(v.id));
      }
      const additions = enabledOptions.filter((o) => !currentIds.has(o.id));
      return [...current, ...additions];
    };
    return {
      label,
      compute,
      checkState
    };
  }, [selectAllProp, options]);
  const selectFilteredConfig = useMemo(() => {
    if (!selectFilteredProp) return void 0;
    return (searchValue) => {
      const filteredOptions = filter(options, searchValue);
      const enabledFilteredOptions = filteredOptions.filter(
        (opt) => !opt.disabled
      );
      const enabledFilteredIds = new Set(
        enabledFilteredOptions.map((o) => o.id)
      );
      const label = (typeof selectFilteredProp === "function" ? selectFilteredProp(searchValue).label : void 0) ?? `Select items matching "${searchValue}"`;
      const checkState = (current) => {
        const currentIds = new Set(current.map((v) => v.id));
        const allFilteredSelected = enabledFilteredOptions.length > 0 && enabledFilteredOptions.every((opt) => currentIds.has(opt.id));
        const someFilteredSelected = enabledFilteredOptions.some(
          (opt) => currentIds.has(opt.id)
        );
        return allFilteredSelected ? "checked" : someFilteredSelected ? "indeterminate" : "unchecked";
      };
      const compute = (current) => {
        const currentIds = new Set(current.map((v) => v.id));
        const allEnabledFilteredSelected = enabledFilteredOptions.length > 0 && enabledFilteredOptions.every((o) => currentIds.has(o.id));
        if (allEnabledFilteredSelected) {
          return current.filter((v) => !enabledFilteredIds.has(v.id));
        }
        const additions = enabledFilteredOptions.filter(
          (o) => !currentIds.has(o.id)
        );
        return [...current, ...additions];
      };
      return {
        label,
        compute,
        checkState
      };
    };
  }, [selectFilteredProp, options, filter]);
  const fieldRef = useRef(null);
  const prevOptionsRef = useRef(options);
  useEffect(() => {
    if (prevOptionsRef.current !== options) {
      prevOptionsRef.current = options;
      fieldRef.current?.invalidate();
    }
  }, [options]);
  return /* @__PURE__ */ jsx(
    MultiSelectMenu,
    {
      ref: fieldRef,
      lazy: false,
      loadOptions: (searchValue) => filter(options, searchValue),
      debounceMs: 0,
      value,
      onSelectedOptionsChange,
      selectAll: selectAllConfig,
      selectFiltered: selectFilteredConfig,
      ...rest
    }
  );
};

export { MultiSelectMenu as M, MultiSelectMenuSync as a };
//# sourceMappingURL=MultiSelectMenuSync-Deki_z-0.js.map
