'use strict';

var chunkOLISEQHS_cjs = require('./chunk-OLISEQHS.cjs');
var React = require('react');
var lib = require('@djangocfg/ui-core/lib');
var jsxRuntime = require('react/jsx-runtime');
var lucideReact = require('lucide-react');
var hooks = require('@djangocfg/ui-core/hooks');

function _interopNamespace(e) {
  if (e && e.__esModule) return e;
  var n = Object.create(null);
  if (e) {
    Object.keys(e).forEach(function (k) {
      if (k !== 'default') {
        var d = Object.getOwnPropertyDescriptor(e, k);
        Object.defineProperty(n, k, d.get ? d : {
          enumerable: true,
          get: function () { return e[k]; }
        });
      }
    });
  }
  n.default = e;
  return Object.freeze(n);
}

var React__namespace = /*#__PURE__*/_interopNamespace(React);

// src/tools/Tree/types.ts
var DEFAULT_TREE_LABELS = {
  loading: "Loading\u2026",
  empty: "Nothing to show",
  error: "Failed to load",
  searchPlaceholder: "Search\u2026",
  searchMatches: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((n) => `${n} match${n === 1 ? "" : "es"}`, "searchMatches"),
  ariaLabel: "Tree"
};

// src/tools/Tree/data/childCache.ts
var createChildCache = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => /* @__PURE__ */ new Map(), "createChildCache");
var resolveChildren = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((cache, node) => {
  if (Array.isArray(node.children)) {
    return { children: node.children, status: "loaded" };
  }
  const entry = cache.get(node.id);
  if (!entry) return { children: null, status: "idle" };
  if (entry.status === "loaded") {
    return { children: entry.children, status: "loaded" };
  }
  return { children: null, status: entry.status, error: entry.error };
}, "resolveChildren");

// src/tools/Tree/data/flatten.ts
var isNodeFolder = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((node) => {
  if (typeof node.isFolder === "boolean") return node.isFolder;
  return Array.isArray(node.children);
}, "isNodeFolder");
function flattenTree({
  roots,
  expandedIds,
  cache,
  filterNode
}) {
  const out = [];
  const walk = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((nodes, level, parentId) => {
    for (const node of nodes) {
      if (filterNode && !filterNode(node)) continue;
      const isFolder = isNodeFolder(node);
      const isExpanded = expandedIds.has(node.id);
      const resolved = isFolder ? resolveChildren(cache, node) : { children: [], status: "loaded" };
      out.push({
        node,
        level,
        parentId,
        isFolder,
        isExpanded,
        isLoading: resolved.status === "loading",
        hasError: resolved.status === "error"
      });
      if (isFolder && isExpanded && resolved.children) {
        walk(resolved.children, level + 1, node.id);
      }
    }
  }, "walk");
  walk(roots, 0, null);
  return out;
}
chunkOLISEQHS_cjs.__name(flattenTree, "flattenTree");

// src/tools/Tree/data/persist.ts
var KEY_PREFIX = "@djangocfg/tree:";
var VERSION = 1;
function safeStorage() {
  if (typeof window === "undefined") return null;
  try {
    return window.localStorage;
  } catch {
    return null;
  }
}
chunkOLISEQHS_cjs.__name(safeStorage, "safeStorage");
function loadTreeState(key) {
  const storage = safeStorage();
  if (!storage) return null;
  try {
    const raw = storage.getItem(KEY_PREFIX + key);
    if (!raw) return null;
    const parsed = JSON.parse(raw);
    if (parsed.version !== VERSION) return null;
    return {
      expandedItems: Array.isArray(parsed.expandedItems) ? parsed.expandedItems : [],
      selectedItems: Array.isArray(parsed.selectedItems) ? parsed.selectedItems : []
    };
  } catch {
    return null;
  }
}
chunkOLISEQHS_cjs.__name(loadTreeState, "loadTreeState");
function saveTreeState(key, state) {
  const storage = safeStorage();
  if (!storage) return;
  try {
    const payload = { ...state, version: VERSION };
    storage.setItem(KEY_PREFIX + key, JSON.stringify(payload));
  } catch {
  }
}
chunkOLISEQHS_cjs.__name(saveTreeState, "saveTreeState");
function clearTreeState(key) {
  const storage = safeStorage();
  if (!storage) return;
  try {
    storage.removeItem(KEY_PREFIX + key);
  } catch {
  }
}
chunkOLISEQHS_cjs.__name(clearTreeState, "clearTreeState");

