"use client";
import * as React from 'react';
import { clsx } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { jsx, jsxs } from 'react/jsx-runtime';

function cn(...inputs) {
  return twMerge(clsx(inputs));
}
function flatten(nodes, expandedSet, level = 1, parentId = null) {
  const out = [];
  for (const node of nodes) {
    const hasChildren = Array.isArray(node.children);
    out.push({
      id: node.id,
      name: node.name,
      level,
      hasChildren,
      disabled: !!node.disabled,
      parentId,
      icon: node.icon
    });
    if (hasChildren && expandedSet.has(node.id) && node.children) {
      out.push(...flatten(node.children, expandedSet, level + 1, node.id));
    }
  }
  return out;
}
function FolderIcon({ open }) {
  return /* @__PURE__ */ jsx(
    "svg",
    {
      xmlns: "http://www.w3.org/2000/svg",
      viewBox: "0 0 24 24",
      fill: "none",
      stroke: "currentColor",
      strokeWidth: "2",
      strokeLinecap: "round",
      strokeLinejoin: "round",
      className: "h-4 w-4 shrink-0",
      "aria-hidden": "true",
      children: open ? /* @__PURE__ */ jsx("path", { d: "M3 7v10a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-7l-2-2H5a2 2 0 0 0-2 2z" }) : /* @__PURE__ */ jsx("path", { d: "M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z" })
    }
  );
}
function FileIcon() {
  return /* @__PURE__ */ jsxs(
    "svg",
    {
      xmlns: "http://www.w3.org/2000/svg",
      viewBox: "0 0 24 24",
      fill: "none",
      stroke: "currentColor",
      strokeWidth: "2",
      strokeLinecap: "round",
      strokeLinejoin: "round",
      className: "h-4 w-4 shrink-0",
      "aria-hidden": "true",
      children: [
        /* @__PURE__ */ jsx("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
        /* @__PURE__ */ jsx("polyline", { points: "14 2 14 8 20 8" })
      ]
    }
  );
}
function Chevron({ expanded }) {
  return /* @__PURE__ */ jsx(
    "svg",
    {
      xmlns: "http://www.w3.org/2000/svg",
      viewBox: "0 0 24 24",
      fill: "none",
      stroke: "currentColor",
      strokeWidth: "2",
      strokeLinecap: "round",
      strokeLinejoin: "round",
      className: cn(
        "h-3 w-3 shrink-0 text-muted-foreground transition-transform duration-[var(--duration-normal,200ms)] ease-out",
        expanded ? "rotate-90" : ""
      ),
      "aria-hidden": "true",
      children: /* @__PURE__ */ jsx("polyline", { points: "9 18 15 12 9 6" })
    }
  );
}
function TreeItem({
  node,
  level,
  expandedSet,
  selected,
  onToggle,
  onSelect,
  onKeyDown,
  registerRef,
  tabbableId
}) {
  const hasChildren = Array.isArray(node.children);
  const isExpanded = hasChildren && expandedSet.has(node.id);
  const isSelected = selected === node.id;
  return /* @__PURE__ */ jsxs("li", { role: "none", children: [
    /* @__PURE__ */ jsxs(
      "div",
      {
        role: "treeitem",
        "aria-level": level,
        "aria-expanded": hasChildren ? isExpanded : void 0,
        "aria-selected": isSelected,
        "aria-disabled": node.disabled || void 0,
        tabIndex: tabbableId === node.id ? 0 : -1,
        ref: (el) => registerRef(node.id, el),
        onClick: (e) => {
          if (node.disabled) return;
          e.stopPropagation();
          onSelect(node.id);
        },
        onKeyDown: (e) => onKeyDown(e, node.id),
        className: cn(
          "flex items-center gap-[var(--space-2,0.5rem)] rounded-md px-[var(--space-2,0.5rem)] py-[var(--space-1,0.25rem)] text-sm cursor-pointer select-none transition-all duration-[var(--duration-normal,200ms)] ease-out",
          "hover:bg-accent hover:text-accent-foreground",
          "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
          isSelected && "bg-accent text-accent-foreground",
          node.disabled && "opacity-50 cursor-not-allowed pointer-events-none"
        ),
        style: { paddingInlineStart: `calc(${level - 1} * 1rem + var(--space-2, 0.5rem))` },
        children: [
          hasChildren ? /* @__PURE__ */ jsx(
            "button",
            {
              type: "button",
              tabIndex: -1,
              "aria-hidden": "true",
              onClick: (e) => {
                e.stopPropagation();
                if (node.disabled) return;
                onToggle(node.id);
              },
              className: "inline-flex h-4 w-4 shrink-0 items-center justify-center rounded-sm hover:bg-accent-foreground/10",
              children: /* @__PURE__ */ jsx(Chevron, { expanded: isExpanded })
            }
          ) : /* @__PURE__ */ jsx("span", { className: "w-3 shrink-0", "aria-hidden": "true" }),
          node.icon ?? (hasChildren ? /* @__PURE__ */ jsx(FolderIcon, { open: isExpanded }) : /* @__PURE__ */ jsx(FileIcon, {})),
          /* @__PURE__ */ jsx("span", { className: "truncate", children: node.name })
        ]
      }
    ),
    hasChildren && isExpanded && node.children ? /* @__PURE__ */ jsx("ul", { role: "group", className: "m-0 list-none p-0", children: node.children.map((child) => /* @__PURE__ */ jsx(
      TreeItem,
      {
        node: child,
        level: level + 1,
        expandedSet,
        selected,
        onToggle,
        onSelect,
        onKeyDown,
        registerRef,
        tabbableId
      },
      child.id
    )) }) : null
  ] });
}
function FileTree({
  nodes,
  defaultExpanded,
  expanded: expandedProp,
  onExpandedChange,
  selected,
  onSelect,
  "aria-label": ariaLabel,
  className
}) {
  const isControlled = expandedProp !== void 0;
  const [internalExpanded, setInternalExpanded] = React.useState(
    defaultExpanded ?? []
  );
  const expanded = isControlled ? expandedProp : internalExpanded;
  const expandedSet = React.useMemo(() => new Set(expanded), [expanded]);
  const itemRefs = React.useRef(/* @__PURE__ */ new Map());
  const registerRef = React.useCallback(
    (id, el) => {
      if (el) itemRefs.current.set(id, el);
      else itemRefs.current.delete(id);
    },
    []
  );
  const flat = React.useMemo(
    () => flatten(nodes, expandedSet),
    [nodes, expandedSet]
  );
  const firstId = flat[0]?.id ?? null;
  const [focusedId, setFocusedId] = React.useState(null);
  const visibleIds = React.useMemo(
    () => new Set(flat.map((n) => n.id)),
    [flat]
  );
  const candidate = focusedId ?? selected ?? firstId;
  const tabbableId = candidate && visibleIds.has(candidate) ? candidate : firstId;
  const setExpanded = React.useCallback(
    (next) => {
      if (!isControlled) setInternalExpanded(next);
      onExpandedChange?.(next);
    },
    [isControlled, onExpandedChange]
  );
  const toggle = React.useCallback(
    (id) => {
      const set = new Set(expanded);
      if (set.has(id)) set.delete(id);
      else set.add(id);
      setExpanded(Array.from(set));
    },
    [expanded, setExpanded]
  );
  const handleSelect = React.useCallback(
    (id) => {
      onSelect?.(id);
      setFocusedId(id);
    },
    [onSelect]
  );
  const focusNode = (id) => {
    setFocusedId(id);
    requestAnimationFrame(() => itemRefs.current.get(id)?.focus());
  };
  const handleKeyDown = (e, id) => {
    const flatNodes = flat;
    const idx = flatNodes.findIndex((n) => n.id === id);
    const node = flatNodes[idx];
    if (!node) return;
    const findEnabled = (start, dir) => {
      let i = start;
      while (i >= 0 && i < flatNodes.length) {
        if (!flatNodes[i].disabled) return flatNodes[i];
        i += dir;
      }
      return null;
    };
    switch (e.key) {
      case "ArrowDown": {
        e.preventDefault();
        const next = findEnabled(idx + 1, 1);
        if (next) focusNode(next.id);
        break;
      }
      case "ArrowUp": {
        e.preventDefault();
        const prev = findEnabled(idx - 1, -1);
        if (prev) focusNode(prev.id);
        break;
      }
      case "ArrowRight": {
        e.preventDefault();
        if (node.hasChildren && !expandedSet.has(node.id)) {
          toggle(node.id);
        } else if (node.hasChildren) {
          const firstChild = flatNodes[idx + 1];
          if (firstChild && firstChild.parentId === node.id)
            focusNode(firstChild.id);
        }
        break;
      }
      case "ArrowLeft": {
        e.preventDefault();
        if (node.hasChildren && expandedSet.has(node.id)) {
          toggle(node.id);
        } else if (node.parentId) {
          focusNode(node.parentId);
        }
        break;
      }
      case "Home": {
        e.preventDefault();
        if (flatNodes[0]) focusNode(flatNodes[0].id);
        break;
      }
      case "End": {
        e.preventDefault();
        const last = flatNodes[flatNodes.length - 1];
        if (last) focusNode(last.id);
        break;
      }
      case "Enter":
      case " ": {
        e.preventDefault();
        if (!node.disabled) {
          if (node.hasChildren) toggle(node.id);
          handleSelect(node.id);
        }
        break;
      }
    }
  };
  return /* @__PURE__ */ jsx(
    "ul",
    {
      role: "tree",
      "aria-label": ariaLabel,
      className: cn("list-none p-0 m-0", className),
      children: nodes.map((node) => /* @__PURE__ */ jsx(
        TreeItem,
        {
          node,
          level: 1,
          expandedSet,
          selected,
          onToggle: toggle,
          onSelect: handleSelect,
          onKeyDown: handleKeyDown,
          registerRef,
          tabbableId
        },
        node.id
      ))
    }
  );
}
FileTree.displayName = "FileTree";

export { FileTree };
//# sourceMappingURL=file-tree.js.map
//# sourceMappingURL=file-tree.js.map