import { useState, useCallback, useRef, useEffect, useMemo, forwardRef } from 'react';
import { jsx, jsxs } from 'react/jsx-runtime';
import { S as Spinner } from './Spinner-B7tTWcP6.js';
import { T as Text } from './Text-mDcQHC2U.js';
import { c as cx } from './index-De1g9FRV.js';
import { I as Icon } from './Icon-B4uCvMU9.js';
import { S as SvgKeyboardArrowRight } from './keyboard_arrow_right-DMloHg_F.js';
import { S as SvgKeyboardArrowDown } from './keyboard_arrow_down-C8WQ38p1.js';
import { b as OptionRow, c as OptionRowSideContent, d as OptionCheckbox, e as OptionContentArea } from './useAdaptiveView-AvsDkChF.js';
import { u as useVirtualizer } from './index-DXeGMe23.js';
import { m as matchSorter } from './match-sorter.esm-CGAauEiU.js';

import './treeSync.css';function createLruCache(maxSize) {
  const map = /* @__PURE__ */ new Map();
  return {
    get(key) {
      if (!map.has(key)) return void 0;
      const value = map.get(key);
      map.delete(key);
      map.set(key, value);
      return value;
    },
    set(key, value) {
      if (map.has(key)) {
        map.delete(key);
      } else if (map.size >= maxSize) {
        const firstKey = map.keys().next().value;
        if (firstKey !== void 0) map.delete(firstKey);
      }
      map.set(key, value);
    },
    has(key) {
      return map.has(key);
    },
    delete(key) {
      map.delete(key);
    },
    clear() {
      map.clear();
    }
  };
}
function mergeChildren(nodes, parentId, children) {
  return nodes.map((node) => {
    if (node.id === parentId) {
      return { ...node, children };
    }
    if (node.children && node.children.length > 0) {
      return {
        ...node,
        children: mergeChildren(node.children, parentId, children)
      };
    }
    return node;
  });
}
function useTreeLoader(options) {
  const {
    loadOptions,
    cache: cacheConfig,
    initialLoad = "auto",
    debounceMs = 200
  } = options;
  const cacheEnabled = cacheConfig?.enabled !== false;
  const cacheMaxSize = cacheConfig?.maxSize ?? 15;
  const [nodes, setNodes] = useState([]);
  const [isLoading, setIsLoading] = useState(false);
  const [isLoadingChildren, setIsLoadingChildren] = useState(/* @__PURE__ */ new Set());
  const [ancestry, setAncestry] = useState(() => /* @__PURE__ */ new Map());
  const recordAncestry = useCallback(
    (parentId, children) => {
      setAncestry((prev) => {
        let changed = false;
        const next = new Map(prev);
        for (const child of children) {
          if (next.get(child.id) !== parentId) {
            next.set(child.id, parentId);
            changed = true;
          }
        }
        return changed ? next : prev;
      });
    },
    []
  );
  const hasOpenedRef = useRef(false);
  const currentSearchRef = useRef("");
  const searchCacheRef = useRef(
    createLruCache(cacheMaxSize)
  );
  const childrenCacheRef = useRef(
    createLruCache(cacheMaxSize)
  );
  const loadOptionsRef = useRef(loadOptions);
  loadOptionsRef.current = loadOptions;
  const loadRootSeqRef = useRef(0);
  const loadRoot = useCallback(
    async (searchValue, bypassCache = false) => {
      currentSearchRef.current = searchValue;
      hasOpenedRef.current = true;
      const seq = ++loadRootSeqRef.current;
      const cached = !bypassCache && cacheEnabled ? searchCacheRef.current.get(searchValue) : void 0;
      if (cached !== void 0) {
        setNodes(cached);
        return;
      }
      setIsLoading(true);
      try {
        const result = await loadOptionsRef.current(searchValue, void 0);
        if (seq !== loadRootSeqRef.current) return;
        if (!bypassCache && cacheEnabled) {
          searchCacheRef.current.set(searchValue, result);
        }
        setNodes(result);
      } finally {
        if (seq === loadRootSeqRef.current) {
          setIsLoading(false);
        }
      }
    },
    [cacheEnabled]
  );
  const debounceTimerRef = useRef(null);
  const search = useCallback(
    (searchValue) => {
      if (debounceTimerRef.current !== null) {
        clearTimeout(debounceTimerRef.current);
      }
      if (debounceMs === 0) {
        void loadRoot(searchValue);
      } else {
        debounceTimerRef.current = setTimeout(() => {
          void loadRoot(searchValue);
        }, debounceMs);
      }
    },
    [loadRoot, debounceMs]
  );
  useEffect(() => {
    return () => {
      if (debounceTimerRef.current !== null) {
        clearTimeout(debounceTimerRef.current);
      }
    };
  }, []);
  const loadChildren = useCallback(
    async (parentNode) => {
      const parentId = parentNode.id;
      if (cacheEnabled) {
        const cached = childrenCacheRef.current.get(parentId);
        if (cached !== void 0) {
          setNodes((prev) => mergeChildren(prev, parentId, cached));
          recordAncestry(parentId, cached);
          return cached;
        }
      }
      setIsLoadingChildren((prev) => {
        const next = new Set(prev);
        next.add(parentId);
        return next;
      });
      try {
        const result = await loadOptionsRef.current("", parentNode);
        if (cacheEnabled) {
          childrenCacheRef.current.set(parentId, result);
        }
        setNodes((prev) => mergeChildren(prev, parentId, result));
        recordAncestry(parentId, result);
        return result;
      } finally {
        setIsLoadingChildren((prev) => {
          const next = new Set(prev);
          next.delete(parentId);
          return next;
        });
      }
    },
    [cacheEnabled, recordAncestry]
  );
  const clearCache = useCallback(() => {
    searchCacheRef.current.clear();
    childrenCacheRef.current.clear();
    setAncestry((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
  }, []);
  const invalidate = useCallback(() => {
    searchCacheRef.current.clear();
    childrenCacheRef.current.clear();
    setAncestry((prev) => prev.size === 0 ? prev : /* @__PURE__ */ new Map());
    void loadRoot(currentSearchRef.current, true);
  }, [loadRoot]);
  const onOpen = useCallback(() => {
    if (hasOpenedRef.current) return;
    hasOpenedRef.current = true;
    void loadRoot("");
  }, [loadRoot]);
  useEffect(() => {
    if (initialLoad === "immediate" || initialLoad === "auto") {
      void loadRoot("");
    }
  }, []);
  return {
    nodes,
    ancestry,
    isLoading,
    isLoadingChildren,
    loadChildren,
    search,
    clearCache,
    invalidate,
    onOpen
  };
}

function buildNodeMap(nodes) {
  const map = /* @__PURE__ */ new Map();
  const visit = (list) => {
    for (const node of list) {
      map.set(node.id, node);
      if (node.children) visit(node.children);
    }
  };
  visit(nodes);
  return map;
}
function buildParentMap(nodes) {
  const parents = /* @__PURE__ */ new Map();
  const visit = (list, parentId) => {
    for (const node of list) {
      if (parentId !== void 0) parents.set(node.id, parentId);
      if (node.children) visit(node.children, node.id);
    }
  };
  visit(nodes);
  return parents;
}
function resolvePath(nodeId, parents) {
  const path = [];
  let current = parents.get(nodeId);
  const seen = /* @__PURE__ */ new Set();
  while (current !== void 0 && !seen.has(current)) {
    seen.add(current);
    path.unshift(current);
    current = parents.get(current);
  }
  return path.length > 0 ? path : void 0;
}
function isBranch(node) {
  return node.children !== void 0;
}
function expandBranchIds(ids, nodes) {
  const nodeMap = buildNodeMap(nodes);
  const expanded = new Set(ids);
  const addDescendants = (node) => {
    if (node.disabled) return;
    expanded.add(node.id);
    if (node.children) {
      for (const child of node.children) {
        addDescendants(child);
      }
    }
  };
  for (const id of ids) {
    const node = nodeMap.get(id);
    if (node && isBranch(node)) {
      addDescendants(node);
    }
  }
  return expanded;
}
function computeCheckStates(nodes, value, selectionMode, valueConsistsOf, orphanAncestorIds = /* @__PURE__ */ new Set()) {
  const rawIds = new Set(value.map((v) => v.id));
  const checkedIdSet = selectionMode === "linked" && (valueConsistsOf === "BRANCH_PRIORITY" || valueConsistsOf === "ALL") ? expandBranchIds(rawIds, nodes) : rawIds;
  const states = /* @__PURE__ */ new Map();
  const computeNode = (node) => {
    if (selectionMode === "independent" || selectionMode === "single" || valueConsistsOf === "BRANCH_ONLY") {
      const state2 = checkedIdSet.has(node.id) ? "checked" : "unchecked";
      states.set(node.id, state2);
      if (node.children) {
        for (const child of node.children) {
          computeNode(child);
        }
      }
      return state2;
    }
    const loadedChildren = Array.isArray(node.children) ? node.children : [];
    const isUnloaded = node.children === null;
    const isPartiallyLoaded = node.childCount !== void 0 && loadedChildren.length < node.childCount;
    const underRepresented = isUnloaded || isPartiallyLoaded;
    if (loadedChildren.length === 0 && !underRepresented) {
      const state2 = checkedIdSet.has(node.id) ? "checked" : "unchecked";
      states.set(node.id, state2);
      return state2;
    }
    const childStates = loadedChildren.map(computeNode);
    const hasAnyChecked = childStates.some(
      (s) => s === "checked" || s === "indeterminate"
    );
    if (underRepresented) {
      let state2;
      if (checkedIdSet.has(node.id)) {
        state2 = "checked";
      } else if (hasAnyChecked || orphanAncestorIds.has(node.id)) {
        state2 = "indeterminate";
      } else {
        state2 = "unchecked";
      }
      states.set(node.id, state2);
      return state2;
    }
    const enabledChildren = loadedChildren.filter((c) => !c.disabled);
    const enabledChildStates = enabledChildren.map(
      (c) => states.get(c.id) ?? "unchecked"
    );
    const disabledChildren = loadedChildren.filter((c) => c.disabled);
    const disabledChildStates = disabledChildren.map(
      (c) => states.get(c.id) ?? "unchecked"
    );
    const allEnabledChecked = enabledChildren.length === 0 ? (
      // No enabled children — only disabled children matter
      disabledChildStates.every((s) => s === "checked")
    ) : enabledChildStates.every((s) => s === "checked");
    let state;
    if (allEnabledChecked && (enabledChildren.length > 0 || disabledChildStates.every((s) => s === "checked"))) {
      state = "checked";
    } else if (hasAnyChecked) {
      state = "indeterminate";
    } else {
      state = "unchecked";
    }
    states.set(node.id, state);
    return state;
  };
  for (const node of nodes) {
    computeNode(node);
  }
  return states;
}
function computeValue(nodes, checkedIds, valueConsistsOf) {
  const result = [];
  const included = /* @__PURE__ */ new Set();
  const allNonDisabledChildrenChecked = (node) => {
    if (!node.children || node.children.length === 0) return false;
    const nonDisabled = node.children.filter((c) => !c.disabled);
    if (nonDisabled.length === 0) return false;
    return nonDisabled.every((c) => {
      if (!isBranch(c)) return checkedIds.has(c.id);
      return allNonDisabledChildrenChecked(c) || checkedIds.has(c.id);
    });
  };
  const toValue = (node, ancestors) => ({
    id: node.id,
    label: node.label,
    extra: node.extra,
    ...ancestors.length > 0 ? { path: [...ancestors] } : {}
  });
  const visit = (node, ancestors) => {
    if (!checkedIds.has(node.id) && !isBranch(node)) {
      return;
    }
    const childAncestors = [...ancestors, node.id];
    switch (valueConsistsOf) {
      case "ALL": {
        if (checkedIds.has(node.id) && !included.has(node.id)) {
          included.add(node.id);
          result.push(toValue(node, ancestors));
        }
        if (node.children) {
          for (const child of node.children) {
            visit(child, childAncestors);
          }
        }
        break;
      }
      case "BRANCH_ONLY": {
        if (isBranch(node)) {
          if (checkedIds.has(node.id) && !included.has(node.id)) {
            included.add(node.id);
            result.push(toValue(node, ancestors));
          }
          if (node.children) {
            for (const child of node.children) {
              visit(child, childAncestors);
            }
          }
        }
        break;
      }
      case "LEAF_ONLY": {
        if (!isBranch(node)) {
          if (checkedIds.has(node.id) && !included.has(node.id)) {
            included.add(node.id);
            result.push(toValue(node, ancestors));
          }
        } else {
          if (node.children) {
            for (const child of node.children) {
              visit(child, childAncestors);
            }
          }
        }
        break;
      }
      case "LEAF_PRIORITY": {
        if (!isBranch(node)) {
          if (checkedIds.has(node.id) && !included.has(node.id)) {
            included.add(node.id);
            result.push(toValue(node, ancestors));
          }
        } else if (!node.children || node.children.length === 0) {
          if (checkedIds.has(node.id) && !included.has(node.id)) {
            included.add(node.id);
            result.push(toValue(node, ancestors));
          }
        } else {
          for (const child of node.children) {
            visit(child, childAncestors);
          }
        }
        break;
      }
      case "BRANCH_PRIORITY": {
        if (!isBranch(node)) {
          if (checkedIds.has(node.id) && !included.has(node.id)) {
            included.add(node.id);
            result.push(toValue(node, ancestors));
          }
        } else {
          if (allNonDisabledChildrenChecked(node)) {
            if (!included.has(node.id)) {
              included.add(node.id);
              result.push(toValue(node, ancestors));
              if (node.children) {
                for (const child of node.children) {
                  markDescendantsIncluded(child);
                }
              }
            }
          } else {
            if (node.children) {
              for (const child of node.children) {
                visit(child, childAncestors);
              }
            }
          }
        }
        break;
      }
    }
  };
  const markDescendantsIncluded = (node) => {
    included.add(node.id);
    if (node.children) {
      for (const child of node.children) {
        markDescendantsIncluded(child);
      }
    }
  };
  for (const node of nodes) {
    visit(node, []);
  }
  return result;
}
function toggleNodeSelection(nodes, currentValue, nodeId, selectionMode, valueConsistsOf) {
  const nodeMap = buildNodeMap(nodes);
  const targetNode = nodeMap.get(nodeId);
  if (!targetNode) return currentValue;
  const currentCheckedIds = new Set(currentValue.map((v) => v.id));
  const isCurrentlySelected = currentCheckedIds.has(nodeId);
  const parentMap = buildParentMap(nodes);
  if (selectionMode === "single") {
    if (isCurrentlySelected) return [];
    const path = resolvePath(targetNode.id, parentMap);
    return [
      {
        id: targetNode.id,
        label: targetNode.label,
        extra: targetNode.extra,
        ...path ? { path } : {}
      }
    ];
  }
  if (selectionMode === "independent") {
    const next = new Set(currentCheckedIds);
    if (isCurrentlySelected) {
      next.delete(nodeId);
    } else {
      next.add(nodeId);
    }
    return buildValueFromIds(next, currentValue, nodeMap, parentMap);
  }
  const checkStates = computeCheckStates(
    nodes,
    currentValue,
    selectionMode,
    valueConsistsOf
  );
  const currentCheckState = checkStates.get(nodeId) ?? "unchecked";
  const shouldDeselect = currentCheckState === "checked" || currentCheckState === "indeterminate";
  const expandedIds = valueConsistsOf === "BRANCH_PRIORITY" || valueConsistsOf === "ALL" ? expandBranchIds(currentCheckedIds, nodes) : currentCheckedIds;
  const nextCheckedIds = new Set(expandedIds);
  if (shouldDeselect) {
    const idsToDeselect = getNonDisabledDescendantIds(targetNode);
    for (const id of idsToDeselect) {
      nextCheckedIds.delete(id);
    }
  } else {
    const idsToSelect = getNonDisabledDescendantIds(targetNode);
    for (const id of idsToSelect) {
      nextCheckedIds.add(id);
    }
  }
  if (valueConsistsOf !== "BRANCH_ONLY") {
    bubbleUpCheckedParents(nodes, nextCheckedIds);
  }
  const computed = computeValue(nodes, nextCheckedIds, valueConsistsOf);
  const emittedIds = new Set(computed.map((v) => v.id));
  const carriedForward = currentValue.filter(
    (v) => nextCheckedIds.has(v.id) && !emittedIds.has(v.id) && !nodeMap.has(v.id)
  );
  return [...computed, ...carriedForward];
}
function bubbleUpCheckedParents(nodes, checkedIds) {
  const visit = (list) => {
    for (const node of list) {
      if (node.children && node.children.length > 0) {
        visit(node.children);
        const nonDisabled = node.children.filter((c) => !c.disabled);
        if (nonDisabled.length > 0 && nonDisabled.every((c) => checkedIds.has(c.id))) {
          if (!node.disabled) checkedIds.add(node.id);
        } else {
          checkedIds.delete(node.id);
        }
      }
    }
  };
  visit(nodes);
}
function getNonDisabledDescendantIds(node) {
  if (node.disabled) return [];
  const result = [node.id];
  if (node.children) {
    for (const child of node.children) {
      result.push(...getNonDisabledDescendantIds(child));
    }
  }
  return result;
}
function buildValueFromIds(checkedIds, currentValue, nodeMap, parentMap) {
  const existingMap = new Map(currentValue.map((v) => [v.id, v]));
  const result = [];
  for (const id of checkedIds) {
    const existing = existingMap.get(id);
    if (existing) {
      result.push(existing);
    } else {
      const node = nodeMap.get(id);
      if (node) {
        const path = resolvePath(node.id, parentMap);
        result.push({
          id: node.id,
          label: node.label,
          extra: node.extra,
          ...path ? { path } : {}
        });
      }
    }
  }
  return result;
}

function handleTreeKeyDown(e, options) {
  const {
    visibleNodes,
    activeIndex,
    setActiveIndex,
    isExpanded,
    toggleExpand,
    toggleNode
  } = options;
  if (visibleNodes.length === 0) return false;
  switch (e.key) {
    case "ArrowDown": {
      e.preventDefault();
      if (activeIndex === -1) {
        setActiveIndex(0);
      } else {
        setActiveIndex(Math.min(activeIndex + 1, visibleNodes.length - 1));
      }
      return true;
    }
    case "ArrowUp": {
      e.preventDefault();
      if (activeIndex === -1) {
        setActiveIndex(visibleNodes.length - 1);
      } else {
        setActiveIndex(Math.max(activeIndex - 1, 0));
      }
      return true;
    }
    case "ArrowRight": {
      if (activeIndex === -1) return false;
      const rightNode = visibleNodes[activeIndex];
      if (!rightNode.isBranch) return false;
      if (!isExpanded(rightNode.node.id)) {
        e.preventDefault();
        toggleExpand(rightNode.node.id);
        return true;
      }
      const nextIndex = activeIndex + 1;
      if (nextIndex < visibleNodes.length) {
        e.preventDefault();
        setActiveIndex(nextIndex);
        return true;
      }
      return false;
    }
    case "ArrowLeft": {
      if (activeIndex === -1) return false;
      const leftNode = visibleNodes[activeIndex];
      if (leftNode.isBranch && isExpanded(leftNode.node.id)) {
        e.preventDefault();
        toggleExpand(leftNode.node.id);
        return true;
      }
      const currentDepth = leftNode.depth;
      if (currentDepth === 0) return false;
      for (let i = activeIndex - 1; i >= 0; i--) {
        if (visibleNodes[i].depth < currentDepth) {
          e.preventDefault();
          setActiveIndex(i);
          return true;
        }
      }
      return false;
    }
    case "Home": {
      e.preventDefault();
      setActiveIndex(0);
      return true;
    }
    case "End": {
      e.preventDefault();
      setActiveIndex(visibleNodes.length - 1);
      return true;
    }
    case "Enter": {
      if (activeIndex === -1) return false;
      e.preventDefault();
      const activeNode = visibleNodes[activeIndex];
      toggleNode(activeNode.node.id);
      return true;
    }
    default:
      return false;
  }
}

const EMPTY_PLACEHOLDER_PREFIX = "\0__tsf_empty__";
function collectAllBranchIds(nodes) {
  const result = [];
  const visit = (list) => {
    for (const node of list) {
      if (node.children !== void 0) {
        result.push(node.id);
        if (node.children) visit(node.children);
      }
    }
  };
  visit(nodes);
  return result;
}
function buildDefaultExpanded(nodes, defaultExpandLevel) {
  const result = /* @__PURE__ */ new Set();
  const visit = (list, depth) => {
    if (depth >= defaultExpandLevel) return;
    for (const node of list) {
      if (node.children !== void 0) {
        result.add(node.id);
        if (node.children) visit(node.children, depth + 1);
      }
    }
  };
  visit(nodes, 0);
  return result;
}
function isBranchNode(node) {
  return node.children !== void 0;
}
function nodeMatchesSearch(node, search) {
  const labelMatch = node.label.toLowerCase().includes(search);
  const searchTextMatch = node.searchText ? node.searchText.toLowerCase().includes(search) : false;
  if (labelMatch || searchTextMatch) return true;
  if (node.children) {
    return node.children.some((child) => nodeMatchesSearch(child, search));
  }
  return false;
}
function flattenTree(nodes, expandedIds, search, depth) {
  const result = [];
  for (const node of nodes) {
    const isBranch = isBranchNode(node);
    if (search !== void 0) {
      if (!nodeMatchesSearch(node, search)) continue;
    }
    const expanded = isBranch ? search !== void 0 ? (
      // During search, auto-expand branches that have matching descendants
      nodeMatchesSearch(node, search)
    ) : expandedIds.has(node.id) : false;
    result.push({
      node,
      depth,
      isExpanded: expanded,
      isBranch,
      isLoading: false
    });
    if (isBranch && expanded) {
      if (node.children && node.children.length > 0) {
        result.push(
          ...flattenTree(node.children, expandedIds, search, depth + 1)
        );
      } else if (node.children && node.children.length === 0) {
        result.push({
          node: {
            id: `${EMPTY_PLACEHOLDER_PREFIX}${node.id}`,
            label: "No items"
          },
          depth: depth + 1,
          isExpanded: false,
          isBranch: false,
          isLoading: false,
          isEmptyPlaceholder: true
        });
      }
    }
  }
  return result;
}
function useTree(options) {
  const {
    nodes,
    value,
    onSelectedOptionsChange,
    selectionMode = "linked",
    valueConsistsOf = "LEAF_PRIORITY",
    defaultExpandLevel = 0,
    expandedIds: controlledExpandedIds,
    onExpandedIdsChange,
    searchValue,
    ancestry,
    loadingChildrenIds
  } = options;
  const isControlled = controlledExpandedIds !== void 0;
  const [uncontrolledExpandedIds, setUncontrolledExpandedIds] = useState(() => buildDefaultExpanded(nodes, defaultExpandLevel));
  const appliedDefaultExpandRef = useRef(nodes.length > 0);
  useEffect(() => {
    if (!appliedDefaultExpandRef.current && nodes.length > 0 && !isControlled) {
      appliedDefaultExpandRef.current = true;
      setUncontrolledExpandedIds(
        buildDefaultExpanded(nodes, defaultExpandLevel)
      );
    }
  }, [nodes, defaultExpandLevel, isControlled]);
  const [activeIndex, setActiveIndex] = useState(-1);
  const expandedIds = isControlled ? controlledExpandedIds : uncontrolledExpandedIds;
  const isExpanded = useCallback(
    (id) => expandedIds.has(id),
    [expandedIds]
  );
  const setExpandedIds = useCallback(
    (next) => {
      if (isControlled) {
        onExpandedIdsChange?.(next);
      } else {
        setUncontrolledExpandedIds(next);
      }
    },
    [isControlled, onExpandedIdsChange]
  );
  const toggleExpand = useCallback(
    (id) => {
      const next = new Set(expandedIds);
      if (next.has(id)) {
        next.delete(id);
      } else {
        next.add(id);
      }
      setExpandedIds(next);
    },
    [expandedIds, setExpandedIds]
  );
  const expandAll = useCallback(() => {
    const allBranchIds = collectAllBranchIds(nodes);
    setExpandedIds(new Set(allBranchIds));
  }, [nodes, setExpandedIds]);
  const collapseAll = useCallback(() => {
    setExpandedIds(/* @__PURE__ */ new Set());
  }, [setExpandedIds]);
  const normalizedSearch = searchValue?.trim().toLowerCase() || void 0;
  const visibleNodes = useMemo(() => {
    const flat = flattenTree(nodes, expandedIds, normalizedSearch, 0);
    if (!loadingChildrenIds || loadingChildrenIds.size === 0) return flat;
    return flat.map(
      (vn) => loadingChildrenIds.has(vn.node.id) ? { ...vn, isLoading: true } : vn
    );
  }, [nodes, expandedIds, normalizedSearch, loadingChildrenIds]);
  const visibleNodeMap = useMemo(() => {
    const map = /* @__PURE__ */ new Map();
    for (const vn of visibleNodes) {
      map.set(vn.node.id, vn);
    }
    return map;
  }, [visibleNodes]);
  const visibleSiblingsMap = useMemo(() => {
    const map = /* @__PURE__ */ new Map();
    const groups = /* @__PURE__ */ new Map();
    const parentStack = [];
    for (const vn of visibleNodes) {
      if (vn.isEmptyPlaceholder) continue;
      while (parentStack.length > 0 && parentStack[parentStack.length - 1].depth >= vn.depth) {
        parentStack.pop();
      }
      const parentKey = parentStack.length > 0 ? parentStack[parentStack.length - 1].id : "__root__";
      let group = groups.get(parentKey);
      if (!group) {
        group = [];
        groups.set(parentKey, group);
      }
      group.push(vn.node.id);
      if (vn.isBranch && vn.isExpanded) {
        parentStack.push({ id: vn.node.id, depth: vn.depth });
      }
    }
    for (const group of groups.values()) {
      for (let i = 0; i < group.length; i++) {
        map.set(group[i], { setSize: group.length, posInSet: i + 1 });
      }
    }
    return map;
  }, [visibleNodes]);
  const orphanAncestorIds = useMemo(() => {
    const loadedIds = /* @__PURE__ */ new Set();
    const collect = (list) => {
      for (const n of list) {
        loadedIds.add(n.id);
        if (n.children) collect(n.children);
      }
    };
    collect(nodes);
    const ancestors = /* @__PURE__ */ new Set();
    for (const v of value) {
      if (loadedIds.has(v.id)) continue;
      if (v.path && v.path.length > 0) {
        for (const ancestorId of v.path) ancestors.add(ancestorId);
        continue;
      }
      if (ancestry) {
        const seen = /* @__PURE__ */ new Set();
        let parent = ancestry.get(v.id);
        while (parent !== void 0 && !seen.has(parent)) {
          seen.add(parent);
          ancestors.add(parent);
          parent = ancestry.get(parent);
        }
      }
    }
    return ancestors;
  }, [nodes, value, ancestry]);
  const checkStates = useMemo(
    () => computeCheckStates(
      nodes,
      value,
      selectionMode,
      valueConsistsOf,
      orphanAncestorIds
    ),
    [nodes, value, selectionMode, valueConsistsOf, orphanAncestorIds]
  );
  const getCheckState = useCallback(
    (id) => checkStates.get(id) ?? "unchecked",
    [checkStates]
  );
  const toggleNode = useCallback(
    (id) => {
      const newValue = toggleNodeSelection(
        nodes,
        value,
        id,
        selectionMode,
        valueConsistsOf
      );
      onSelectedOptionsChange(newValue);
    },
    [nodes, value, selectionMode, valueConsistsOf, onSelectedOptionsChange]
  );
  const onKeyDown = useCallback(
    (e) => {
      return handleTreeKeyDown(e, {
        visibleNodes,
        activeIndex,
        setActiveIndex,
        isExpanded,
        toggleExpand,
        toggleNode
      });
    },
    [visibleNodes, activeIndex, isExpanded, toggleExpand, toggleNode]
  );
  const activeDescendantId = activeIndex >= 0 && activeIndex < visibleNodes.length ? `treeitem-${visibleNodes[activeIndex].node.id}` : void 0;
  const setActiveNodeId = useCallback(
    (id) => {
      const idx = visibleNodes.findIndex((vn) => vn.node.id === id);
      if (idx !== -1) setActiveIndex(idx);
    },
    [visibleNodes]
  );
  const getTreeItemProps = useCallback(
    (id) => {
      const visibleNode = visibleNodeMap.get(id);
      const depth = visibleNode?.depth ?? 0;
      const isBranch = visibleNode?.isBranch ?? false;
      const expanded = isBranch ? visibleNode?.isExpanded ?? isExpanded(id) : void 0;
      const siblingInfo = visibleSiblingsMap.get(id);
      const setSize = siblingInfo?.setSize ?? 1;
      const posInSet = siblingInfo?.posInSet ?? 1;
      const checkState = getCheckState(id);
      const isSelected = checkState === "checked";
      const props = {
        id: `treeitem-${id}`,
        role: "treeitem",
        "aria-level": depth + 1,
        "aria-setsize": setSize,
        "aria-posinset": posInSet,
        "aria-selected": isSelected
      };
      if (isBranch) {
        props["aria-expanded"] = expanded;
      }
      return props;
    },
    [visibleNodeMap, isExpanded, visibleSiblingsMap, getCheckState]
  );
  const treeProps = useMemo(
    () => ({
      role: "tree",
      "aria-activedescendant": activeDescendantId,
      "aria-multiselectable": selectionMode !== "single"
    }),
    [activeDescendantId, selectionMode]
  );
  return {
    nodes,
    visibleNodes,
    getCheckState,
    toggleNode,
    isExpanded,
    toggleExpand,
    expandAll,
    collapseAll,
    activeDescendantId,
    setActiveNodeId,
    onKeyDown,
    getTreeItemProps,
    treeProps
  };
}

const active = "_active_r0ti0_40";
const styles$1 = {
	"tree-row": "_tree-row_r0ti0_2",
	"expand-icon": "_expand-icon_r0ti0_8",
	"label-container": "_label-container_r0ti0_29",
	active: active
};

const INDENT_BASE = 16;
const INDENT_PER_DEPTH = 24;
const EXPAND_ICON_WIDTH = 32;
function TreeRow({
  visible,
  checkState,
  itemProps,
  branchesSelectable = true,
  leavesSelectable = true,
  emptyBranchesSelectable = true,
  readOnly = false,
  isActive,
  onToggleNode,
  onToggleExpand,
  onHover,
  onLoadChildren,
  depth
}) {
  const { node, isExpanded, isBranch, isLoading, isEmptyPlaceholder } = visible;
  if (isEmptyPlaceholder) {
    return /* @__PURE__ */ jsx(
      OptionRow,
      {
        disabled: true,
        className: styles$1["tree-row"],
        style: {
          paddingInlineStart: INDENT_BASE + depth * INDENT_PER_DEPTH + EXPAND_ICON_WIDTH
        },
        children: /* @__PURE__ */ jsx("span", { className: styles$1["label-container"], children: /* @__PURE__ */ jsx(Text, { children: node.label }) })
      }
    );
  }
  const isDisabled = node.disabled === true;
  const isEmptyBranch = isBranch && Array.isArray(node.children) && node.children.length === 0;
  const isSelectable = (() => {
    if (!isBranch) return leavesSelectable;
    if (!branchesSelectable) return false;
    if (isEmptyBranch && !emptyBranchesSelectable) return false;
    return true;
  })();
  const showExpandIcon = isBranch;
  const paddingInlineStart = INDENT_BASE + depth * INDENT_PER_DEPTH + (showExpandIcon ? 0 : EXPAND_ICON_WIDTH);
  const rowDisabled = readOnly || isDisabled || !isSelectable && !isBranch;
  const expandNode = () => {
    onToggleExpand(node.id);
    if (node.children === null) onLoadChildren?.(node);
  };
  const handleExpandClick = (e) => {
    e.stopPropagation();
    if (isDisabled) return;
    expandNode();
  };
  const handleRowClick = () => {
    if (isDisabled) return;
    if (isBranch) {
      if (isSelectable) {
        onToggleNode(node.id);
      } else {
        expandNode();
      }
    } else if (isSelectable) {
      onToggleNode(node.id);
    }
  };
  let expandIconElement = null;
  if (showExpandIcon) {
    if (isLoading && checkState !== "loading") {
      expandIconElement = /* @__PURE__ */ jsx(Spinner, { size: "small", "aria-hidden": "true" });
    } else if (isExpanded) {
      expandIconElement = /* @__PURE__ */ jsx(Icon, { svg: SvgKeyboardArrowDown, size: "small", inherit: true, "aria-hidden": "true" });
    } else {
      expandIconElement = /* @__PURE__ */ jsx(
        Icon,
        {
          svg: SvgKeyboardArrowRight,
          size: "small",
          inherit: true,
          "aria-hidden": "true"
        }
      );
    }
  }
  return /* @__PURE__ */ jsxs(
    OptionRow,
    {
      ...itemProps,
      disabled: rowDisabled,
      style: { paddingInlineStart },
      className: cx(styles$1["tree-row"], {
        [styles$1["active"]]: isActive
      }),
      onMouseDown: (e) => e.preventDefault(),
      onMouseEnter: () => onHover?.(node.id),
      onClick: handleRowClick,
      "aria-disabled": rowDisabled || void 0,
      children: [
        showExpandIcon && /* @__PURE__ */ jsx(
          "span",
          {
            className: styles$1["expand-icon"],
            onClick: handleExpandClick,
            "aria-hidden": "true",
            children: expandIconElement
          }
        ),
        isSelectable && /* @__PURE__ */ jsx(OptionRowSideContent, { children: /* @__PURE__ */ jsx(OptionCheckbox, { checkState }) }),
        /* @__PURE__ */ jsx(OptionContentArea, { label: node.label, content: node.content })
      ]
    }
  );
}

const panel = "_panel_1nd4g_1";
const styles = {
	panel: panel
};

const TreePanel = forwardRef(
  function TreePanelInner({
    visibleNodes,
    getCheckState,
    toggleNode,
    toggleExpand,
    getTreeItemProps,
    treeProps,
    activeDescendantId,
    branchesSelectable,
    leavesSelectable,
    emptyBranchesSelectable,
    readOnly,
    onHover,
    onLoadChildren
  }, ref) {
    return /* @__PURE__ */ jsx(
      "div",
      {
        ref,
        ...treeProps,
        className: cx(styles.panel, treeProps.className),
        children: visibleNodes.map((visible) => /* @__PURE__ */ jsx(
          TreeRow,
          {
            visible,
            checkState: getCheckState(visible.node.id),
            itemProps: getTreeItemProps(visible.node.id),
            isActive: activeDescendantId === `treeitem-${visible.node.id}`,
            branchesSelectable,
            leavesSelectable,
            emptyBranchesSelectable,
            readOnly,
            onToggleNode: toggleNode,
            onToggleExpand: toggleExpand,
            onHover,
            onLoadChildren,
            depth: visible.depth
          },
          visible.node.id
        ))
      }
    );
  }
);
TreePanel.displayName = "TreePanel";

const EST_PADDING_BLOCK = 16;
const EST_LINE_HEIGHT = 18;
const EST_CHIP_ROW_HEIGHT = 28;
const EST_SIDE_CONTENT_HEIGHT = 40;
const EST_BASE_ROW_HEIGHT = EST_PADDING_BLOCK + EST_LINE_HEIGHT;
function estimateTreeRowHeight(visible) {
  const content = visible.node.content;
  if (!content) return EST_BASE_ROW_HEIGHT;
  let contentHeight = EST_LINE_HEIGHT;
  if (content.description) contentHeight += EST_LINE_HEIGHT;
  if (content.chips?.length) contentHeight += EST_CHIP_ROW_HEIGHT;
  if (content.avatar || content.icon)
    contentHeight = Math.max(contentHeight, EST_SIDE_CONTENT_HEIGHT);
  return EST_PADDING_BLOCK + contentHeight;
}
const VirtualizedTreePanel = forwardRef(function VirtualizedTreePanelInner(props, ref) {
  const {
    visibleNodes,
    getCheckState,
    toggleNode,
    toggleExpand,
    getTreeItemProps,
    treeProps,
    activeDescendantId,
    branchesSelectable = true,
    leavesSelectable = true,
    emptyBranchesSelectable = true,
    readOnly = false,
    onHover,
    onLoadChildren
  } = props;
  const scrollRef = useRef(null);
  const estimateSize = useCallback(
    (index) => estimateTreeRowHeight(visibleNodes[index]),
    [visibleNodes]
  );
  const virtualizer = useVirtualizer({
    count: visibleNodes.length,
    getScrollElement: () => scrollRef.current,
    estimateSize,
    overscan: 5
  });
  const virtualItems = virtualizer.getVirtualItems();
  return /* @__PURE__ */ jsx(
    "div",
    {
      ref: (node) => {
        scrollRef.current = node;
        if (typeof ref === "function") ref(node);
        else if (ref) ref.current = node;
      },
      ...treeProps,
      className: cx(styles.panel, treeProps.className),
      children: /* @__PURE__ */ jsx(
        "div",
        {
          style: {
            height: virtualizer.getTotalSize(),
            width: "100%",
            position: "relative"
          },
          children: virtualItems.map((virtualItem) => {
            const visible = visibleNodes[virtualItem.index];
            return /* @__PURE__ */ jsx(
              "div",
              {
                style: {
                  position: "absolute",
                  top: 0,
                  left: 0,
                  width: "100%",
                  height: `${virtualItem.size}px`,
                  transform: `translateY(${virtualItem.start}px)`
                },
                children: /* @__PURE__ */ jsx(
                  TreeRow,
                  {
                    visible,
                    checkState: getCheckState(visible.node.id),
                    itemProps: getTreeItemProps(visible.node.id),
                    isActive: activeDescendantId === `treeitem-${visible.node.id}`,
                    branchesSelectable,
                    leavesSelectable,
                    emptyBranchesSelectable,
                    readOnly,
                    onToggleNode: toggleNode,
                    onToggleExpand: toggleExpand,
                    onHover,
                    onLoadChildren,
                    depth: visible.depth
                  }
                )
              },
              visible.node.id
            );
          })
        }
      )
    }
  );
});

const TreeContent = forwardRef(
  function TreeContent2({ loading, hasNoVisibleNodes, virtualize, panelProps }, ref) {
    if (loading) {
      return /* @__PURE__ */ jsx(
        "div",
        {
          style: {
            padding: "var(--a2-spacing-200)",
            display: "flex",
            justifyContent: "center"
          },
          children: /* @__PURE__ */ jsx(Spinner, { size: "medium" })
        }
      );
    }
    if (hasNoVisibleNodes) {
      return /* @__PURE__ */ jsx(
        "div",
        {
          role: "presentation",
          style: {
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
            padding: "1rem",
            textAlign: "center"
          },
          children: /* @__PURE__ */ jsx(Text, { subdued: true, size: "small", children: "No match found" })
        }
      );
    }
    return virtualize ? /* @__PURE__ */ jsx(VirtualizedTreePanel, { ref, ...panelProps }) : /* @__PURE__ */ jsx(TreePanel, { ref, ...panelProps });
  }
);
TreeContent.displayName = "TreeContent";

function findNode(nodes, id) {
  for (const node of nodes) {
    if (node.id === id) return node;
    if (node.children) {
      const found = findNode(node.children, id);
      if (found) return found;
    }
  }
  return void 0;
}
function hasUnloadedDescendants(node) {
  if (node.children === null) return true;
  if (!node.children) return false;
  return node.children.some(hasUnloadedDescendants);
}
function findUnloadedDescendants(node) {
  const result = [];
  if (!node.children) return result;
  for (const child of node.children) {
    if (child.children === null) {
      result.push(child);
    } else if (child.children) {
      result.push(...findUnloadedDescendants(child));
    }
  }
  return result;
}

function useTreeLazyCascade(options) {
  const {
    tree,
    loader,
    isSingleSelect,
    branchesSelectable,
    leavesSelectable,
    emptyBranchesSelectable,
    readOnly = false,
    onCloseAfterSingleSelect
  } = options;
  const pendingToggleRef = useRef(null);
  const loadedForToggleRef = useRef(/* @__PURE__ */ new Set());
  const [pendingToggleNodeId, setPendingToggleNodeId] = useState(null);
  useEffect(() => {
    if (pendingToggleRef.current === null) return;
    const nodeId = pendingToggleRef.current;
    const node = findNode(tree.nodes, nodeId);
    if (!node) return;
    if (node.children === null) return;
    if (loader.isLoadingChildren.size > 0) return;
    const unloaded = findUnloadedDescendants(node).filter(
      (n) => !loadedForToggleRef.current.has(n.id)
    );
    if (unloaded.length > 0) {
      for (const n of unloaded) {
        loadedForToggleRef.current.add(n.id);
        void loader.loadChildren(n);
      }
      return;
    }
    pendingToggleRef.current = null;
    loadedForToggleRef.current = /* @__PURE__ */ new Set();
    setPendingToggleNodeId(null);
    tree.toggleNode(nodeId);
    if (isSingleSelect) {
      onCloseAfterSingleSelect?.();
    }
  }, [tree, isSingleSelect, onCloseAfterSingleSelect, loader]);
  const handleToggleNode = useCallback(
    (nodeId) => {
      if (readOnly) return;
      const node = findNode(tree.nodes, nodeId);
      if (!node) return;
      const isBranch = node.children !== void 0;
      const isEmptyBranch = isBranch && Array.isArray(node.children) && node.children.length === 0;
      const isNodeSelectable = isBranch ? branchesSelectable && (isEmptyBranch ? emptyBranchesSelectable : true) : leavesSelectable;
      if (!isNodeSelectable) {
        if (isBranch) {
          tree.toggleExpand(nodeId);
          if (node.children === null) {
            void loader.loadChildren(node);
          }
        }
        return;
      }
      if (hasUnloadedDescendants(node)) {
        pendingToggleRef.current = nodeId;
        loadedForToggleRef.current = /* @__PURE__ */ new Set();
        setPendingToggleNodeId(nodeId);
        if (node.children === null) {
          loadedForToggleRef.current.add(node.id);
          void loader.loadChildren(node);
        } else {
          const unloaded = findUnloadedDescendants(node);
          for (const n of unloaded) {
            loadedForToggleRef.current.add(n.id);
            void loader.loadChildren(n);
          }
        }
        return;
      }
      tree.toggleNode(nodeId);
      if (isSingleSelect) {
        onCloseAfterSingleSelect?.();
      }
    },
    [
      tree,
      isSingleSelect,
      onCloseAfterSingleSelect,
      loader,
      readOnly,
      branchesSelectable,
      leavesSelectable,
      emptyBranchesSelectable
    ]
  );
  const reset = useCallback(() => {
    pendingToggleRef.current = null;
    loadedForToggleRef.current = /* @__PURE__ */ new Set();
    setPendingToggleNodeId(null);
  }, []);
  return { handleToggleNode, pendingToggleNodeId, reset };
}

function applyChildCounts(nodes) {
  return nodes.map((node) => {
    if (!Array.isArray(node.children)) return node;
    const children = applyChildCounts(node.children);
    return {
      ...node,
      children,
      childCount: node.childCount ?? children.length
    };
  });
}
function flattenNodes(nodes) {
  return nodes.flatMap((node) => [
    node,
    ...node.children ? flattenNodes(node.children) : []
  ]);
}
function pruneTree(nodes, matchedIds) {
  const result = [];
  for (const node of nodes) {
    if (matchedIds.has(node.id)) {
      result.push(node);
    } else if (node.children && node.children.length > 0) {
      const filteredChildren = pruneTree(node.children, matchedIds);
      if (filteredChildren.length > 0) {
        result.push({ ...node, children: filteredChildren });
      }
    }
  }
  return result;
}
function defaultTreeSyncFilter(nodes, searchValue) {
  if (!searchValue) return nodes;
  const flat = flattenNodes(nodes);
  const matched = matchSorter(flat, searchValue, {
    keys: ["label", "searchText"]
  });
  const matchedIds = new Set(matched.map((n) => n.id));
  return pruneTree(nodes, matchedIds);
}
function toTreeSyncFilterFn(filter) {
  if (typeof filter === "function") return filter;
  return (nodes, searchValue) => {
    if (!searchValue) return nodes;
    const flat = flattenNodes(nodes);
    const matched = matchSorter(flat, searchValue, filter);
    const matchedIds = new Set(matched.map((n) => n.id));
    return pruneTree(nodes, matchedIds);
  };
}

export { TreeContent as T, useTree as a, useTreeLazyCascade as b, applyChildCounts as c, defaultTreeSyncFilter as d, findNode as f, toTreeSyncFilterFn as t, useTreeLoader as u };
//# sourceMappingURL=treeSync-oC-VZpP0.js.map
