@particle-academy/react-fancy 1.7.4 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/index.cjs +169 -68
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +68 -1
- package/dist/index.d.ts +68 -1
- package/dist/index.js +168 -69
- package/dist/index.js.map +1 -1
- package/docs/TreeNav.md +178 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -125,6 +125,7 @@ npx vite build # Build demo app (verifies imports work)
|
|
|
125
125
|
| Breadcrumbs | Navigation breadcrumb trail with separator | [docs](docs/Breadcrumbs.md) |
|
|
126
126
|
| Navbar | Responsive navigation bar with hamburger collapse | [docs](docs/Navbar.md) |
|
|
127
127
|
| Pagination | Page navigation with prev/next and ellipsis | [docs](docs/Pagination.md) |
|
|
128
|
+
| TreeNav | Hierarchical file/folder tree with expand/collapse, selection, and extension-based icons | [docs](docs/TreeNav.md) |
|
|
128
129
|
|
|
129
130
|
### Rich Content
|
|
130
131
|
|
package/dist/index.cjs
CHANGED
|
@@ -10028,7 +10028,7 @@ function DiagramEntity({
|
|
|
10028
10028
|
}
|
|
10029
10029
|
DiagramEntity.displayName = "DiagramEntity";
|
|
10030
10030
|
var HEADER_HEIGHT = 36;
|
|
10031
|
-
var FIELD_HEIGHT =
|
|
10031
|
+
var FIELD_HEIGHT = 29;
|
|
10032
10032
|
var SYMBOL_SIZE = 12;
|
|
10033
10033
|
function oneSymbol(pt, direction) {
|
|
10034
10034
|
const s = SYMBOL_SIZE * 0.6;
|
|
@@ -10125,82 +10125,32 @@ function DiagramRelation({
|
|
|
10125
10125
|
const toFieldY = toFieldIdx >= 0 ? HEADER_HEIGHT + toFieldIdx * FIELD_HEIGHT + FIELD_HEIGHT / 2 : toRect.height / 2;
|
|
10126
10126
|
const fromCx = fromRect.x + fromRect.width / 2;
|
|
10127
10127
|
const toCx = toRect.x + toRect.width / 2;
|
|
10128
|
-
const fromCy = fromRect.y + fromRect.height / 2;
|
|
10129
|
-
const toCy = toRect.y + toRect.height / 2;
|
|
10130
|
-
const dx = Math.abs(fromCx - toCx);
|
|
10131
|
-
const dy = Math.abs(fromCy - toCy);
|
|
10132
10128
|
let fromPt, toPt;
|
|
10133
10129
|
let fromDir;
|
|
10134
10130
|
let toDir;
|
|
10135
|
-
if (
|
|
10136
|
-
|
|
10137
|
-
|
|
10138
|
-
|
|
10139
|
-
|
|
10140
|
-
toDir = "left";
|
|
10141
|
-
} else {
|
|
10142
|
-
fromPt = { x: fromRect.x, y: fromRect.y + fromFieldY };
|
|
10143
|
-
toPt = { x: toRect.x + toRect.width, y: toRect.y + toFieldY };
|
|
10144
|
-
fromDir = "left";
|
|
10145
|
-
toDir = "right";
|
|
10146
|
-
}
|
|
10131
|
+
if (fromCx <= toCx) {
|
|
10132
|
+
fromPt = { x: fromRect.x + fromRect.width, y: fromRect.y + fromFieldY };
|
|
10133
|
+
toPt = { x: toRect.x, y: toRect.y + toFieldY };
|
|
10134
|
+
fromDir = "right";
|
|
10135
|
+
toDir = "left";
|
|
10147
10136
|
} else {
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
toDir = "up";
|
|
10153
|
-
} else {
|
|
10154
|
-
fromPt = { x: fromRect.x + fromRect.width / 2, y: fromRect.y };
|
|
10155
|
-
toPt = { x: toRect.x + toRect.width / 2, y: toRect.y + toRect.height };
|
|
10156
|
-
fromDir = "up";
|
|
10157
|
-
toDir = "down";
|
|
10158
|
-
}
|
|
10137
|
+
fromPt = { x: fromRect.x, y: fromRect.y + fromFieldY };
|
|
10138
|
+
toPt = { x: toRect.x + toRect.width, y: toRect.y + toFieldY };
|
|
10139
|
+
fromDir = "left";
|
|
10140
|
+
toDir = "right";
|
|
10159
10141
|
}
|
|
10160
10142
|
const offsetFrom = { ...fromPt };
|
|
10161
10143
|
const offsetTo = { ...toPt };
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
case "left":
|
|
10167
|
-
offsetFrom.x -= SYMBOL_SIZE;
|
|
10168
|
-
break;
|
|
10169
|
-
case "down":
|
|
10170
|
-
offsetFrom.y += SYMBOL_SIZE;
|
|
10171
|
-
break;
|
|
10172
|
-
case "up":
|
|
10173
|
-
offsetFrom.y -= SYMBOL_SIZE;
|
|
10174
|
-
break;
|
|
10175
|
-
}
|
|
10176
|
-
switch (toDir) {
|
|
10177
|
-
case "right":
|
|
10178
|
-
offsetTo.x += SYMBOL_SIZE;
|
|
10179
|
-
break;
|
|
10180
|
-
case "left":
|
|
10181
|
-
offsetTo.x -= SYMBOL_SIZE;
|
|
10182
|
-
break;
|
|
10183
|
-
case "down":
|
|
10184
|
-
offsetTo.y += SYMBOL_SIZE;
|
|
10185
|
-
break;
|
|
10186
|
-
case "up":
|
|
10187
|
-
offsetTo.y -= SYMBOL_SIZE;
|
|
10188
|
-
break;
|
|
10189
|
-
}
|
|
10144
|
+
if (fromDir === "right") offsetFrom.x += SYMBOL_SIZE;
|
|
10145
|
+
else offsetFrom.x -= SYMBOL_SIZE;
|
|
10146
|
+
if (toDir === "left") offsetTo.x -= SYMBOL_SIZE;
|
|
10147
|
+
else offsetTo.x += SYMBOL_SIZE;
|
|
10190
10148
|
const adx = Math.abs(offsetTo.x - offsetFrom.x);
|
|
10191
10149
|
const ady = Math.abs(offsetTo.y - offsetFrom.y);
|
|
10192
|
-
|
|
10193
|
-
|
|
10194
|
-
|
|
10195
|
-
|
|
10196
|
-
const cp2x = offsetTo.x + (offsetTo.x > offsetFrom.x ? -off : off);
|
|
10197
|
-
linePath = `M${offsetFrom.x},${offsetFrom.y} C${cp1x},${offsetFrom.y} ${cp2x},${offsetTo.y} ${offsetTo.x},${offsetTo.y}`;
|
|
10198
|
-
} else {
|
|
10199
|
-
const off = Math.max(ady * 0.4, 20);
|
|
10200
|
-
const cp1y = offsetFrom.y + (offsetTo.y > offsetFrom.y ? off : -off);
|
|
10201
|
-
const cp2y = offsetTo.y + (offsetTo.y > offsetFrom.y ? -off : off);
|
|
10202
|
-
linePath = `M${offsetFrom.x},${offsetFrom.y} C${offsetFrom.x},${cp1y} ${offsetTo.x},${cp2y} ${offsetTo.x},${offsetTo.y}`;
|
|
10203
|
-
}
|
|
10150
|
+
const off = Math.max(adx * 0.4, ady * 0.25, 40);
|
|
10151
|
+
const cp1x = offsetFrom.x + (fromDir === "right" ? off : -off);
|
|
10152
|
+
const cp2x = offsetTo.x + (toDir === "left" ? -off : off);
|
|
10153
|
+
const linePath = `M${offsetFrom.x},${offsetFrom.y} C${cp1x},${offsetFrom.y} ${cp2x},${offsetTo.y} ${offsetTo.x},${offsetTo.y}`;
|
|
10204
10154
|
const startSymbol = getSymbolPath(type, "start", fromPt, fromDir);
|
|
10205
10155
|
const endSymbol = getSymbolPath(type, "end", toPt, toDir);
|
|
10206
10156
|
return { linePath, startSymbol, endSymbol, midX: (offsetFrom.x + offsetTo.x) / 2, midY: (offsetFrom.y + offsetTo.y) / 2 };
|
|
@@ -10553,6 +10503,155 @@ var Diagram = Object.assign(DiagramRoot, {
|
|
|
10553
10503
|
Relation: DiagramRelation,
|
|
10554
10504
|
Toolbar: DiagramToolbar
|
|
10555
10505
|
});
|
|
10506
|
+
var TreeNavContext = react.createContext(null);
|
|
10507
|
+
function useTreeNav() {
|
|
10508
|
+
const ctx = react.useContext(TreeNavContext);
|
|
10509
|
+
if (!ctx) {
|
|
10510
|
+
throw new Error("useTreeNav must be used within a <TreeNav> component");
|
|
10511
|
+
}
|
|
10512
|
+
return ctx;
|
|
10513
|
+
}
|
|
10514
|
+
var EXT_COLORS = {
|
|
10515
|
+
ts: "#3178c6",
|
|
10516
|
+
tsx: "#3178c6",
|
|
10517
|
+
js: "#f7df1e",
|
|
10518
|
+
jsx: "#f7df1e",
|
|
10519
|
+
php: "#777bb4",
|
|
10520
|
+
html: "#e34c26",
|
|
10521
|
+
htm: "#e34c26",
|
|
10522
|
+
css: "#264de4",
|
|
10523
|
+
json: "#a1a1aa",
|
|
10524
|
+
md: "#71717a",
|
|
10525
|
+
yaml: "#cb171e",
|
|
10526
|
+
yml: "#cb171e"
|
|
10527
|
+
};
|
|
10528
|
+
function FileIcon({ ext }) {
|
|
10529
|
+
const color = ext && EXT_COLORS[ext.toLowerCase()] || "#71717a";
|
|
10530
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "shrink-0", children: [
|
|
10531
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4 1h5.5L13 4.5V14a1 1 0 01-1 1H4a1 1 0 01-1-1V2a1 1 0 011-1z", stroke: color, strokeWidth: "1.2" }),
|
|
10532
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 1v4h4", stroke: color, strokeWidth: "1.2" })
|
|
10533
|
+
] });
|
|
10534
|
+
}
|
|
10535
|
+
function FolderIcon({ open }) {
|
|
10536
|
+
if (open) {
|
|
10537
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "shrink-0", children: [
|
|
10538
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1.5 3.5a1 1 0 011-1h3l1.5 1.5H13a1 1 0 011 1V5H2.5V3.5z", fill: "#fbbf24" }),
|
|
10539
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1 6h13l-1.5 7.5H2.5L1 6z", fill: "#fbbf24", opacity: "0.7" })
|
|
10540
|
+
] });
|
|
10541
|
+
}
|
|
10542
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", className: "shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M1.5 3a1 1 0 011-1h3l1.5 1.5H13a1 1 0 011 1v8a1 1 0 01-1 1H2.5a1 1 0 01-1-1V3z", fill: "#fbbf24" }) });
|
|
10543
|
+
}
|
|
10544
|
+
function ChevronIcon({ open }) {
|
|
10545
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
10546
|
+
"svg",
|
|
10547
|
+
{
|
|
10548
|
+
width: "14",
|
|
10549
|
+
height: "14",
|
|
10550
|
+
viewBox: "0 0 24 24",
|
|
10551
|
+
fill: "none",
|
|
10552
|
+
stroke: "currentColor",
|
|
10553
|
+
strokeWidth: "2",
|
|
10554
|
+
strokeLinecap: "round",
|
|
10555
|
+
strokeLinejoin: "round",
|
|
10556
|
+
className: cn("shrink-0 transition-transform duration-150", open && "rotate-90"),
|
|
10557
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("polyline", { points: "9 18 15 12 9 6" })
|
|
10558
|
+
}
|
|
10559
|
+
);
|
|
10560
|
+
}
|
|
10561
|
+
function TreeNode({ node, depth }) {
|
|
10562
|
+
const { selectedId, onSelect, expandedIds, toggle, indentSize, showIcons } = useTreeNav();
|
|
10563
|
+
const isFolder = node.type === "folder" || node.children && node.children.length > 0;
|
|
10564
|
+
const isExpanded = expandedIds.includes(node.id);
|
|
10565
|
+
const isSelected = selectedId === node.id;
|
|
10566
|
+
const paddingLeft = depth * indentSize + 4;
|
|
10567
|
+
const handleClick = () => {
|
|
10568
|
+
if (node.disabled) return;
|
|
10569
|
+
if (isFolder) {
|
|
10570
|
+
toggle(node.id);
|
|
10571
|
+
}
|
|
10572
|
+
onSelect?.(node.id, node);
|
|
10573
|
+
};
|
|
10574
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-react-fancy-tree-node": "", children: [
|
|
10575
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
10576
|
+
"button",
|
|
10577
|
+
{
|
|
10578
|
+
type: "button",
|
|
10579
|
+
onClick: handleClick,
|
|
10580
|
+
disabled: node.disabled,
|
|
10581
|
+
className: cn(
|
|
10582
|
+
"flex w-full items-center gap-1 rounded-md py-0.5 text-left text-[13px] transition-colors",
|
|
10583
|
+
isSelected ? "bg-blue-500/15 text-blue-600 dark:text-blue-400" : "text-zinc-700 hover:bg-zinc-100 dark:text-zinc-300 dark:hover:bg-zinc-800",
|
|
10584
|
+
node.disabled && "pointer-events-none opacity-40"
|
|
10585
|
+
),
|
|
10586
|
+
style: { paddingLeft },
|
|
10587
|
+
children: [
|
|
10588
|
+
isFolder && /* @__PURE__ */ jsxRuntime.jsx(ChevronIcon, { open: isExpanded }),
|
|
10589
|
+
!isFolder && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "w-3.5 shrink-0" }),
|
|
10590
|
+
showIcons && (node.icon ?? (isFolder ? /* @__PURE__ */ jsxRuntime.jsx(FolderIcon, { open: isExpanded }) : /* @__PURE__ */ jsxRuntime.jsx(FileIcon, { ext: node.ext ?? node.label.split(".").pop() }))),
|
|
10591
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: node.label })
|
|
10592
|
+
]
|
|
10593
|
+
}
|
|
10594
|
+
),
|
|
10595
|
+
isFolder && isExpanded && node.children && /* @__PURE__ */ jsxRuntime.jsx("div", { "data-react-fancy-tree-node-children": "", children: node.children.map((child) => /* @__PURE__ */ jsxRuntime.jsx(TreeNode, { node: child, depth: depth + 1 }, child.id)) })
|
|
10596
|
+
] });
|
|
10597
|
+
}
|
|
10598
|
+
TreeNode.displayName = "TreeNode";
|
|
10599
|
+
function collectFolderIds(nodes) {
|
|
10600
|
+
const ids = [];
|
|
10601
|
+
for (const node of nodes) {
|
|
10602
|
+
if (node.children && node.children.length > 0) {
|
|
10603
|
+
ids.push(node.id);
|
|
10604
|
+
ids.push(...collectFolderIds(node.children));
|
|
10605
|
+
}
|
|
10606
|
+
}
|
|
10607
|
+
return ids;
|
|
10608
|
+
}
|
|
10609
|
+
function TreeNavRoot({
|
|
10610
|
+
nodes,
|
|
10611
|
+
selectedId,
|
|
10612
|
+
onSelect,
|
|
10613
|
+
expandedIds: controlledExpanded,
|
|
10614
|
+
defaultExpandedIds,
|
|
10615
|
+
onExpandedChange,
|
|
10616
|
+
defaultExpandAll = false,
|
|
10617
|
+
indentSize = 16,
|
|
10618
|
+
showIcons = true,
|
|
10619
|
+
className
|
|
10620
|
+
}) {
|
|
10621
|
+
const [internalExpanded, setInternalExpanded] = react.useState(() => {
|
|
10622
|
+
if (defaultExpandedIds) return defaultExpandedIds;
|
|
10623
|
+
if (defaultExpandAll) return collectFolderIds(nodes);
|
|
10624
|
+
return [];
|
|
10625
|
+
});
|
|
10626
|
+
const isControlled = controlledExpanded !== void 0;
|
|
10627
|
+
const expandedIds = isControlled ? controlledExpanded : internalExpanded;
|
|
10628
|
+
const toggle = react.useCallback(
|
|
10629
|
+
(id) => {
|
|
10630
|
+
const next = expandedIds.includes(id) ? expandedIds.filter((v) => v !== id) : [...expandedIds, id];
|
|
10631
|
+
if (!isControlled) {
|
|
10632
|
+
setInternalExpanded(next);
|
|
10633
|
+
}
|
|
10634
|
+
onExpandedChange?.(next);
|
|
10635
|
+
},
|
|
10636
|
+
[expandedIds, isControlled, onExpandedChange]
|
|
10637
|
+
);
|
|
10638
|
+
const ctx = react.useMemo(
|
|
10639
|
+
() => ({ selectedId, onSelect, expandedIds, toggle, indentSize, showIcons }),
|
|
10640
|
+
[selectedId, onSelect, expandedIds, toggle, indentSize, showIcons]
|
|
10641
|
+
);
|
|
10642
|
+
return /* @__PURE__ */ jsxRuntime.jsx(TreeNavContext.Provider, { value: ctx, children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
10643
|
+
"nav",
|
|
10644
|
+
{
|
|
10645
|
+
"data-react-fancy-tree-nav": "",
|
|
10646
|
+
className: cn("flex flex-col gap-0.5 py-1 text-sm", className),
|
|
10647
|
+
children: nodes.map((node) => /* @__PURE__ */ jsxRuntime.jsx(TreeNode, { node, depth: 0 }, node.id))
|
|
10648
|
+
}
|
|
10649
|
+
) });
|
|
10650
|
+
}
|
|
10651
|
+
TreeNavRoot.displayName = "TreeNav";
|
|
10652
|
+
var TreeNav = Object.assign(TreeNavRoot, {
|
|
10653
|
+
Node: TreeNode
|
|
10654
|
+
});
|
|
10556
10655
|
|
|
10557
10656
|
exports.Accordion = Accordion;
|
|
10558
10657
|
exports.Action = Action;
|
|
@@ -10615,6 +10714,7 @@ exports.TimePicker = TimePicker;
|
|
|
10615
10714
|
exports.Timeline = Timeline;
|
|
10616
10715
|
exports.Toast = Toast;
|
|
10617
10716
|
exports.Tooltip = Tooltip;
|
|
10717
|
+
exports.TreeNav = TreeNav;
|
|
10618
10718
|
exports.cn = cn;
|
|
10619
10719
|
exports.configureIcons = configureIcons;
|
|
10620
10720
|
exports.find = find;
|
|
@@ -10650,5 +10750,6 @@ exports.usePopover = usePopover;
|
|
|
10650
10750
|
exports.useSidebar = useSidebar;
|
|
10651
10751
|
exports.useTabs = useTabs;
|
|
10652
10752
|
exports.useToast = useToast;
|
|
10753
|
+
exports.useTreeNav = useTreeNav;
|
|
10653
10754
|
//# sourceMappingURL=index.cjs.map
|
|
10654
10755
|
//# sourceMappingURL=index.cjs.map
|