import { jsxs, jsx } from 'react/jsx-runtime';
import { forwardRef, useId, useRef, useState, useCallback, useEffect, useImperativeHandle, useMemo } from 'react';
import { c as cx } from './index-De1g9FRV.js';
import { F as FieldMessage } from './FieldMessage-mTGdycSA.js';
import { w as warnDeprecatedErrorUsage } from './utils-CM48ODEJ.js';
import { F as FieldLabel } from './FieldLabel-Cm5A1Jcx.js';
import { S as SrOnly } from './SrOnly-pnf8ajnh.js';
import { u as useAdaptiveView, O as OptionsDialog, a as OptionsPopover } from './useAdaptiveView-AvsDkChF.js';
import { B as Button } from './Button-BNzCI47m.js';
import { C as Chip } from './Chip-CxgryK86.js';
import { F as Flex } from './Flex-Bb3iHExM.js';
import { S as SvgChevronRight } from './chevron_right-BdpsxX7x.js';
import { u as useChipLayout } from './useChipLayout-BWZfKDgd.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 styles$1 } from './SelectTriggerBase.module-Brmw1nYG.js';
import { u as useLayoutPropsUtil } from './useLayoutPropsUtil-DjqeAJZ9.js';

import './TreeSelectFieldSync.css';const input = "_input_82zwk_2";
const error = "_error_82zwk_30";
const disabled = "_disabled_82zwk_37";
const chip = "_chip_82zwk_106";
const prefix = "_prefix_82zwk_173";
const suffix = "_suffix_82zwk_174";
const small = "_small_82zwk_202";
const large = "_large_82zwk_208";
const styles = {
	"input-wrapper": "_input-wrapper_82zwk_2",
	input: input,
	error: error,
	disabled: disabled,
	"input-flex": "_input-flex_82zwk_47",
	"rows-wrapper": "_rows-wrapper_82zwk_95",
	"chip-wrapper": "_chip-wrapper_82zwk_106",
	chip: chip,
	"buttons-wrapper": "_buttons-wrapper_82zwk_118",
	"toggle-button-wrapper": "_toggle-button-wrapper_82zwk_135",
	"toggle-button": "_toggle-button_82zwk_135",
	prefix: prefix,
	suffix: suffix,
	small: small,
	large: large
};