// src/tools/Tree/data/appearance.ts
var DENSITY_PRESETS = {
  compact: { rowHeight: 24, iconSize: 14, fontSize: 13, gap: 6 },
  cozy: { rowHeight: 28, iconSize: 16, fontSize: 13, gap: 8 },
  comfortable: { rowHeight: 32, iconSize: 16, fontSize: 14, gap: 8 }
};
var DEFAULT_TREE_APPEARANCE = {
  density: "cozy",
  ...DENSITY_PRESETS.cozy,
  iconStrokeWidth: 1.5,
  indent: 16,
  accent: "default",
  radius: "sm",
  indentGuideOpacity: 0.4,
  showActiveIndicator: true
};
function resolveAppearance(input, outerIndent) {
  if (!input && outerIndent === void 0) return DEFAULT_TREE_APPEARANCE;
  const density = input?.density ?? "cozy";
  const preset = DENSITY_PRESETS[density];
  return {
    density,
    rowHeight: input?.rowHeight ?? preset.rowHeight,
    iconSize: input?.iconSize ?? preset.iconSize,
    iconStrokeWidth: input?.iconStrokeWidth ?? DEFAULT_TREE_APPEARANCE.iconStrokeWidth,
    fontSize: input?.fontSize ?? preset.fontSize,
    gap: input?.gap ?? preset.gap,
    indent: input?.indent ?? outerIndent ?? DEFAULT_TREE_APPEARANCE.indent,
    accent: input?.accent ?? DEFAULT_TREE_APPEARANCE.accent,
    radius: input?.radius ?? DEFAULT_TREE_APPEARANCE.radius,
    indentGuideOpacity: input?.indentGuideOpacity ?? DEFAULT_TREE_APPEARANCE.indentGuideOpacity,
    showActiveIndicator: input?.showActiveIndicator ?? DEFAULT_TREE_APPEARANCE.showActiveIndicator
  };
}
chunkOLISEQHS_cjs.__name(resolveAppearance, "resolveAppearance");
function appearanceToStyle(a) {
  return {
    ["--tree-row-height"]: `${a.rowHeight}px`,
    ["--tree-icon-size"]: `${a.iconSize}px`,
    ["--tree-icon-stroke"]: a.iconStrokeWidth,
    ["--tree-font-size"]: `${a.fontSize}px`,
    ["--tree-gap"]: `${a.gap}px`,
    ["--tree-indent"]: `${a.indent}px`,
    ["--tree-guide-opacity"]: a.indentGuideOpacity
  };
}
chunkOLISEQHS_cjs.__name(appearanceToStyle, "appearanceToStyle");
var RADIUS_CLASS = {
  none: "rounded-none",
  sm: "rounded-sm",
  md: "rounded-md"
};
function radiusClass(a) {
  return RADIUS_CLASS[a.radius];
}
chunkOLISEQHS_cjs.__name(radiusClass, "radiusClass");
var HOVER = {
  subtle: "hover:bg-foreground/[.03]",
  default: "hover:bg-foreground/[.06]",
  strong: "hover:bg-foreground/[.09]"
};
var FOCUSED_NOT_SELECTED = {
  subtle: "data-[focused=true]:bg-foreground/[.05]",
  default: "data-[focused=true]:bg-foreground/[.08]",
  strong: "data-[focused=true]:bg-foreground/[.12]"
};
var SELECTED_INACTIVE = {
  subtle: "data-[selected=true]:bg-foreground/[.06]",
  default: "data-[selected=true]:bg-foreground/[.10]",
  strong: "data-[selected=true]:bg-foreground/[.14]"
};
var SELECTED_ACTIVE = {
  subtle: "data-[selected=true]:group-focus-within/tree:bg-primary/10 data-[selected=true]:group-focus-within/tree:text-primary",
  default: "data-[selected=true]:group-focus-within/tree:bg-primary/15 data-[selected=true]:group-focus-within/tree:text-primary",
  strong: "data-[selected=true]:group-focus-within/tree:bg-primary/25 data-[selected=true]:group-focus-within/tree:text-primary"
};
function rowStateClasses(a) {
  return [
    HOVER[a.accent],
    FOCUSED_NOT_SELECTED[a.accent],
    SELECTED_INACTIVE[a.accent],
    SELECTED_ACTIVE[a.accent]
  ].join(" ");
}
chunkOLISEQHS_cjs.__name(rowStateClasses, "rowStateClasses");
var reducer = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((state, action) => {
  switch (action.type) {
    case "expand": {
      if (state.expanded.has(action.id)) return state;
      const next = new Set(state.expanded);
      next.add(action.id);
      return { ...state, expanded: next };
    }
    case "collapse": {
      if (!state.expanded.has(action.id)) return state;
      const next = new Set(state.expanded);
      next.delete(action.id);
      return { ...state, expanded: next };
    }
    case "toggle": {
      const next = new Set(state.expanded);
      if (next.has(action.id)) next.delete(action.id);
      else next.add(action.id);
      return { ...state, expanded: next };
    }
    case "set-expanded":
      return { ...state, expanded: new Set(action.ids) };
    case "select": {
      if (action.mode === "none") return state;
      if (action.mode === "single") {
        return { ...state, selected: /* @__PURE__ */ new Set([action.id]), focused: action.id };
      }
      const next = new Set(state.selected);
      if (next.has(action.id)) next.delete(action.id);
      else next.add(action.id);
      return { ...state, selected: next, focused: action.id };
    }
    case "select-many":
      return { ...state, selected: new Set(action.ids) };
    case "clear-selection":
      return { ...state, selected: /* @__PURE__ */ new Set() };
    case "focus":
      return { ...state, focused: action.id };
    case "set-query":
      return { ...state, query: action.q };
    case "cache-tick":
      return { ...state, cacheTick: state.cacheTick + 1 };
    default:
      return state;
  }
}, "reducer");
var TreeContext = React.createContext(null);
function useTreeContext() {
  const ctx = React__namespace.useContext(TreeContext);
  if (!ctx) {
    throw new Error("useTreeContext must be used inside <TreeProvider>");
  }
  return ctx;
}
chunkOLISEQHS_cjs.__name(useTreeContext, "useTreeContext");
var setEqualsArr = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((set, arr) => {
  if (set.size !== arr.length) return false;
  for (const id of arr) if (!set.has(id)) return false;
  return true;
}, "setEqualsArr");
var collectAllIds = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((roots, cache, out) => {
  for (const node of roots) {
    if (Array.isArray(node.children)) {
      out.push(node.id);
      collectAllIds(node.children, cache, out);
    } else if (node.isFolder) {
      out.push(node.id);
      const entry = cache.get(node.id);
      if (entry?.children) collectAllIds(entry.children, cache, out);
    }
  }
}, "collectAllIds");
function TreeProvider(props) {
  const {
    data,
    getItemName,
    loadChildren,
    selectionMode = "single",
    activationMode = "single-click",
    initialExpandedIds,
    initialSelectedIds,
    indent,
    appearance,
    onSelectionChange,
    onExpansionChange,
    onActivate,
    filterNode,
    enableSearch = false,
    showIndentGuides = false,
    renderIcon,
    renderLabel,
    renderActions,
    renderContextMenu,
    labels: labelsOverride,
    persistKey,
    persistSelection = false,
    children
  } = props;
  const labels = React.useMemo(
    () => ({ ...DEFAULT_TREE_LABELS, ...labelsOverride }),
    [labelsOverride]
  );
  const resolvedAppearance = React.useMemo(
    () => resolveAppearance(appearance, indent),
    [appearance, indent]
  );
  const persisted = React.useMemo(
    () => persistKey ? loadTreeState(persistKey) : null,
    [persistKey]
  );
  const [state, dispatch] = React.useReducer(reducer, void 0, () => ({
    expanded: new Set(persisted?.expandedItems ?? initialExpandedIds ?? []),
    selected: new Set(
      (persistSelection ? persisted?.selectedItems : void 0) ?? initialSelectedIds ?? []
    ),
    focused: null,
    query: "",
    cacheTick: 0
  }));
  const cacheRef = React.useRef(createChildCache());
  const inflightRef = React.useRef(/* @__PURE__ */ new Map());
  const fetchChildren = React.useCallback(
    async (node) => {
      if (!loadChildren) return;
      if (Array.isArray(node.children)) return;
      const existing = cacheRef.current.get(node.id);
      if (existing?.status === "loaded" || existing?.status === "loading") return;
      const inflight = inflightRef.current.get(node.id);
      if (inflight) return inflight;
      cacheRef.current.set(node.id, { status: "loading", children: [] });
      dispatch({ type: "cache-tick" });
      const promise = (async () => {
        try {
          const children2 = await loadChildren(node);
          cacheRef.current.set(node.id, { status: "loaded", children: children2 });
        } catch (err) {
          cacheRef.current.set(node.id, {
            status: "error",
            children: [],
            error: err instanceof Error ? err.message : String(err)
          });
        } finally {
          inflightRef.current.delete(node.id);
          dispatch({ type: "cache-tick" });
        }
      })();
      inflightRef.current.set(node.id, promise);
      return promise;
    },
    [loadChildren]
  );
  const nodeById = React.useMemo(() => {
    const map = /* @__PURE__ */ new Map();
    const walk = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((nodes) => {
      for (const n of nodes) {
        map.set(n.id, n);
        if (Array.isArray(n.children)) walk(n.children);
        else {
          const entry = cacheRef.current.get(n.id);
          if (entry?.children) walk(entry.children);
        }
      }
    }, "walk");
    walk(data);
    return map;
  }, [data, state.cacheTick]);
  React.useEffect(() => {
    if (!loadChildren) return;
    for (const id of state.expanded) {
      const node = nodeById.get(id);
      if (!node) continue;
      void fetchChildren(node);
    }
  }, [loadChildren, state.expanded, state.cacheTick, nodeById, fetchChildren]);
  const flatRows = React.useMemo(
    () => flattenTree({
      roots: data,
      expandedIds: state.expanded,
      cache: cacheRef.current,
      filterNode
    }),
    [data, state.expanded, state.cacheTick, filterNode]
  );
  const matchingIds = React.useMemo(() => {
    const set = /* @__PURE__ */ new Set();
    if (!enableSearch || state.query.trim() === "") return set;
    const q = state.query.trim().toLowerCase();
    for (const row of flatRows) {
      if (getItemName(row.node).toLowerCase().includes(q)) {
        set.add(row.node.id);
      }
    }
    return set;
  }, [enableSearch, state.query, flatRows, getItemName]);
  const onSelectionChangeRef = React.useRef(onSelectionChange);
  const onExpansionChangeRef = React.useRef(onExpansionChange);
  const onActivateRef = React.useRef(onActivate);
  onSelectionChangeRef.current = onSelectionChange;
  onExpansionChangeRef.current = onExpansionChange;
  onActivateRef.current = onActivate;
  const lastSelectedArrRef = React.useRef([...state.selected]);
  const lastExpandedArrRef = React.useRef([...state.expanded]);
  React.useEffect(() => {
    const arr = [...state.expanded];
    if (!setEqualsArr(state.expanded, lastExpandedArrRef.current)) {
      lastExpandedArrRef.current = arr;
      onExpansionChangeRef.current?.(arr);
      if (persistKey) {
        saveTreeState(persistKey, {
          expandedItems: arr,
          selectedItems: persistSelection ? [...state.selected] : []
        });
      }
    }
  }, [state.expanded, persistKey, persistSelection, state.selected]);
  React.useEffect(() => {
    const arr = [...state.selected];
    if (!setEqualsArr(state.selected, lastSelectedArrRef.current)) {
      lastSelectedArrRef.current = arr;
      onSelectionChangeRef.current?.(arr);
      if (persistKey && persistSelection) {
        saveTreeState(persistKey, {
          expandedItems: [...state.expanded],
          selectedItems: arr
        });
      }
    }
  }, [state.selected, persistKey, persistSelection, state.expanded]);
  const expand = React.useCallback((id) => dispatch({ type: "expand", id }), []);
  const collapse = React.useCallback((id) => dispatch({ type: "collapse", id }), []);
  const toggle = React.useCallback((id) => dispatch({ type: "toggle", id }), []);
  const expandAll = React.useCallback(() => {
    const ids = [];
    collectAllIds(data, cacheRef.current, ids);
    dispatch({ type: "set-expanded", ids });
  }, [data]);
  const collapseAll = React.useCallback(
    () => dispatch({ type: "set-expanded", ids: [] }),
    []
  );
  const select = React.useCallback(
    (id) => dispatch({ type: "select", id, mode: selectionMode }),
    [selectionMode]
  );
  const setSelectedIds = React.useCallback(
    (ids) => dispatch({ type: "select-many", ids }),
    []
  );
  const clearSelection = React.useCallback(() => dispatch({ type: "clear-selection" }), []);
  const setFocus = React.useCallback(
    (id) => dispatch({ type: "focus", id }),
    []
  );
  const setQuery = React.useCallback((q) => dispatch({ type: "set-query", q }), []);
  const refresh = React.useCallback(
    async (id) => {
      const node = nodeById.get(id);
      if (!node || !loadChildren) return;
      cacheRef.current.delete(id);
      dispatch({ type: "cache-tick" });
      await fetchChildren(node);
    },
    [nodeById, loadChildren, fetchChildren]
  );
  const refreshAll = React.useCallback(async () => {
    cacheRef.current.clear();
    dispatch({ type: "cache-tick" });
    if (!loadChildren) return;
    await Promise.all(
      [...state.expanded].map((id) => {
        const node = nodeById.get(id);
        return node ? fetchChildren(node) : void 0;
      })
    );
  }, [loadChildren, state.expanded, nodeById, fetchChildren]);
  const activate = React.useCallback(
    (node, opts = { preview: false }) => onActivateRef.current?.(node, opts),
    []
  );
  const value = React.useMemo(
    () => ({
      expanded: state.expanded,
      selected: state.selected,
      focused: state.focused,
      query: state.query,
      flatRows,
      matchingIds,
      expand,
      collapse,
      toggle,
      expandAll,
      collapseAll,
      select,
      setSelectedIds,
      clearSelection,
      setFocus,
      setQuery,
      refresh,
      refreshAll,
      activate,
      labels,
      appearance: resolvedAppearance,
      indent: resolvedAppearance.indent,
      selectionMode,
      activationMode,
      enableSearch,
      showIndentGuides,
      getItemName,
      renderIcon,
      renderLabel,
      renderActions,
      renderContextMenu
    }),
    [
      state.expanded,
      state.selected,
      state.focused,
      state.query,
      flatRows,
      matchingIds,
      expand,
      collapse,
      toggle,
      expandAll,
      collapseAll,
      select,
      setSelectedIds,
      clearSelection,
      setFocus,
      setQuery,
      refresh,
      refreshAll,
      activate,
      labels,
      resolvedAppearance,
      selectionMode,
      activationMode,
      enableSearch,
      showIndentGuides,
      getItemName,
      renderIcon,
      renderLabel,
      renderActions,
      renderContextMenu
    ]
  );
  return /* @__PURE__ */ jsxRuntime.jsx(TreeContext.Provider, { value, children });
}
chunkOLISEQHS_cjs.__name(TreeProvider, "TreeProvider");
function TreeChevron({ isExpanded, isFolder, className }) {
  const { appearance } = useTreeContext();
  const size = { width: "var(--tree-icon-size)", height: "var(--tree-icon-size)" };
  if (!isFolder) {
    return /* @__PURE__ */ jsxRuntime.jsx(
      "span",
      {
        "aria-hidden": true,
        style: size,
        className: lib.cn("inline-block shrink-0", className)
      }
    );
  }
  const Icon = isExpanded ? lucideReact.ChevronDown : lucideReact.ChevronRight;
  return /* @__PURE__ */ jsxRuntime.jsx(
    Icon,
    {
      "aria-hidden": true,
      strokeWidth: appearance.iconStrokeWidth,
      style: size,
      className: lib.cn(
        "shrink-0 text-muted-foreground/70 transition-transform",
        className
      )
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeChevron, "TreeChevron");
function TreeIcon({ isFolder, isExpanded, className }) {
  const { appearance } = useTreeContext();
  const Icon = isFolder ? isExpanded ? lucideReact.FolderOpen : lucideReact.Folder : lucideReact.File;
  return /* @__PURE__ */ jsxRuntime.jsx(
    Icon,
    {
      "aria-hidden": true,
      strokeWidth: appearance.iconStrokeWidth,
      style: { width: "var(--tree-icon-size)", height: "var(--tree-icon-size)" },
      className: lib.cn(
        "shrink-0",
        isFolder ? "text-foreground/70" : "text-muted-foreground/80",
        className
      )
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeIcon, "TreeIcon");
function TreeIndentGuides({ level, indent }) {
  const { appearance } = useTreeContext();
  if (level <= 0) return null;
  return /* @__PURE__ */ jsxRuntime.jsx(
    "span",
    {
      "aria-hidden": true,
      className: "pointer-events-none absolute inset-y-0 left-0",
      style: { width: 8 + level * indent, opacity: appearance.indentGuideOpacity },
      children: Array.from({ length: level }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
        "span",
        {
          className: "absolute inset-y-0 w-px bg-border",
          style: { left: 8 + i * indent + indent / 2 - 0.5 }
        },
        i
      ))
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeIndentGuides, "TreeIndentGuides");
function TreeLabel({ children, isMatchingSearch, className }) {
  return /* @__PURE__ */ jsxRuntime.jsx(
    "span",
    {
      style: { fontSize: "var(--tree-font-size)" },
      className: lib.cn(
        "truncate leading-tight tracking-[-0.005em]",
        isMatchingSearch && "font-medium text-foreground",
        className
      ),
      children
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeLabel, "TreeLabel");
function TreeRow({ row, className }) {
  const ctx = useTreeContext();
  const {
    appearance,
    activationMode,
    showIndentGuides,
    selected,
    focused,
    matchingIds,
    select,
    toggle,
    setFocus,
    activate,
    getItemName,
    renderIcon,
    renderLabel,
    renderActions,
    renderContextMenu
  } = ctx;
  const { node, level, isFolder, isExpanded, isLoading } = row;
  const isSelected = selected.has(node.id);
  const isFocused = focused === node.id;
  const isMatchingSearch = matchingIds.has(node.id);
  const slot = {
    node,
    level,
    isSelected,
    isExpanded,
    isFocused,
    isFolder,
    isLoading,
    isMatchingSearch
  };
  const handleClick = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((e) => {
    if (node.disabled) return;
    setFocus(node.id);
    select(node.id);
    if (isFolder) {
      toggle(node.id);
    } else if (activationMode === "single-click") {
      activate(node, { preview: false });
    } else if (activationMode === "single-click-preview") {
      activate(node, { preview: true });
    }
    e.currentTarget.scrollIntoView?.({ block: "nearest" });
  }, "handleClick");
  const handleDoubleClick = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
    if (node.disabled) return;
    if (isFolder) return;
    activate(node, { preview: false });
  }, "handleDoubleClick");
  const trigger = /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      role: "treeitem",
      "aria-level": level + 1,
      "aria-expanded": isFolder ? isExpanded : void 0,
      "aria-selected": isSelected || void 0,
      "aria-current": isSelected ? "true" : void 0,
      "aria-disabled": node.disabled || void 0,
      "data-tree-row": "",
      "data-id": node.id,
      "data-activation-mode": activationMode,
      "data-selected": isSelected ? "true" : void 0,
      "data-focused": isFocused && !isSelected ? "true" : void 0,
      "data-folder": isFolder || void 0,
      "data-expanded": isExpanded || void 0,
      tabIndex: isFocused ? 0 : -1,
      style: {
        paddingLeft: 6 + level * appearance.indent,
        height: "var(--tree-row-height)",
        gap: "var(--tree-gap)"
      },
      onClick: handleClick,
      onDoubleClick: handleDoubleClick,
      onFocus: () => setFocus(node.id),
      className: lib.cn(
        "group/row relative flex w-full select-none items-center pr-2 text-left",
        "transition-colors outline-none",
        node.disabled ? "cursor-not-allowed" : "cursor-pointer",
        radiusClass(appearance),
        rowStateClasses(appearance),
        "focus-visible:ring-1 focus-visible:ring-ring/50",
        isMatchingSearch && "ring-1 ring-primary/30",
        node.disabled && "opacity-50",
        className
      ),
      children: [
        appearance.showActiveIndicator && isSelected ? /* @__PURE__ */ jsxRuntime.jsx(
          "span",
          {
            "aria-hidden": true,
            className: lib.cn(
              "absolute left-0 top-1 bottom-1 w-0.5 rounded-r-full",
              "bg-foreground/30 group-focus-within/tree:bg-primary"
            )
          }
        ) : null,
        showIndentGuides && level > 0 ? /* @__PURE__ */ jsxRuntime.jsx(TreeIndentGuides, { level, indent: appearance.indent }) : null,
        /* @__PURE__ */ jsxRuntime.jsx(TreeChevron, { isExpanded, isFolder }),
        isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
          lucideReact.Loader2,
          {
            "aria-hidden": true,
            strokeWidth: appearance.iconStrokeWidth,
            style: { width: "var(--tree-icon-size)", height: "var(--tree-icon-size)" },
            className: "shrink-0 animate-spin text-muted-foreground/70"
          }
        ) : renderIcon ? renderIcon(slot) : /* @__PURE__ */ jsxRuntime.jsx(TreeIcon, { isFolder, isExpanded }),
        /* @__PURE__ */ jsxRuntime.jsx(
          "span",
          {
            className: "flex min-w-0 flex-1 items-center",
            style: { gap: "var(--tree-gap)" },
            children: renderLabel ? renderLabel(slot) : /* @__PURE__ */ jsxRuntime.jsx(TreeLabel, { isMatchingSearch, children: getItemName(node) })
          }
        ),
        renderActions ? /* @__PURE__ */ jsxRuntime.jsx(
          "span",
          {
            className: "ml-auto flex shrink-0 items-center gap-1 opacity-0 transition-opacity group-hover/row:opacity-100 group-focus-within/row:opacity-100",
            onClick: (e) => e.stopPropagation(),
            children: renderActions(slot)
          }
        ) : null
      ]
    }
  );
  if (renderContextMenu) {
    return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: renderContextMenu(slot, trigger) });
  }
  return trigger;
}
chunkOLISEQHS_cjs.__name(TreeRow, "TreeRow");
function TreeEmpty({ children, className }) {
  return /* @__PURE__ */ jsxRuntime.jsx(
    "div",
    {
      className: lib.cn(
        "flex h-full min-h-32 items-center justify-center px-4 py-6 text-sm text-muted-foreground",
        className
      ),
      children
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeEmpty, "TreeEmpty");
function TreeContent({ children, className, ariaLabel }) {
  const { flatRows, labels, selected, focused, matchingIds, appearance } = useTreeContext();
  if (flatRows.length === 0) {
    return /* @__PURE__ */ jsxRuntime.jsx(TreeEmpty, { children: labels.empty });
  }
  return /* @__PURE__ */ jsxRuntime.jsx(
    "div",
    {
      role: "tree",
      "aria-label": ariaLabel ?? labels.ariaLabel,
      className: lib.cn("relative flex flex-col py-1", className),
      style: appearanceToStyle(appearance),
      children: flatRows.map((row) => {
        const slot = {
          node: row.node,
          level: row.level,
          isSelected: selected.has(row.node.id),
          isExpanded: row.isExpanded,
          isFocused: focused === row.node.id,
          isFolder: row.isFolder,
          isLoading: row.isLoading,
          isMatchingSearch: matchingIds.has(row.node.id)
        };
        const node = children ? children(slot) : /* @__PURE__ */ jsxRuntime.jsx(TreeRow, { row });
        return /* @__PURE__ */ jsxRuntime.jsx(React.Fragment, { children: node }, row.node.id);
      })
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeContent, "TreeContent");
function useTreeLabels() {
  return useTreeContext().labels;
}
chunkOLISEQHS_cjs.__name(useTreeLabels, "useTreeLabels");
function useTreeRows() {
  return useTreeContext().flatRows;
}
chunkOLISEQHS_cjs.__name(useTreeRows, "useTreeRows");
function useTreeSelection() {
  const ctx = useTreeContext();
  const selectedIds = React.useMemo(() => [...ctx.selected], [ctx.selected]);
  const isSelected = React.useCallback(
    (id) => ctx.selected.has(id),
    [ctx.selected]
  );
  return React.useMemo(
    () => ({
      selectedIds,
      select: ctx.select,
      setSelectedIds: ctx.setSelectedIds,
      clear: ctx.clearSelection,
      isSelected
    }),
    [selectedIds, ctx.select, ctx.setSelectedIds, ctx.clearSelection, isSelected]
  );
}
chunkOLISEQHS_cjs.__name(useTreeSelection, "useTreeSelection");
function useTreeExpansion() {
  const ctx = useTreeContext();
  const expandedIds = React.useMemo(() => [...ctx.expanded], [ctx.expanded]);
  const isExpanded = React.useCallback(
    (id) => ctx.expanded.has(id),
    [ctx.expanded]
  );
  return React.useMemo(
    () => ({
      expandedIds,
      expand: ctx.expand,
      collapse: ctx.collapse,
      toggle: ctx.toggle,
      expandAll: ctx.expandAll,
      collapseAll: ctx.collapseAll,
      isExpanded
    }),
    [
      expandedIds,
      ctx.expand,
      ctx.collapse,
      ctx.toggle,
      ctx.expandAll,
      ctx.collapseAll,
      isExpanded
    ]
  );
}
chunkOLISEQHS_cjs.__name(useTreeExpansion, "useTreeExpansion");
function useTreeFocus() {
  const ctx = useTreeContext();
  return React.useMemo(
    () => ({ focusedId: ctx.focused, setFocus: ctx.setFocus }),
    [ctx.focused, ctx.setFocus]
  );
}
chunkOLISEQHS_cjs.__name(useTreeFocus, "useTreeFocus");
function useTreeSearch() {
  const ctx = useTreeContext();
  return React.useMemo(
    () => ({
      isOpen: ctx.enableSearch,
      query: ctx.query,
      setQuery: ctx.setQuery,
      matchingIds: ctx.matchingIds,
      matchCount: ctx.matchingIds.size
    }),
    [ctx.enableSearch, ctx.query, ctx.setQuery, ctx.matchingIds]
  );
}
chunkOLISEQHS_cjs.__name(useTreeSearch, "useTreeSearch");
function useTreeActions() {
  const ctx = useTreeContext();
  return React.useMemo(
    () => ({
      expand: ctx.expand,
      collapse: ctx.collapse,
      toggle: ctx.toggle,
      expandAll: ctx.expandAll,
      collapseAll: ctx.collapseAll,
      refresh: ctx.refresh,
      refreshAll: ctx.refreshAll,
      activate: ctx.activate
    }),
    [
      ctx.expand,
      ctx.collapse,
      ctx.toggle,
      ctx.expandAll,
      ctx.collapseAll,
      ctx.refresh,
      ctx.refreshAll,
      ctx.activate
    ]
  );
}
chunkOLISEQHS_cjs.__name(useTreeActions, "useTreeActions");
function TreeSearchInput({ className, showMatches = true }) {
  const { labels } = useTreeContext();
  const { query, setQuery, matchCount } = useTreeSearch();
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      className: lib.cn(
        "flex items-center gap-2 rounded-md border border-border bg-background px-2",
        className
      ),
      children: [
        /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { "aria-hidden": true, className: "size-3.5 shrink-0 text-muted-foreground" }),
        /* @__PURE__ */ jsxRuntime.jsx(
          "input",
          {
            type: "search",
            value: query,
            onChange: (e) => setQuery(e.target.value),
            placeholder: labels.searchPlaceholder,
            className: "h-7 w-full bg-transparent text-sm outline-none placeholder:text-muted-foreground"
          }
        ),
        showMatches && query.trim().length > 0 ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "shrink-0 text-xs text-muted-foreground", children: labels.searchMatches(matchCount) }) : null,
        query.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
          "button",
          {
            type: "button",
            onClick: () => setQuery(""),
            "aria-label": "Clear search",
            className: "shrink-0 rounded p-1 text-muted-foreground hover:bg-accent hover:text-foreground",
            children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { "aria-hidden": true, className: "size-3.5" })
          }
        ) : null
      ]
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeSearchInput, "TreeSearchInput");
function useTreeKeyboard({
  rows,
  focusedId,
  enabled = true,
  onFocus,
  onSelect,
  onActivate,
  onExpand,
  onCollapse,
  onClearSelection
}) {
  const rowsRef = React.useRef(rows);
  const focusedIdRef = React.useRef(focusedId);
  rowsRef.current = rows;
  focusedIdRef.current = focusedId;
  const getCurrent = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
    const r = rowsRef.current;
    const id = focusedIdRef.current;
    const idx = id ? r.findIndex((x) => x.node.id === id) : -1;
    return { rows: r, idx, current: idx >= 0 ? r[idx] : null };
  }, "getCurrent");
  const refDown = hooks.useHotkey(
    "down",
    () => {
      const { rows: r, idx } = getCurrent();
      if (r.length === 0) return;
      const next = r[Math.min(idx + 1, r.length - 1)] ?? r[0];
      onFocus(next.node.id);
    },
    { enabled, preventDefault: true, description: "Next row" }
  );
  const refUp = hooks.useHotkey(
    "up",
    () => {
      const { rows: r, idx } = getCurrent();
      if (r.length === 0) return;
      const prev = r[Math.max(idx - 1, 0)] ?? r[0];
      onFocus(prev.node.id);
    },
    { enabled, preventDefault: true, description: "Previous row" }
  );
  const refHome = hooks.useHotkey(
    "home",
    () => {
      const { rows: r } = getCurrent();
      if (r.length === 0) return;
      onFocus(r[0].node.id);
    },
    { enabled, preventDefault: true, description: "First row" }
  );
  const refEnd = hooks.useHotkey(
    "end",
    () => {
      const { rows: r } = getCurrent();
      if (r.length === 0) return;
      onFocus(r[r.length - 1].node.id);
    },
    { enabled, preventDefault: true, description: "Last row" }
  );
  const refRight = hooks.useHotkey(
    "right",
    () => {
      const { rows: r, idx, current } = getCurrent();
      if (!current) return;
      if (current.isFolder && !current.isExpanded) {
        onExpand(current.node.id);
      } else if (current.isFolder && current.isExpanded) {
        const next = r[idx + 1];
        if (next) onFocus(next.node.id);
      }
    },
    { enabled, preventDefault: true, description: "Expand / first child" }
  );
  const refLeft = hooks.useHotkey(
    "left",
    () => {
      const { current } = getCurrent();
      if (!current) return;
      if (current.isFolder && current.isExpanded) {
        onCollapse(current.node.id);
      } else if (current.parentId) {
        onFocus(current.parentId);
      }
    },
    { enabled, preventDefault: true, description: "Collapse / parent" }
  );
  const refActivate = hooks.useHotkey(
    ["enter", "space"],
    () => {
      const { current } = getCurrent();
      if (!current) return;
      onSelect(current.node.id);
      if (current.isFolder) {
        if (current.isExpanded) onCollapse(current.node.id);
        else onExpand(current.node.id);
      } else {
        onActivate(current.node.id);
      }
    },
    { enabled, preventDefault: true, description: "Activate / toggle" }
  );
  const refEscape = hooks.useHotkey(
    "escape",
    () => onClearSelection(),
    { enabled, preventDefault: true, description: "Clear selection" }
  );
  const ref = React.useCallback(
    (instance) => {
      refDown(instance);
      refUp(instance);
      refHome(instance);
      refEnd(instance);
      refRight(instance);
      refLeft(instance);
      refActivate(instance);
      refEscape(instance);
    },
    [refDown, refUp, refHome, refEnd, refRight, refLeft, refActivate, refEscape]
  );
  return { ref };
}
chunkOLISEQHS_cjs.__name(useTreeKeyboard, "useTreeKeyboard");
var FLUSH_MS = 600;
function useTreeTypeAhead({
  rows,
  getItemName,
  containerRef,
  onMatch,
  enabled = true
}) {
  const bufferRef = React.useRef("");
  const timerRef = React.useRef(null);
  const rowsRef = React.useRef(rows);
  const getNameRef = React.useRef(getItemName);
  const onMatchRef = React.useRef(onMatch);
  rowsRef.current = rows;
  getNameRef.current = getItemName;
  onMatchRef.current = onMatch;
  React.useEffect(() => {
    if (!enabled) return;
    const target = containerRef.current;
    if (!target) return;
    const reset = /* @__PURE__ */ chunkOLISEQHS_cjs.__name(() => {
      bufferRef.current = "";
      if (timerRef.current) {
        clearTimeout(timerRef.current);
        timerRef.current = null;
      }
    }, "reset");
    const handler = /* @__PURE__ */ chunkOLISEQHS_cjs.__name((e) => {
      const tag = e.target?.tagName;
      if (tag === "INPUT" || tag === "TEXTAREA") return;
      if (e.target?.isContentEditable) return;
      if (e.metaKey || e.ctrlKey || e.altKey) return;
      if (e.key === "Escape" || e.key === "Enter" || e.key === "Tab" || e.key.startsWith("Arrow") || e.key === "Home" || e.key === "End" || e.key === "PageUp" || e.key === "PageDown") {
        reset();
        return;
      }
      if (e.key.length !== 1) return;
      bufferRef.current += e.key.toLowerCase();
      if (timerRef.current) clearTimeout(timerRef.current);
      timerRef.current = setTimeout(reset, FLUSH_MS);
      const prefix = bufferRef.current;
      const hit = rowsRef.current.find(
        (row) => getNameRef.current(row.node).toLowerCase().startsWith(prefix)
      );
      if (hit) {
        e.preventDefault();
        onMatchRef.current(hit.node.id);
      }
    }, "handler");
    target.addEventListener("keydown", handler);
    return () => {
      target.removeEventListener("keydown", handler);
      reset();
    };
  }, [containerRef, enabled]);
}
chunkOLISEQHS_cjs.__name(useTreeTypeAhead, "useTreeTypeAhead");
function TreeRoot(props) {
  const {
    data,
    getItemName,
    loadChildren,
    selectionMode,
    activationMode,
    initialExpandedIds,
    initialSelectedIds,
    indent,
    appearance,
    onSelectionChange,
    onExpansionChange,
    onActivate,
    filterNode,
    enableSearch = false,
    enableTypeAhead = true,
    showIndentGuides = false,
    renderRow,
    renderIcon,
    renderLabel,
    renderActions,
    renderContextMenu,
    labels,
    persistKey,
    persistSelection = false,
    className,
    style
  } = props;
  return /* @__PURE__ */ jsxRuntime.jsx(
    TreeProvider,
    {
      data,
      getItemName,
      loadChildren,
      selectionMode,
      activationMode,
      initialExpandedIds,
      initialSelectedIds,
      indent,
      appearance,
      onSelectionChange,
      onExpansionChange,
      onActivate,
      filterNode,
      enableSearch,
      showIndentGuides,
      renderIcon,
      renderLabel,
      renderActions,
      renderContextMenu,
      labels,
      persistKey,
      persistSelection,
      children: /* @__PURE__ */ jsxRuntime.jsx(
        TreeRootShell,
        {
          className,
          style,
          enableSearch,
          enableTypeAhead,
          renderRow
        }
      )
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeRoot, "TreeRoot");
function TreeRootShell({
  className,
  style,
  enableSearch,
  enableTypeAhead,
  renderRow
}) {
  const containerRef = React.useRef(null);
  const ctx = useTreeContext();
  const { ref: keyboardRef } = useTreeKeyboard({
    rows: ctx.flatRows,
    focusedId: ctx.focused,
    onFocus: ctx.setFocus,
    onSelect: ctx.select,
    onActivate: /* @__PURE__ */ chunkOLISEQHS_cjs.__name((id) => {
      const row = ctx.flatRows.find((r) => r.node.id === id);
      if (row) ctx.activate(row.node, { preview: false });
    }, "onActivate"),
    onExpand: ctx.expand,
    onCollapse: ctx.collapse,
    onClearSelection: ctx.clearSelection
  });
  const setContainerRef = React.useCallback(
    (instance) => {
      containerRef.current = instance;
      keyboardRef(instance);
    },
    [keyboardRef]
  );
  const onTypeAheadMatch = React.useCallback(
    (id) => {
      ctx.setFocus(id);
      const el = containerRef.current?.querySelector(
        `[data-tree-row][data-id="${CSS.escape(id)}"]`
      );
      el?.scrollIntoView({ block: "nearest" });
    },
    [ctx]
  );
  useTreeTypeAhead({
    rows: ctx.flatRows,
    getItemName: ctx.getItemName,
    containerRef,
    onMatch: onTypeAheadMatch,
    enabled: enableTypeAhead
  });
  return /* @__PURE__ */ jsxRuntime.jsxs(
    "div",
    {
      ref: setContainerRef,
      tabIndex: 0,
      className: lib.cn(
        "group/tree flex h-full w-full flex-col gap-2 outline-none",
        className
      ),
      style: { ...appearanceToStyle(ctx.appearance), ...style },
      "data-tree-root": "",
      children: [
        enableSearch ? /* @__PURE__ */ jsxRuntime.jsx(TreeSearchInput, { className: "mx-2 mt-2" }) : null,
        /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-h-0 flex-1 overflow-auto px-1", children: /* @__PURE__ */ jsxRuntime.jsx(TreeContent, { children: renderRow }) })
      ]
    }
  );
}
chunkOLISEQHS_cjs.__name(TreeRootShell, "TreeRootShell");
var TreeRoot_default = TreeRoot;

exports.DEFAULT_TREE_APPEARANCE = DEFAULT_TREE_APPEARANCE;
exports.DEFAULT_TREE_LABELS = DEFAULT_TREE_LABELS;
exports.TreeChevron = TreeChevron;
exports.TreeContent = TreeContent;
exports.TreeEmpty = TreeEmpty;
exports.TreeIcon = TreeIcon;
exports.TreeIndentGuides = TreeIndentGuides;
exports.TreeLabel = TreeLabel;
exports.TreeProvider = TreeProvider;
exports.TreeRoot = TreeRoot;
exports.TreeRoot_default = TreeRoot_default;
exports.TreeRow = TreeRow;
exports.TreeSearchInput = TreeSearchInput;
exports.appearanceToStyle = appearanceToStyle;
exports.clearTreeState = clearTreeState;
exports.createChildCache = createChildCache;
exports.flattenTree = flattenTree;
exports.loadTreeState = loadTreeState;
exports.resolveAppearance = resolveAppearance;
exports.resolveChildren = resolveChildren;
exports.saveTreeState = saveTreeState;
exports.useTreeActions = useTreeActions;
exports.useTreeContext = useTreeContext;
exports.useTreeExpansion = useTreeExpansion;
exports.useTreeFocus = useTreeFocus;
exports.useTreeKeyboard = useTreeKeyboard;
exports.useTreeLabels = useTreeLabels;
exports.useTreeRows = useTreeRows;
exports.useTreeSearch = useTreeSearch;
exports.useTreeSelection = useTreeSelection;
exports.useTreeTypeAhead = useTreeTypeAhead;
//# sourceMappingURL=chunk-IEEAENLX.cjs.map
//# sourceMappingURL=chunk-IEEAENLX.cjs.map