@moontra/moonui 2.1.9 → 2.2.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.mts +1656 -0
- package/dist/index.d.ts +1656 -0
- package/dist/index.js +1847 -469
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1679 -471
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -4
- package/src/components/ui/accordion.tsx +1 -1
- package/src/components/ui/alert.tsx +123 -93
- package/src/components/ui/aspect-ratio.tsx +1 -1
- package/src/components/ui/avatar.tsx +8 -6
- package/src/components/ui/badge.tsx +20 -18
- package/src/components/ui/breadcrumb.tsx +12 -10
- package/src/components/ui/button.stories.tsx +4 -2
- package/src/components/ui/button.tsx +71 -68
- package/src/components/ui/calendar.tsx +2 -2
- package/src/components/ui/card-input.tsx +231 -0
- package/src/components/ui/card.stories.tsx +2 -0
- package/src/components/ui/card.tsx +9 -7
- package/src/components/ui/checkbox.tsx +1 -1
- package/src/components/ui/collapsible.tsx +3 -3
- package/src/components/ui/color-picker.tsx +2 -2
- package/src/components/ui/command.tsx +4 -4
- package/src/components/ui/data-table.stories.tsx +4 -2
- package/src/components/ui/data-table.tsx +8 -8
- package/src/components/ui/date-picker.stories.tsx +2 -0
- package/src/components/ui/date-picker.tsx +25 -15
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/ui/draggable-list.tsx +3 -1
- package/src/components/ui/dropdown-menu.tsx +2 -2
- package/src/components/ui/file-upload.tsx +2 -2
- package/src/components/ui/github-stars.tsx +1 -1
- package/src/components/ui/index.ts +357 -45
- package/src/components/ui/input.stories.tsx +2 -0
- package/src/components/ui/input.tsx +3 -1
- package/src/components/ui/locked-component.tsx +222 -0
- package/src/components/ui/moon-logo.tsx +1 -1
- package/src/components/ui/pagination.tsx +10 -8
- package/src/components/ui/phone-input.tsx +172 -0
- package/src/components/ui/popover-pro.tsx +424 -0
- package/src/components/ui/popover.tsx +3 -3
- package/src/components/ui/progress.tsx +1 -1
- package/src/components/ui/radio-group.tsx +1 -1
- package/src/components/ui/rich-text-editor/index.tsx +2 -1
- package/src/components/ui/scroll-area.tsx +48 -0
- package/src/components/ui/select.tsx +7 -3
- package/src/components/ui/separator.tsx +2 -2
- package/src/components/ui/skeleton.tsx +19 -7
- package/src/components/ui/slider.tsx +1 -1
- package/src/components/ui/swipeable-card.tsx +2 -0
- package/src/components/ui/switch.tsx +1 -1
- package/src/components/ui/table.tsx +7 -5
- package/src/components/ui/tabs.tsx +6 -1
- package/src/components/ui/tags-input.tsx +130 -0
- package/src/components/ui/textarea.tsx +3 -1
- package/src/components/ui/toast.tsx +5 -3
- package/src/components/ui/toggle.tsx +2 -3
- package/src/components/ui/tooltip.tsx +2 -5
- package/src/index.tsx +4 -46
- package/src/lib/micro-interactions.ts +2 -1
- package/src/lib/performance-profiler.ts +79 -0
- package/src/styles/index.css +315 -2
- package/src/use-performance-optimizer.ts +26 -26
- package/src/use-scroll-animation.ts +5 -7
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
1
3
|
import * as React from "react";
|
|
2
4
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
5
|
|
|
@@ -127,7 +129,7 @@ const TableHeader = React.forwardRef<
|
|
|
127
129
|
HTMLTableSectionElement,
|
|
128
130
|
React.HTMLAttributes<HTMLTableSectionElement>
|
|
129
131
|
>(({ className, ...props }, ref) => (
|
|
130
|
-
<thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
|
|
132
|
+
<thead ref={ref} className={cn("moonui-theme", "[&_tr]:border-b", className)} {...props} />
|
|
131
133
|
));
|
|
132
134
|
TableHeader.displayName = "TableHeader";
|
|
133
135
|
|
|
@@ -148,7 +150,7 @@ const TableBody = React.forwardRef<
|
|
|
148
150
|
return (
|
|
149
151
|
<tbody
|
|
150
152
|
ref={ref}
|
|
151
|
-
className={cn("[&_tr:last-child]:border-0", className)}
|
|
153
|
+
className={cn("moonui-theme", "[&_tr:last-child]:border-0", className)}
|
|
152
154
|
{...props}
|
|
153
155
|
>
|
|
154
156
|
{hasChildren ? (
|
|
@@ -177,7 +179,7 @@ const TableFooter = React.forwardRef<
|
|
|
177
179
|
>(({ className, ...props }, ref) => (
|
|
178
180
|
<tfoot
|
|
179
181
|
ref={ref}
|
|
180
|
-
className={cn("bg-primary text-primary-foreground font-medium", className)}
|
|
182
|
+
className={cn("moonui-theme", "bg-primary text-primary-foreground font-medium", className)}
|
|
181
183
|
{...props}
|
|
182
184
|
/>
|
|
183
185
|
));
|
|
@@ -290,7 +292,7 @@ const TableCell = React.forwardRef<
|
|
|
290
292
|
>(({ className, ...props }, ref) => (
|
|
291
293
|
<td
|
|
292
294
|
ref={ref}
|
|
293
|
-
className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
|
295
|
+
className={cn("moonui-theme", "p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
|
|
294
296
|
{...props}
|
|
295
297
|
/>
|
|
296
298
|
));
|
|
@@ -302,7 +304,7 @@ const TableCaption = React.forwardRef<
|
|
|
302
304
|
>(({ className, ...props }, ref) => (
|
|
303
305
|
<caption
|
|
304
306
|
ref={ref}
|
|
305
|
-
className={cn("mt-4 text-sm text-muted-foreground", className)}
|
|
307
|
+
className={cn("moonui-theme", "mt-4 text-sm text-muted-foreground", className)}
|
|
306
308
|
{...props}
|
|
307
309
|
/>
|
|
308
310
|
));
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
1
3
|
import * as React from "react";
|
|
2
4
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
3
5
|
import { cva, type VariantProps } from "class-variance-authority";
|
|
@@ -18,6 +20,7 @@ const Tabs = React.forwardRef<
|
|
|
18
20
|
>(({ vertical = false, ...props }, ref) => (
|
|
19
21
|
<TabsPrimitive.Root
|
|
20
22
|
ref={ref}
|
|
23
|
+
className={cn("moonui-theme", props.className)}
|
|
21
24
|
orientation={vertical ? "vertical" : "horizontal"}
|
|
22
25
|
{...props}
|
|
23
26
|
/>
|
|
@@ -68,7 +71,7 @@ const TabsList = React.forwardRef<
|
|
|
68
71
|
>(({ className, variant, orientation, fullWidth, ...props }, ref) => (
|
|
69
72
|
<TabsPrimitive.List
|
|
70
73
|
ref={ref}
|
|
71
|
-
className={cn(tabsListVariants({ variant, orientation, fullWidth, className }))}
|
|
74
|
+
className={cn("moonui-theme", tabsListVariants({ variant, orientation, fullWidth, className }))}
|
|
72
75
|
{...props}
|
|
73
76
|
/>
|
|
74
77
|
));
|
|
@@ -145,6 +148,7 @@ const TabsTrigger = React.forwardRef<
|
|
|
145
148
|
<TabsPrimitive.Trigger
|
|
146
149
|
ref={ref}
|
|
147
150
|
className={cn(
|
|
151
|
+
"moonui-theme",
|
|
148
152
|
tabsTriggerVariants({ variant, size, orientation, fullWidth }),
|
|
149
153
|
fadeTabs && "data-[state=inactive]:opacity-60",
|
|
150
154
|
className
|
|
@@ -182,6 +186,7 @@ const TabsContent = React.forwardRef<
|
|
|
182
186
|
<TabsPrimitive.Content
|
|
183
187
|
ref={ref}
|
|
184
188
|
className={cn(
|
|
189
|
+
"moonui-theme",
|
|
185
190
|
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
186
191
|
animated && "data-[state=active]:animate-fadeIn data-[state=inactive]:animate-fadeOut",
|
|
187
192
|
className
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { X } from "lucide-react";
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
import { Badge } from "./badge";
|
|
7
|
+
|
|
8
|
+
export interface TagsInputProps
|
|
9
|
+
extends Omit<React.InputHTMLAttributes<HTMLInputElement>, "value" | "onChange"> {
|
|
10
|
+
value: string[];
|
|
11
|
+
onChange: (tags: string[]) => void;
|
|
12
|
+
maxTags?: number;
|
|
13
|
+
variant?: "primary" | "secondary" | "destructive" | "outline";
|
|
14
|
+
allowDuplicates?: boolean;
|
|
15
|
+
delimiter?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const TagsInput = React.forwardRef<HTMLInputElement, TagsInputProps>(
|
|
19
|
+
(
|
|
20
|
+
{
|
|
21
|
+
value = [],
|
|
22
|
+
onChange,
|
|
23
|
+
maxTags,
|
|
24
|
+
variant = "primary",
|
|
25
|
+
allowDuplicates = false,
|
|
26
|
+
delimiter = ",",
|
|
27
|
+
placeholder = "Add tag and press Enter",
|
|
28
|
+
className,
|
|
29
|
+
disabled,
|
|
30
|
+
...props
|
|
31
|
+
},
|
|
32
|
+
ref
|
|
33
|
+
) => {
|
|
34
|
+
const [inputValue, setInputValue] = React.useState("");
|
|
35
|
+
const [error, setError] = React.useState("");
|
|
36
|
+
|
|
37
|
+
const handleKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
|
|
38
|
+
if (e.key === "Enter" || e.key === delimiter) {
|
|
39
|
+
e.preventDefault();
|
|
40
|
+
addTag();
|
|
41
|
+
} else if (e.key === "Backspace" && inputValue === "" && value.length > 0) {
|
|
42
|
+
removeTag(value.length - 1);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const addTag = () => {
|
|
47
|
+
const tag = inputValue.trim();
|
|
48
|
+
|
|
49
|
+
if (!tag) return;
|
|
50
|
+
|
|
51
|
+
if (!allowDuplicates && value.includes(tag)) {
|
|
52
|
+
setError("This tag already exists");
|
|
53
|
+
setTimeout(() => setError(""), 2000);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (maxTags && value.length >= maxTags) {
|
|
58
|
+
setError(`Maximum ${maxTags} tags allowed`);
|
|
59
|
+
setTimeout(() => setError(""), 2000);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
onChange([...value, tag]);
|
|
64
|
+
setInputValue("");
|
|
65
|
+
setError("");
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const removeTag = (index: number) => {
|
|
69
|
+
if (disabled) return;
|
|
70
|
+
onChange(value.filter((_, i) => i !== index));
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<div className="relative">
|
|
75
|
+
<div
|
|
76
|
+
className={cn(
|
|
77
|
+
"flex min-h-[2.5rem] w-full flex-wrap gap-2 rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background",
|
|
78
|
+
"dark:bg-zinc-950/50 dark:border-zinc-800",
|
|
79
|
+
"focus-within:outline-none focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2",
|
|
80
|
+
disabled && "cursor-not-allowed opacity-50",
|
|
81
|
+
className
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
{value.map((tag, index) => (
|
|
85
|
+
<Badge
|
|
86
|
+
key={index}
|
|
87
|
+
variant={variant}
|
|
88
|
+
className="gap-1 pr-1.5"
|
|
89
|
+
>
|
|
90
|
+
<span className="text-xs">{tag}</span>
|
|
91
|
+
{!disabled && (
|
|
92
|
+
<button
|
|
93
|
+
type="button"
|
|
94
|
+
onClick={() => removeTag(index)}
|
|
95
|
+
className="ml-1 rounded-full outline-none ring-offset-background focus:ring-2 focus:ring-ring focus:ring-offset-2"
|
|
96
|
+
>
|
|
97
|
+
<X className="h-3 w-3 text-muted-foreground hover:text-foreground transition-colors" />
|
|
98
|
+
</button>
|
|
99
|
+
)}
|
|
100
|
+
</Badge>
|
|
101
|
+
))}
|
|
102
|
+
<input
|
|
103
|
+
ref={ref}
|
|
104
|
+
type="text"
|
|
105
|
+
value={inputValue}
|
|
106
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
107
|
+
onKeyDown={handleKeyDown}
|
|
108
|
+
onBlur={addTag}
|
|
109
|
+
disabled={disabled}
|
|
110
|
+
placeholder={value.length === 0 ? placeholder : ""}
|
|
111
|
+
className={cn(
|
|
112
|
+
"flex-1 bg-transparent outline-none placeholder:text-muted-foreground",
|
|
113
|
+
"dark:placeholder:text-zinc-500",
|
|
114
|
+
"min-w-[120px]",
|
|
115
|
+
disabled && "cursor-not-allowed"
|
|
116
|
+
)}
|
|
117
|
+
{...props}
|
|
118
|
+
/>
|
|
119
|
+
</div>
|
|
120
|
+
{error && (
|
|
121
|
+
<p className="mt-1 text-xs text-destructive">{error}</p>
|
|
122
|
+
)}
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
TagsInput.displayName = "TagsInput";
|
|
129
|
+
|
|
130
|
+
export { TagsInput };
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
1
3
|
import * as React from "react"
|
|
2
4
|
|
|
3
5
|
import { cn } from "../../lib/utils"
|
|
@@ -31,7 +33,7 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
|
31
33
|
}, ref) => {
|
|
32
34
|
return (
|
|
33
35
|
<textarea
|
|
34
|
-
className={cn(
|
|
36
|
+
className={cn("moonui-theme",
|
|
35
37
|
"flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
|
|
36
38
|
className
|
|
37
39
|
)}
|
|
@@ -57,7 +57,7 @@ const Toast = React.forwardRef<
|
|
|
57
57
|
return (
|
|
58
58
|
<ToastPrimitives.Root
|
|
59
59
|
ref={ref}
|
|
60
|
-
className={cn(toastVariants({ variant }), className)}
|
|
60
|
+
className={cn("moonui-theme", toastVariants({ variant }), className)}
|
|
61
61
|
{...props}
|
|
62
62
|
/>
|
|
63
63
|
);
|
|
@@ -103,7 +103,7 @@ const ToastTitle = React.forwardRef<
|
|
|
103
103
|
>(({ className, ...props }, ref) => (
|
|
104
104
|
<ToastPrimitives.Title
|
|
105
105
|
ref={ref}
|
|
106
|
-
className={cn("text-sm font-semibold [&+div]:text-xs", className)}
|
|
106
|
+
className={cn("moonui-theme", "text-sm font-semibold [&+div]:text-xs", className)}
|
|
107
107
|
{...props}
|
|
108
108
|
/>
|
|
109
109
|
));
|
|
@@ -115,7 +115,7 @@ const ToastDescription = React.forwardRef<
|
|
|
115
115
|
>(({ className, ...props }, ref) => (
|
|
116
116
|
<ToastPrimitives.Description
|
|
117
117
|
ref={ref}
|
|
118
|
-
className={cn("text-sm opacity-90", className)}
|
|
118
|
+
className={cn("moonui-theme", "text-sm opacity-90", className)}
|
|
119
119
|
{...props}
|
|
120
120
|
/>
|
|
121
121
|
));
|
|
@@ -207,6 +207,8 @@ const reducer = (state: State, action: any): State => {
|
|
|
207
207
|
...state,
|
|
208
208
|
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
209
209
|
};
|
|
210
|
+
default:
|
|
211
|
+
return state;
|
|
210
212
|
}
|
|
211
213
|
};
|
|
212
214
|
|
|
@@ -36,12 +36,11 @@ const Toggle = React.forwardRef<
|
|
|
36
36
|
>(({ className, variant, size, ...props }, ref) => (
|
|
37
37
|
<TogglePrimitive.Root
|
|
38
38
|
ref={ref}
|
|
39
|
-
className={cn(toggleVariants({ variant, size, className }))}
|
|
39
|
+
className={cn("moonui-theme", toggleVariants({ variant, size, className }))}
|
|
40
40
|
{...props}
|
|
41
41
|
/>
|
|
42
42
|
))
|
|
43
43
|
|
|
44
44
|
Toggle.displayName = TogglePrimitive.Root.displayName
|
|
45
45
|
|
|
46
|
-
export { Toggle, toggleVariants }
|
|
47
|
-
export type { ToggleProps }
|
|
46
|
+
export { Toggle, toggleVariants }
|
|
@@ -43,7 +43,7 @@ const TooltipArrow = React.forwardRef<
|
|
|
43
43
|
>(({ className, ...props }, ref) => (
|
|
44
44
|
<TooltipPrimitive.Arrow
|
|
45
45
|
ref={ref}
|
|
46
|
-
className={cn("fill-current", className)}
|
|
46
|
+
className={cn("moonui-theme", "fill-current", className)}
|
|
47
47
|
{...props}
|
|
48
48
|
/>
|
|
49
49
|
))
|
|
@@ -62,7 +62,7 @@ const TooltipContent = React.forwardRef<
|
|
|
62
62
|
<TooltipPrimitive.Content
|
|
63
63
|
ref={ref}
|
|
64
64
|
sideOffset={sideOffset}
|
|
65
|
-
className={cn(tooltipVariants({ variant, size }), className)}
|
|
65
|
+
className={cn("moonui-theme", tooltipVariants({ variant, size }), className)}
|
|
66
66
|
{...props}
|
|
67
67
|
>
|
|
68
68
|
{props.children}
|
|
@@ -80,7 +80,6 @@ export interface SimpleTooltipProps {
|
|
|
80
80
|
side?: "top" | "right" | "bottom" | "left"
|
|
81
81
|
align?: "start" | "center" | "end"
|
|
82
82
|
delayDuration?: number
|
|
83
|
-
skipDelayDuration?: number
|
|
84
83
|
sideOffset?: number
|
|
85
84
|
showArrow?: boolean
|
|
86
85
|
className?: string
|
|
@@ -102,7 +101,6 @@ const SimpleTooltip = React.forwardRef<
|
|
|
102
101
|
side = "top",
|
|
103
102
|
align = "center",
|
|
104
103
|
delayDuration = 400,
|
|
105
|
-
skipDelayDuration = 300,
|
|
106
104
|
sideOffset = 4,
|
|
107
105
|
showArrow = false,
|
|
108
106
|
className,
|
|
@@ -119,7 +117,6 @@ const SimpleTooltip = React.forwardRef<
|
|
|
119
117
|
defaultOpen={defaultOpen}
|
|
120
118
|
onOpenChange={onOpenChange}
|
|
121
119
|
delayDuration={delayDuration}
|
|
122
|
-
skipDelayDuration={skipDelayDuration}
|
|
123
120
|
>
|
|
124
121
|
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
|
125
122
|
<TooltipContent
|
package/src/index.tsx
CHANGED
|
@@ -1,47 +1,5 @@
|
|
|
1
|
-
//
|
|
2
|
-
|
|
1
|
+
// Export all components with MoonUI prefix
|
|
2
|
+
export * from "./components/ui";
|
|
3
3
|
|
|
4
|
-
//
|
|
5
|
-
export { cn } from "./lib/utils"
|
|
6
|
-
|
|
7
|
-
export * from "./components/ui/accordion"
|
|
8
|
-
export * from "./components/ui/alert"
|
|
9
|
-
export * from "./components/ui/aspect-ratio"
|
|
10
|
-
export * from "./components/ui/avatar"
|
|
11
|
-
export * from "./components/ui/badge"
|
|
12
|
-
export * from "./components/ui/breadcrumb"
|
|
13
|
-
export * from "./components/ui/button"
|
|
14
|
-
export * from "./components/ui/card"
|
|
15
|
-
export * from "./components/ui/checkbox"
|
|
16
|
-
export * from "./components/ui/collapsible"
|
|
17
|
-
export * from "./components/ui/color-picker"
|
|
18
|
-
export * from "./components/ui/command"
|
|
19
|
-
export * from "./components/ui/data-table"
|
|
20
|
-
export * from "./components/ui/date-picker"
|
|
21
|
-
export * from "./components/ui/dialog"
|
|
22
|
-
export * from "./components/ui/draggable-list"
|
|
23
|
-
export * from "./components/ui/dropdown-menu"
|
|
24
|
-
export * from "./components/ui/file-upload"
|
|
25
|
-
export * from "./components/ui/gesture-drawer"
|
|
26
|
-
export * from "./components/ui/github-stars"
|
|
27
|
-
export * from "./components/ui/input"
|
|
28
|
-
export * from "./components/ui/label"
|
|
29
|
-
export * from "./components/ui/moon-logo"
|
|
30
|
-
export * from "./components/ui/pagination"
|
|
31
|
-
export * from "./components/ui/popover"
|
|
32
|
-
export * from "./components/ui/progress"
|
|
33
|
-
export * from "./components/ui/radio-group"
|
|
34
|
-
export * from "./components/ui/rich-text-editor"
|
|
35
|
-
export * from "./components/ui/select"
|
|
36
|
-
export * from "./components/ui/separator"
|
|
37
|
-
export * from "./components/ui/simple-editor"
|
|
38
|
-
export * from "./components/ui/skeleton"
|
|
39
|
-
export * from "./components/ui/slider"
|
|
40
|
-
export * from "./components/ui/swipeable-card"
|
|
41
|
-
export * from "./components/ui/switch"
|
|
42
|
-
export * from "./components/ui/table"
|
|
43
|
-
export * from "./components/ui/tabs"
|
|
44
|
-
export * from "./components/ui/textarea"
|
|
45
|
-
export * from "./components/ui/toast"
|
|
46
|
-
export * from "./components/ui/toggle"
|
|
47
|
-
export * from "./components/ui/tooltip"
|
|
4
|
+
// Re-export utilities
|
|
5
|
+
export { cn } from "./lib/utils";
|
|
@@ -252,4 +252,5 @@ export const notificationAnimation = {
|
|
|
252
252
|
transition: springAnimations.bouncy,
|
|
253
253
|
};
|
|
254
254
|
|
|
255
|
-
export type MicroInteractionProps = VariantProps<typeof microInteractionVariants>;
|
|
255
|
+
export type MicroInteractionProps = VariantProps<typeof microInteractionVariants>;
|
|
256
|
+
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export interface PerformanceMetric {
|
|
2
|
+
name: string;
|
|
3
|
+
duration: number;
|
|
4
|
+
timestamp: number;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface PerformanceSummary {
|
|
8
|
+
totalRenderTime: number;
|
|
9
|
+
componentCount: number;
|
|
10
|
+
averageRenderTime: number;
|
|
11
|
+
slowestComponent: string | null;
|
|
12
|
+
memoryUsage?: number;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
class PerformanceProfiler {
|
|
16
|
+
private measurements: Map<string, PerformanceMetric[]> = new Map();
|
|
17
|
+
|
|
18
|
+
startMeasurement(category: string, name: string): () => void {
|
|
19
|
+
const startTime = performance.now();
|
|
20
|
+
|
|
21
|
+
return () => {
|
|
22
|
+
const endTime = performance.now();
|
|
23
|
+
const duration = endTime - startTime;
|
|
24
|
+
|
|
25
|
+
if (!this.measurements.has(category)) {
|
|
26
|
+
this.measurements.set(category, []);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
this.measurements.get(category)!.push({
|
|
30
|
+
name,
|
|
31
|
+
duration,
|
|
32
|
+
timestamp: Date.now()
|
|
33
|
+
});
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
getMeasurements(category: string): PerformanceMetric[] {
|
|
38
|
+
return this.measurements.get(category) || [];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
getPerformanceSummary(): PerformanceSummary {
|
|
42
|
+
const componentMetrics = this.getMeasurements('component');
|
|
43
|
+
|
|
44
|
+
if (componentMetrics.length === 0) {
|
|
45
|
+
return {
|
|
46
|
+
totalRenderTime: 0,
|
|
47
|
+
componentCount: 0,
|
|
48
|
+
averageRenderTime: 0,
|
|
49
|
+
slowestComponent: null
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const totalRenderTime = componentMetrics.reduce((sum, m) => sum + m.duration, 0);
|
|
54
|
+
const slowestComponent = componentMetrics.reduce((max, m) =>
|
|
55
|
+
m.duration > (max?.duration || 0) ? m : max, componentMetrics[0]);
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
totalRenderTime,
|
|
59
|
+
componentCount: componentMetrics.length,
|
|
60
|
+
averageRenderTime: totalRenderTime / componentMetrics.length,
|
|
61
|
+
slowestComponent: slowestComponent.name,
|
|
62
|
+
memoryUsage: (performance as any).memory?.usedJSHeapSize
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
clearMeasurements(category?: string): void {
|
|
67
|
+
if (category) {
|
|
68
|
+
this.measurements.delete(category);
|
|
69
|
+
} else {
|
|
70
|
+
this.measurements.clear();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
measureComponent(componentName: string): () => void {
|
|
75
|
+
return this.startMeasurement('component', componentName);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export const performanceProfiler = new PerformanceProfiler();
|