const TreeSelectFieldInput = ({
  id,
  placeholder,
  size,
  disabled,
  readOnly,
  error,
  single,
  singleRow,
  maxChips,
  prefix,
  suffix,
  selectedOptions,
  onRemoveOption,
  getChipProps,
  inputRef,
  inputWrapperRef,
  searchValue,
  onSearchChange,
  onInputKeyDown,
  onWrapperClick,
  isOpen,
  ariaControls,
  disableSearch,
  comboboxProps
}) => {
  const { rowsRef, displayCount, hiddenCount } = useChipLayout({
    selectedOptionsLength: selectedOptions.length,
    singleRow,
    maxChips
  });
  const inputWrapperClassNames = cx(styles["input-wrapper"], {
    [styles["small"]]: size === "small",
    [styles["large"]]: size === "large",
    [styles["disabled"]]: disabled || readOnly
  });
  const handleWrapperClick = (e) => {
    const target = e.target;
    if (target.closest("button") || target.tagName === "INPUT") {
      return;
    }
    onWrapperClick?.();
    inputRef?.current?.focus();
  };
  return (
    // eslint-disable-next-line jsx-a11y/no-static-element-interactions, jsx-a11y/click-events-have-key-events -- Click-to-focus container; Despite the disabled rules, this remains accessible. Keyboard interaction is provided by the input and nested buttons are filtered out in the handler.
    /* @__PURE__ */ jsxs(
      "div",
      {
        ref: inputWrapperRef,
        className: inputWrapperClassNames,
        onClick: handleWrapperClick,
        children: [
          prefix ? /* @__PURE__ */ jsx("div", { className: styles["prefix"], children: prefix }) : null,
          /* @__PURE__ */ jsxs("div", { className: styles["rows-wrapper"], ref: rowsRef, children: [
            !single && selectedOptions.length > 0 ? selectedOptions.slice(0, displayCount).map((option, index) => /* @__PURE__ */ jsx(
              "div",
              {
                className: styles["chip-wrapper"],
                children: /* @__PURE__ */ jsx(
                  Chip,
                  {
                    ...getChipProps?.(option),
                    label: option.label,
                    onClose: disabled || readOnly ? void 0 : () => onRemoveOption(option),
                    className: styles["chip"],
                    title: option.label
                  }
                )
              },
              `selected-item-${option.id}-${index}`
            )) : null,
            hiddenCount > 0 ? /* @__PURE__ */ jsx("div", { className: styles["chip-wrapper"], children: /* @__PURE__ */ jsx(
              Chip,
              {
                label: `+${hiddenCount}`,
                className: styles["chip"],
                title: selectedOptions.slice(displayCount).map((option) => option.label).join(", ")
              }
            ) }) : null,
            !disableSearch && !single ? /* @__PURE__ */ jsx(Flex, { className: styles["input-flex"], alignItems: "center", children: /* @__PURE__ */ jsx(
              "input",
              {
                ref: inputRef,
                id,
                type: "text",
                className: cx(styles["input"], { [styles["error"]]: error }),
                role: "combobox",
                "aria-controls": ariaControls,
                "aria-expanded": isOpen,
                "aria-haspopup": "tree",
                value: searchValue,
                onChange: onSearchChange,
                onKeyDown: (e) => onInputKeyDown?.(e),
                disabled,
                readOnly,
                placeholder: selectedOptions.length === 0 ? placeholder : void 0,
                ...comboboxProps
              }
            ) }) : null,
            single || disableSearch ? /* @__PURE__ */ jsx(Flex, { className: styles["input-flex"], alignItems: "center", children: /* @__PURE__ */ jsx(
              "input",
              {
                ref: inputRef,
                id,
                type: "text",
                className: cx(styles["input"], { [styles["error"]]: error }),
                role: "combobox",
                "aria-controls": ariaControls,
                "aria-expanded": isOpen,
                "aria-haspopup": "tree",
                readOnly: true,
                value: single && selectedOptions.length > 0 ? selectedOptions[0].label : "",
                disabled,
                placeholder: selectedOptions.length === 0 ? placeholder : void 0,
                onKeyDown: (e) => onInputKeyDown?.(e),
                ...comboboxProps
              }
            ) }) : null
          ] }),
          suffix ? /* @__PURE__ */ jsx("div", { className: styles["suffix"], children: suffix }) : null,
          /* @__PURE__ */ jsx("div", { className: styles["buttons-wrapper"], children: /* @__PURE__ */ jsx("div", { className: styles["toggle-button-wrapper"], children: /* @__PURE__ */ jsx(
            Button,
            {
              icon: SvgChevronRight,
              "aria-label": "toggle menu",
              appearance: "ghost",
              size: "small",
              disabled,
              "aria-expanded": isOpen,
              className: styles["toggle-button"],
              tabIndex: -1,
              onClick: () => {
                onWrapperClick?.();
                inputRef?.current?.focus();
              }
            }
          ) }) })
        ]
      }
    )
  );
};
TreeSelectFieldInput.displayName = "TreeSelectFieldInput";

