@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.
Files changed (153) hide show
  1. package/dist/{chunk-CP7QYV4N.mjs → chunk-44KW6PTD.mjs} +1 -0
  2. package/dist/{chunk-KQAHAGUG.js → chunk-AOUGWGQR.js} +2 -2
  3. package/dist/{chunk-J7PTSP4Q.mjs → chunk-EAZQH4IX.mjs} +8 -0
  4. package/dist/chunk-EOFYA6CS.js +53 -0
  5. package/dist/{chunk-O4TK3AKI.mjs → chunk-EYIFKSU2.mjs} +1 -1
  6. package/dist/{chunk-WMXX2UMD.mjs → chunk-FEOONYZ2.mjs} +2 -2
  7. package/dist/{chunk-66DYOZI3.js → chunk-FORVP7XH.js} +6 -5
  8. package/dist/{chunk-7X6N54LX.mjs → chunk-G5KMD72J.mjs} +6 -5
  9. package/dist/{chunk-5MZCQW4U.js → chunk-IXHMIR5P.js} +4 -4
  10. package/dist/{chunk-CCGUGCCV.js → chunk-JAJNLRY5.js} +9 -9
  11. package/dist/{chunk-WOZWC6KO.mjs → chunk-JMPVDU2C.mjs} +35 -4
  12. package/dist/{chunk-E5KWKBH3.mjs → chunk-LEPXSM36.mjs} +10 -24
  13. package/dist/{chunk-3XVIJY3H.mjs → chunk-MRVMXKZO.mjs} +1 -1
  14. package/dist/{chunk-UMIQYHO6.mjs → chunk-OOI3SJDU.mjs} +10 -4
  15. package/dist/{chunk-B5EMQBVL.js → chunk-OTPUWWEB.js} +8 -3
  16. package/dist/{chunk-FSMKJAZT.js → chunk-PVRQNCSJ.js} +9 -23
  17. package/dist/chunk-Q5KEB4UK.mjs +51 -0
  18. package/dist/{chunk-XUQ5A7ZA.js → chunk-QEY7BWIR.js} +10 -4
  19. package/dist/{chunk-QZASHJNJ.js → chunk-QJR6MCTR.js} +8 -0
  20. package/dist/{chunk-IOI3YFTY.js → chunk-U2ZLDPOE.js} +8 -8
  21. package/dist/{chunk-ZJXBC33M.js → chunk-UFHMEEGL.js} +1 -0
  22. package/dist/{chunk-5P3KAS53.js → chunk-WJ7T7FAH.js} +34 -3
  23. package/dist/{chunk-6L4KPPOD.mjs → chunk-XMG3IVGZ.mjs} +8 -3
  24. package/dist/{chunk-XCCC7HVN.mjs → chunk-ZVG2UKKC.mjs} +1 -1
  25. package/dist/highlight-cursor/cursor-provider.js +2 -2
  26. package/dist/highlight-cursor/cursor-provider.mjs +1 -1
  27. package/dist/highlight-cursor/enhanced-components.js +11 -10
  28. package/dist/highlight-cursor/enhanced-components.mjs +6 -5
  29. package/dist/highlight-cursor/index.js +15 -14
  30. package/dist/highlight-cursor/index.mjs +7 -6
  31. package/dist/highlight-cursor/tabs-cursor-context.js +3 -3
  32. package/dist/highlight-cursor/tabs-cursor-context.mjs +1 -1
  33. package/dist/highlight-cursor/with-cursor.js +2 -2
  34. package/dist/highlight-cursor/with-cursor.mjs +1 -1
  35. package/dist/index.d.mts +1 -0
  36. package/dist/index.d.ts +1 -0
  37. package/dist/index.js +64 -59
  38. package/dist/index.mjs +15 -14
  39. package/dist/ui/avatar.js +2 -1
  40. package/dist/ui/avatar.mjs +2 -1
  41. package/dist/ui/badge.js +2 -1
  42. package/dist/ui/badge.mjs +2 -1
  43. package/dist/ui/button.js +2 -1
  44. package/dist/ui/button.mjs +2 -1
  45. package/dist/ui/card.js +2 -1
  46. package/dist/ui/card.mjs +2 -1
  47. package/dist/ui/checkbox.js +2 -1
  48. package/dist/ui/checkbox.mjs +2 -1
  49. package/dist/ui/collapsible.js +2 -1
  50. package/dist/ui/collapsible.mjs +2 -1
  51. package/dist/ui/dropdown.js +2 -1
  52. package/dist/ui/dropdown.mjs +2 -1
  53. package/dist/ui/index.js +49 -48
  54. package/dist/ui/index.mjs +12 -11
  55. package/dist/ui/input-numbers.js +4 -3
  56. package/dist/ui/input-numbers.mjs +3 -2
  57. package/dist/ui/input.js +2 -1
  58. package/dist/ui/input.mjs +2 -1
  59. package/dist/ui/layout.js +2 -1
  60. package/dist/ui/layout.mjs +2 -1
  61. package/dist/ui/modal.js +2 -1
  62. package/dist/ui/modal.mjs +2 -1
  63. package/dist/ui/popover.js +2 -1
  64. package/dist/ui/popover.mjs +2 -1
  65. package/dist/ui/radio-group.js +2 -1
  66. package/dist/ui/radio-group.mjs +2 -1
  67. package/dist/ui/select.js +2 -1
  68. package/dist/ui/select.mjs +2 -1
  69. package/dist/ui/slider.js +2 -1
  70. package/dist/ui/slider.mjs +2 -1
  71. package/dist/ui/switch.js +2 -1
  72. package/dist/ui/switch.mjs +2 -1
  73. package/dist/ui/tabs.js +8 -7
  74. package/dist/ui/tabs.mjs +4 -3
  75. package/dist/ui/textarea.js +2 -1
  76. package/dist/ui/textarea.mjs +2 -1
  77. package/dist/ui/theme-context.js +6 -3
  78. package/dist/ui/theme-context.mjs +4 -1
  79. package/dist/ui/timeline.js +6 -5
  80. package/dist/ui/timeline.mjs +5 -4
  81. package/dist/ui/toast.d.mts +1 -1
  82. package/dist/ui/toast.d.ts +1 -1
  83. package/dist/ui/toast.js +3 -3
  84. package/dist/ui/toast.mjs +1 -1
  85. package/dist/ui/toggle-group.js +2 -1
  86. package/dist/ui/toggle-group.mjs +2 -1
  87. package/dist/ui/toggle.js +2 -1
  88. package/dist/ui/toggle.mjs +2 -1
  89. package/dist/ui/tooltip.d.mts +3 -3
  90. package/dist/ui/tooltip.d.ts +3 -3
  91. package/dist/ui/tooltip.js +7 -6
  92. package/dist/ui/tooltip.mjs +3 -2
  93. package/dist/ui/tree-view-v2.js +5 -4
  94. package/dist/ui/tree-view-v2.mjs +4 -3
  95. package/dist/ui/version-badge.js +2 -1
  96. package/dist/ui/version-badge.mjs +2 -1
  97. package/dist/utils/index.d.mts +1 -0
  98. package/dist/utils/index.d.ts +1 -0
  99. package/dist/utils/index.js +6 -1
  100. package/dist/utils/index.mjs +2 -1
  101. package/dist/utils/use-local-storage.d.mts +3 -0
  102. package/dist/utils/use-local-storage.d.ts +3 -0
  103. package/dist/utils/use-local-storage.js +10 -0
  104. package/dist/utils/use-local-storage.mjs +1 -0
  105. package/package.json +6 -4
  106. package/src/highlight-cursor/cursor-context.tsx +23 -0
  107. package/src/highlight-cursor/cursor-provider.tsx +264 -0
  108. package/src/highlight-cursor/enhanced-components.tsx +16 -0
  109. package/src/highlight-cursor/index.ts +21 -0
  110. package/src/highlight-cursor/tabs-cursor-context.tsx +121 -0
  111. package/src/highlight-cursor/types.ts +40 -0
  112. package/src/highlight-cursor/with-cursor.tsx +144 -0
  113. package/src/index.css +5 -0
  114. package/src/index.ts +5 -0
  115. package/src/styles/theme.css +80 -0
  116. package/src/styles/toast.css +64 -0
  117. package/src/styles/variables.css +194 -0
  118. package/src/ui/avatar.tsx +92 -0
  119. package/src/ui/badge.tsx +68 -0
  120. package/src/ui/button.tsx +100 -0
  121. package/src/ui/card.tsx +88 -0
  122. package/src/ui/checkbox.tsx +76 -0
  123. package/src/ui/collapsible.tsx +36 -0
  124. package/src/ui/dropdown.tsx +375 -0
  125. package/src/ui/index.ts +31 -0
  126. package/src/ui/input-numbers.tsx +201 -0
  127. package/src/ui/input.tsx +141 -0
  128. package/src/ui/layout.tsx +41 -0
  129. package/src/ui/modal.tsx +198 -0
  130. package/src/ui/popover.tsx +59 -0
  131. package/src/ui/radio-group.tsx +56 -0
  132. package/src/ui/select.tsx +292 -0
  133. package/src/ui/sheet.tsx +131 -0
  134. package/src/ui/slider.tsx +189 -0
  135. package/src/ui/switch.tsx +43 -0
  136. package/src/ui/tabs.tsx +128 -0
  137. package/src/ui/textarea.tsx +52 -0
  138. package/src/ui/theme-context.tsx +80 -0
  139. package/src/ui/timeline.tsx +717 -0
  140. package/src/ui/toast.tsx +27 -0
  141. package/src/ui/toggle-group.tsx +82 -0
  142. package/src/ui/toggle.tsx +88 -0
  143. package/src/ui/tooltip.tsx +132 -0
  144. package/src/ui/tree-view-v2.tsx +300 -0
  145. package/src/ui/tree-view.tsx +550 -0
  146. package/src/ui/version-badge.tsx +65 -0
  147. package/src/utils/cn.ts +21 -0
  148. package/src/utils/index.ts +2 -0
  149. package/src/utils/use-local-storage.ts +59 -0
  150. package/dist/chunk-OBWGRG45.js +0 -21
  151. package/dist/chunk-Z6XRPTIQ.mjs +0 -15
  152. /package/dist/{chunk-QS7WS6DD.mjs → chunk-M4IVLZIH.mjs} +0 -0
  153. /package/dist/{chunk-ST4P6AN6.js → chunk-XWS4SIB5.js} +0 -0
@@ -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 };