@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
package/src/ui/toast.tsx
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import { Toaster as Sonner, ToasterProps, toast } from "sonner";
|
|
3
|
+
|
|
4
|
+
const Toaster = ({ ...props }: ToasterProps) => {
|
|
5
|
+
const [isClient, setIsClient] = useState(false);
|
|
6
|
+
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
setIsClient(true);
|
|
9
|
+
}, []);
|
|
10
|
+
|
|
11
|
+
// Don't render anything during SSR
|
|
12
|
+
if (!isClient) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<Sonner
|
|
18
|
+
toastOptions={{
|
|
19
|
+
className: "vk-toast liquid:liquid-bg",
|
|
20
|
+
descriptionClassName: "vk-toast-description",
|
|
21
|
+
}}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export { Toaster, toast };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import * as ToggleGroupPrimitive from "@radix-ui/react-toggle-group";
|
|
2
|
+
import { type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { ComponentProps, createContext, useContext } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../utils";
|
|
6
|
+
import { toggleVariants } from "./toggle";
|
|
7
|
+
|
|
8
|
+
const ToggleGroupContext = createContext<VariantProps<typeof toggleVariants>>({
|
|
9
|
+
size: "base",
|
|
10
|
+
variant: "primary",
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
function ToggleGroup({
|
|
14
|
+
className,
|
|
15
|
+
variant,
|
|
16
|
+
size,
|
|
17
|
+
children,
|
|
18
|
+
...props
|
|
19
|
+
}: ComponentProps<typeof ToggleGroupPrimitive.Root> & VariantProps<typeof toggleVariants>) {
|
|
20
|
+
return (
|
|
21
|
+
<ToggleGroupPrimitive.Root
|
|
22
|
+
data-slot="toggle-group"
|
|
23
|
+
data-variant={variant}
|
|
24
|
+
data-size={size}
|
|
25
|
+
className={cn(
|
|
26
|
+
"group/toggle-group",
|
|
27
|
+
"flex",
|
|
28
|
+
"w-fit",
|
|
29
|
+
"items-center",
|
|
30
|
+
"rounded-uk-md",
|
|
31
|
+
"data-[variant=outline]:shadow-xs",
|
|
32
|
+
className,
|
|
33
|
+
)}
|
|
34
|
+
{...props}
|
|
35
|
+
>
|
|
36
|
+
<ToggleGroupContext.Provider value={{ variant, size }}>
|
|
37
|
+
{children}
|
|
38
|
+
</ToggleGroupContext.Provider>
|
|
39
|
+
</ToggleGroupPrimitive.Root>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function ToggleGroupItem({
|
|
44
|
+
className,
|
|
45
|
+
children,
|
|
46
|
+
variant,
|
|
47
|
+
size,
|
|
48
|
+
...props
|
|
49
|
+
}: ComponentProps<typeof ToggleGroupPrimitive.Item> & VariantProps<typeof toggleVariants>) {
|
|
50
|
+
const context = useContext(ToggleGroupContext);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<ToggleGroupPrimitive.Item
|
|
54
|
+
data-slot="toggle-group-item"
|
|
55
|
+
data-variant={context.variant || variant}
|
|
56
|
+
data-size={context.size || size}
|
|
57
|
+
className={cn(
|
|
58
|
+
toggleVariants({
|
|
59
|
+
variant: context.variant || variant,
|
|
60
|
+
size: context.size || size,
|
|
61
|
+
}),
|
|
62
|
+
"min-w-0",
|
|
63
|
+
"flex-1",
|
|
64
|
+
"shrink-0",
|
|
65
|
+
"rounded-none",
|
|
66
|
+
"shadow-none",
|
|
67
|
+
"first:rounded-l-md",
|
|
68
|
+
"last:rounded-r-md",
|
|
69
|
+
"focus:z-10",
|
|
70
|
+
"focus-visible:z-10",
|
|
71
|
+
"data-[variant=outline]:border-l-0",
|
|
72
|
+
"data-[variant=outline]:first:border-l",
|
|
73
|
+
className,
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
>
|
|
77
|
+
{children}
|
|
78
|
+
</ToggleGroupPrimitive.Item>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export { ToggleGroup, ToggleGroupItem };
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import * as TogglePrimitive from "@radix-ui/react-toggle";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { ComponentProps } from "react";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../utils";
|
|
6
|
+
|
|
7
|
+
const toggleVariants = cva(
|
|
8
|
+
[
|
|
9
|
+
"inline-flex",
|
|
10
|
+
"items-center",
|
|
11
|
+
"justify-center",
|
|
12
|
+
"shrink-0",
|
|
13
|
+
"whitespace-nowrap",
|
|
14
|
+
"font-normal",
|
|
15
|
+
"transition-all",
|
|
16
|
+
"[&_svg]:shrink-0 ",
|
|
17
|
+
"[&_svg]:pointer-events-none",
|
|
18
|
+
"outline-none",
|
|
19
|
+
"disabled:bg-transparent",
|
|
20
|
+
"disabled:text-bg-tertiary",
|
|
21
|
+
"disabled:cursor-not-allowed",
|
|
22
|
+
],
|
|
23
|
+
{
|
|
24
|
+
variants: {
|
|
25
|
+
variant: {
|
|
26
|
+
primary: [
|
|
27
|
+
"text-text-primary",
|
|
28
|
+
"data-[state=on]:text-text-highlight",
|
|
29
|
+
"data-[state=on]:bg-brand-primary",
|
|
30
|
+
"hover:bg-bg-secondary",
|
|
31
|
+
],
|
|
32
|
+
secondary: [
|
|
33
|
+
"text-text-secondary",
|
|
34
|
+
"data-[state=on]:text-text-highlight",
|
|
35
|
+
"data-[state=on]:bg-bg-secondary",
|
|
36
|
+
"hover:bg-bg-secondary ",
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
size: {
|
|
40
|
+
sm: [
|
|
41
|
+
"text-uk-sm",
|
|
42
|
+
"leading-uk-sm",
|
|
43
|
+
"gap-xxs",
|
|
44
|
+
"p-xs",
|
|
45
|
+
"[&_svg:not([class*='size-'])]:size-3",
|
|
46
|
+
"rounded-uk-xs",
|
|
47
|
+
],
|
|
48
|
+
base: [
|
|
49
|
+
"text-uk-md",
|
|
50
|
+
"leading-uk-md",
|
|
51
|
+
"gap-xs",
|
|
52
|
+
"p-sm",
|
|
53
|
+
"[&_svg:not([class*='size-'])]:size-3.5",
|
|
54
|
+
"rounded-uk-sm",
|
|
55
|
+
],
|
|
56
|
+
lg: [
|
|
57
|
+
"text-uk-lg",
|
|
58
|
+
"leading-uk-lg",
|
|
59
|
+
"gap-xs",
|
|
60
|
+
"p-md",
|
|
61
|
+
"[&_svg:not([class*='size-'])]:size-4",
|
|
62
|
+
"rounded-uk-md",
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
defaultVariants: {
|
|
67
|
+
variant: "primary",
|
|
68
|
+
size: "base",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
function Toggle({
|
|
74
|
+
className,
|
|
75
|
+
variant,
|
|
76
|
+
size,
|
|
77
|
+
...props
|
|
78
|
+
}: ComponentProps<typeof TogglePrimitive.Root> & VariantProps<typeof toggleVariants>) {
|
|
79
|
+
return (
|
|
80
|
+
<TogglePrimitive.Root
|
|
81
|
+
data-slot="toggle"
|
|
82
|
+
className={cn(toggleVariants({ variant, size, className }))}
|
|
83
|
+
{...props}
|
|
84
|
+
/>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export { Toggle, toggleVariants };
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
2
|
+
import { ComponentProps, useEffect, useState } from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../utils";
|
|
5
|
+
|
|
6
|
+
function TooltipProvider({
|
|
7
|
+
delayDuration = 0,
|
|
8
|
+
children,
|
|
9
|
+
...props
|
|
10
|
+
}: ComponentProps<typeof TooltipPrimitive.Provider>) {
|
|
11
|
+
const [isClient, setIsClient] = useState(false);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
setIsClient(true);
|
|
15
|
+
}, []);
|
|
16
|
+
|
|
17
|
+
// Don't render anything during SSR
|
|
18
|
+
if (!isClient) {
|
|
19
|
+
return <>{children}</>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<TooltipPrimitive.Provider
|
|
24
|
+
data-slot="tooltip-provider"
|
|
25
|
+
delayDuration={delayDuration}
|
|
26
|
+
{...props}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</TooltipPrimitive.Provider>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function Tooltip({ children, ...props }: ComponentProps<typeof TooltipPrimitive.Root>) {
|
|
34
|
+
const [isClient, setIsClient] = useState(false);
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
setIsClient(true);
|
|
38
|
+
}, []);
|
|
39
|
+
|
|
40
|
+
// During SSR, just render children without tooltip functionality
|
|
41
|
+
if (!isClient) {
|
|
42
|
+
return <>{children}</>;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<TooltipProvider>
|
|
47
|
+
<TooltipPrimitive.Root data-slot="tooltip" {...props}>
|
|
48
|
+
{children}
|
|
49
|
+
</TooltipPrimitive.Root>
|
|
50
|
+
</TooltipProvider>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function TooltipTrigger({ ...props }: ComponentProps<typeof TooltipPrimitive.Trigger>) {
|
|
55
|
+
const [isClient, setIsClient] = useState(false);
|
|
56
|
+
|
|
57
|
+
useEffect(() => {
|
|
58
|
+
setIsClient(true);
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
// During SSR, just render children without tooltip functionality
|
|
62
|
+
if (!isClient) {
|
|
63
|
+
return <>{props.children}</>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function TooltipContent({
|
|
70
|
+
className,
|
|
71
|
+
sideOffset = 0,
|
|
72
|
+
children,
|
|
73
|
+
...props
|
|
74
|
+
}: ComponentProps<typeof TooltipPrimitive.Content>) {
|
|
75
|
+
const [isClient, setIsClient] = useState(false);
|
|
76
|
+
|
|
77
|
+
useEffect(() => {
|
|
78
|
+
setIsClient(true);
|
|
79
|
+
}, []);
|
|
80
|
+
|
|
81
|
+
// During SSR, don't render tooltip content
|
|
82
|
+
if (!isClient) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<TooltipPrimitive.Portal>
|
|
88
|
+
<TooltipPrimitive.Content
|
|
89
|
+
data-slot="tooltip-content"
|
|
90
|
+
sideOffset={sideOffset}
|
|
91
|
+
className={cn(
|
|
92
|
+
"bg-bg-secondary",
|
|
93
|
+
"text-text-primary",
|
|
94
|
+
"animate-in",
|
|
95
|
+
"text-uk-sm",
|
|
96
|
+
"leading-uk-sm",
|
|
97
|
+
"rounded-uk-md",
|
|
98
|
+
"px-lg",
|
|
99
|
+
"py-md",
|
|
100
|
+
"shadow-[0_8px_24px_0_var(--color-shadow-secondary)]",
|
|
101
|
+
"fade-in-0",
|
|
102
|
+
"zoom-in-95",
|
|
103
|
+
"data-[state=closed]:animate-out",
|
|
104
|
+
"data-[state=closed]:fade-out-0",
|
|
105
|
+
"data-[state=closed]:zoom-out-95",
|
|
106
|
+
"data-[side=bottom]:slide-in-from-top-2",
|
|
107
|
+
"data-[side=left]:slide-in-from-right-2",
|
|
108
|
+
"data-[side=right]:slide-in-from-left-2",
|
|
109
|
+
"data-[side=top]:slide-in-from-bottom-2",
|
|
110
|
+
"z-50",
|
|
111
|
+
"w-fit",
|
|
112
|
+
"origin-(--radix-tooltip-content-transform-origin)",
|
|
113
|
+
"text-balance",
|
|
114
|
+
"liquid:liquid-bg",
|
|
115
|
+
className,
|
|
116
|
+
)}
|
|
117
|
+
{...props}
|
|
118
|
+
>
|
|
119
|
+
{children}
|
|
120
|
+
<TooltipPrimitive.Arrow
|
|
121
|
+
className="fill-bg-secondary liquid:liquid-bg z-50 h-[9px] w-[20px] border-none"
|
|
122
|
+
style={{
|
|
123
|
+
clipPath: "polygon(50% 100%, 0% 0%, 100% 0%)",
|
|
124
|
+
borderRadius: "1px 1px 0 0",
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
</TooltipPrimitive.Content>
|
|
128
|
+
</TooltipPrimitive.Portal>
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
import { CaseSensitive, ChevronDown, Regex, Search } from "lucide-react";
|
|
2
|
+
import { useEffect, useMemo, useState, type ReactNode } from "react";
|
|
3
|
+
|
|
4
|
+
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "./tooltip";
|
|
5
|
+
import { cn } from "../utils/cn";
|
|
6
|
+
|
|
7
|
+
type TreeDataItem = {
|
|
8
|
+
id: string;
|
|
9
|
+
parentId: string | null;
|
|
10
|
+
label: string;
|
|
11
|
+
isCollapsible?: boolean;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
type TreeViewProps<T extends TreeDataItem> = {
|
|
16
|
+
data: T[];
|
|
17
|
+
getIcon: (item: T) => ReactNode;
|
|
18
|
+
onVisibleDataChange?: (data: (T & { indent: number; isLast: boolean; ancestors: T[] })[]) => void;
|
|
19
|
+
onItemHover?: (id: string | null) => void;
|
|
20
|
+
className?: string;
|
|
21
|
+
hoveredId?: string | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function TreeView<T extends TreeDataItem>({
|
|
25
|
+
data,
|
|
26
|
+
getIcon,
|
|
27
|
+
onVisibleDataChange,
|
|
28
|
+
onItemHover,
|
|
29
|
+
className,
|
|
30
|
+
hoveredId,
|
|
31
|
+
}: TreeViewProps<T>) {
|
|
32
|
+
const [expandedItems, setExpandedItems] = useState(() => {
|
|
33
|
+
const initial = new Set<string>();
|
|
34
|
+
data.forEach((item) => {
|
|
35
|
+
if (item.isCollapsible) initial.add(item.id);
|
|
36
|
+
});
|
|
37
|
+
return initial;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const [searchQuery, setSearchQuery] = useState("");
|
|
41
|
+
const [isCaseSensitive, setIsCaseSensitive] = useState(false);
|
|
42
|
+
const [isRegex, setIsRegex] = useState(false);
|
|
43
|
+
const [isRegexValid, setIsRegexValid] = useState(true);
|
|
44
|
+
|
|
45
|
+
const toggleItem = (id: string) => {
|
|
46
|
+
setExpandedItems((prev) => {
|
|
47
|
+
const newSet = new Set(prev);
|
|
48
|
+
if (newSet.has(id)) {
|
|
49
|
+
newSet.delete(id);
|
|
50
|
+
} else {
|
|
51
|
+
newSet.add(id);
|
|
52
|
+
}
|
|
53
|
+
return newSet;
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const childrenMap = useMemo(() => {
|
|
58
|
+
const map = new Map<string | null, T[]>();
|
|
59
|
+
data.forEach((item) => {
|
|
60
|
+
if (!map.has(item.parentId)) {
|
|
61
|
+
map.set(item.parentId, []);
|
|
62
|
+
}
|
|
63
|
+
map.get(item.parentId)!.push(item);
|
|
64
|
+
});
|
|
65
|
+
return map;
|
|
66
|
+
}, [data]);
|
|
67
|
+
|
|
68
|
+
const dataWithMeta = useMemo(() => {
|
|
69
|
+
const dataMap = new Map(data.map((item) => [item.id, item]));
|
|
70
|
+
|
|
71
|
+
const getAncestors = (item: T) => {
|
|
72
|
+
const ancestors: T[] = [];
|
|
73
|
+
let current = item.parentId;
|
|
74
|
+
while (current) {
|
|
75
|
+
const parent = dataMap.get(current);
|
|
76
|
+
if (parent) {
|
|
77
|
+
ancestors.unshift(parent);
|
|
78
|
+
current = parent.parentId;
|
|
79
|
+
} else {
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return ancestors;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
return data.map((item) => {
|
|
87
|
+
const siblings = childrenMap.get(item.parentId) || [];
|
|
88
|
+
const isLast = siblings.length > 0 && siblings[siblings.length - 1].id === item.id;
|
|
89
|
+
const ancestors = getAncestors(item);
|
|
90
|
+
const indent = ancestors.length;
|
|
91
|
+
return { ...item, indent, isLast, ancestors };
|
|
92
|
+
});
|
|
93
|
+
}, [data, childrenMap]);
|
|
94
|
+
|
|
95
|
+
const visibleData = useMemo(() => {
|
|
96
|
+
const dataMap = new Map(data.map((item) => [item.id, item]));
|
|
97
|
+
|
|
98
|
+
if (!searchQuery) {
|
|
99
|
+
const isVisible = (item: { ancestors: T[] }) => {
|
|
100
|
+
return item.ancestors.every((ancestor) => expandedItems.has(ancestor.id));
|
|
101
|
+
};
|
|
102
|
+
return dataWithMeta.filter(isVisible);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const matchingAndAncestorIds = new Set<string>();
|
|
106
|
+
let regex: RegExp | null = null;
|
|
107
|
+
|
|
108
|
+
if (isRegex) {
|
|
109
|
+
try {
|
|
110
|
+
regex = new RegExp(searchQuery, isCaseSensitive ? "" : "i");
|
|
111
|
+
if (!isRegexValid) setIsRegexValid(true);
|
|
112
|
+
} catch {
|
|
113
|
+
if (isRegexValid) setIsRegexValid(false);
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
data.forEach((item) => {
|
|
119
|
+
let labelMatches = false;
|
|
120
|
+
if (regex) {
|
|
121
|
+
labelMatches = regex.test(item.label);
|
|
122
|
+
} else {
|
|
123
|
+
const source = isCaseSensitive ? item.label : item.label.toLowerCase();
|
|
124
|
+
const query = isCaseSensitive ? searchQuery : searchQuery.toLowerCase();
|
|
125
|
+
labelMatches = source.includes(query);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (labelMatches) {
|
|
129
|
+
matchingAndAncestorIds.add(item.id);
|
|
130
|
+
let current = item.parentId;
|
|
131
|
+
while (current) {
|
|
132
|
+
const parent = dataMap.get(current);
|
|
133
|
+
if (parent) {
|
|
134
|
+
matchingAndAncestorIds.add(parent.id);
|
|
135
|
+
current = parent.parentId;
|
|
136
|
+
} else {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
return dataWithMeta.filter((item) => matchingAndAncestorIds.has(item.id));
|
|
144
|
+
}, [searchQuery, dataWithMeta, expandedItems, isCaseSensitive, isRegex, isRegexValid, data]);
|
|
145
|
+
|
|
146
|
+
useEffect(() => {
|
|
147
|
+
onVisibleDataChange?.(visibleData);
|
|
148
|
+
}, [visibleData, onVisibleDataChange]);
|
|
149
|
+
|
|
150
|
+
const renderLabel = (label: string) => {
|
|
151
|
+
if (!searchQuery || !isRegexValid) {
|
|
152
|
+
return label;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let regex;
|
|
156
|
+
try {
|
|
157
|
+
regex = new RegExp(`(${searchQuery})`, isCaseSensitive ? "g" : "gi");
|
|
158
|
+
} catch {
|
|
159
|
+
return label;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const parts = label.split(regex);
|
|
163
|
+
|
|
164
|
+
return (
|
|
165
|
+
<>
|
|
166
|
+
{parts.map((part, i) =>
|
|
167
|
+
i % 2 === 1 ? (
|
|
168
|
+
<span key={i} className="rounded-uk-xs bg-yellow-300 dark:bg-yellow-700">
|
|
169
|
+
{part}
|
|
170
|
+
</span>
|
|
171
|
+
) : (
|
|
172
|
+
part
|
|
173
|
+
),
|
|
174
|
+
)}
|
|
175
|
+
</>
|
|
176
|
+
);
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
return (
|
|
180
|
+
<div className={cn("border-line-secondary/20 flex flex-col", className)}>
|
|
181
|
+
<div className="border-line-secondary/20 flex h-[32px] shrink-0 items-center gap-1 border-b px-2">
|
|
182
|
+
<Search className="text-text-secondary size-4" />
|
|
183
|
+
<input
|
|
184
|
+
type="text"
|
|
185
|
+
placeholder="Search..."
|
|
186
|
+
className={cn(
|
|
187
|
+
"text-uk-md w-full bg-transparent focus:outline-none",
|
|
188
|
+
!isRegexValid && "text-red-500",
|
|
189
|
+
)}
|
|
190
|
+
value={searchQuery}
|
|
191
|
+
onChange={(e) => setSearchQuery(e.target.value)}
|
|
192
|
+
/>
|
|
193
|
+
<TooltipProvider delayDuration={200}>
|
|
194
|
+
<Tooltip>
|
|
195
|
+
<TooltipTrigger asChild>
|
|
196
|
+
<button
|
|
197
|
+
onClick={() => setIsCaseSensitive((prev) => !prev)}
|
|
198
|
+
className={cn("rounded-uk-sm p-1", isCaseSensitive && "bg-bg-tertiary")}
|
|
199
|
+
>
|
|
200
|
+
<CaseSensitive className="size-4" />
|
|
201
|
+
</button>
|
|
202
|
+
</TooltipTrigger>
|
|
203
|
+
<TooltipContent>
|
|
204
|
+
<p>Case Sensitive</p>
|
|
205
|
+
</TooltipContent>
|
|
206
|
+
</Tooltip>
|
|
207
|
+
<Tooltip>
|
|
208
|
+
<TooltipTrigger asChild>
|
|
209
|
+
<button
|
|
210
|
+
onClick={() => setIsRegex((prev) => !prev)}
|
|
211
|
+
className={cn("rounded-uk-sm p-1", isRegex && "bg-bg-tertiary")}
|
|
212
|
+
>
|
|
213
|
+
<Regex className="size-4" />
|
|
214
|
+
</button>
|
|
215
|
+
</TooltipTrigger>
|
|
216
|
+
<TooltipContent>
|
|
217
|
+
<p>Use Regular Expression</p>
|
|
218
|
+
</TooltipContent>
|
|
219
|
+
</Tooltip>
|
|
220
|
+
</TooltipProvider>
|
|
221
|
+
</div>
|
|
222
|
+
<div className="overflow-y-auto">
|
|
223
|
+
{visibleData.map((item) => (
|
|
224
|
+
<div
|
|
225
|
+
key={item.id}
|
|
226
|
+
className={cn(
|
|
227
|
+
"group relative flex h-[32px] cursor-pointer items-center",
|
|
228
|
+
hoveredId === item.id && "bg-bg-secondary/80",
|
|
229
|
+
)}
|
|
230
|
+
onMouseEnter={() => onItemHover?.(item.id)}
|
|
231
|
+
onMouseLeave={() => onItemHover?.(null)}
|
|
232
|
+
>
|
|
233
|
+
{/* Guide Lines */}
|
|
234
|
+
<div className="absolute top-0 left-[-0.28rem] z-0 flex h-full items-center">
|
|
235
|
+
{item.ancestors.map((ancestor, index) => {
|
|
236
|
+
const parentIsLast = dataWithMeta.find((d) => d.id === ancestor.id)?.isLast;
|
|
237
|
+
return (
|
|
238
|
+
<div
|
|
239
|
+
key={index}
|
|
240
|
+
className={cn(
|
|
241
|
+
"h-full w-[1.25rem]",
|
|
242
|
+
parentIsLast ? "" : "border-l",
|
|
243
|
+
"border-line-secondary/20",
|
|
244
|
+
)}
|
|
245
|
+
/>
|
|
246
|
+
);
|
|
247
|
+
})}
|
|
248
|
+
{item.indent > 0 && (
|
|
249
|
+
<div className="relative h-full w-[1.24rem]">
|
|
250
|
+
<div
|
|
251
|
+
className={cn(
|
|
252
|
+
"absolute top-0 left-0 h-1/2 w-1/2 border-b border-l",
|
|
253
|
+
item.isLast ? "rounded-bl-md" : "",
|
|
254
|
+
"border-line-secondary/20",
|
|
255
|
+
)}
|
|
256
|
+
/>
|
|
257
|
+
{!item.isLast && (
|
|
258
|
+
<div className="border-line-secondary/20 absolute top-1/2 left-0 h-1/2 w-1/2 border-l" />
|
|
259
|
+
)}
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
263
|
+
|
|
264
|
+
<div
|
|
265
|
+
className="text-uk-md z-10 flex w-full items-center gap-2 px-2 whitespace-nowrap"
|
|
266
|
+
style={{ paddingLeft: `${item.indent * 1.25 + 0.5}rem` }}
|
|
267
|
+
>
|
|
268
|
+
<div className="relative flex size-4 items-center justify-center">
|
|
269
|
+
{item.isCollapsible && (
|
|
270
|
+
<button
|
|
271
|
+
onClick={() => toggleItem(item.id)}
|
|
272
|
+
className="absolute z-20 flex items-center justify-center opacity-0 transition-opacity group-hover:opacity-100"
|
|
273
|
+
>
|
|
274
|
+
<ChevronDown
|
|
275
|
+
className={cn(
|
|
276
|
+
"size-4 transition-transform",
|
|
277
|
+
!expandedItems.has(item.id) && "-rotate-90",
|
|
278
|
+
)}
|
|
279
|
+
/>
|
|
280
|
+
</button>
|
|
281
|
+
)}
|
|
282
|
+
<div
|
|
283
|
+
className={cn(
|
|
284
|
+
"transition-opacity",
|
|
285
|
+
item.isCollapsible && "group-hover:opacity-0",
|
|
286
|
+
)}
|
|
287
|
+
>
|
|
288
|
+
{getIcon(item)}
|
|
289
|
+
</div>
|
|
290
|
+
</div>
|
|
291
|
+
<span className="truncate">{renderLabel(item.label)}</span>
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
))}
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export { TreeView as TreeViewV2, type TreeDataItem as TreeDataItemV2 };
|