const TreeSelectField = forwardRef((propsWithLayout, ref) => {
  const { layoutStyles, componentProps } = useLayoutPropsUtil(propsWithLayout);
  const {
    id: idProp,
    label,
    labelNode,
    hideLabel,
    placeholder,
    size,
    error,
    hint,
    moreInfo,
    labelAiMark,
    description,
    warning,
    required,
    disabled,
    readOnly,
    prefix,
    suffix,
    className,
    style,
    disableSearch,
    debounceMs = 200,
    searchValue: searchValueProp,
    onSearchChange,
    singleRow,
    maxChips,
    getChipProps,
    virtualize,
    value,
    onSelectedOptionsChange,
    selectionMode = "linked",
    valueConsistsOf = "LEAF_PRIORITY",
    defaultExpandLevel,
    expandedIds,
    onExpandedIdsChange,
    loadOptions,
    cache,
    initialLoad,
    displayMenuAs: displayMenuAsProp = "auto"
  } = componentProps;
  const autoId = useId();
  const id = idProp ?? autoId;
  const helperUid = useId();
  const moreInfoUid = useId();
  const inputRef = useRef(null);
  const inputWrapperRef = useRef(null);
  const treePanelRef = useRef(null);
  warnDeprecatedErrorUsage("TreeSelectField", error);
  const errorMessages = typeof error === "boolean" || error === void 0 ? void 0 : error;
  const hasHelperText = !!(hint || errorMessages || warning || description);
  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 isSearchControlled = searchValueProp !== void 0;
  const [internalSearchValue, setInternalSearchValue] = useState("");
  const searchValue = isSearchControlled ? searchValueProp : internalSearchValue;
  const handleSearchChange = useCallback(
    (v) => {
      if (!isSearchControlled) {
        setInternalSearchValue(v);
      }
      onSearchChange?.(v);
    },
    [isSearchControlled, onSearchChange]
  );
  const loader = useTreeLoader({
    loadOptions,
    cache,
    initialLoad,
    debounceMs
  });
  const [isOpen, setIsOpen] = useState(false);
  const openMenu = useCallback(() => {
    if (disabled) return;
    setIsOpen(true);
    loader.onOpen();
  }, [disabled, loader]);
  const isFirstSearchEffect = useRef(true);
  useEffect(() => {
    if (isFirstSearchEffect.current) {
      isFirstSearchEffect.current = false;
      return;
    }
    loader.search(searchValue ?? "");
  }, [searchValue]);
  const tree = useTree({
    nodes: loader.nodes,
    value,
    onSelectedOptionsChange,
    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(() => {
    closeMenuRef.current();
    inputRef.current?.focus();
  }, []);
  const {
    handleToggleNode,
    pendingToggleNodeId,
    reset: resetLazyCascade
  } = useTreeLazyCascade({
    tree,
    loader,
    isSingleSelect,
    branchesSelectable,
    leavesSelectable,
    emptyBranchesSelectable,
    readOnly,
    onCloseAfterSingleSelect
  });
  const closeMenu = useCallback(() => {
    setIsOpen(false);
    handleSearchChange("");
    resetLazyCascade();
  }, [handleSearchChange, resetLazyCascade]);
  useEffect(() => {
    closeMenuRef.current = closeMenu;
  }, [closeMenu]);
  const getActiveNode = useCallback(() => {
    const descId = tree.activeDescendantId;
    if (!descId) return void 0;
    const nodeId = descId.replace(/^treeitem-/, "");
    const stringMatch = findNode(tree.nodes, nodeId);
    if (stringMatch) return stringMatch;
    const numId = Number(nodeId);
    return Number.isNaN(numId) ? void 0 : findNode(tree.nodes, numId);
  }, [tree.activeDescendantId, tree.nodes]);
  const handleInputKeyDown = useCallback(
    (e) => {
      if (e.key === "Escape") {
        if (isOpen) {
          e.stopPropagation();
          closeMenu();
        }
        return;
      }
      if (e.key === "Tab") {
        if (isOpen) closeMenu();
        return;
      }
      if (e.key === "ArrowDown") {
        e.preventDefault();
        if (!isOpen) {
          openMenu();
        }
        tree.onKeyDown(e);
        return;
      }
      if (e.key === "Backspace" && !isSingleSelect && !readOnly && searchValue === "" && value.length > 0) {
        const next = value.slice(0, -1);
        onSelectedOptionsChange(next);
        return;
      }
      if (isOpen) {
        if (e.key === "Enter" || e.key === " " && disableSearch) {
          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);
      }
    },
    [
      isOpen,
      openMenu,
      closeMenu,
      isSingleSelect,
      readOnly,
      searchValue,
      value,
      onSelectedOptionsChange,
      tree,
      getActiveNode,
      handleToggleNode,
      loader,
      disableSearch
    ]
  );
  const handleLoadChildren = useCallback(
    (node) => {
      void loader.loadChildren(node);
    },
    [loader]
  );
  const panelProps = {
    visibleNodes: tree.visibleNodes,
    getCheckState: (id2) => id2 === pendingToggleNodeId ? "loading" : tree.getCheckState(id2),
    toggleNode: handleToggleNode,
    toggleExpand: tree.toggleExpand,
    getTreeItemProps: tree.getTreeItemProps,
    treeProps: { ...tree.treeProps, id: `${id}-tree`, "aria-label": label },
    activeDescendantId: tree.activeDescendantId,
    branchesSelectable,
    leavesSelectable,
    emptyBranchesSelectable,
    readOnly,
    onHover: tree.setActiveNodeId,
    onLoadChildren: handleLoadChildren
  };
  const labelContent = /* @__PURE__ */ jsx(
    FieldLabel,
    {
      el: "label",
      htmlFor: `${id}-input`,
      required,
      moreInfo,
      moreInfoId: moreInfo ? moreInfoUid : void 0,
      aiMark: labelAiMark,
      children: labelNode ?? label
    }
  );
  return /* @__PURE__ */ jsxs(
    "div",
    {
      className: cx(styles$1["search-field"], className),
      style: { ...layoutStyles, ...style },
      children: [
        hideLabel ? /* @__PURE__ */ jsx(SrOnly, { children: labelContent }) : labelContent,
        /* @__PURE__ */ jsx(
          TreeSelectFieldInput,
          {
            id: `${id}-input`,
            placeholder,
            size,
            disabled,
            readOnly,
            error: !!error,
            single: isSingleSelect,
            singleRow,
            maxChips,
            getChipProps,
            prefix,
            suffix,
            selectedOptions: value,
            onRemoveOption: (option) => onSelectedOptionsChange(value.filter((v) => v.id !== option.id)),
            inputRef,
            inputWrapperRef,
            searchValue,
            onSearchChange: (e) => {
              handleSearchChange(e.target.value);
              if (!isOpen) openMenu();
            },
            onInputKeyDown: handleInputKeyDown,
            onWrapperClick: () => {
              if (!disabled) {
                if (isOpen) {
                  closeMenu();
                } else {
                  openMenu();
                }
                inputRef.current?.focus();
              }
            },
            isOpen,
            ariaControls: `${id}-tree`,
            disableSearch,
            comboboxProps: {
              "aria-describedby": [hasHelperText && helperUid, moreInfo && moreInfoUid].filter(Boolean).join(" ") || void 0,
              "aria-invalid": error ? true : void 0,
              "aria-activedescendant": isOpen ? tree.activeDescendantId : void 0,
              onClick: disabled ? void 0 : () => {
                if (isOpen) {
                  closeMenu();
                } else {
                  openMenu();
                }
              }
            }
          }
        ),
        hasHelperText ? /* @__PURE__ */ jsx(
          FieldMessage,
          {
            id: helperUid,
            hint,
            error: errorMessages,
            warning,
            description
          }
        ) : null,
        displayAs === "dialog" ? /* @__PURE__ */ jsx(
          OptionsDialog,
          {
            id: `${id}-dialog`,
            isOpen: isOpen && !disabled,
            onClose: closeMenu,
            title: label,
            initialFocusResolver: (elements) => elements.find((el) => el.tagName === "INPUT") || elements[0],
            field: /* @__PURE__ */ jsx(
              TreeSelectFieldInput,
              {
                id: `${id}-dialog-input`,
                placeholder,
                size: "medium",
                disabled,
                readOnly,
                error: !!error,
                single: isSingleSelect,
                singleRow: true,
                maxChips: 20,
                getChipProps,
                prefix,
                suffix,
                selectedOptions: value,
                onRemoveOption: (option) => onSelectedOptionsChange(value.filter((v) => v.id !== option.id)),
                searchValue,
                onSearchChange: (e) => {
                  handleSearchChange(e.target.value);
                  if (!isOpen) openMenu();
                },
                onInputKeyDown: handleInputKeyDown,
                isOpen,
                ariaControls: `${id}-tree`,
                disableSearch: false,
                comboboxProps: {
                  "aria-activedescendant": isOpen ? tree.activeDescendantId : void 0
                }
              }
            ),
            children: isOpen && !disabled ? /* @__PURE__ */ jsx(
              TreeContent,
              {
                ref: treePanelRef,
                loading: loader.isLoading,
                hasNoVisibleNodes: tree.visibleNodes.length === 0,
                virtualize,
                panelProps
              }
            ) : null
          }
        ) : /* @__PURE__ */ jsx(
          OptionsPopover,
          {
            id: `${id}-popover`,
            referenceElement: inputWrapperRef,
            open: isOpen && !disabled,
            onClose: closeMenu,
            width: "reference",
            children: isOpen && !disabled ? /* @__PURE__ */ jsx(
              TreeContent,
              {
                ref: treePanelRef,
                loading: loader.isLoading,
                hasNoVisibleNodes: tree.visibleNodes.length === 0,
                virtualize,
                panelProps
              }
            ) : null
          }
        )
      ]
    }
  );
});
TreeSelectField.displayName = "TreeSelectField";

const TreeSelectFieldSync = (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 fieldRef = useRef(null);
  const prevOptionsRef = useRef(options);
  useEffect(() => {
    if (prevOptionsRef.current !== options) {
      prevOptionsRef.current = options;
      fieldRef.current?.invalidate();
    }
  }, [options]);
  return /* @__PURE__ */ jsx(
    TreeSelectField,
    {
      ref: fieldRef,
      loadOptions,
      debounceMs: 0,
      ...rest
    }
  );
};
TreeSelectFieldSync.displayName = "TreeSelectFieldSync";

export { TreeSelectField as T, TreeSelectFieldSync as a };
//# sourceMappingURL=TreeSelectFieldSync-CdtC8d3Q.js.map
