@vuer-ai/vuer-uikit 0.0.11 → 0.0.13
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/{chunk-CP7QYV4N.mjs → chunk-44KW6PTD.mjs} +1 -0
- package/dist/{chunk-KQAHAGUG.js → chunk-AOUGWGQR.js} +2 -2
- package/dist/{chunk-J7PTSP4Q.mjs → chunk-EAZQH4IX.mjs} +8 -0
- package/dist/chunk-EOFYA6CS.js +53 -0
- package/dist/{chunk-O4TK3AKI.mjs → chunk-EYIFKSU2.mjs} +1 -1
- package/dist/{chunk-WMXX2UMD.mjs → chunk-FEOONYZ2.mjs} +2 -2
- package/dist/{chunk-66DYOZI3.js → chunk-FORVP7XH.js} +6 -5
- package/dist/{chunk-7X6N54LX.mjs → chunk-G5KMD72J.mjs} +6 -5
- package/dist/{chunk-5MZCQW4U.js → chunk-IXHMIR5P.js} +4 -4
- package/dist/{chunk-CCGUGCCV.js → chunk-JAJNLRY5.js} +9 -9
- package/dist/{chunk-WOZWC6KO.mjs → chunk-JMPVDU2C.mjs} +35 -4
- package/dist/{chunk-E5KWKBH3.mjs → chunk-LEPXSM36.mjs} +10 -24
- package/dist/{chunk-3XVIJY3H.mjs → chunk-MRVMXKZO.mjs} +1 -1
- package/dist/{chunk-UMIQYHO6.mjs → chunk-OOI3SJDU.mjs} +10 -4
- package/dist/{chunk-B5EMQBVL.js → chunk-OTPUWWEB.js} +8 -3
- package/dist/{chunk-FSMKJAZT.js → chunk-PVRQNCSJ.js} +9 -23
- package/dist/chunk-Q5KEB4UK.mjs +51 -0
- package/dist/{chunk-XUQ5A7ZA.js → chunk-QEY7BWIR.js} +10 -4
- package/dist/{chunk-QZASHJNJ.js → chunk-QJR6MCTR.js} +8 -0
- package/dist/{chunk-IOI3YFTY.js → chunk-U2ZLDPOE.js} +8 -8
- package/dist/{chunk-ZJXBC33M.js → chunk-UFHMEEGL.js} +1 -0
- package/dist/{chunk-5P3KAS53.js → chunk-WJ7T7FAH.js} +34 -3
- package/dist/{chunk-6L4KPPOD.mjs → chunk-XMG3IVGZ.mjs} +8 -3
- package/dist/{chunk-XCCC7HVN.mjs → chunk-ZVG2UKKC.mjs} +1 -1
- package/dist/highlight-cursor/cursor-provider.js +2 -2
- package/dist/highlight-cursor/cursor-provider.mjs +1 -1
- package/dist/highlight-cursor/enhanced-components.js +11 -10
- package/dist/highlight-cursor/enhanced-components.mjs +6 -5
- package/dist/highlight-cursor/index.js +15 -14
- package/dist/highlight-cursor/index.mjs +7 -6
- package/dist/highlight-cursor/tabs-cursor-context.js +3 -3
- package/dist/highlight-cursor/tabs-cursor-context.mjs +1 -1
- package/dist/highlight-cursor/with-cursor.js +2 -2
- package/dist/highlight-cursor/with-cursor.mjs +1 -1
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +64 -59
- package/dist/index.mjs +15 -14
- package/dist/ui/avatar.js +2 -1
- package/dist/ui/avatar.mjs +2 -1
- package/dist/ui/badge.js +2 -1
- package/dist/ui/badge.mjs +2 -1
- package/dist/ui/button.js +2 -1
- package/dist/ui/button.mjs +2 -1
- package/dist/ui/card.js +2 -1
- package/dist/ui/card.mjs +2 -1
- package/dist/ui/checkbox.js +2 -1
- package/dist/ui/checkbox.mjs +2 -1
- package/dist/ui/collapsible.js +2 -1
- package/dist/ui/collapsible.mjs +2 -1
- package/dist/ui/dropdown.js +2 -1
- package/dist/ui/dropdown.mjs +2 -1
- package/dist/ui/index.js +49 -48
- package/dist/ui/index.mjs +12 -11
- package/dist/ui/input-numbers.js +4 -3
- package/dist/ui/input-numbers.mjs +3 -2
- package/dist/ui/input.js +2 -1
- package/dist/ui/input.mjs +2 -1
- package/dist/ui/layout.js +2 -1
- package/dist/ui/layout.mjs +2 -1
- package/dist/ui/modal.js +2 -1
- package/dist/ui/modal.mjs +2 -1
- package/dist/ui/popover.js +2 -1
- package/dist/ui/popover.mjs +2 -1
- package/dist/ui/radio-group.js +2 -1
- package/dist/ui/radio-group.mjs +2 -1
- package/dist/ui/select.js +2 -1
- package/dist/ui/select.mjs +2 -1
- package/dist/ui/slider.js +2 -1
- package/dist/ui/slider.mjs +2 -1
- package/dist/ui/switch.js +2 -1
- package/dist/ui/switch.mjs +2 -1
- package/dist/ui/tabs.js +8 -7
- package/dist/ui/tabs.mjs +4 -3
- package/dist/ui/textarea.js +2 -1
- package/dist/ui/textarea.mjs +2 -1
- package/dist/ui/theme-context.js +6 -3
- package/dist/ui/theme-context.mjs +4 -1
- package/dist/ui/timeline.js +6 -5
- package/dist/ui/timeline.mjs +5 -4
- package/dist/ui/toast.d.mts +1 -1
- package/dist/ui/toast.d.ts +1 -1
- package/dist/ui/toast.js +3 -3
- package/dist/ui/toast.mjs +1 -1
- package/dist/ui/toggle-group.js +2 -1
- package/dist/ui/toggle-group.mjs +2 -1
- package/dist/ui/toggle.js +2 -1
- package/dist/ui/toggle.mjs +2 -1
- package/dist/ui/tooltip.d.mts +3 -3
- package/dist/ui/tooltip.d.ts +3 -3
- package/dist/ui/tooltip.js +7 -6
- package/dist/ui/tooltip.mjs +3 -2
- package/dist/ui/tree-view-v2.js +5 -4
- package/dist/ui/tree-view-v2.mjs +4 -3
- package/dist/ui/version-badge.js +2 -1
- package/dist/ui/version-badge.mjs +2 -1
- package/dist/utils/index.d.mts +1 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +6 -1
- package/dist/utils/index.mjs +2 -1
- package/dist/utils/use-local-storage.d.mts +3 -0
- package/dist/utils/use-local-storage.d.ts +3 -0
- package/dist/utils/use-local-storage.js +10 -0
- package/dist/utils/use-local-storage.mjs +1 -0
- package/package.json +6 -4
- package/src/highlight-cursor/cursor-context.tsx +23 -0
- package/src/highlight-cursor/cursor-provider.tsx +264 -0
- package/src/highlight-cursor/enhanced-components.tsx +16 -0
- package/src/highlight-cursor/index.ts +21 -0
- package/src/highlight-cursor/tabs-cursor-context.tsx +121 -0
- package/src/highlight-cursor/types.ts +40 -0
- package/src/highlight-cursor/with-cursor.tsx +144 -0
- package/src/index.css +5 -0
- package/src/index.ts +5 -0
- package/src/styles/theme.css +80 -0
- package/src/styles/toast.css +64 -0
- package/src/styles/variables.css +194 -0
- package/src/ui/avatar.tsx +92 -0
- package/src/ui/badge.tsx +68 -0
- package/src/ui/button.tsx +100 -0
- package/src/ui/card.tsx +88 -0
- package/src/ui/checkbox.tsx +76 -0
- package/src/ui/collapsible.tsx +36 -0
- package/src/ui/dropdown.tsx +375 -0
- package/src/ui/index.ts +31 -0
- package/src/ui/input-numbers.tsx +201 -0
- package/src/ui/input.tsx +141 -0
- package/src/ui/layout.tsx +41 -0
- package/src/ui/modal.tsx +198 -0
- package/src/ui/popover.tsx +59 -0
- package/src/ui/radio-group.tsx +56 -0
- package/src/ui/select.tsx +292 -0
- package/src/ui/sheet.tsx +131 -0
- package/src/ui/slider.tsx +189 -0
- package/src/ui/switch.tsx +43 -0
- package/src/ui/tabs.tsx +128 -0
- package/src/ui/textarea.tsx +52 -0
- package/src/ui/theme-context.tsx +80 -0
- package/src/ui/timeline.tsx +717 -0
- package/src/ui/toast.tsx +27 -0
- package/src/ui/toggle-group.tsx +82 -0
- package/src/ui/toggle.tsx +88 -0
- package/src/ui/tooltip.tsx +132 -0
- package/src/ui/tree-view-v2.tsx +300 -0
- package/src/ui/tree-view.tsx +550 -0
- package/src/ui/version-badge.tsx +65 -0
- package/src/utils/cn.ts +21 -0
- package/src/utils/index.ts +2 -0
- package/src/utils/use-local-storage.ts +59 -0
- package/dist/chunk-OBWGRG45.js +0 -21
- package/dist/chunk-Z6XRPTIQ.mjs +0 -15
- /package/dist/{chunk-QS7WS6DD.mjs → chunk-M4IVLZIH.mjs} +0 -0
- /package/dist/{chunk-ST4P6AN6.js → chunk-XWS4SIB5.js} +0 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
|
2
|
+
import { cva } from "class-variance-authority";
|
|
3
|
+
import { ChevronRight, ChevronDown, Folder, File } from "lucide-react";
|
|
4
|
+
import {
|
|
5
|
+
forwardRef,
|
|
6
|
+
HTMLAttributes,
|
|
7
|
+
useCallback,
|
|
8
|
+
useMemo,
|
|
9
|
+
useState,
|
|
10
|
+
type ReactNode,
|
|
11
|
+
type ElementRef,
|
|
12
|
+
type ComponentPropsWithoutRef,
|
|
13
|
+
type DragEvent,
|
|
14
|
+
type ComponentType,
|
|
15
|
+
type SVGProps,
|
|
16
|
+
} from "react";
|
|
17
|
+
|
|
18
|
+
import { cn } from "../utils/cn";
|
|
19
|
+
|
|
20
|
+
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>;
|
|
21
|
+
|
|
22
|
+
const treeVariants = cva(
|
|
23
|
+
"text-uk-md group py-1 h-9 text-text-primary rounded transition-colors cursor-pointer text-left relative z-20",
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const selectedTreeVariants = cva("");
|
|
27
|
+
|
|
28
|
+
const dragOverVariants = cva("bg-primary/20 text-primary-foreground");
|
|
29
|
+
|
|
30
|
+
interface TreeDataItem {
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
icon?: IconComponent;
|
|
34
|
+
selectedIcon?: IconComponent;
|
|
35
|
+
openIcon?: IconComponent;
|
|
36
|
+
children?: TreeDataItem[];
|
|
37
|
+
actions?: ReactNode;
|
|
38
|
+
onClick?: () => void;
|
|
39
|
+
draggable?: boolean;
|
|
40
|
+
droppable?: boolean;
|
|
41
|
+
disabled?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type TreeProps = HTMLAttributes<HTMLDivElement> & {
|
|
45
|
+
data: TreeDataItem[] | TreeDataItem;
|
|
46
|
+
initialSelectedItemId?: string;
|
|
47
|
+
onSelectChange?: (item: TreeDataItem | undefined) => void;
|
|
48
|
+
expandAll?: boolean;
|
|
49
|
+
defaultNodeIcon?: IconComponent;
|
|
50
|
+
defaultLeafIcon?: IconComponent;
|
|
51
|
+
onDocumentDrag?: (sourceItem: TreeDataItem, targetItem: TreeDataItem) => void;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const TreeView = forwardRef<HTMLDivElement, TreeProps>(
|
|
55
|
+
(
|
|
56
|
+
{
|
|
57
|
+
data,
|
|
58
|
+
initialSelectedItemId,
|
|
59
|
+
onSelectChange,
|
|
60
|
+
expandAll,
|
|
61
|
+
defaultLeafIcon,
|
|
62
|
+
defaultNodeIcon,
|
|
63
|
+
className,
|
|
64
|
+
onDocumentDrag,
|
|
65
|
+
...props
|
|
66
|
+
},
|
|
67
|
+
ref,
|
|
68
|
+
) => {
|
|
69
|
+
const [selectedItemId, setSelectedItemId] = useState<string | undefined>(initialSelectedItemId);
|
|
70
|
+
|
|
71
|
+
const [draggedItem, setDraggedItem] = useState<TreeDataItem | null>(null);
|
|
72
|
+
|
|
73
|
+
const [hoverBackgroundStyle, setHoverBackgroundStyle] = useState<{
|
|
74
|
+
display: "block" | "none";
|
|
75
|
+
top: number;
|
|
76
|
+
height: number;
|
|
77
|
+
}>({ display: "none", top: 0, height: 0 });
|
|
78
|
+
|
|
79
|
+
const handleSelectChange = useCallback(
|
|
80
|
+
(item: TreeDataItem | undefined) => {
|
|
81
|
+
setSelectedItemId(item?.id);
|
|
82
|
+
if (onSelectChange) {
|
|
83
|
+
onSelectChange(item);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
[onSelectChange],
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
const handleDragStart = useCallback((item: TreeDataItem) => {
|
|
90
|
+
setDraggedItem(item);
|
|
91
|
+
}, []);
|
|
92
|
+
|
|
93
|
+
const handleDrop = useCallback(
|
|
94
|
+
(targetItem: TreeDataItem) => {
|
|
95
|
+
if (draggedItem && onDocumentDrag && draggedItem.id !== targetItem.id) {
|
|
96
|
+
onDocumentDrag(draggedItem, targetItem);
|
|
97
|
+
}
|
|
98
|
+
setDraggedItem(null);
|
|
99
|
+
},
|
|
100
|
+
[draggedItem, onDocumentDrag],
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const handleItemHover = useCallback((element: HTMLElement | null) => {
|
|
104
|
+
if (element) {
|
|
105
|
+
const rect = element.getBoundingClientRect();
|
|
106
|
+
const containerRect = element.closest("[data-tree-container]")?.getBoundingClientRect();
|
|
107
|
+
if (containerRect) {
|
|
108
|
+
setHoverBackgroundStyle({
|
|
109
|
+
display: "block",
|
|
110
|
+
top: rect.top - containerRect.top,
|
|
111
|
+
height: rect.height,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
} else {
|
|
115
|
+
setHoverBackgroundStyle({ display: "none", top: 0, height: 0 });
|
|
116
|
+
}
|
|
117
|
+
}, []);
|
|
118
|
+
|
|
119
|
+
const expandedItemIds = useMemo(() => {
|
|
120
|
+
if (!initialSelectedItemId) {
|
|
121
|
+
return [] as string[];
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const ids: string[] = [];
|
|
125
|
+
|
|
126
|
+
function walkTreeItems(items: TreeDataItem[] | TreeDataItem, targetId: string) {
|
|
127
|
+
if (items instanceof Array) {
|
|
128
|
+
for (let i = 0; i < items.length; i++) {
|
|
129
|
+
ids.push(items[i]!.id);
|
|
130
|
+
if (walkTreeItems(items[i]!, targetId) && !expandAll) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
if (!expandAll) ids.pop();
|
|
134
|
+
}
|
|
135
|
+
} else if (!expandAll && items.id === targetId) {
|
|
136
|
+
return true;
|
|
137
|
+
} else if (items.children) {
|
|
138
|
+
return walkTreeItems(items.children, targetId);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
walkTreeItems(data, initialSelectedItemId);
|
|
143
|
+
return ids;
|
|
144
|
+
}, [data, expandAll, initialSelectedItemId]);
|
|
145
|
+
|
|
146
|
+
return (
|
|
147
|
+
<div
|
|
148
|
+
className={cn(
|
|
149
|
+
"bg-bg-primary rounded-uk-md relative max-w-[300px] min-w-[240px] overflow-hidden p-2",
|
|
150
|
+
className,
|
|
151
|
+
)}
|
|
152
|
+
data-tree-container
|
|
153
|
+
>
|
|
154
|
+
<div
|
|
155
|
+
className="bg-bg-tertiary rounded-uk-sm pointer-events-none absolute right-2 left-2 z-10 transition-all duration-150"
|
|
156
|
+
style={{
|
|
157
|
+
display: hoverBackgroundStyle.display,
|
|
158
|
+
top: hoverBackgroundStyle.top,
|
|
159
|
+
height: hoverBackgroundStyle.height,
|
|
160
|
+
}}
|
|
161
|
+
/>
|
|
162
|
+
<TreeItem
|
|
163
|
+
data={data}
|
|
164
|
+
ref={ref}
|
|
165
|
+
selectedItemId={selectedItemId}
|
|
166
|
+
handleSelectChange={handleSelectChange}
|
|
167
|
+
expandedItemIds={expandedItemIds}
|
|
168
|
+
defaultLeafIcon={defaultLeafIcon}
|
|
169
|
+
defaultNodeIcon={defaultNodeIcon}
|
|
170
|
+
handleDragStart={handleDragStart}
|
|
171
|
+
handleDrop={handleDrop}
|
|
172
|
+
draggedItem={draggedItem}
|
|
173
|
+
onItemHover={handleItemHover}
|
|
174
|
+
{...props}
|
|
175
|
+
/>
|
|
176
|
+
<div
|
|
177
|
+
className="h-[48px] w-full"
|
|
178
|
+
onDrop={() => {
|
|
179
|
+
handleDrop({ id: "", name: "parent_div" });
|
|
180
|
+
}}
|
|
181
|
+
></div>
|
|
182
|
+
</div>
|
|
183
|
+
);
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
TreeView.displayName = "TreeView";
|
|
187
|
+
|
|
188
|
+
type TreeItemProps = TreeProps & {
|
|
189
|
+
selectedItemId?: string;
|
|
190
|
+
handleSelectChange: (item: TreeDataItem | undefined) => void;
|
|
191
|
+
expandedItemIds: string[];
|
|
192
|
+
defaultNodeIcon?: IconComponent;
|
|
193
|
+
defaultLeafIcon?: IconComponent;
|
|
194
|
+
handleDragStart?: (item: TreeDataItem) => void;
|
|
195
|
+
handleDrop?: (item: TreeDataItem) => void;
|
|
196
|
+
draggedItem: TreeDataItem | null;
|
|
197
|
+
onItemHover?: (element: HTMLElement | null) => void;
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const TreeItem = forwardRef<HTMLDivElement, TreeItemProps>(
|
|
201
|
+
(
|
|
202
|
+
{
|
|
203
|
+
className,
|
|
204
|
+
data,
|
|
205
|
+
selectedItemId,
|
|
206
|
+
handleSelectChange,
|
|
207
|
+
expandedItemIds,
|
|
208
|
+
defaultLeafIcon,
|
|
209
|
+
defaultNodeIcon,
|
|
210
|
+
handleDragStart,
|
|
211
|
+
handleDrop,
|
|
212
|
+
draggedItem,
|
|
213
|
+
onItemHover,
|
|
214
|
+
...props
|
|
215
|
+
},
|
|
216
|
+
ref,
|
|
217
|
+
) => {
|
|
218
|
+
if (!(data instanceof Array)) {
|
|
219
|
+
data = [data];
|
|
220
|
+
}
|
|
221
|
+
return (
|
|
222
|
+
<div ref={ref} role="tree" className={className} {...props}>
|
|
223
|
+
<ul style={{ margin: 0, paddingLeft: "1rem" }}>
|
|
224
|
+
{data.map((item) => (
|
|
225
|
+
<li key={item.id} style={{ margin: 0 }}>
|
|
226
|
+
{item.children ? (
|
|
227
|
+
<TreeNode
|
|
228
|
+
item={item}
|
|
229
|
+
handleSelectChange={handleSelectChange}
|
|
230
|
+
expandedItemIds={expandedItemIds}
|
|
231
|
+
selectedItemId={selectedItemId}
|
|
232
|
+
defaultNodeIcon={defaultNodeIcon}
|
|
233
|
+
defaultLeafIcon={defaultLeafIcon}
|
|
234
|
+
handleDragStart={handleDragStart}
|
|
235
|
+
handleDrop={handleDrop}
|
|
236
|
+
draggedItem={draggedItem}
|
|
237
|
+
onItemHover={onItemHover}
|
|
238
|
+
/>
|
|
239
|
+
) : (
|
|
240
|
+
<TreeLeaf
|
|
241
|
+
item={item}
|
|
242
|
+
selectedItemId={selectedItemId}
|
|
243
|
+
handleSelectChange={handleSelectChange}
|
|
244
|
+
defaultLeafIcon={defaultLeafIcon}
|
|
245
|
+
handleDragStart={handleDragStart}
|
|
246
|
+
handleDrop={handleDrop}
|
|
247
|
+
draggedItem={draggedItem}
|
|
248
|
+
onItemHover={onItemHover}
|
|
249
|
+
/>
|
|
250
|
+
)}
|
|
251
|
+
</li>
|
|
252
|
+
))}
|
|
253
|
+
</ul>
|
|
254
|
+
</div>
|
|
255
|
+
);
|
|
256
|
+
},
|
|
257
|
+
);
|
|
258
|
+
TreeItem.displayName = "TreeItem";
|
|
259
|
+
|
|
260
|
+
const TreeNode = ({
|
|
261
|
+
item,
|
|
262
|
+
handleSelectChange,
|
|
263
|
+
expandedItemIds,
|
|
264
|
+
selectedItemId,
|
|
265
|
+
defaultNodeIcon,
|
|
266
|
+
defaultLeafIcon,
|
|
267
|
+
handleDragStart,
|
|
268
|
+
handleDrop,
|
|
269
|
+
draggedItem,
|
|
270
|
+
onItemHover,
|
|
271
|
+
}: {
|
|
272
|
+
item: TreeDataItem;
|
|
273
|
+
handleSelectChange: (item: TreeDataItem | undefined) => void;
|
|
274
|
+
expandedItemIds: string[];
|
|
275
|
+
selectedItemId?: string;
|
|
276
|
+
defaultNodeIcon?: IconComponent;
|
|
277
|
+
defaultLeafIcon?: IconComponent;
|
|
278
|
+
handleDragStart?: (item: TreeDataItem) => void;
|
|
279
|
+
handleDrop?: (item: TreeDataItem) => void;
|
|
280
|
+
draggedItem: TreeDataItem | null;
|
|
281
|
+
onItemHover?: (element: HTMLElement | null) => void;
|
|
282
|
+
}) => {
|
|
283
|
+
const [value, setValue] = useState(expandedItemIds.includes(item.id) ? [item.id] : []);
|
|
284
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
285
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
286
|
+
|
|
287
|
+
const onDragStart = (e: DragEvent) => {
|
|
288
|
+
if (!item.draggable) {
|
|
289
|
+
e.preventDefault();
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
e.dataTransfer.setData("text/plain", item.id);
|
|
293
|
+
handleDragStart?.(item);
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
const onDragOver = (e: DragEvent) => {
|
|
297
|
+
if (item.droppable !== false && draggedItem && draggedItem.id !== item.id) {
|
|
298
|
+
e.preventDefault();
|
|
299
|
+
setIsDragOver(true);
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const onDragLeave = () => {
|
|
304
|
+
setIsDragOver(false);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const onDrop = (e: DragEvent) => {
|
|
308
|
+
e.preventDefault();
|
|
309
|
+
setIsDragOver(false);
|
|
310
|
+
handleDrop?.(item);
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<AccordionPrimitive.Root type="multiple" value={value} onValueChange={(s) => setValue(s)}>
|
|
315
|
+
<AccordionPrimitive.Item value={item.id} className="m-0">
|
|
316
|
+
<AccordionTrigger
|
|
317
|
+
style={{ margin: 0 }}
|
|
318
|
+
className={cn(
|
|
319
|
+
treeVariants(),
|
|
320
|
+
selectedItemId === item.id && selectedTreeVariants(),
|
|
321
|
+
isDragOver && dragOverVariants(),
|
|
322
|
+
item.disabled &&
|
|
323
|
+
"hover:bg-bg-primary pointer-events-none cursor-not-allowed opacity-50",
|
|
324
|
+
)}
|
|
325
|
+
onClick={() => {
|
|
326
|
+
if (item.disabled) return;
|
|
327
|
+
handleSelectChange(item);
|
|
328
|
+
item.onClick?.();
|
|
329
|
+
}}
|
|
330
|
+
onMouseEnter={(e) => {
|
|
331
|
+
setIsHovered(true);
|
|
332
|
+
onItemHover?.(e.currentTarget);
|
|
333
|
+
}}
|
|
334
|
+
onMouseLeave={() => {
|
|
335
|
+
setIsHovered(false);
|
|
336
|
+
onItemHover?.(null);
|
|
337
|
+
}}
|
|
338
|
+
draggable={!!item.draggable && !item.disabled}
|
|
339
|
+
onDragStart={onDragStart}
|
|
340
|
+
onDragOver={onDragOver}
|
|
341
|
+
onDragLeave={onDragLeave}
|
|
342
|
+
onDrop={onDrop}
|
|
343
|
+
>
|
|
344
|
+
{isHovered ? (
|
|
345
|
+
value.includes(item.id) ? (
|
|
346
|
+
<ChevronDown className="text-text-primary mr-2 h-4 w-4 shrink-0 transition-transform duration-200" />
|
|
347
|
+
) : (
|
|
348
|
+
<ChevronRight className="text-text-primary mr-2 h-4 w-4 shrink-0 transition-transform duration-200" />
|
|
349
|
+
)
|
|
350
|
+
) : (
|
|
351
|
+
<TreeIcon
|
|
352
|
+
item={item}
|
|
353
|
+
isSelected={selectedItemId === item.id}
|
|
354
|
+
isOpen={value.includes(item.id)}
|
|
355
|
+
default={defaultNodeIcon}
|
|
356
|
+
/>
|
|
357
|
+
)}
|
|
358
|
+
<span className="text-text-primary text-uk-md flex-grow truncate text-left">
|
|
359
|
+
{item.name}
|
|
360
|
+
</span>
|
|
361
|
+
<TreeActions isSelected={selectedItemId === item.id && !item.disabled}>
|
|
362
|
+
{item.actions}
|
|
363
|
+
</TreeActions>
|
|
364
|
+
</AccordionTrigger>
|
|
365
|
+
<AccordionContent>
|
|
366
|
+
<TreeItem
|
|
367
|
+
data={item.children ? item.children : item}
|
|
368
|
+
selectedItemId={selectedItemId}
|
|
369
|
+
handleSelectChange={handleSelectChange}
|
|
370
|
+
expandedItemIds={expandedItemIds}
|
|
371
|
+
defaultLeafIcon={defaultLeafIcon}
|
|
372
|
+
defaultNodeIcon={defaultNodeIcon}
|
|
373
|
+
handleDragStart={handleDragStart}
|
|
374
|
+
handleDrop={handleDrop}
|
|
375
|
+
draggedItem={draggedItem}
|
|
376
|
+
onItemHover={onItemHover}
|
|
377
|
+
/>
|
|
378
|
+
</AccordionContent>
|
|
379
|
+
</AccordionPrimitive.Item>
|
|
380
|
+
</AccordionPrimitive.Root>
|
|
381
|
+
);
|
|
382
|
+
};
|
|
383
|
+
|
|
384
|
+
const TreeLeaf = forwardRef<
|
|
385
|
+
HTMLDivElement,
|
|
386
|
+
HTMLAttributes<HTMLDivElement> & {
|
|
387
|
+
item: TreeDataItem;
|
|
388
|
+
selectedItemId?: string;
|
|
389
|
+
handleSelectChange: (item: TreeDataItem | undefined) => void;
|
|
390
|
+
defaultLeafIcon?: IconComponent;
|
|
391
|
+
handleDragStart?: (item: TreeDataItem) => void;
|
|
392
|
+
handleDrop?: (item: TreeDataItem) => void;
|
|
393
|
+
draggedItem: TreeDataItem | null;
|
|
394
|
+
onItemHover?: (element: HTMLElement | null) => void;
|
|
395
|
+
}
|
|
396
|
+
>(
|
|
397
|
+
(
|
|
398
|
+
{
|
|
399
|
+
className,
|
|
400
|
+
item,
|
|
401
|
+
selectedItemId,
|
|
402
|
+
handleSelectChange,
|
|
403
|
+
defaultLeafIcon,
|
|
404
|
+
handleDragStart,
|
|
405
|
+
handleDrop,
|
|
406
|
+
draggedItem,
|
|
407
|
+
onItemHover,
|
|
408
|
+
...props
|
|
409
|
+
},
|
|
410
|
+
ref,
|
|
411
|
+
) => {
|
|
412
|
+
const [isDragOver, setIsDragOver] = useState(false);
|
|
413
|
+
|
|
414
|
+
const onDragStart = (e: DragEvent) => {
|
|
415
|
+
if (!item.draggable || item.disabled) {
|
|
416
|
+
e.preventDefault();
|
|
417
|
+
return;
|
|
418
|
+
}
|
|
419
|
+
e.dataTransfer.setData("text/plain", item.id);
|
|
420
|
+
handleDragStart?.(item);
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const onDragOver = (e: DragEvent) => {
|
|
424
|
+
if (item.droppable !== false && !item.disabled && draggedItem && draggedItem.id !== item.id) {
|
|
425
|
+
e.preventDefault();
|
|
426
|
+
setIsDragOver(true);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
const onDragLeave = () => {
|
|
431
|
+
setIsDragOver(false);
|
|
432
|
+
};
|
|
433
|
+
|
|
434
|
+
const onDrop = (e: DragEvent) => {
|
|
435
|
+
if (item.disabled) return;
|
|
436
|
+
e.preventDefault();
|
|
437
|
+
setIsDragOver(false);
|
|
438
|
+
handleDrop?.(item);
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
return (
|
|
442
|
+
<div
|
|
443
|
+
ref={ref}
|
|
444
|
+
className={cn(
|
|
445
|
+
"relative flex items-center",
|
|
446
|
+
treeVariants(),
|
|
447
|
+
className,
|
|
448
|
+
selectedItemId === item.id && selectedTreeVariants(),
|
|
449
|
+
isDragOver && dragOverVariants(),
|
|
450
|
+
item.disabled && "hover:bg-bg-primary pointer-events-none cursor-not-allowed opacity-50",
|
|
451
|
+
)}
|
|
452
|
+
onClick={() => {
|
|
453
|
+
if (item.disabled) return;
|
|
454
|
+
handleSelectChange(item);
|
|
455
|
+
item.onClick?.();
|
|
456
|
+
}}
|
|
457
|
+
onMouseEnter={(e) => onItemHover?.(e.currentTarget)}
|
|
458
|
+
onMouseLeave={() => onItemHover?.(null)}
|
|
459
|
+
draggable={!!item.draggable && !item.disabled}
|
|
460
|
+
onDragStart={onDragStart}
|
|
461
|
+
onDragOver={onDragOver}
|
|
462
|
+
onDragLeave={onDragLeave}
|
|
463
|
+
onDrop={onDrop}
|
|
464
|
+
{...props}
|
|
465
|
+
>
|
|
466
|
+
<TreeIcon item={item} isSelected={selectedItemId === item.id} default={defaultLeafIcon} />
|
|
467
|
+
<span className="text-text-primary text-uk-md flex-grow truncate text-left">
|
|
468
|
+
{item.name}
|
|
469
|
+
</span>
|
|
470
|
+
<TreeActions isSelected={selectedItemId === item.id && !item.disabled}>
|
|
471
|
+
{item.actions}
|
|
472
|
+
</TreeActions>
|
|
473
|
+
</div>
|
|
474
|
+
);
|
|
475
|
+
},
|
|
476
|
+
);
|
|
477
|
+
TreeLeaf.displayName = "TreeLeaf";
|
|
478
|
+
|
|
479
|
+
const AccordionTrigger = forwardRef<
|
|
480
|
+
ElementRef<typeof AccordionPrimitive.Trigger>,
|
|
481
|
+
ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
|
482
|
+
>(({ className, children, ...props }, ref) => (
|
|
483
|
+
<AccordionPrimitive.Header style={{ margin: 0 }}>
|
|
484
|
+
<AccordionPrimitive.Trigger
|
|
485
|
+
ref={ref}
|
|
486
|
+
className={cn(
|
|
487
|
+
"relative flex w-full flex-1 items-center justify-between text-left font-medium transition-all",
|
|
488
|
+
className,
|
|
489
|
+
)}
|
|
490
|
+
{...props}
|
|
491
|
+
>
|
|
492
|
+
{children}
|
|
493
|
+
</AccordionPrimitive.Trigger>
|
|
494
|
+
</AccordionPrimitive.Header>
|
|
495
|
+
));
|
|
496
|
+
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
|
497
|
+
|
|
498
|
+
const AccordionContent = forwardRef<
|
|
499
|
+
ElementRef<typeof AccordionPrimitive.Content>,
|
|
500
|
+
ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
|
501
|
+
>(({ className, children, ...props }, ref) => (
|
|
502
|
+
<AccordionPrimitive.Content
|
|
503
|
+
ref={ref}
|
|
504
|
+
className={cn(
|
|
505
|
+
"data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down text-uk-md overflow-hidden transition-all",
|
|
506
|
+
className,
|
|
507
|
+
)}
|
|
508
|
+
{...props}
|
|
509
|
+
>
|
|
510
|
+
<div className="pt-0 pb-0">{children}</div>
|
|
511
|
+
</AccordionPrimitive.Content>
|
|
512
|
+
));
|
|
513
|
+
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
|
514
|
+
|
|
515
|
+
const TreeIcon = ({
|
|
516
|
+
item,
|
|
517
|
+
isOpen,
|
|
518
|
+
isSelected,
|
|
519
|
+
default: defaultIcon,
|
|
520
|
+
}: {
|
|
521
|
+
item: TreeDataItem;
|
|
522
|
+
isOpen?: boolean;
|
|
523
|
+
isSelected?: boolean;
|
|
524
|
+
default?: IconComponent;
|
|
525
|
+
}) => {
|
|
526
|
+
let Icon = defaultIcon;
|
|
527
|
+
if (isSelected && item.selectedIcon) {
|
|
528
|
+
Icon = item.selectedIcon;
|
|
529
|
+
} else if (isOpen && item.openIcon) {
|
|
530
|
+
Icon = item.openIcon;
|
|
531
|
+
} else if (item.icon) {
|
|
532
|
+
Icon = item.icon;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
if (!Icon) {
|
|
536
|
+
Icon = item.children && item.children.length > 0 ? Folder : File;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return Icon ? <Icon className="text-text-primary mr-2 h-4 w-4 shrink-0" /> : <></>;
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
const TreeActions = ({ children, isSelected }: { children: ReactNode; isSelected: boolean }) => {
|
|
543
|
+
return (
|
|
544
|
+
<div className={cn(isSelected ? "block" : "hidden", "absolute right-3 group-hover:block")}>
|
|
545
|
+
{children}
|
|
546
|
+
</div>
|
|
547
|
+
);
|
|
548
|
+
};
|
|
549
|
+
|
|
550
|
+
export { TreeView, type TreeDataItem };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { Badge } from "./badge";
|
|
4
|
+
|
|
5
|
+
// Version info embedded at build time - safe for production
|
|
6
|
+
const PACKAGE_VERSION = "0.0.11";
|
|
7
|
+
const PACKAGE_NAME = "@vuer-ai/vuer-uikit";
|
|
8
|
+
|
|
9
|
+
interface VersionBadgeProps {
|
|
10
|
+
/**
|
|
11
|
+
* Additional CSS classes to apply to the badge
|
|
12
|
+
*/
|
|
13
|
+
className?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Whether to show just the version or include "v" prefix
|
|
16
|
+
*/
|
|
17
|
+
showPrefix?: boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Whether the badge should be clickable (links to npm)
|
|
20
|
+
*/
|
|
21
|
+
linkable?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Override the version display (useful for testing)
|
|
24
|
+
*/
|
|
25
|
+
version?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* A badge component that displays the current package version and optionally links to npm
|
|
30
|
+
*/
|
|
31
|
+
export function VersionBadge({
|
|
32
|
+
className,
|
|
33
|
+
showPrefix = true,
|
|
34
|
+
linkable = true,
|
|
35
|
+
version: overrideVersion,
|
|
36
|
+
}: VersionBadgeProps) {
|
|
37
|
+
const currentVersion = overrideVersion || PACKAGE_VERSION;
|
|
38
|
+
const displayVersion = showPrefix ? `v${currentVersion}` : currentVersion;
|
|
39
|
+
const npmUrl = `https://www.npmjs.com/package/${PACKAGE_NAME}/v/${currentVersion}`;
|
|
40
|
+
|
|
41
|
+
const badge = (
|
|
42
|
+
<Badge
|
|
43
|
+
variant="accent"
|
|
44
|
+
className={className}
|
|
45
|
+
style={linkable ? { cursor: "pointer" } : undefined}
|
|
46
|
+
>
|
|
47
|
+
{displayVersion}
|
|
48
|
+
</Badge>
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
if (linkable) {
|
|
52
|
+
return (
|
|
53
|
+
<a
|
|
54
|
+
href={npmUrl}
|
|
55
|
+
target="_blank"
|
|
56
|
+
rel="noopener noreferrer"
|
|
57
|
+
className="no-underline hover:no-underline"
|
|
58
|
+
>
|
|
59
|
+
{badge}
|
|
60
|
+
</a>
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return badge;
|
|
65
|
+
}
|
package/src/utils/cn.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { type ClassValue, clsx } from "clsx";
|
|
2
|
+
import { extendTailwindMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
const twMerge = extendTailwindMerge({
|
|
5
|
+
override: {
|
|
6
|
+
classGroups: {
|
|
7
|
+
"font-size": [
|
|
8
|
+
"text-uk-xs",
|
|
9
|
+
"text-uk-sm",
|
|
10
|
+
"text-uk-md",
|
|
11
|
+
"text-uk-lg",
|
|
12
|
+
"text-uk-xl",
|
|
13
|
+
"text-uk-xxl",
|
|
14
|
+
],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export function cn(...inputs: ClassValue[]) {
|
|
20
|
+
return twMerge(clsx(inputs));
|
|
21
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export function useLocalStorage<T>(
|
|
4
|
+
key: string,
|
|
5
|
+
initialValue: T,
|
|
6
|
+
): [T, (value: T | ((val: T) => T)) => void] {
|
|
7
|
+
const [storedValue, setStoredValue] = useState<T>(() => {
|
|
8
|
+
if (typeof window === "undefined") {
|
|
9
|
+
return initialValue;
|
|
10
|
+
}
|
|
11
|
+
try {
|
|
12
|
+
const item = window.localStorage.getItem(key);
|
|
13
|
+
if (item === null) return initialValue;
|
|
14
|
+
|
|
15
|
+
// Handle different data types
|
|
16
|
+
if (typeof initialValue === "string") {
|
|
17
|
+
return item as T;
|
|
18
|
+
} else if (typeof initialValue === "number") {
|
|
19
|
+
return (parseFloat(item) || initialValue) as T;
|
|
20
|
+
} else if (typeof initialValue === "boolean") {
|
|
21
|
+
return (item === "true") as T;
|
|
22
|
+
} else {
|
|
23
|
+
// For objects and arrays, parse JSON
|
|
24
|
+
return JSON.parse(item);
|
|
25
|
+
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.warn(`Error reading localStorage key "${key}":`, error);
|
|
28
|
+
return initialValue;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const setValue = useCallback(
|
|
33
|
+
(value: T | ((val: T) => T)) => {
|
|
34
|
+
try {
|
|
35
|
+
// Allow value to be a function for functional updates
|
|
36
|
+
const valueToStore = value instanceof Function ? value(storedValue) : value;
|
|
37
|
+
setStoredValue(valueToStore);
|
|
38
|
+
|
|
39
|
+
// Save to localStorage only on client
|
|
40
|
+
if (typeof window !== "undefined") {
|
|
41
|
+
let stringValue: string;
|
|
42
|
+
if (typeof valueToStore === "string") {
|
|
43
|
+
stringValue = valueToStore;
|
|
44
|
+
} else if (typeof valueToStore === "number" || typeof valueToStore === "boolean") {
|
|
45
|
+
stringValue = valueToStore.toString();
|
|
46
|
+
} else {
|
|
47
|
+
stringValue = JSON.stringify(valueToStore);
|
|
48
|
+
}
|
|
49
|
+
window.localStorage.setItem(key, stringValue);
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
console.warn(`Error setting localStorage key "${key}":`, error);
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
[key, storedValue],
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
return [storedValue, setValue];
|
|
59
|
+
}
|