@lobehub/ui
Version:
Lobe UI is an open-source UI component library for building AIGC web apps
124 lines (123 loc) • 4.72 kB
JavaScript
"use client";
import Icon from "../../Icon/Icon.mjs";
import { controlHeight } from "../controlSize.mjs";
import Checkbox_default from "../Checkbox/Checkbox.mjs";
import { styles } from "./style.mjs";
import { useTreeContext } from "./context.mjs";
import { hasChildren } from "./utils.mjs";
import { memo } from "react";
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
import { cx } from "antd-style";
import { ChevronRight, File, Folder } from "lucide-react";
import { Collapsible } from "@base-ui/react/collapsible";
//#region src/base-ui/Tree/TreeNode.tsx
const ARC = 6;
const SWITCHER_CENTER = 11.5;
const guidePath = (depth, isLast, trail, indent, height) => {
const cx = (i) => i * indent + SWITCHER_CENTER;
let d = "";
trail.forEach((more, i) => {
if (more && i > 0) d += `M${cx(i)} 0V${height}`;
});
const x = cx(depth - 1);
const y = height / 2;
const elbow = `M${x} ${y - ARC}A${ARC} ${ARC} 0 0 0 ${x + ARC} ${y}H${x + indent - 2}`;
d += isLast ? `M${x} 0V${y - ARC}${elbow.slice(elbow.indexOf("A"))}` : `M${x} 0V${height}${elbow}`;
return d;
};
const TreeNode = memo(({ node, depth, isLast, trail }) => {
const ctx = useTreeContext();
const expandable = hasChildren(node);
const expanded = expandable && ctx.expanded.has(node.key);
const disabled = ctx.disabled || !!node.disabled;
const checkable = ctx.checkable && node.checkable !== false;
const checkState = ctx.checked.has(node.key) ? "checked" : ctx.halfChecked.has(node.key) ? "mixed" : "unchecked";
const rowHeight = typeof ctx.styles.node?.height === "number" ? ctx.styles.node.height : controlHeight[ctx.size];
const switcherIcon = typeof ctx.switcherIcon === "function" ? ctx.switcherIcon({
expanded,
node
}) : ctx.switcherIcon ?? /* @__PURE__ */ jsx(Icon, {
icon: ChevronRight,
size: 14
});
const rowStyle = {
height: rowHeight,
paddingInlineStart: depth * ctx.indent,
...ctx.styles.node
};
const select = (event) => ctx.toggleSelect(node, event);
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsxs("div", {
"data-scope-item": true,
"aria-disabled": disabled || void 0,
"aria-expanded": expandable ? expanded : void 0,
"aria-level": depth + 1,
"aria-selected": ctx.selected.has(node.key),
"data-id": node.key,
role: "treeitem",
style: rowStyle,
tabIndex: ctx.activeKey === node.key ? 0 : -1,
"aria-checked": checkable ? checkState === "mixed" ? "mixed" : checkState === "checked" : void 0,
className: cx(styles.node, ctx.blockNode && styles.nodeBlock, disabled && styles.nodeDisabled, ctx.classNames.node),
onClick: ctx.blockNode ? select : void 0,
onContextMenu: (event) => ctx.onRightClick(event, node),
onFocus: () => ctx.setActiveKey(node.key),
children: [
ctx.showLine && depth > 0 && /* @__PURE__ */ jsx("svg", {
"aria-hidden": true,
className: cx(styles.guide, ctx.classNames.guide),
height: rowHeight,
style: ctx.styles.guide,
width: depth * ctx.indent,
children: /* @__PURE__ */ jsx("path", { d: guidePath(depth, isLast, trail, ctx.indent, rowHeight) })
}),
/* @__PURE__ */ jsx("button", {
"aria-hidden": true,
style: ctx.styles.switcher,
tabIndex: -1,
type: "button",
className: cx(styles.switcher, !expandable && styles.switcherLeaf, ctx.classNames.switcher),
onClick: (event) => {
event.stopPropagation();
ctx.toggleExpand(node);
},
children: switcherIcon
}),
checkable && /* @__PURE__ */ jsx(Checkbox_default, {
checked: checkState === "checked",
className: styles.checkbox,
disabled,
indeterminate: checkState === "mixed",
onChange: () => ctx.toggleCheck(node),
onClick: (event) => event.stopPropagation()
}),
ctx.showIcon && /* @__PURE__ */ jsx("span", {
className: styles.icon,
children: node.icon ?? /* @__PURE__ */ jsx(Icon, {
icon: expandable ? Folder : File,
size: 16
})
}),
/* @__PURE__ */ jsx("span", {
style: ctx.styles.title,
className: cx(styles.title, ctx.blockNode ? styles.titleBlock : styles.titleInline, ctx.classNames.title),
onClick: ctx.blockNode ? void 0 : select,
children: ctx.titleRender ? ctx.titleRender(node) : node.title
})
]
}), expandable && /* @__PURE__ */ jsx(Collapsible.Root, {
open: expanded,
children: /* @__PURE__ */ jsx(Collapsible.Panel, {
className: styles.panel,
children: node.children.map((child, i) => /* @__PURE__ */ jsx(TreeNode, {
depth: depth + 1,
isLast: i === node.children.length - 1,
node: child,
trail: [...trail, !isLast]
}, child.key))
})
})] });
});
TreeNode.displayName = "TreeNode";
//#endregion
export { TreeNode as default };
//# sourceMappingURL=TreeNode.mjs.map