@particle-academy/react-fancy 1.7.3 → 1.8.0
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 +175 -70
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -2
- package/dist/index.d.ts +71 -2
- package/dist/index.js +174 -71
- package/dist/index.js.map +1 -1
- 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 | — |
|
|
128
129
|
|
|
129
130
|
### Rich Content
|
|
130
131
|
|
package/dist/index.cjs
CHANGED
|
@@ -8629,7 +8629,7 @@ function extensionEditorClasses(extensions) {
|
|
|
8629
8629
|
].join(" ");
|
|
8630
8630
|
}).join(" ");
|
|
8631
8631
|
}
|
|
8632
|
-
function EditorContent({ className }) {
|
|
8632
|
+
function EditorContent({ className, maxHeight }) {
|
|
8633
8633
|
const { contentRef, lineSpacing, placeholder, extensions, _initialHtml, _onInput } = useEditor();
|
|
8634
8634
|
const initialized = react.useRef(false);
|
|
8635
8635
|
react.useEffect(() => {
|
|
@@ -8652,10 +8652,14 @@ function EditorContent({ className }) {
|
|
|
8652
8652
|
"data-react-fancy-editor-content": "",
|
|
8653
8653
|
"data-placeholder": placeholder,
|
|
8654
8654
|
onInput: _onInput,
|
|
8655
|
-
style: {
|
|
8655
|
+
style: {
|
|
8656
|
+
lineHeight: lineSpacing,
|
|
8657
|
+
maxHeight: maxHeight ? `${maxHeight}px` : void 0
|
|
8658
|
+
},
|
|
8656
8659
|
className: cn(
|
|
8657
8660
|
"min-h-[120px] px-4 py-3 text-sm outline-none",
|
|
8658
8661
|
"focus:outline-none",
|
|
8662
|
+
maxHeight && "overflow-y-auto",
|
|
8659
8663
|
proseClasses,
|
|
8660
8664
|
extClasses,
|
|
8661
8665
|
"empty:before:content-[attr(data-placeholder)] empty:before:text-zinc-400 empty:before:pointer-events-none",
|
|
@@ -10024,7 +10028,7 @@ function DiagramEntity({
|
|
|
10024
10028
|
}
|
|
10025
10029
|
DiagramEntity.displayName = "DiagramEntity";
|
|
10026
10030
|
var HEADER_HEIGHT = 36;
|
|
10027
|
-
var FIELD_HEIGHT =
|
|
10031
|
+
var FIELD_HEIGHT = 29;
|
|
10028
10032
|
var SYMBOL_SIZE = 12;
|
|
10029
10033
|
function oneSymbol(pt, direction) {
|
|
10030
10034
|
const s = SYMBOL_SIZE * 0.6;
|
|
@@ -10121,82 +10125,32 @@ function DiagramRelation({
|
|
|
10121
10125
|
const toFieldY = toFieldIdx >= 0 ? HEADER_HEIGHT + toFieldIdx * FIELD_HEIGHT + FIELD_HEIGHT / 2 : toRect.height / 2;
|
|
10122
10126
|
const fromCx = fromRect.x + fromRect.width / 2;
|
|
10123
10127
|
const toCx = toRect.x + toRect.width / 2;
|
|
10124
|
-
const fromCy = fromRect.y + fromRect.height / 2;
|
|
10125
|
-
const toCy = toRect.y + toRect.height / 2;
|
|
10126
|
-
const dx = Math.abs(fromCx - toCx);
|
|
10127
|
-
const dy = Math.abs(fromCy - toCy);
|
|
10128
10128
|
let fromPt, toPt;
|
|
10129
10129
|
let fromDir;
|
|
10130
10130
|
let toDir;
|
|
10131
|
-
if (
|
|
10132
|
-
|
|
10133
|
-
|
|
10134
|
-
|
|
10135
|
-
|
|
10136
|
-
toDir = "left";
|
|
10137
|
-
} else {
|
|
10138
|
-
fromPt = { x: fromRect.x, y: fromRect.y + fromFieldY };
|
|
10139
|
-
toPt = { x: toRect.x + toRect.width, y: toRect.y + toFieldY };
|
|
10140
|
-
fromDir = "left";
|
|
10141
|
-
toDir = "right";
|
|
10142
|
-
}
|
|
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";
|
|
10143
10136
|
} else {
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
toDir = "up";
|
|
10149
|
-
} else {
|
|
10150
|
-
fromPt = { x: fromRect.x + fromRect.width / 2, y: fromRect.y };
|
|
10151
|
-
toPt = { x: toRect.x + toRect.width / 2, y: toRect.y + toRect.height };
|
|
10152
|
-
fromDir = "up";
|
|
10153
|
-
toDir = "down";
|
|
10154
|
-
}
|
|
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";
|
|
10155
10141
|
}
|
|
10156
10142
|
const offsetFrom = { ...fromPt };
|
|
10157
10143
|
const offsetTo = { ...toPt };
|
|
10158
|
-
|
|
10159
|
-
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
case "left":
|
|
10163
|
-
offsetFrom.x -= SYMBOL_SIZE;
|
|
10164
|
-
break;
|
|
10165
|
-
case "down":
|
|
10166
|
-
offsetFrom.y += SYMBOL_SIZE;
|
|
10167
|
-
break;
|
|
10168
|
-
case "up":
|
|
10169
|
-
offsetFrom.y -= SYMBOL_SIZE;
|
|
10170
|
-
break;
|
|
10171
|
-
}
|
|
10172
|
-
switch (toDir) {
|
|
10173
|
-
case "right":
|
|
10174
|
-
offsetTo.x += SYMBOL_SIZE;
|
|
10175
|
-
break;
|
|
10176
|
-
case "left":
|
|
10177
|
-
offsetTo.x -= SYMBOL_SIZE;
|
|
10178
|
-
break;
|
|
10179
|
-
case "down":
|
|
10180
|
-
offsetTo.y += SYMBOL_SIZE;
|
|
10181
|
-
break;
|
|
10182
|
-
case "up":
|
|
10183
|
-
offsetTo.y -= SYMBOL_SIZE;
|
|
10184
|
-
break;
|
|
10185
|
-
}
|
|
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;
|
|
10186
10148
|
const adx = Math.abs(offsetTo.x - offsetFrom.x);
|
|
10187
10149
|
const ady = Math.abs(offsetTo.y - offsetFrom.y);
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
const cp2x = offsetTo.x + (offsetTo.x > offsetFrom.x ? -off : off);
|
|
10193
|
-
linePath = `M${offsetFrom.x},${offsetFrom.y} C${cp1x},${offsetFrom.y} ${cp2x},${offsetTo.y} ${offsetTo.x},${offsetTo.y}`;
|
|
10194
|
-
} else {
|
|
10195
|
-
const off = Math.max(ady * 0.4, 20);
|
|
10196
|
-
const cp1y = offsetFrom.y + (offsetTo.y > offsetFrom.y ? off : -off);
|
|
10197
|
-
const cp2y = offsetTo.y + (offsetTo.y > offsetFrom.y ? -off : off);
|
|
10198
|
-
linePath = `M${offsetFrom.x},${offsetFrom.y} C${offsetFrom.x},${cp1y} ${offsetTo.x},${cp2y} ${offsetTo.x},${offsetTo.y}`;
|
|
10199
|
-
}
|
|
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}`;
|
|
10200
10154
|
const startSymbol = getSymbolPath(type, "start", fromPt, fromDir);
|
|
10201
10155
|
const endSymbol = getSymbolPath(type, "end", toPt, toDir);
|
|
10202
10156
|
return { linePath, startSymbol, endSymbol, midX: (offsetFrom.x + offsetTo.x) / 2, midY: (offsetFrom.y + offsetTo.y) / 2 };
|
|
@@ -10549,6 +10503,155 @@ var Diagram = Object.assign(DiagramRoot, {
|
|
|
10549
10503
|
Relation: DiagramRelation,
|
|
10550
10504
|
Toolbar: DiagramToolbar
|
|
10551
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
|
+
});
|
|
10552
10655
|
|
|
10553
10656
|
exports.Accordion = Accordion;
|
|
10554
10657
|
exports.Action = Action;
|
|
@@ -10611,6 +10714,7 @@ exports.TimePicker = TimePicker;
|
|
|
10611
10714
|
exports.Timeline = Timeline;
|
|
10612
10715
|
exports.Toast = Toast;
|
|
10613
10716
|
exports.Tooltip = Tooltip;
|
|
10717
|
+
exports.TreeNav = TreeNav;
|
|
10614
10718
|
exports.cn = cn;
|
|
10615
10719
|
exports.configureIcons = configureIcons;
|
|
10616
10720
|
exports.find = find;
|
|
@@ -10646,5 +10750,6 @@ exports.usePopover = usePopover;
|
|
|
10646
10750
|
exports.useSidebar = useSidebar;
|
|
10647
10751
|
exports.useTabs = useTabs;
|
|
10648
10752
|
exports.useToast = useToast;
|
|
10753
|
+
exports.useTreeNav = useTreeNav;
|
|
10649
10754
|
//# sourceMappingURL=index.cjs.map
|
|
10650
10755
|
//# sourceMappingURL=index.cjs.map
|