import { j as jsxRuntimeExports } from "./jsx-runtime-mMz73N64.js";
import { memo, useState, useCallback, useMemo } from "react";
import { p as m0, v as useSelectionUtils, w as useDataUtilContext, m as useDragAndDrop, q as CONTEXT_TARGET, C as CONTEXT_SELECTED, M as MaybeEditable, x as useCombinedStateAndRef, o as useSyncedRef, y as useHeldRef, B as Button, z as Core, A as Selector, F as edit, G as componentRenderProp } from "./Toggle-DcOiWiHG.js";
import { L as Link } from "./Link-BaPpY2rf.js";
import { C as CSS } from "./css-DoalWJF6.js";
import { A, i as usePassthrough } from "./aggregator-Dl2jHye5.js";
import "./Center-hbwH7rg9.js";
import { L as List } from "./List-aJEsDHql.js";
const shouldExpand = (node, expanded) => expanded.includes(node.key);
const sortAndSplice = (nodes, sort) => {
  if (sort) {
    nodes.sort((a, b) => a.name.localeCompare(b.name));
  }
  let found = false;
  for (let i = 0; i < nodes.length; i++) {
    const node = nodes[i];
    if (node.forcePosition != null && i !== node.forcePosition) {
      found = true;
      nodes.splice(i, 1);
      nodes.splice(node.forcePosition, 0, node);
    }
  }
  if (found)
    return sortAndSplice(nodes, false);
  return nodes;
};
const flatten = ({
  nodes,
  expanded,
  depth = 0,
  sort = true
}) => {
  if (depth === 0 && sort)
    nodes = nodes.sort((a, b) => a.name.localeCompare(b.name));
  const flattened = [];
  nodes.forEach((node, index) => {
    const expand = shouldExpand(node, expanded);
    flattened.push({ ...node, depth, expanded: expand, index });
    if (expand && node.children != null) {
      node.children = sortAndSplice(node.children, sort);
      flattened.push(
        ...flatten({ nodes: node.children, expanded, depth: depth + 1, sort })
      );
    }
  });
  return flattened;
};
const moveNode = ({ tree, destination, keys }) => {
  keys = A(keys);
  keys.forEach((key) => {
    const node = findNode({ tree, key });
    if (node == null)
      return;
    removeNode({ tree, keys: key });
    setNode({ tree, destination, additions: node });
  });
  return tree;
};
const removeNode = ({ tree, keys }) => {
  keys = A(keys);
  const treeKeys = tree.map((node) => node.key);
  keys.forEach((key) => {
    var _a;
    const index = treeKeys.indexOf(key);
    if (index !== -1)
      tree.splice(index, 1);
    else {
      const parent = findNodeParent({ tree, key });
      if (parent != null)
        parent.children = (_a = parent.children) == null ? void 0 : _a.filter((child) => child.key !== key);
    }
  });
  return tree;
};
const setNode = ({ tree, destination, additions }) => {
  additions = A(additions);
  const node = findNode({ tree, key: destination });
  if (node == null)
    throw new Error(`Could not find node with key ${destination}`);
  if (node.children == null)
    node.children = [];
  const addedKeys = additions.map((node2) => node2.key);
  node.children = [
    ...additions,
    ...node.children.filter((child) => !addedKeys.includes(child.key))
  ];
  return tree;
};
const updateNode = ({
  tree,
  key,
  updater,
  throwOnMissing = true
}) => {
  var _a, _b;
  const node = findNode({ tree, key });
  if (node == null) {
    if (throwOnMissing)
      throw new Error(`Could not find node with key ${key}`);
    return tree;
  }
  const parent = findNodeParent({ tree, key });
  if (parent != null) {
    const index = (_a = parent.children) == null ? void 0 : _a.findIndex((child) => child.key === key);
    if (index != null && index !== -1)
      (_b = parent.children) == null ? void 0 : _b.splice(index, 1, updater(node));
  } else {
    tree.splice(
      tree.findIndex((node2) => node2.key === key),
      1,
      updater(node)
    );
  }
  return tree;
};
const findNode = ({
  tree,
  key,
  depth = 0
}) => {
  for (let i = 0; i < tree.length; i++) {
    const node = tree[i];
    if (node.key === key) {
      const n = node;
      n.depth = depth;
      n.position = i;
      return n;
    }
    if (node.children != null) {
      const found = findNode({ tree: node.children, key, depth: depth + 1 });
      if (found != null)
        return found;
    }
  }
  return null;
};
const findNodes = ({ tree, keys }) => {
  const nodes = [];
  for (const key of keys) {
    const node = findNode({ tree, key });
    if (node != null)
      nodes.push(node);
  }
  return nodes;
};
const findNodeParent = ({ tree, key }) => {
  for (const node of tree) {
    if (node.children != null) {
      if (node.children.some((child) => child.key === key))
        return node;
      const found = findNodeParent({ tree: node.children, key });
      if (found != null)
        return found;
    }
  }
  return null;
};
const HAUL_TYPE = "tree-item";
const SHIFT_TRIGGERS = [["Shift"]];
const use = (props) => {
  const {
    onExpand,
    nodes,
    initialExpanded = [],
    sort = true,
    selected: propsSelected,
    onSelectedChange
  } = props ?? {};
  const [expanded, setExpanded, ref] = useCombinedStateAndRef(initialExpanded);
  const [selected, setSelected] = usePassthrough({
    initial: [],
    value: propsSelected,
    onChange: onSelectedChange
  });
  const flat = useMemo(
    () => flatten({ nodes, expanded, sort }),
    [nodes, expanded, sort]
  );
  const flatRef = useSyncedRef(flat);
  const shiftRef = useHeldRef({ triggers: SHIFT_TRIGGERS });
  const handleSelect = useCallback(
    (keys, { clicked }) => {
      setSelected(keys);
      const n = flatRef.current.find((node) => node.key === clicked);
      if ((n == null ? void 0 : n.hasChildren) === false)
        return;
      if (clicked == null || shiftRef.current.held)
        return;
      const currentlyExpanded = ref.current;
      const action = currentlyExpanded.some((key) => key === clicked) ? "contract" : "expand";
      let nextExpanded = currentlyExpanded;
      if (action === "contract")
        nextExpanded = currentlyExpanded.filter((key) => key !== clicked);
      else
        nextExpanded = [...currentlyExpanded, clicked];
      setExpanded(nextExpanded);
      onExpand == null ? void 0 : onExpand({ current: nextExpanded, action, clicked });
    },
    [onExpand, flatRef, setExpanded, setSelected]
  );
  return {
    onSelect: handleSelect,
    selected,
    expanded,
    nodes: flat
  };
};
const expandedCaret = /* @__PURE__ */ jsxRuntimeExports.jsx(m0.Caret.Down, { className: CSS.B("caret") });
const collapsedCaret = /* @__PURE__ */ jsxRuntimeExports.jsx(m0.Caret.Right, { className: CSS.B("caret") });
const DefaultItem = memo(
  ({
    entry,
    selected,
    onSelect,
    onDrop,
    onRename,
    onSuccessfulDrop,
    onDoubleClick,
    loading = false,
    useMargin = false,
    translate
  }) => {
    const {
      key,
      hasChildren = false,
      allowRename = false,
      children,
      icon,
      name,
      depth,
      expanded,
      href,
      haulItems = []
    } = entry;
    const { getSelected } = useSelectionUtils();
    const { getSourceData } = useDataUtilContext();
    const actuallyHasChildren = hasChildren || children != null && children.length > 0;
    const startIcons = [];
    if (actuallyHasChildren)
      startIcons.push(expanded ? expandedCaret : collapsedCaret);
    if (icon != null)
      startIcons.push(icon);
    const endIcons = [];
    if (loading)
      endIcons.push(/* @__PURE__ */ jsxRuntimeExports.jsx(m0.Loading, { className: CSS.B("loading-indicator") }));
    const [draggingOver, setDraggingOver] = useState(false);
    const { startDrag, ...dropProps } = useDragAndDrop({
      type: "Tree.Item",
      key,
      canDrop: useCallback(({ items: entities, source }) => {
        const keys = entities.map((item) => item.key);
        setDraggingOver(false);
        return source.type === "Tree.Item" && !keys.includes(key);
      }, []),
      onDrop: useCallback((props) => (onDrop == null ? void 0 : onDrop(key, props)) ?? [], [key, onDrop]),
      onDragOver: useCallback(() => setDraggingOver(true), [])
    });
    const handleDragStart = () => {
      const selectedItemKeys = getSelected();
      const selectedItems = getSourceData().filter(
        (item) => selectedItemKeys.includes(item.key)
      );
      if (selectedItemKeys.includes(key)) {
        const selectedHaulItems = selectedItems.map(({ key: key2, haulItems: haulItems2 }) => [{ type: HAUL_TYPE, key: key2 }, ...haulItems2 ?? []]).flat();
        return startDrag(selectedHaulItems, (props) => onSuccessfulDrop == null ? void 0 : onSuccessfulDrop(key, props));
      }
      startDrag(
        [{ type: HAUL_TYPE, key }, ...haulItems],
        (props) => onSuccessfulDrop == null ? void 0 : onSuccessfulDrop(key, props)
      );
    };
    const offsetKey = useMargin ? "marginLeft" : "paddingLeft";
    const baseProps = {
      id: key,
      variant: "text",
      draggable: true,
      className: CSS(
        CSS.BE("list", "item"),
        CONTEXT_TARGET,
        draggingOver && CSS.M("dragging-over"),
        selected && CONTEXT_SELECTED,
        CSS.selected(selected),
        actuallyHasChildren && CSS.M("has-children")
      ),
      onDragLeave: () => setDraggingOver(false),
      onDragStart: handleDragStart,
      onClick: () => onSelect == null ? void 0 : onSelect(key),
      style: {
        position: translate != null ? "absolute" : "relative",
        transform: `translateY(${translate}px)`,
        [offsetKey]: `${depth * 1.5 + 1}rem`
      },
      startIcon: startIcons,
      iconSpacing: "small",
      noWrap: true,
      endIcon: endIcons,
      onDoubleClick: (e) => onDoubleClick == null ? void 0 : onDoubleClick(key, e),
      href,
      ...dropProps
    };
    const Base = href != null ? Link : Button;
    return /* @__PURE__ */ jsxRuntimeExports.jsx(Base, { className: CSS.BE("list", "item"), ...baseProps, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
      MaybeEditable,
      {
        id: `text-${key}`,
        level: "p",
        allowDoubleClick: false,
        value: name,
        disabled: !allowRename,
        onChange: (name2) => onRename == null ? void 0 : onRename(key, name2)
      }
    ) });
  }
);
DefaultItem.displayName = "Tree.Item";
const defaultChild = componentRenderProp(DefaultItem);
const Tree = ({
  nodes,
  selected = [],
  onSelect,
  onDrop,
  onRename,
  onSuccessfulDrop,
  onDoubleClick,
  className,
  children = defaultChild,
  itemHeight = 27,
  useMargin = false,
  virtual = true,
  ...props
}) => {
  const Core$1 = virtual ? Core.Virtual : Core;
  return /* @__PURE__ */ jsxRuntimeExports.jsx(List, { data: nodes, children: /* @__PURE__ */ jsxRuntimeExports.jsx(Selector, { value: selected, onChange: onSelect, allowMultiple: true, replaceOnSingle: true, children: /* @__PURE__ */ jsxRuntimeExports.jsx(
    Core$1,
    {
      itemHeight,
      className: CSS(className, CSS.B("tree")),
      ...props,
      children: (props2) => children({
        ...props2,
        useMargin,
        onDrop,
        onRename,
        onSuccessfulDrop,
        onDoubleClick
      })
    }
  ) }) });
};
const startRenaming = (key) => edit(`text-${key}`);
const external = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
  __proto__: null,
  DefaultItem,
  HAUL_TYPE,
  Tree,
  findNode,
  findNodeParent,
  findNodes,
  flatten,
  moveNode,
  removeNode,
  setNode,
  shouldExpand,
  sortAndSplice,
  startRenaming,
  updateNode,
  use
}, Symbol.toStringTag, { value: "Module" }));
export {
  external as e
};
//# sourceMappingURL=external-Doprw1Uu.js.map
