@postxl/ui-components 1.5.5 → 1.6.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/dist/index.d.ts +26 -13
- package/dist/index.js +338 -89
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -70,7 +70,7 @@ import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
|
70
70
|
import { DayPicker, getDefaultClassNames } from "react-day-picker";
|
|
71
71
|
import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
|
|
72
72
|
import useEmblaCarousel from "embla-carousel-react";
|
|
73
|
-
import { BaselineIcon, BookmarkIcon, CalendarIcon as CalendarIcon$1, Check, CheckIcon as CheckIcon$1, CheckSquareIcon, ChevronDownIcon as ChevronDownIcon$1, ChevronRightIcon as ChevronRightIcon$1, ChevronUpIcon as ChevronUpIcon$1, CircleCheckIcon, CopyIcon, DownloadIcon, EraserIcon, EyeIcon, EyeOffIcon, FilterX, GlobeIcon, GripHorizontalIcon, HashIcon, ListChecksIcon, ListIcon, ListTreeIcon, MessageSquareIcon, MinusIcon, PanelLeftIcon, PanelRightIcon, PencilIcon, PinIcon, PinOffIcon, PlusIcon, SaveIcon, Settings2Icon, SquareIcon, TagIcon, TextInitialIcon, Trash2Icon, TrashIcon, XIcon } from "lucide-react";
|
|
73
|
+
import { BaselineIcon, BookmarkIcon, CalendarIcon as CalendarIcon$1, Check, CheckIcon as CheckIcon$1, CheckSquareIcon, ChevronDownIcon as ChevronDownIcon$1, ChevronRightIcon as ChevronRightIcon$1, ChevronUpIcon as ChevronUpIcon$1, CircleCheckIcon, CopyIcon, DownloadIcon, EraserIcon, EyeIcon, EyeOffIcon, FilterX, GlobeIcon, GripHorizontalIcon, GripVerticalIcon, HashIcon, ListChecksIcon, ListIcon, ListTreeIcon, MessageSquareIcon, MinusIcon, PanelLeftIcon, PanelRightIcon, PencilIcon, PinIcon, PinOffIcon, PlusIcon, SaveIcon, Settings2Icon, SquareIcon, TagIcon, TextInitialIcon, Trash2Icon, TrashIcon, XIcon } from "lucide-react";
|
|
74
74
|
import * as CollapsePrimitive from "@radix-ui/react-collapsible";
|
|
75
75
|
import { Command as Command$1 } from "cmdk";
|
|
76
76
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
@@ -91,6 +91,7 @@ import { DndContext, DragOverlay, KeyboardCode, KeyboardSensor, MeasuringStrateg
|
|
|
91
91
|
import { SortableContext, arrayMove, defaultAnimateLayoutChanges, horizontalListSortingStrategy, useSortable, verticalListSortingStrategy } from "@dnd-kit/sortable";
|
|
92
92
|
import { CSS } from "@dnd-kit/utilities";
|
|
93
93
|
import * as ReactDOM from "react-dom";
|
|
94
|
+
import { createPortal } from "react-dom";
|
|
94
95
|
import * as MenubarPrimitive from "@radix-ui/react-menubar";
|
|
95
96
|
import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu";
|
|
96
97
|
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
|
|
@@ -9756,12 +9757,24 @@ function SidebarMenuSubButton({ asChild = false, size = "md", isActive = false,
|
|
|
9756
9757
|
const SidebarTabsContext = React$11.createContext(null);
|
|
9757
9758
|
function SidebarTabsProvider({ children, storageKey }) {
|
|
9758
9759
|
const [tabsMap, setTabsMap] = React$11.useState({});
|
|
9760
|
+
const [portalTargets, setPortalTargets] = React$11.useState({});
|
|
9759
9761
|
const fallbackKey = React$11.useId();
|
|
9760
9762
|
const [storedActiveTabs, setStoredActiveTabs] = useLocalStorageState(storageKey ?? fallbackKey, {
|
|
9761
9763
|
defaultValue: {},
|
|
9762
9764
|
storageSync: !!storageKey
|
|
9763
9765
|
});
|
|
9764
9766
|
const [activeTab, setActiveTabState] = React$11.useState(storedActiveTabs);
|
|
9767
|
+
const setPortalTarget = React$11.useCallback((side, tabId, element) => {
|
|
9768
|
+
setPortalTargets((prev) => {
|
|
9769
|
+
const next = new Map(prev[side]);
|
|
9770
|
+
if (element) next.set(tabId, element);
|
|
9771
|
+
else next.delete(tabId);
|
|
9772
|
+
return {
|
|
9773
|
+
...prev,
|
|
9774
|
+
[side]: next
|
|
9775
|
+
};
|
|
9776
|
+
});
|
|
9777
|
+
}, []);
|
|
9765
9778
|
const register = React$11.useCallback((side, tab) => {
|
|
9766
9779
|
setTabsMap((prev) => {
|
|
9767
9780
|
const next = new Map(prev[side]);
|
|
@@ -9815,13 +9828,17 @@ function SidebarTabsProvider({ children, storageKey }) {
|
|
|
9815
9828
|
activeTab: resolvedActiveTab,
|
|
9816
9829
|
register,
|
|
9817
9830
|
unregister,
|
|
9818
|
-
setActiveTab
|
|
9831
|
+
setActiveTab,
|
|
9832
|
+
portalTargets,
|
|
9833
|
+
setPortalTarget
|
|
9819
9834
|
}), [
|
|
9820
9835
|
sortedTabs,
|
|
9821
9836
|
resolvedActiveTab,
|
|
9822
9837
|
register,
|
|
9823
9838
|
unregister,
|
|
9824
|
-
setActiveTab
|
|
9839
|
+
setActiveTab,
|
|
9840
|
+
portalTargets,
|
|
9841
|
+
setPortalTarget
|
|
9825
9842
|
]);
|
|
9826
9843
|
return /* @__PURE__ */ jsx(SidebarTabsContext.Provider, {
|
|
9827
9844
|
value,
|
|
@@ -9850,21 +9867,17 @@ function useSidebarTabs(side) {
|
|
|
9850
9867
|
|
|
9851
9868
|
//#endregion
|
|
9852
9869
|
//#region src/sidebar/sidebar-tab.tsx
|
|
9853
|
-
function SidebarTab({ side, id, icon, label,
|
|
9870
|
+
function SidebarTab({ side, id, icon, label, order, badge, children }) {
|
|
9854
9871
|
const ctx = React$10.useContext(SidebarTabsContext);
|
|
9855
9872
|
if (!ctx) throw new Error("SidebarTab must be used within a SidebarTabsProvider.");
|
|
9856
9873
|
const { register, unregister } = ctx;
|
|
9857
|
-
const renderRef = React$10.useRef(render);
|
|
9858
|
-
renderRef.current = render;
|
|
9859
9874
|
const iconRef = React$10.useRef(icon);
|
|
9860
9875
|
iconRef.current = icon;
|
|
9861
|
-
const stableRender = React$10.useCallback(() => renderRef.current(), []);
|
|
9862
9876
|
React$10.useEffect(() => {
|
|
9863
9877
|
register(side, {
|
|
9864
9878
|
id,
|
|
9865
9879
|
icon: iconRef.current,
|
|
9866
9880
|
label,
|
|
9867
|
-
render: stableRender,
|
|
9868
9881
|
order,
|
|
9869
9882
|
badge
|
|
9870
9883
|
});
|
|
@@ -9876,17 +9889,17 @@ function SidebarTab({ side, id, icon, label, render, order, badge }) {
|
|
|
9876
9889
|
order,
|
|
9877
9890
|
badge,
|
|
9878
9891
|
register,
|
|
9879
|
-
unregister
|
|
9880
|
-
stableRender
|
|
9892
|
+
unregister
|
|
9881
9893
|
]);
|
|
9882
|
-
|
|
9894
|
+
const target = ctx.portalTargets[side]?.get(id);
|
|
9895
|
+
if (!target) return null;
|
|
9896
|
+
return createPortal(children, target);
|
|
9883
9897
|
}
|
|
9884
9898
|
function DynamicTabbedSidebar({ side, orientation = "horizontal", collapsible = "offcanvas", className,...sidebarProps }) {
|
|
9885
9899
|
const ctx = React$10.useContext(SidebarTabsContext);
|
|
9886
9900
|
if (!ctx) throw new Error("DynamicTabbedSidebar must be used within a SidebarTabsProvider.");
|
|
9887
9901
|
const tabs = ctx.tabs[side] ?? [];
|
|
9888
9902
|
const activeTabId = ctx.activeTab[side] ?? null;
|
|
9889
|
-
const activeTab = tabs.find((t) => t.id === activeTabId);
|
|
9890
9903
|
const isVertical = orientation === "vertical";
|
|
9891
9904
|
const effectiveCollapsible = isVertical && collapsible === "offcanvas" ? "icon" : collapsible;
|
|
9892
9905
|
if (tabs.length === 0) return null;
|
|
@@ -9899,23 +9912,49 @@ function DynamicTabbedSidebar({ side, orientation = "horizontal", collapsible =
|
|
|
9899
9912
|
side,
|
|
9900
9913
|
tabs,
|
|
9901
9914
|
activeTabId,
|
|
9902
|
-
activeTab,
|
|
9903
9915
|
isVertical,
|
|
9904
9916
|
collapsible: effectiveCollapsible
|
|
9905
9917
|
}), /* @__PURE__ */ jsx(SidebarRail, {})] })
|
|
9906
9918
|
});
|
|
9907
9919
|
}
|
|
9908
|
-
function
|
|
9920
|
+
function TabPortalTargets({ side, tabs, activeTabId, hidden }) {
|
|
9921
|
+
const ctx = React$10.useContext(SidebarTabsContext);
|
|
9922
|
+
const callbackRefs = React$10.useRef(new Map());
|
|
9923
|
+
const getRef = (tabId) => {
|
|
9924
|
+
let ref = callbackRefs.current.get(tabId);
|
|
9925
|
+
if (!ref) {
|
|
9926
|
+
ref = (el) => {
|
|
9927
|
+
ctx.setPortalTarget(side, tabId, el);
|
|
9928
|
+
};
|
|
9929
|
+
callbackRefs.current.set(tabId, ref);
|
|
9930
|
+
}
|
|
9931
|
+
return ref;
|
|
9932
|
+
};
|
|
9933
|
+
return /* @__PURE__ */ jsx(Fragment, { children: tabs.map((tab) => {
|
|
9934
|
+
const isVisible = !hidden && tab.id === activeTabId;
|
|
9935
|
+
return /* @__PURE__ */ jsx("div", {
|
|
9936
|
+
ref: getRef(tab.id),
|
|
9937
|
+
hidden: !isVisible || void 0,
|
|
9938
|
+
className: isVisible ? "flex flex-col flex-1 min-h-0" : void 0
|
|
9939
|
+
}, tab.id);
|
|
9940
|
+
}) });
|
|
9941
|
+
}
|
|
9942
|
+
function TabbedSidebarContent({ side, tabs, activeTabId, isVertical, collapsible }) {
|
|
9909
9943
|
const { state } = useSidebar(side);
|
|
9910
9944
|
const isCollapsed = state === "collapsed";
|
|
9911
9945
|
const isIconCollapsible = collapsible === "icon";
|
|
9912
|
-
if (isCollapsed && isIconCollapsible) return /* @__PURE__ */
|
|
9946
|
+
if (isCollapsed && isIconCollapsible) return /* @__PURE__ */ jsxs("div", {
|
|
9913
9947
|
className: "flex h-full flex-col",
|
|
9914
|
-
children: /* @__PURE__ */ jsx(VerticalTabBar, {
|
|
9948
|
+
children: [/* @__PURE__ */ jsx(VerticalTabBar, {
|
|
9915
9949
|
side,
|
|
9916
9950
|
tabs,
|
|
9917
9951
|
activeTabId
|
|
9918
|
-
})
|
|
9952
|
+
}), /* @__PURE__ */ jsx(TabPortalTargets, {
|
|
9953
|
+
side,
|
|
9954
|
+
tabs,
|
|
9955
|
+
activeTabId,
|
|
9956
|
+
hidden: true
|
|
9957
|
+
})]
|
|
9919
9958
|
});
|
|
9920
9959
|
if (isVertical) return /* @__PURE__ */ jsxs("div", {
|
|
9921
9960
|
className: "flex h-full flex-row",
|
|
@@ -9923,7 +9962,11 @@ function TabbedSidebarContent({ side, tabs, activeTabId, activeTab, isVertical,
|
|
|
9923
9962
|
side,
|
|
9924
9963
|
tabs,
|
|
9925
9964
|
activeTabId
|
|
9926
|
-
}), /* @__PURE__ */ jsx(SidebarContent, { children:
|
|
9965
|
+
}), /* @__PURE__ */ jsx(SidebarContent, { children: /* @__PURE__ */ jsx(TabPortalTargets, {
|
|
9966
|
+
side,
|
|
9967
|
+
tabs,
|
|
9968
|
+
activeTabId
|
|
9969
|
+
}) })]
|
|
9927
9970
|
});
|
|
9928
9971
|
return /* @__PURE__ */ jsxs("div", {
|
|
9929
9972
|
className: "flex h-full flex-col",
|
|
@@ -9934,7 +9977,11 @@ function TabbedSidebarContent({ side, tabs, activeTabId, activeTab, isVertical,
|
|
|
9934
9977
|
activeTabId
|
|
9935
9978
|
}),
|
|
9936
9979
|
tabs.length > 0 && /* @__PURE__ */ jsx(SidebarSeparator, {}),
|
|
9937
|
-
/* @__PURE__ */ jsx(SidebarContent, { children:
|
|
9980
|
+
/* @__PURE__ */ jsx(SidebarContent, { children: /* @__PURE__ */ jsx(TabPortalTargets, {
|
|
9981
|
+
side,
|
|
9982
|
+
tabs,
|
|
9983
|
+
activeTabId
|
|
9984
|
+
}) })
|
|
9938
9985
|
]
|
|
9939
9986
|
});
|
|
9940
9987
|
}
|
|
@@ -11288,112 +11335,314 @@ ToggleGroupItem.displayName = ToggleGroupPrimitive.Item.displayName;
|
|
|
11288
11335
|
|
|
11289
11336
|
//#endregion
|
|
11290
11337
|
//#region src/tree-view/tree-view.tsx
|
|
11291
|
-
const
|
|
11338
|
+
const DragContext = React$1.createContext({
|
|
11339
|
+
dragState: {
|
|
11340
|
+
draggedNodeId: null,
|
|
11341
|
+
dropTarget: null
|
|
11342
|
+
},
|
|
11343
|
+
setDragState: () => {}
|
|
11344
|
+
});
|
|
11345
|
+
/** Checks if `targetId` is a descendant of `ancestorId` in the tree. Used to prevent dropping a node into itself. */
|
|
11346
|
+
function isDescendant(nodes, ancestorId, targetId) {
|
|
11347
|
+
for (const node of nodes) {
|
|
11348
|
+
if (node.id === ancestorId && node.children) return containsNode(node.children, targetId);
|
|
11349
|
+
if (node.children && isDescendant(node.children, ancestorId, targetId)) return true;
|
|
11350
|
+
}
|
|
11351
|
+
return false;
|
|
11352
|
+
}
|
|
11353
|
+
/** Recursively checks if any node in the subtree has the given id. */
|
|
11354
|
+
function containsNode(nodes, targetId) {
|
|
11355
|
+
for (const node of nodes) {
|
|
11356
|
+
if (node.id === targetId) return true;
|
|
11357
|
+
if (node.children && containsNode(node.children, targetId)) return true;
|
|
11358
|
+
}
|
|
11359
|
+
return false;
|
|
11360
|
+
}
|
|
11361
|
+
/** Finds and returns a node by id from anywhere in the tree. */
|
|
11362
|
+
function findNode(nodes, id) {
|
|
11363
|
+
for (const node of nodes) {
|
|
11364
|
+
if (node.id === id) return node;
|
|
11365
|
+
if (node.children) {
|
|
11366
|
+
const found = findNode(node.children, id);
|
|
11367
|
+
if (found) return found;
|
|
11368
|
+
}
|
|
11369
|
+
}
|
|
11370
|
+
return void 0;
|
|
11371
|
+
}
|
|
11372
|
+
const TreeBranch = ({ node, level, onNodeSelect, onGroupSelect, onToggleGroup, onNodeDelete, onNodeDrop, defaultExpandedIds = [], selectedId, rootData }) => {
|
|
11292
11373
|
const isGroup = node.type === "group";
|
|
11293
11374
|
const hasChildren = node.children && node.children.length > 0;
|
|
11375
|
+
const { dragState, setDragState } = React$1.useContext(DragContext);
|
|
11376
|
+
/** Determines drop position (before/after/inside) based on cursor proximity to element edges. */
|
|
11377
|
+
const handleDragOver = (e) => {
|
|
11378
|
+
if (!dragState.draggedNodeId || dragState.draggedNodeId === node.id) return;
|
|
11379
|
+
if (isDescendant(rootData, dragState.draggedNodeId, node.id)) return;
|
|
11380
|
+
e.preventDefault();
|
|
11381
|
+
e.stopPropagation();
|
|
11382
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
11383
|
+
const distFromTop = e.clientY - rect.top;
|
|
11384
|
+
const distFromBottom = rect.bottom - e.clientY;
|
|
11385
|
+
const isExpanded = isGroup && hasChildren && defaultExpandedIds.includes(node.id);
|
|
11386
|
+
let position;
|
|
11387
|
+
if (distFromTop < 10) position = "before";
|
|
11388
|
+
else if (!isExpanded && distFromBottom < 10) position = "after";
|
|
11389
|
+
else position = "inside";
|
|
11390
|
+
if (node.locked && position !== "inside") return;
|
|
11391
|
+
setDragState((prev) => {
|
|
11392
|
+
if (prev.dropTarget?.nodeId === node.id && prev.dropTarget.position === position) return prev;
|
|
11393
|
+
return {
|
|
11394
|
+
...prev,
|
|
11395
|
+
dropTarget: {
|
|
11396
|
+
nodeId: node.id,
|
|
11397
|
+
position
|
|
11398
|
+
}
|
|
11399
|
+
};
|
|
11400
|
+
});
|
|
11401
|
+
};
|
|
11402
|
+
/** Fires onNodeDrop with the source node and target info, then resets drag state. */
|
|
11403
|
+
const handleDrop = (e) => {
|
|
11404
|
+
e.preventDefault();
|
|
11405
|
+
e.stopPropagation();
|
|
11406
|
+
if (!dragState.draggedNodeId || !dragState.dropTarget || !onNodeDrop) return;
|
|
11407
|
+
const sourceNode = findNode(rootData, dragState.draggedNodeId);
|
|
11408
|
+
if (!sourceNode) return;
|
|
11409
|
+
onNodeDrop(sourceNode, {
|
|
11410
|
+
node,
|
|
11411
|
+
position: dragState.dropTarget.position
|
|
11412
|
+
});
|
|
11413
|
+
setDragState({
|
|
11414
|
+
draggedNodeId: null,
|
|
11415
|
+
dropTarget: null
|
|
11416
|
+
});
|
|
11417
|
+
};
|
|
11418
|
+
/** Clears drop target when the cursor leaves this node (but not when moving to a child element). */
|
|
11419
|
+
const handleDragLeave = (e) => {
|
|
11420
|
+
if (!e.currentTarget.contains(e.relatedTarget)) setDragState((prev) => prev.dropTarget?.nodeId === node.id ? {
|
|
11421
|
+
...prev,
|
|
11422
|
+
dropTarget: null
|
|
11423
|
+
} : prev);
|
|
11424
|
+
};
|
|
11425
|
+
const isDraggedOver = dragState.dropTarget?.nodeId === node.id;
|
|
11426
|
+
const dropPosition = dragState.dropTarget?.position;
|
|
11427
|
+
const isDragging = dragState.draggedNodeId === node.id;
|
|
11428
|
+
const indent = level * 24;
|
|
11429
|
+
const dropLineBase = "after:absolute after:right-0 after:h-[2.5px] after:bg-primary after:left-(--drop-left) after:z-10";
|
|
11430
|
+
const showLine = isDraggedOver && dropPosition;
|
|
11431
|
+
const dropLineClass = cn("relative", {
|
|
11432
|
+
[`after:top-0 ${dropLineBase}`]: showLine === "before",
|
|
11433
|
+
[`after:-bottom-px ${dropLineBase}`]: showLine === "after" || showLine === "inside",
|
|
11434
|
+
"opacity-50": isDragging
|
|
11435
|
+
});
|
|
11436
|
+
const dropLineStyle = (offset) => isDraggedOver ? { "--drop-left": `${offset + (dropPosition === "inside" ? 24 : 0)}px` } : {};
|
|
11437
|
+
const dropHighlightClass = cn({ "bg-accent text-accent-foreground": isDraggedOver && dropPosition === "inside" });
|
|
11294
11438
|
if (!isGroup) return /* @__PURE__ */ jsxs("div", {
|
|
11295
11439
|
"data-test-id": `tree-node-${node.id}`,
|
|
11296
|
-
className: cn("relative flex items-center gap-2 py-1.5 px-2 rounded-md cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors border border-transparent hover:border hover:border-(--discreet-border)", node.className, { "bg-primary text-primary-foreground hover:border hover:border-(--discreet-border)": selectedId === node.id }),
|
|
11297
|
-
style: {
|
|
11298
|
-
|
|
11440
|
+
className: cn("relative group/node flex items-center gap-2 py-1.5 px-2 rounded-md cursor-pointer hover:bg-accent hover:text-accent-foreground transition-colors border border-transparent hover:border hover:border-(--discreet-border)", node.className, { "bg-primary text-primary-foreground hover:border hover:border-(--discreet-border)": selectedId === node.id }, dropLineClass, dropHighlightClass),
|
|
11441
|
+
style: {
|
|
11442
|
+
marginLeft: `${indent}px`,
|
|
11443
|
+
...dropLineStyle(0)
|
|
11444
|
+
},
|
|
11445
|
+
onClick: (e) => {
|
|
11446
|
+
if (e.target.closest("[data-slot=\"drag-handle\"]")) return;
|
|
11447
|
+
onNodeSelect?.(node);
|
|
11448
|
+
},
|
|
11449
|
+
onDragOver: onNodeDrop ? handleDragOver : void 0,
|
|
11450
|
+
onDrop: onNodeDrop ? handleDrop : void 0,
|
|
11451
|
+
onDragLeave: onNodeDrop ? handleDragLeave : void 0,
|
|
11299
11452
|
children: [
|
|
11300
11453
|
node.icon,
|
|
11301
11454
|
/* @__PURE__ */ jsx("span", {
|
|
11302
11455
|
className: "text-sm select-none truncate",
|
|
11303
11456
|
children: node.name
|
|
11304
11457
|
}),
|
|
11305
|
-
node.trailing
|
|
11458
|
+
node.trailing,
|
|
11459
|
+
!node.locked && (onNodeDelete || onNodeDrop) && /* @__PURE__ */ jsx(NodeActions, {
|
|
11460
|
+
node,
|
|
11461
|
+
onNodeDelete,
|
|
11462
|
+
onNodeDrop
|
|
11463
|
+
})
|
|
11306
11464
|
]
|
|
11307
11465
|
});
|
|
11308
11466
|
return /* @__PURE__ */ jsxs(AccordionItem, {
|
|
11309
11467
|
value: node.id,
|
|
11310
|
-
className: "border-0",
|
|
11468
|
+
className: "border-0 overflow-y-visible overflow-x-clip",
|
|
11311
11469
|
"data-test-id": `tree-group-${node.id}`,
|
|
11312
|
-
children: [/* @__PURE__ */ jsxs(
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
|
|
11316
|
-
|
|
11317
|
-
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
}
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
}
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
|
|
11470
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
11471
|
+
className: cn("relative group/node overflow-y-visible overflow-x-clip", dropLineClass),
|
|
11472
|
+
style: dropLineStyle(indent),
|
|
11473
|
+
onDragOver: onNodeDrop ? handleDragOver : void 0,
|
|
11474
|
+
onDrop: onNodeDrop ? handleDrop : void 0,
|
|
11475
|
+
onDragLeave: onNodeDrop ? handleDragLeave : void 0,
|
|
11476
|
+
children: [/* @__PURE__ */ jsxs(AccordionTrigger, {
|
|
11477
|
+
style: { "--margin-left": `calc(${level * 24 + 16}px - 16px)` },
|
|
11478
|
+
className: cn("relative flex flex-1 overflow-hidden items-center gap-2 py-1.5 px-2 rounded-md ml-(--margin-left) group-hover/node:bg-accent group-hover/node:text-accent-foreground hover:no-underline transition-colors border border-transparent group-hover/node:border group-hover/node:border-(--discreet-border)", "[&>svg:last-child]:hidden", node.className, { "bg-primary text-primary-foreground": selectedId === node.id }, dropHighlightClass),
|
|
11479
|
+
children: [
|
|
11480
|
+
/* @__PURE__ */ jsx(PlusIcon, {
|
|
11481
|
+
"data-test-id": `tree-expand-${node.id}`,
|
|
11482
|
+
className: "size-4 shrink-0 hidden [[data-state=closed]>&]:block hover:border rounded",
|
|
11483
|
+
onClick: () => {
|
|
11484
|
+
onToggleGroup?.({
|
|
11485
|
+
isExpanded: true,
|
|
11486
|
+
node
|
|
11487
|
+
});
|
|
11488
|
+
}
|
|
11489
|
+
}),
|
|
11490
|
+
/* @__PURE__ */ jsx(MinusIcon, {
|
|
11491
|
+
"data-test-id": `tree-collapse-${node.id}`,
|
|
11492
|
+
className: "size-4 shrink-0 hidden [[data-state=open]>&]:block hover:border rounded",
|
|
11493
|
+
onClick: () => {
|
|
11494
|
+
onToggleGroup?.({
|
|
11495
|
+
isExpanded: false,
|
|
11496
|
+
node
|
|
11497
|
+
});
|
|
11498
|
+
}
|
|
11499
|
+
}),
|
|
11500
|
+
/* @__PURE__ */ jsxs("div", {
|
|
11501
|
+
"data-test-id": `tree-group-label-${node.id}`,
|
|
11502
|
+
className: "flex w-[calc(100%-16px)] gap-2",
|
|
11503
|
+
onClick: (e) => {
|
|
11504
|
+
e.stopPropagation();
|
|
11505
|
+
if (e.target.closest("[data-slot=\"dropdown-menu-trigger\"]")) return;
|
|
11506
|
+
if (e.target.closest("[data-slot=\"drag-handle\"]")) return;
|
|
11507
|
+
onGroupSelect?.(node);
|
|
11508
|
+
},
|
|
11509
|
+
children: [
|
|
11510
|
+
node.icon,
|
|
11511
|
+
/* @__PURE__ */ jsx("span", {
|
|
11512
|
+
className: "text-sm select-none truncate text-left",
|
|
11513
|
+
children: node.name
|
|
11514
|
+
}),
|
|
11515
|
+
node.trailing
|
|
11516
|
+
]
|
|
11517
|
+
})
|
|
11518
|
+
]
|
|
11519
|
+
}), !node.locked && (onNodeDelete || onNodeDrop) && /* @__PURE__ */ jsx(NodeActions, {
|
|
11520
|
+
node,
|
|
11521
|
+
onNodeDelete,
|
|
11522
|
+
onNodeDrop
|
|
11523
|
+
})]
|
|
11353
11524
|
}), hasChildren && /* @__PURE__ */ jsxs(AccordionContent, {
|
|
11354
|
-
className: "pb-0 pt-0 relative",
|
|
11525
|
+
className: "pb-0 pt-0 relative overflow-y-visible overflow-x-clip",
|
|
11355
11526
|
children: [/* @__PURE__ */ jsx("div", {
|
|
11356
11527
|
style: { "--left-offset": `calc(${level * 24 + 32}px - 16px)` },
|
|
11357
11528
|
className: "before:absolute before:top-0 before:start-(--left-offset) before:w-0.5 before:-ms-px before:h-full before:bg-sidebar-ring dark:before:bg-sidebar-ring"
|
|
11358
11529
|
}), /* @__PURE__ */ jsx(Accordion, {
|
|
11359
11530
|
type: "multiple",
|
|
11360
11531
|
value: defaultExpandedIds,
|
|
11361
|
-
children: node.children?.map((child
|
|
11532
|
+
children: node.children?.map((child) => /* @__PURE__ */ jsx(TreeBranch, {
|
|
11362
11533
|
node: child,
|
|
11363
11534
|
level: level + 1,
|
|
11364
11535
|
onNodeSelect,
|
|
11365
11536
|
onGroupSelect,
|
|
11366
11537
|
onToggleGroup,
|
|
11367
|
-
|
|
11368
|
-
|
|
11538
|
+
onNodeDelete,
|
|
11539
|
+
onNodeDrop,
|
|
11369
11540
|
defaultExpandedIds,
|
|
11370
|
-
selectedId
|
|
11541
|
+
selectedId,
|
|
11542
|
+
rootData
|
|
11371
11543
|
}, child.id))
|
|
11372
11544
|
})]
|
|
11373
11545
|
})]
|
|
11374
11546
|
});
|
|
11375
11547
|
};
|
|
11376
|
-
const TreeView = React$1.forwardRef(({ data, onNodeSelect, onGroupSelect, onToggleGroup, className, defaultExpandedIds = [], selectedId }, ref) => {
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11548
|
+
const TreeView = React$1.forwardRef(({ data, onNodeSelect, onGroupSelect, onToggleGroup, onNodeDelete, onNodeDrop, className, defaultExpandedIds = [], selectedId }, ref) => {
|
|
11549
|
+
const [dragState, setDragState] = React$1.useState({
|
|
11550
|
+
draggedNodeId: null,
|
|
11551
|
+
dropTarget: null
|
|
11552
|
+
});
|
|
11553
|
+
return /* @__PURE__ */ jsx(DragContext.Provider, {
|
|
11554
|
+
value: {
|
|
11555
|
+
dragState,
|
|
11556
|
+
setDragState
|
|
11557
|
+
},
|
|
11558
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
11559
|
+
ref,
|
|
11560
|
+
className: cn("w-full select-none", className),
|
|
11561
|
+
onDragOver: onNodeDrop && dragState.draggedNodeId ? (e) => e.preventDefault() : void 0,
|
|
11562
|
+
children: /* @__PURE__ */ jsx(Accordion, {
|
|
11563
|
+
type: "multiple",
|
|
11564
|
+
value: defaultExpandedIds,
|
|
11565
|
+
children: data.map((node) => /* @__PURE__ */ jsx(TreeBranch, {
|
|
11566
|
+
node,
|
|
11567
|
+
level: 0,
|
|
11568
|
+
onNodeSelect,
|
|
11569
|
+
onGroupSelect,
|
|
11570
|
+
onToggleGroup,
|
|
11571
|
+
onNodeDelete,
|
|
11572
|
+
onNodeDrop,
|
|
11573
|
+
defaultExpandedIds,
|
|
11574
|
+
selectedId,
|
|
11575
|
+
rootData: data
|
|
11576
|
+
}, node.id))
|
|
11577
|
+
})
|
|
11394
11578
|
})
|
|
11395
11579
|
});
|
|
11396
11580
|
});
|
|
11581
|
+
const NodeActions = ({ node, onNodeDelete, onNodeDrop }) => {
|
|
11582
|
+
const [showDeleteConfirmation, setShowDeleteConfirmation] = React$1.useState(false);
|
|
11583
|
+
const containerRef = React$1.useRef(null);
|
|
11584
|
+
const { setDragState } = React$1.useContext(DragContext);
|
|
11585
|
+
React$1.useEffect(() => {
|
|
11586
|
+
if (!showDeleteConfirmation) return;
|
|
11587
|
+
const handleClickOutside = (e) => {
|
|
11588
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) setShowDeleteConfirmation(false);
|
|
11589
|
+
};
|
|
11590
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
11591
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
11592
|
+
}, [showDeleteConfirmation]);
|
|
11593
|
+
return /* @__PURE__ */ jsx("div", {
|
|
11594
|
+
ref: containerRef,
|
|
11595
|
+
className: "absolute top-1/2 -translate-y-1/2 end-1 flex items-center gap-0.5 rounded transition-none group-hover/node:bg-accent",
|
|
11596
|
+
children: showDeleteConfirmation && onNodeDelete ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Button, {
|
|
11597
|
+
variant: "ghost",
|
|
11598
|
+
size: "iconXs",
|
|
11599
|
+
onClick: (e) => {
|
|
11600
|
+
e.stopPropagation();
|
|
11601
|
+
setShowDeleteConfirmation(false);
|
|
11602
|
+
},
|
|
11603
|
+
children: /* @__PURE__ */ jsx(XIcon, { className: "size-4" })
|
|
11604
|
+
}), /* @__PURE__ */ jsx(Button, {
|
|
11605
|
+
variant: "destructive",
|
|
11606
|
+
size: "iconXs",
|
|
11607
|
+
onClick: (e) => {
|
|
11608
|
+
e.stopPropagation();
|
|
11609
|
+
onNodeDelete(node);
|
|
11610
|
+
setShowDeleteConfirmation(false);
|
|
11611
|
+
},
|
|
11612
|
+
children: /* @__PURE__ */ jsx(CheckIcon$1, { className: "size-4" })
|
|
11613
|
+
})] }) : /* @__PURE__ */ jsxs(Fragment, { children: [onNodeDelete && /* @__PURE__ */ jsx(Button, {
|
|
11614
|
+
variant: "ghost",
|
|
11615
|
+
size: "iconXs",
|
|
11616
|
+
className: "opacity-0 group-hover/node:opacity-100",
|
|
11617
|
+
onClick: (e) => {
|
|
11618
|
+
e.stopPropagation();
|
|
11619
|
+
setShowDeleteConfirmation(true);
|
|
11620
|
+
},
|
|
11621
|
+
children: /* @__PURE__ */ jsx(Trash2Icon, { className: "size-4" })
|
|
11622
|
+
}), onNodeDrop && /* @__PURE__ */ jsx(Button, {
|
|
11623
|
+
variant: "ghost",
|
|
11624
|
+
size: "iconXs",
|
|
11625
|
+
"data-slot": "drag-handle",
|
|
11626
|
+
draggable: true,
|
|
11627
|
+
className: "opacity-0 group-hover/node:opacity-100 cursor-grab active:cursor-grabbing bg-transparent",
|
|
11628
|
+
onDragStart: (e) => {
|
|
11629
|
+
e.dataTransfer.effectAllowed = "move";
|
|
11630
|
+
e.dataTransfer.setData("text/plain", node.id);
|
|
11631
|
+
setDragState({
|
|
11632
|
+
draggedNodeId: node.id,
|
|
11633
|
+
dropTarget: null
|
|
11634
|
+
});
|
|
11635
|
+
},
|
|
11636
|
+
onDragEnd: () => {
|
|
11637
|
+
setDragState({
|
|
11638
|
+
draggedNodeId: null,
|
|
11639
|
+
dropTarget: null
|
|
11640
|
+
});
|
|
11641
|
+
},
|
|
11642
|
+
children: /* @__PURE__ */ jsx(GripVerticalIcon, { className: "size-4" })
|
|
11643
|
+
})] })
|
|
11644
|
+
});
|
|
11645
|
+
};
|
|
11397
11646
|
TreeView.displayName = "TreeView";
|
|
11398
11647
|
|
|
11399
11648
|
//#endregion
|