import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
import { forwardRef, useId, useRef, useState, useCallback, useEffect, useImperativeHandle, useMemo } from 'react';
import { u as useAdaptiveView, O as OptionsDialog, a as OptionsPopover } from './useAdaptiveView-AvsDkChF.js';
import { M as MenuFooter } from './MenuFooter-Ck9L599M.js';
import { r as resolveConfirmation, u as useConfirmationDraft, a as arrayIdsEqual } from './confirmationTypes-CG7xl50f.js';
import { u as useTreeLoader, a as useTree, b as useTreeLazyCascade, f as findNode, T as TreeContent, d as defaultTreeSyncFilter, t as toTreeSyncFilterFn, c as applyChildCounts } from './treeSync-oC-VZpP0.js';
import { S as SearchField } from './SearchField-B7fIOJf-.js';

const TreeSelectMenu = forwardRef((props, ref) => {
  const {
    id: idProp,
    trigger,
    onMenuKeyDown,
    onImplicitClose,
    onExplicitClose,
    label,
    searchPlaceholder,
    displayMenuAs: displayMenuAsProp = "auto",
    virtualize,
    disableSearch,
    popoverWidth,
    debounceMs = 200,
    onSearchChange,
    value,
    onSelectedOptionsChange,
    selectionMode = "linked",
    valueConsistsOf = "LEAF_PRIORITY",
    defaultExpandLevel,
    expandedIds,
    onExpandedIdsChange,
    loadOptions,
    cache,
    initialLoad,
    confirmation: confirmationProp,
    clear
  } = props;
  const confirmation = resolveConfirmation(confirmationProp);
  const autoId = useId();
  const id = idProp ?? autoId;
  const triggerRef = useRef(null);
  const searchRef = useRef(null);
  const treePanelRef = useRef(null);
  const isSingleSelect = selectionMode === "single";
  const branchesSelectable = isSingleSelect ? valueConsistsOf !== "LEAF_ONLY" : true;
  const leavesSelectable = valueConsistsOf !== "BRANCH_ONLY";
  const emptyBranchesSelectable = valueConsistsOf !== "LEAF_ONLY";
  const { isMobile } = useAdaptiveView();
  const displayAs = displayMenuAsProp === "auto" ? isMobile ? "dialog" : "popover" : displayMenuAsProp;
  const [inputValue, setInputValue] = useState("");
  const searchValue = disableSearch ? "" : inputValue;
  const handleSearchChange = useCallback(
    (v) => {
      setInputValue(v);
      onSearchChange?.(v);
    },
    [onSearchChange]
  );
  const loader = useTreeLoader({
    loadOptions,
    cache,
    initialLoad,
    debounceMs
  });
  const [isOpen, setIsOpen] = useState(false);
  const pendingApplyRef = useRef(false);
  const {
    displayValue: draftValue,
    setDraft,
    apply,
    cancel
  } = useConfirmationDraft({
    value,
    onCommit: onSelectedOptionsChange,
    isOpen,
    enabled: confirmation.enabled,
    isEqual: arrayIdsEqual
  });
  const openMenu = useCallback(() => {
    setIsOpen(true);
    loader.onOpen();
  }, [loader]);
  const isFirstSearchEffect = useRef(true);
  useEffect(() => {
    if (isFirstSearchEffect.current) {
      isFirstSearchEffect.current = false;
      return;
    }
    loader.search(searchValue ?? "");
  }, [searchValue]);
  const tree = useTree({
    nodes: loader.nodes,
    value: draftValue,
    onSelectedOptionsChange: setDraft,
    selectionMode,
    valueConsistsOf,
    defaultExpandLevel,
    expandedIds,
    onExpandedIdsChange,
    searchValue,
    ancestry: loader.ancestry,
    loadingChildrenIds: loader.isLoadingChildren
  });
  useImperativeHandle(
    ref,
    () => ({
      clearCache: loader.clearCache,
      invalidate: loader.invalidate,
      expandAll: tree.expandAll,
      collapseAll: tree.collapseAll
    }),
    [loader.clearCache, loader.invalidate, tree.expandAll, tree.collapseAll]
  );
  const closeMenuRef = useRef(() => void 0);
  const onCloseAfterSingleSelect = useCallback(() => {
    if (confirmation.enabled) return;
    closeMenuRef.current();
  }, [confirmation.enabled]);
  const {
    handleToggleNode,
    pendingToggleNodeId,
    reset: resetLazyCascade
  } = useTreeLazyCascade({
    tree,
    loader,
    isSingleSelect,
    branchesSelectable,
    leavesSelectable,
    emptyBranchesSelectable,
    onCloseAfterSingleSelect
  });
  const closeMenu = useCallback(
    (opts) => {
      if (confirmation.enabled && !pendingApplyRef.current) cancel();
      pendingApplyRef.current = false;
      setIsOpen(false);
      handleSearchChange("");
      resetLazyCascade();
      const shouldRestoreFocus = opts?.restoreFocus ?? true;
      if (shouldRestoreFocus) {
        onExplicitClose?.();
        requestAnimationFrame(() => {
          triggerRef.current?.focus();
        });
      } else {
        onImplicitClose?.();
      }
    },
    [
      confirmation.enabled,
      cancel,
      handleSearchChange,
      resetLazyCascade,
      onImplicitClose,
      onExplicitClose
    ]
  );
  useEffect(() => {
    closeMenuRef.current = () => closeMenu();
  }, [closeMenu]);
  const handleApply = useCallback(() => {
    apply();
    pendingApplyRef.current = true;
    closeMenu();
  }, [apply, closeMenu]);
  const handleCancel = useCallback(() => {
    closeMenu();
  }, [closeMenu]);
  const getActiveNode = useCallback(() => {
    const descId = tree.activeDescendantId;
    if (!descId) return void 0;
    const nodeId = descId.replace(/^treeitem-/, "");
    const numId = Number(nodeId);
    return findNode(tree.nodes, Number.isNaN(numId) ? nodeId : numId);
  }, [tree.activeDescendantId, tree.nodes]);
  const handleMenuKeyDown = useCallback(
    (e) => {
      onMenuKeyDown?.(e);
      if (e.defaultPrevented) return;
      if (e.key === "Escape") {
        e.stopPropagation();
        closeMenu();
        return;
      }
      if (e.key === "Tab") {
        closeMenu({ restoreFocus: false });
        return;
      }
      if (e.key === "ArrowDown" || e.key === "ArrowUp") {
        e.preventDefault();
        tree.onKeyDown(e);
        return;
      }
      if (e.key === "Enter" || disableSearch && e.key === " ") {
        const activeNode = getActiveNode();
        if (activeNode) {
          e.preventDefault();
          handleToggleNode(activeNode.id);
        }
        return;
      }
      if (e.key === "ArrowRight") {
        const activeNode = getActiveNode();
        if (activeNode?.children === null) {
          e.preventDefault();
          tree.toggleExpand(activeNode.id);
          void loader.loadChildren(activeNode);
          return;
        }
      }
      tree.onKeyDown(e);
    },
    [
      onMenuKeyDown,
      closeMenu,
      tree,
      getActiveNode,
      handleToggleNode,
      loader,
      disableSearch
    ]
  );
  const handleTriggerKeyDown = useCallback(
    (e) => {
      if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
        e.preventDefault();
        if (!isOpen) {
          openMenu();
        }
      }
    },
    [isOpen, openMenu]
  );
  const handleTriggerClick = useCallback(() => {
    if (isOpen) {
      closeMenu();
    } else {
      openMenu();
    }
  }, [isOpen, openMenu, closeMenu]);
  useEffect(() => {
    if (!isOpen || displayAs !== "popover") return;
    const frameId = requestAnimationFrame(() => {
      if (!disableSearch) {
        searchRef.current?.focus();
      } else {
        treePanelRef.current?.focus();
      }
    });
    return () => cancelAnimationFrame(frameId);
  }, [isOpen, displayAs, disableSearch]);
  const handleLoadChildren = useCallback(
    (node) => {
      void loader.loadChildren(node);
    },
    [loader]
  );
  const treePropsMerged = disableSearch ? {
    ...tree.treeProps,
    id: `${id}-tree`,
    "aria-label": label,
    onKeyDown: handleMenuKeyDown,
    tabIndex: 0
  } : { ...tree.treeProps, id: `${id}-tree`, "aria-label": label };
  const panelProps = {
    visibleNodes: tree.visibleNodes,
    getCheckState: (nodeId) => nodeId === pendingToggleNodeId ? "loading" : tree.getCheckState(nodeId),
    toggleNode: handleToggleNode,
    toggleExpand: tree.toggleExpand,
    getTreeItemProps: tree.getTreeItemProps,
    treeProps: treePropsMerged,
    activeDescendantId: tree.activeDescendantId,
    branchesSelectable,
    leavesSelectable,
    emptyBranchesSelectable,
    onHover: tree.setActiveNodeId,
    onLoadChildren: handleLoadChildren
  };
  const handleInputChange = useCallback(
    (e) => {
      handleSearchChange(e.target.value);
    },
    [handleSearchChange]
  );
  const handleSearchClear = useCallback(() => {
    handleSearchChange("");
  }, [handleSearchChange]);
  const searchInput = !disableSearch ? /* @__PURE__ */ jsx(
    SearchField,
    {
      ref: searchRef,
      value: inputValue,
      onChange: handleInputChange,
      onKeyDown: handleMenuKeyDown,
      onClear: handleSearchClear,
      "aria-activedescendant": tree.activeDescendantId,
      "aria-controls": `${id}-tree`,
      placeholder: searchPlaceholder,
      type: "search"
    }
  ) : null;
  const handleClickOutside = useCallback(() => {
    closeMenu({ restoreFocus: false });
  }, [closeMenu]);
  const treeContent = /* @__PURE__ */ jsx(
    TreeContent,
    {
      ref: treePanelRef,
      loading: loader.isLoading,
      hasNoVisibleNodes: tree.visibleNodes.length === 0,
      virtualize,
      panelProps
    }
  );
  const triggerProps = {
    ref: triggerRef,
    onClick: handleTriggerClick,
    onKeyDown: handleTriggerKeyDown,
    "aria-haspopup": "tree",
    "aria-controls": `${id}-tree`,
    "aria-expanded": isOpen,
    "data-state": isOpen ? "open" : "close"
  };
  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;
  const footer = hasFooter ? /* @__PURE__ */ jsx(MenuFooter, { clear: wrappedClear, confirmation: confirmationConfig }) : 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,
          initialFocusResolver: !disableSearch ? (elements) => elements.find((el) => el.tagName === "INPUT") || elements[0] : void 0,
          children: isOpen ? treeContent : null
        }
      )
    ] });
  }
  return /* @__PURE__ */ jsxs(Fragment, { children: [
    trigger(triggerProps),
    /* @__PURE__ */ jsxs(
      OptionsPopover,
      {
        id: `${id}-popover`,
        referenceElement: triggerRef,
        open: isOpen,
        onClose: handleClickOutside,
        width: popoverWidth ?? 320,
        footer,
        children: [
          searchInput,
          isOpen ? treeContent : null
        ]
      }
    )
  ] });
});
TreeSelectMenu.displayName = "TreeSelectMenu";

const TreeSelectMenuSync = (props) => {
  const {
    options,
    filter: filterProp = defaultTreeSyncFilter,
    ...rest
  } = props;
  const filter = useMemo(() => toTreeSyncFilterFn(filterProp), [filterProp]);
  const processedOptions = useMemo(() => applyChildCounts(options), [options]);
  const loadOptions = useCallback(
    (searchValue) => {
      return filter(processedOptions, searchValue);
    },
    [filter, processedOptions]
  );
  const menuRef = useRef(null);
  const prevOptionsRef = useRef(options);
  useEffect(() => {
    if (prevOptionsRef.current !== options) {
      prevOptionsRef.current = options;
      menuRef.current?.invalidate();
    }
  }, [options]);
  return /* @__PURE__ */ jsx(
    TreeSelectMenu,
    {
      ref: menuRef,
      loadOptions,
      debounceMs: 0,
      ...rest
    }
  );
};
TreeSelectMenuSync.displayName = "TreeSelectMenuSync";

export { TreeSelectMenuSync as T, TreeSelectMenu as a };
//# sourceMappingURL=TreeSelectMenuSync-CPuQWUNU.js.map
