@moontra/moonui-pro 2.0.22 → 2.1.0
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.mjs +215 -214
- package/package.json +4 -2
- package/src/__tests__/use-intersection-observer.test.tsx +216 -0
- package/src/__tests__/use-local-storage.test.tsx +174 -0
- package/src/__tests__/use-pro-access.test.tsx +183 -0
- package/src/components/advanced-chart/advanced-chart.test.tsx +281 -0
- package/src/components/advanced-chart/index.tsx +412 -0
- package/src/components/advanced-forms/index.tsx +431 -0
- package/src/components/animated-button/index.tsx +202 -0
- package/src/components/calendar/event-dialog.tsx +372 -0
- package/src/components/calendar/index.tsx +557 -0
- package/src/components/color-picker/index.tsx +434 -0
- package/src/components/dashboard/index.tsx +334 -0
- package/src/components/data-table/data-table.test.tsx +187 -0
- package/src/components/data-table/index.tsx +368 -0
- package/src/components/draggable-list/index.tsx +100 -0
- package/src/components/enhanced/button.tsx +360 -0
- package/src/components/enhanced/card.tsx +272 -0
- package/src/components/enhanced/dialog.tsx +248 -0
- package/src/components/enhanced/index.ts +3 -0
- package/src/components/error-boundary/index.tsx +111 -0
- package/src/components/file-upload/file-upload.test.tsx +242 -0
- package/src/components/file-upload/index.tsx +362 -0
- package/src/components/floating-action-button/index.tsx +209 -0
- package/src/components/github-stars/index.tsx +414 -0
- package/src/components/health-check/index.tsx +441 -0
- package/src/components/hover-card-3d/index.tsx +170 -0
- package/src/components/index.ts +76 -0
- package/src/components/kanban/index.tsx +436 -0
- package/src/components/lazy-component/index.tsx +342 -0
- package/src/components/magnetic-button/index.tsx +170 -0
- package/src/components/memory-efficient-data/index.tsx +352 -0
- package/src/components/optimized-image/index.tsx +427 -0
- package/src/components/performance-debugger/index.tsx +591 -0
- package/src/components/performance-monitor/index.tsx +775 -0
- package/src/components/pinch-zoom/index.tsx +172 -0
- package/src/components/rich-text-editor/index-old-backup.tsx +443 -0
- package/src/components/rich-text-editor/index.tsx +1537 -0
- package/src/components/rich-text-editor/slash-commands-extension.ts +220 -0
- package/src/components/rich-text-editor/slash-commands.css +35 -0
- package/src/components/rich-text-editor/table-styles.css +65 -0
- package/src/components/spotlight-card/index.tsx +194 -0
- package/src/components/swipeable-card/index.tsx +100 -0
- package/src/components/timeline/index.tsx +333 -0
- package/src/components/ui/animated-button.tsx +185 -0
- package/src/components/ui/avatar.tsx +135 -0
- package/src/components/ui/badge.tsx +225 -0
- package/src/components/ui/button.tsx +221 -0
- package/src/components/ui/card.tsx +141 -0
- package/src/components/ui/checkbox.tsx +256 -0
- package/src/components/ui/color-picker.tsx +95 -0
- package/src/components/ui/dialog.tsx +332 -0
- package/src/components/ui/dropdown-menu.tsx +200 -0
- package/src/components/ui/hover-card-3d.tsx +103 -0
- package/src/components/ui/index.ts +33 -0
- package/src/components/ui/input.tsx +219 -0
- package/src/components/ui/label.tsx +26 -0
- package/src/components/ui/magnetic-button.tsx +129 -0
- package/src/components/ui/popover.tsx +183 -0
- package/src/components/ui/select.tsx +273 -0
- package/src/components/ui/separator.tsx +140 -0
- package/src/components/ui/slider.tsx +351 -0
- package/src/components/ui/spotlight-card.tsx +119 -0
- package/src/components/ui/switch.tsx +83 -0
- package/src/components/ui/tabs.tsx +195 -0
- package/src/components/ui/textarea.tsx +25 -0
- package/src/components/ui/toast.tsx +313 -0
- package/src/components/ui/tooltip.tsx +152 -0
- package/src/components/virtual-list/index.tsx +369 -0
- package/src/hooks/use-chart.ts +205 -0
- package/src/hooks/use-data-table.ts +182 -0
- package/src/hooks/use-docs-pro-access.ts +13 -0
- package/src/hooks/use-license-check.ts +65 -0
- package/src/hooks/use-subscription.ts +19 -0
- package/src/index.ts +14 -0
- package/src/lib/micro-interactions.ts +255 -0
- package/src/lib/utils.ts +6 -0
- package/src/patterns/login-form/index.tsx +276 -0
- package/src/patterns/login-form/types.ts +67 -0
- package/src/setupTests.ts +41 -0
- package/src/styles/design-system.css +365 -0
- package/src/styles/index.css +4 -0
- package/src/styles/tailwind.css +6 -0
- package/src/styles/tokens.css +453 -0
- package/src/types/moonui.d.ts +22 -0
- package/src/use-intersection-observer.tsx +154 -0
- package/src/use-local-storage.tsx +71 -0
- package/src/use-paddle.ts +138 -0
- package/src/use-performance-optimizer.ts +379 -0
- package/src/use-pro-access.ts +141 -0
- package/src/use-scroll-animation.ts +221 -0
- package/src/use-subscription.ts +37 -0
- package/src/use-toast.ts +32 -0
- package/src/utils/chart-helpers.ts +257 -0
- package/src/utils/cn.ts +69 -0
- package/src/utils/data-processing.ts +151 -0
- package/src/utils/license-guard.tsx +177 -0
- package/src/utils/license-validator.tsx +183 -0
- package/src/utils/package-guard.ts +60 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "../../lib/utils";
|
|
6
|
+
|
|
7
|
+
/* -------------------------------------------------------------------------------------------------
|
|
8
|
+
* Tabs Root
|
|
9
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
10
|
+
interface TabsProps extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.Root> {
|
|
11
|
+
/** Mobil görünümde dikey düzen için */
|
|
12
|
+
vertical?: boolean;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const Tabs = React.forwardRef<
|
|
16
|
+
React.ElementRef<typeof TabsPrimitive.Root>,
|
|
17
|
+
TabsProps
|
|
18
|
+
>(({ vertical = false, ...props }, ref) => (
|
|
19
|
+
<TabsPrimitive.Root
|
|
20
|
+
ref={ref}
|
|
21
|
+
orientation={vertical ? "vertical" : "horizontal"}
|
|
22
|
+
{...props}
|
|
23
|
+
/>
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
Tabs.displayName = TabsPrimitive.Root.displayName;
|
|
27
|
+
|
|
28
|
+
/* -------------------------------------------------------------------------------------------------
|
|
29
|
+
* TabsList
|
|
30
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
31
|
+
const tabsListVariants = cva(
|
|
32
|
+
"flex items-center justify-start transition-all duration-200",
|
|
33
|
+
{
|
|
34
|
+
variants: {
|
|
35
|
+
variant: {
|
|
36
|
+
default: "bg-muted rounded-md p-1 text-muted-foreground dark:bg-gray-800/80 dark:text-gray-400",
|
|
37
|
+
pills: "bg-transparent gap-2 p-0 dark:text-gray-400",
|
|
38
|
+
underline: "bg-transparent border-b border-border dark:border-gray-700 gap-4 dark:text-gray-400",
|
|
39
|
+
cards: "bg-transparent gap-2 p-0 dark:text-gray-400",
|
|
40
|
+
minimal: "bg-transparent gap-1 p-0 dark:text-gray-400",
|
|
41
|
+
},
|
|
42
|
+
orientation: {
|
|
43
|
+
horizontal: "flex-row",
|
|
44
|
+
vertical: "flex-col items-start gap-1"
|
|
45
|
+
},
|
|
46
|
+
fullWidth: {
|
|
47
|
+
true: "w-full"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
defaultVariants: {
|
|
51
|
+
variant: "default",
|
|
52
|
+
orientation: "horizontal",
|
|
53
|
+
fullWidth: false
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
interface TabsListProps
|
|
59
|
+
extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>,
|
|
60
|
+
VariantProps<typeof tabsListVariants> {
|
|
61
|
+
/** Dikey düzende göster */
|
|
62
|
+
orientation?: "horizontal" | "vertical";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const TabsList = React.forwardRef<
|
|
66
|
+
React.ElementRef<typeof TabsPrimitive.List>,
|
|
67
|
+
TabsListProps
|
|
68
|
+
>(({ className, variant, orientation, fullWidth, ...props }, ref) => (
|
|
69
|
+
<TabsPrimitive.List
|
|
70
|
+
ref={ref}
|
|
71
|
+
className={cn(tabsListVariants({ variant, orientation, fullWidth, className }))}
|
|
72
|
+
{...props}
|
|
73
|
+
/>
|
|
74
|
+
));
|
|
75
|
+
|
|
76
|
+
TabsList.displayName = TabsPrimitive.List.displayName;
|
|
77
|
+
|
|
78
|
+
/* -------------------------------------------------------------------------------------------------
|
|
79
|
+
* TabsTrigger
|
|
80
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
81
|
+
const tabsTriggerVariants = cva(
|
|
82
|
+
"inline-flex items-center justify-center whitespace-nowrap font-medium ring-offset-background transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
|
83
|
+
{
|
|
84
|
+
variants: {
|
|
85
|
+
variant: {
|
|
86
|
+
default: "rounded-md data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm dark:data-[state=active]:bg-gray-900 dark:data-[state=active]:text-gray-100 dark:data-[state=active]:shadow-gray-950/10",
|
|
87
|
+
underline: "rounded-none border-b-2 border-transparent pb-2 data-[state=active]:border-primary data-[state=active]:bg-transparent data-[state=active]:text-foreground dark:data-[state=active]:border-primary/80 dark:data-[state=active]:text-gray-100",
|
|
88
|
+
pills: "rounded-full bg-muted hover:bg-muted/80 data-[state=active]:bg-primary data-[state=active]:text-primary-foreground dark:bg-gray-800 dark:hover:bg-gray-700 dark:data-[state=active]:bg-primary/90 dark:data-[state=active]:text-white",
|
|
89
|
+
cards: "rounded-md bg-muted/50 hover:bg-muted data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-md dark:bg-gray-800/50 dark:hover:bg-gray-700/50 dark:data-[state=active]:bg-gray-900 dark:data-[state=active]:text-gray-100 dark:data-[state=active]:shadow-gray-950/20",
|
|
90
|
+
minimal: "rounded-sm bg-transparent hover:bg-muted/30 data-[state=active]:bg-transparent data-[state=active]:text-foreground data-[state=active]:underline data-[state=active]:underline-offset-4 dark:hover:bg-gray-800/30 dark:data-[state=active]:text-gray-200",
|
|
91
|
+
},
|
|
92
|
+
size: {
|
|
93
|
+
sm: "h-7 px-2 text-xs",
|
|
94
|
+
md: "h-9 px-3 py-1.5 text-sm",
|
|
95
|
+
lg: "h-10 px-4 py-2 text-base",
|
|
96
|
+
},
|
|
97
|
+
orientation: {
|
|
98
|
+
horizontal: "",
|
|
99
|
+
vertical: "justify-start w-full text-left"
|
|
100
|
+
},
|
|
101
|
+
fullWidth: {
|
|
102
|
+
true: "w-full"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
defaultVariants: {
|
|
106
|
+
variant: "default",
|
|
107
|
+
size: "md",
|
|
108
|
+
orientation: "horizontal",
|
|
109
|
+
fullWidth: false
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
interface TabsTriggerProps
|
|
115
|
+
extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>,
|
|
116
|
+
VariantProps<typeof tabsTriggerVariants> {
|
|
117
|
+
/** İkon konumu */
|
|
118
|
+
iconPosition?: "left" | "right" | "none";
|
|
119
|
+
/** İkon */
|
|
120
|
+
icon?: React.ReactNode;
|
|
121
|
+
/** Badge içeriği */
|
|
122
|
+
badge?: React.ReactNode;
|
|
123
|
+
/** Aktif olmayan durumda yarı saydam */
|
|
124
|
+
fadeTabs?: boolean;
|
|
125
|
+
/** Dikey düzende göster */
|
|
126
|
+
orientation?: "horizontal" | "vertical";
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const TabsTrigger = React.forwardRef<
|
|
130
|
+
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
|
131
|
+
TabsTriggerProps
|
|
132
|
+
>(({
|
|
133
|
+
className,
|
|
134
|
+
variant,
|
|
135
|
+
size,
|
|
136
|
+
icon,
|
|
137
|
+
iconPosition = "left",
|
|
138
|
+
badge,
|
|
139
|
+
fadeTabs = false,
|
|
140
|
+
orientation,
|
|
141
|
+
fullWidth,
|
|
142
|
+
children,
|
|
143
|
+
...props
|
|
144
|
+
}, ref) => (
|
|
145
|
+
<TabsPrimitive.Trigger
|
|
146
|
+
ref={ref}
|
|
147
|
+
className={cn(
|
|
148
|
+
tabsTriggerVariants({ variant, size, orientation, fullWidth }),
|
|
149
|
+
fadeTabs && "data-[state=inactive]:opacity-60",
|
|
150
|
+
className
|
|
151
|
+
)}
|
|
152
|
+
{...props}
|
|
153
|
+
>
|
|
154
|
+
{icon && iconPosition === "left" && (
|
|
155
|
+
<span className="mr-2">{icon}</span>
|
|
156
|
+
)}
|
|
157
|
+
{children}
|
|
158
|
+
{icon && iconPosition === "right" && (
|
|
159
|
+
<span className="ml-2">{icon}</span>
|
|
160
|
+
)}
|
|
161
|
+
{badge && (
|
|
162
|
+
<span className="ml-2">{badge}</span>
|
|
163
|
+
)}
|
|
164
|
+
</TabsPrimitive.Trigger>
|
|
165
|
+
));
|
|
166
|
+
|
|
167
|
+
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
|
168
|
+
|
|
169
|
+
/* -------------------------------------------------------------------------------------------------
|
|
170
|
+
* TabsContent
|
|
171
|
+
* -----------------------------------------------------------------------------------------------*/
|
|
172
|
+
interface TabsContentProps
|
|
173
|
+
extends React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content> {
|
|
174
|
+
/** İçerik animasyonu */
|
|
175
|
+
animated?: boolean;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const TabsContent = React.forwardRef<
|
|
179
|
+
React.ElementRef<typeof TabsPrimitive.Content>,
|
|
180
|
+
TabsContentProps
|
|
181
|
+
>(({ className, animated = false, ...props }, ref) => (
|
|
182
|
+
<TabsPrimitive.Content
|
|
183
|
+
ref={ref}
|
|
184
|
+
className={cn(
|
|
185
|
+
"focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
186
|
+
animated && "data-[state=active]:animate-fadeIn data-[state=inactive]:animate-fadeOut",
|
|
187
|
+
className
|
|
188
|
+
)}
|
|
189
|
+
{...props}
|
|
190
|
+
/>
|
|
191
|
+
));
|
|
192
|
+
|
|
193
|
+
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
|
194
|
+
|
|
195
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
|
|
6
|
+
export interface TextareaProps
|
|
7
|
+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
|
|
8
|
+
|
|
9
|
+
const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
|
10
|
+
({ className, ...props }, ref) => {
|
|
11
|
+
return (
|
|
12
|
+
<textarea
|
|
13
|
+
className={cn(
|
|
14
|
+
"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",
|
|
15
|
+
className
|
|
16
|
+
)}
|
|
17
|
+
ref={ref}
|
|
18
|
+
{...props}
|
|
19
|
+
/>
|
|
20
|
+
)
|
|
21
|
+
}
|
|
22
|
+
)
|
|
23
|
+
Textarea.displayName = "Textarea"
|
|
24
|
+
|
|
25
|
+
export { Textarea }
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import * as ToastPrimitives from "@radix-ui/react-toast";
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
6
|
+
import { X } from "lucide-react";
|
|
7
|
+
import { cn } from "../../lib/utils";
|
|
8
|
+
|
|
9
|
+
const ToastProvider = ToastPrimitives.Provider;
|
|
10
|
+
|
|
11
|
+
const ToastViewport = React.forwardRef<
|
|
12
|
+
React.ElementRef<typeof ToastPrimitives.Viewport>,
|
|
13
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Viewport>
|
|
14
|
+
>(({ className, ...props }, ref) => (
|
|
15
|
+
<ToastPrimitives.Viewport
|
|
16
|
+
ref={ref}
|
|
17
|
+
className={cn(
|
|
18
|
+
"fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]",
|
|
19
|
+
className
|
|
20
|
+
)}
|
|
21
|
+
{...props}
|
|
22
|
+
/>
|
|
23
|
+
));
|
|
24
|
+
ToastViewport.displayName = ToastPrimitives.Viewport.displayName;
|
|
25
|
+
|
|
26
|
+
const toastVariants = cva(
|
|
27
|
+
"group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-8 shadow-sm transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full",
|
|
28
|
+
{
|
|
29
|
+
variants: {
|
|
30
|
+
variant: {
|
|
31
|
+
default: "border bg-background text-foreground",
|
|
32
|
+
destructive:
|
|
33
|
+
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
|
34
|
+
success:
|
|
35
|
+
"border-green-200 bg-green-50 text-green-900 dark:border-green-800 dark:bg-green-900/20 dark:text-green-100",
|
|
36
|
+
warning:
|
|
37
|
+
"border-orange-200 bg-orange-50 text-orange-900 dark:border-orange-800 dark:bg-orange-900/20 dark:text-orange-100",
|
|
38
|
+
info:
|
|
39
|
+
"border-blue-200 bg-blue-50 text-blue-900 dark:border-blue-800 dark:bg-blue-900/20 dark:text-blue-100",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
defaultVariants: {
|
|
43
|
+
variant: "default",
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
type ToastProps = React.ComponentPropsWithoutRef<typeof ToastPrimitives.Root> &
|
|
49
|
+
VariantProps<typeof toastVariants>;
|
|
50
|
+
|
|
51
|
+
type ToastActionElement = React.ReactElement<typeof ToastAction>;
|
|
52
|
+
|
|
53
|
+
const Toast = React.forwardRef<
|
|
54
|
+
React.ElementRef<typeof ToastPrimitives.Root>,
|
|
55
|
+
ToastProps
|
|
56
|
+
>(({ className, variant, ...props }, ref) => {
|
|
57
|
+
return (
|
|
58
|
+
<ToastPrimitives.Root
|
|
59
|
+
ref={ref}
|
|
60
|
+
className={cn(toastVariants({ variant }), className)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
});
|
|
65
|
+
Toast.displayName = ToastPrimitives.Root.displayName;
|
|
66
|
+
|
|
67
|
+
const ToastAction = React.forwardRef<
|
|
68
|
+
React.ElementRef<typeof ToastPrimitives.Action>,
|
|
69
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Action>
|
|
70
|
+
>(({ className, ...props }, ref) => (
|
|
71
|
+
<ToastPrimitives.Action
|
|
72
|
+
ref={ref}
|
|
73
|
+
className={cn(
|
|
74
|
+
"inline-flex h-8 shrink-0 items-center justify-center rounded-md border bg-transparent px-3 text-sm font-medium transition-colors hover:bg-secondary focus:outline-none focus:ring-1 focus:ring-ring disabled:pointer-events-none disabled:opacity-50 group-[.destructive]:border-muted/40 group-[.destructive]:hover:border-destructive/30 group-[.destructive]:hover:bg-destructive group-[.destructive]:hover:text-destructive-foreground group-[.destructive]:focus:ring-destructive",
|
|
75
|
+
className
|
|
76
|
+
)}
|
|
77
|
+
{...props}
|
|
78
|
+
/>
|
|
79
|
+
));
|
|
80
|
+
ToastAction.displayName = ToastPrimitives.Action.displayName;
|
|
81
|
+
|
|
82
|
+
const ToastClose = React.forwardRef<
|
|
83
|
+
React.ElementRef<typeof ToastPrimitives.Close>,
|
|
84
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Close>
|
|
85
|
+
>(({ className, ...props }, ref) => (
|
|
86
|
+
<ToastPrimitives.Close
|
|
87
|
+
ref={ref}
|
|
88
|
+
className={cn(
|
|
89
|
+
"absolute right-1 top-1 rounded-md p-1 text-foreground/50 opacity-0 transition-opacity hover:text-foreground focus:opacity-100 focus:outline-none focus:ring-1 group-hover:opacity-100 group-[.destructive]:text-red-300 group-[.destructive]:hover:text-red-50 group-[.destructive]:focus:ring-red-400 group-[.destructive]:focus:ring-offset-red-600",
|
|
90
|
+
className
|
|
91
|
+
)}
|
|
92
|
+
toast-close=""
|
|
93
|
+
{...props}
|
|
94
|
+
>
|
|
95
|
+
<X className="h-4 w-4" />
|
|
96
|
+
</ToastPrimitives.Close>
|
|
97
|
+
));
|
|
98
|
+
ToastClose.displayName = ToastPrimitives.Close.displayName;
|
|
99
|
+
|
|
100
|
+
const ToastTitle = React.forwardRef<
|
|
101
|
+
React.ElementRef<typeof ToastPrimitives.Title>,
|
|
102
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Title>
|
|
103
|
+
>(({ className, ...props }, ref) => (
|
|
104
|
+
<ToastPrimitives.Title
|
|
105
|
+
ref={ref}
|
|
106
|
+
className={cn("text-sm font-semibold [&+div]:text-xs", className)}
|
|
107
|
+
{...props}
|
|
108
|
+
/>
|
|
109
|
+
));
|
|
110
|
+
ToastTitle.displayName = ToastPrimitives.Title.displayName;
|
|
111
|
+
|
|
112
|
+
const ToastDescription = React.forwardRef<
|
|
113
|
+
React.ElementRef<typeof ToastPrimitives.Description>,
|
|
114
|
+
React.ComponentPropsWithoutRef<typeof ToastPrimitives.Description>
|
|
115
|
+
>(({ className, ...props }, ref) => (
|
|
116
|
+
<ToastPrimitives.Description
|
|
117
|
+
ref={ref}
|
|
118
|
+
className={cn("text-sm opacity-90", className)}
|
|
119
|
+
{...props}
|
|
120
|
+
/>
|
|
121
|
+
));
|
|
122
|
+
ToastDescription.displayName = ToastPrimitives.Description.displayName;
|
|
123
|
+
|
|
124
|
+
type ToastData = {
|
|
125
|
+
id: string;
|
|
126
|
+
title?: React.ReactNode;
|
|
127
|
+
description?: React.ReactNode;
|
|
128
|
+
action?: ToastActionElement;
|
|
129
|
+
variant?: ToastProps["variant"];
|
|
130
|
+
duration?: number;
|
|
131
|
+
open?: boolean;
|
|
132
|
+
onOpenChange?: (open: boolean) => void;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const TOAST_LIMIT = 5;
|
|
136
|
+
const TOAST_REMOVE_DELAY = 1000000;
|
|
137
|
+
|
|
138
|
+
type State = {
|
|
139
|
+
toasts: ToastData[];
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>();
|
|
143
|
+
|
|
144
|
+
const addToRemoveQueue = (toastId: string) => {
|
|
145
|
+
if (toastTimeouts.has(toastId)) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const timeout = setTimeout(() => {
|
|
150
|
+
toastTimeouts.delete(toastId);
|
|
151
|
+
dispatch({
|
|
152
|
+
type: "REMOVE_TOAST",
|
|
153
|
+
toastId: toastId,
|
|
154
|
+
});
|
|
155
|
+
}, TOAST_REMOVE_DELAY);
|
|
156
|
+
|
|
157
|
+
toastTimeouts.set(toastId, timeout);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const reducer = (state: State, action: any): State => {
|
|
161
|
+
switch (action.type) {
|
|
162
|
+
case "ADD_TOAST":
|
|
163
|
+
return {
|
|
164
|
+
...state,
|
|
165
|
+
toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
case "UPDATE_TOAST":
|
|
169
|
+
return {
|
|
170
|
+
...state,
|
|
171
|
+
toasts: state.toasts.map((t) =>
|
|
172
|
+
t.id === action.toast.id ? { ...t, ...action.toast } : t
|
|
173
|
+
),
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
case "DISMISS_TOAST": {
|
|
177
|
+
const { toastId } = action;
|
|
178
|
+
|
|
179
|
+
if (toastId) {
|
|
180
|
+
addToRemoveQueue(toastId);
|
|
181
|
+
} else {
|
|
182
|
+
state.toasts.forEach((toast) => {
|
|
183
|
+
addToRemoveQueue(toast.id);
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return {
|
|
188
|
+
...state,
|
|
189
|
+
toasts: state.toasts.map((t) =>
|
|
190
|
+
t.id === toastId || toastId === undefined
|
|
191
|
+
? {
|
|
192
|
+
...t,
|
|
193
|
+
open: false,
|
|
194
|
+
}
|
|
195
|
+
: t
|
|
196
|
+
),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
case "REMOVE_TOAST":
|
|
200
|
+
if (action.toastId === undefined) {
|
|
201
|
+
return {
|
|
202
|
+
...state,
|
|
203
|
+
toasts: [],
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return {
|
|
207
|
+
...state,
|
|
208
|
+
toasts: state.toasts.filter((t) => t.id !== action.toastId),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const listeners: Array<(state: State) => void> = [];
|
|
214
|
+
|
|
215
|
+
let memoryState: State = { toasts: [] };
|
|
216
|
+
let toastCounter = 0;
|
|
217
|
+
|
|
218
|
+
function dispatch(action: any) {
|
|
219
|
+
memoryState = reducer(memoryState, action);
|
|
220
|
+
listeners.forEach((listener) => {
|
|
221
|
+
listener(memoryState);
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
type Toast = Omit<ToastData, "id">;
|
|
226
|
+
|
|
227
|
+
function toast({ ...props }: Toast) {
|
|
228
|
+
const id = `toast-${Date.now()}-${++toastCounter}`;
|
|
229
|
+
|
|
230
|
+
const update = (props: ToastData) =>
|
|
231
|
+
dispatch({
|
|
232
|
+
type: "UPDATE_TOAST",
|
|
233
|
+
toast: { ...props, id },
|
|
234
|
+
});
|
|
235
|
+
const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id });
|
|
236
|
+
|
|
237
|
+
dispatch({
|
|
238
|
+
type: "ADD_TOAST",
|
|
239
|
+
toast: {
|
|
240
|
+
...props,
|
|
241
|
+
id,
|
|
242
|
+
open: true,
|
|
243
|
+
onOpenChange: (open: boolean) => {
|
|
244
|
+
if (!open) dismiss();
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
id: id,
|
|
251
|
+
dismiss,
|
|
252
|
+
update,
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
function useToast() {
|
|
257
|
+
const [state, setState] = React.useState<State>(memoryState);
|
|
258
|
+
|
|
259
|
+
React.useEffect(() => {
|
|
260
|
+
listeners.push(setState);
|
|
261
|
+
return () => {
|
|
262
|
+
const index = listeners.indexOf(setState);
|
|
263
|
+
if (index > -1) {
|
|
264
|
+
listeners.splice(index, 1);
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
}, [state]);
|
|
268
|
+
|
|
269
|
+
return {
|
|
270
|
+
...state,
|
|
271
|
+
toast,
|
|
272
|
+
dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function Toaster() {
|
|
277
|
+
const { toasts } = useToast();
|
|
278
|
+
|
|
279
|
+
return (
|
|
280
|
+
<ToastProvider>
|
|
281
|
+
{toasts.map(function ({ id, title, description, action, variant, open, onOpenChange, ...props }) {
|
|
282
|
+
return (
|
|
283
|
+
<Toast key={id} variant={variant} open={open} onOpenChange={onOpenChange} {...props}>
|
|
284
|
+
<div className="grid gap-1">
|
|
285
|
+
{title && <ToastTitle>{title}</ToastTitle>}
|
|
286
|
+
{description && (
|
|
287
|
+
<ToastDescription>{description}</ToastDescription>
|
|
288
|
+
)}
|
|
289
|
+
</div>
|
|
290
|
+
{action}
|
|
291
|
+
<ToastClose />
|
|
292
|
+
</Toast>
|
|
293
|
+
);
|
|
294
|
+
})}
|
|
295
|
+
<ToastViewport />
|
|
296
|
+
</ToastProvider>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export {
|
|
301
|
+
type ToastProps,
|
|
302
|
+
type ToastActionElement,
|
|
303
|
+
ToastProvider,
|
|
304
|
+
ToastViewport,
|
|
305
|
+
Toast,
|
|
306
|
+
ToastTitle,
|
|
307
|
+
ToastDescription,
|
|
308
|
+
ToastAction,
|
|
309
|
+
ToastClose,
|
|
310
|
+
Toaster,
|
|
311
|
+
toast,
|
|
312
|
+
useToast,
|
|
313
|
+
};
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import * as React from "react"
|
|
4
|
+
import * as TooltipPrimitive from "@radix-ui/react-tooltip"
|
|
5
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
6
|
+
|
|
7
|
+
import { cn } from "../../lib/utils"
|
|
8
|
+
|
|
9
|
+
const TooltipProvider = TooltipPrimitive.Provider
|
|
10
|
+
|
|
11
|
+
const tooltipVariants = cva(
|
|
12
|
+
"z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
13
|
+
{
|
|
14
|
+
variants: {
|
|
15
|
+
variant: {
|
|
16
|
+
default: "bg-primary text-primary-foreground",
|
|
17
|
+
secondary: "bg-secondary text-secondary-foreground",
|
|
18
|
+
success: "bg-green-500 text-white border-green-600",
|
|
19
|
+
warning: "bg-yellow-500 text-white border-yellow-600",
|
|
20
|
+
destructive: "bg-destructive text-destructive-foreground",
|
|
21
|
+
info: "bg-blue-500 text-white border-blue-600",
|
|
22
|
+
},
|
|
23
|
+
size: {
|
|
24
|
+
sm: "px-2 py-1 text-xs",
|
|
25
|
+
md: "px-3 py-1.5 text-sm",
|
|
26
|
+
lg: "px-4 py-2 text-base",
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
defaultVariants: {
|
|
30
|
+
variant: "default",
|
|
31
|
+
size: "md",
|
|
32
|
+
},
|
|
33
|
+
}
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
const Tooltip = TooltipPrimitive.Root
|
|
37
|
+
|
|
38
|
+
const TooltipTrigger = TooltipPrimitive.Trigger
|
|
39
|
+
|
|
40
|
+
const TooltipArrow = React.forwardRef<
|
|
41
|
+
React.ElementRef<typeof TooltipPrimitive.Arrow>,
|
|
42
|
+
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Arrow>
|
|
43
|
+
>(({ className, ...props }, ref) => (
|
|
44
|
+
<TooltipPrimitive.Arrow
|
|
45
|
+
ref={ref}
|
|
46
|
+
className={cn("fill-current", className)}
|
|
47
|
+
{...props}
|
|
48
|
+
/>
|
|
49
|
+
))
|
|
50
|
+
TooltipArrow.displayName = TooltipPrimitive.Arrow.displayName
|
|
51
|
+
|
|
52
|
+
export interface TooltipContentProps
|
|
53
|
+
extends React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>,
|
|
54
|
+
VariantProps<typeof tooltipVariants> {
|
|
55
|
+
showArrow?: boolean
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const TooltipContent = React.forwardRef<
|
|
59
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
60
|
+
TooltipContentProps
|
|
61
|
+
>(({ className, variant, size, showArrow = false, sideOffset = 4, ...props }, ref) => (
|
|
62
|
+
<TooltipPrimitive.Content
|
|
63
|
+
ref={ref}
|
|
64
|
+
sideOffset={sideOffset}
|
|
65
|
+
className={cn(tooltipVariants({ variant, size }), className)}
|
|
66
|
+
{...props}
|
|
67
|
+
>
|
|
68
|
+
{props.children}
|
|
69
|
+
{showArrow && <TooltipArrow />}
|
|
70
|
+
</TooltipPrimitive.Content>
|
|
71
|
+
))
|
|
72
|
+
TooltipContent.displayName = TooltipPrimitive.Content.displayName
|
|
73
|
+
|
|
74
|
+
// Simplified Tooltip component for easy usage
|
|
75
|
+
export interface SimpleTooltipProps {
|
|
76
|
+
children: React.ReactElement
|
|
77
|
+
content: React.ReactNode
|
|
78
|
+
variant?: TooltipContentProps["variant"]
|
|
79
|
+
size?: TooltipContentProps["size"]
|
|
80
|
+
side?: "top" | "right" | "bottom" | "left"
|
|
81
|
+
align?: "start" | "center" | "end"
|
|
82
|
+
delayDuration?: number
|
|
83
|
+
skipDelayDuration?: number
|
|
84
|
+
sideOffset?: number
|
|
85
|
+
showArrow?: boolean
|
|
86
|
+
className?: string
|
|
87
|
+
open?: boolean
|
|
88
|
+
defaultOpen?: boolean
|
|
89
|
+
onOpenChange?: (open: boolean) => void
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const SimpleTooltip = React.forwardRef<
|
|
93
|
+
React.ElementRef<typeof TooltipPrimitive.Content>,
|
|
94
|
+
SimpleTooltipProps
|
|
95
|
+
>(
|
|
96
|
+
(
|
|
97
|
+
{
|
|
98
|
+
children,
|
|
99
|
+
content,
|
|
100
|
+
variant = "default",
|
|
101
|
+
size = "md",
|
|
102
|
+
side = "top",
|
|
103
|
+
align = "center",
|
|
104
|
+
delayDuration = 400,
|
|
105
|
+
skipDelayDuration = 300,
|
|
106
|
+
sideOffset = 4,
|
|
107
|
+
showArrow = false,
|
|
108
|
+
className,
|
|
109
|
+
open,
|
|
110
|
+
defaultOpen,
|
|
111
|
+
onOpenChange,
|
|
112
|
+
...props
|
|
113
|
+
},
|
|
114
|
+
ref
|
|
115
|
+
) => {
|
|
116
|
+
return (
|
|
117
|
+
<Tooltip
|
|
118
|
+
open={open}
|
|
119
|
+
defaultOpen={defaultOpen}
|
|
120
|
+
onOpenChange={onOpenChange}
|
|
121
|
+
delayDuration={delayDuration}
|
|
122
|
+
skipDelayDuration={skipDelayDuration}
|
|
123
|
+
>
|
|
124
|
+
<TooltipTrigger asChild>{children}</TooltipTrigger>
|
|
125
|
+
<TooltipContent
|
|
126
|
+
ref={ref}
|
|
127
|
+
side={side}
|
|
128
|
+
align={align}
|
|
129
|
+
variant={variant}
|
|
130
|
+
size={size}
|
|
131
|
+
sideOffset={sideOffset}
|
|
132
|
+
showArrow={showArrow}
|
|
133
|
+
className={className}
|
|
134
|
+
{...props}
|
|
135
|
+
>
|
|
136
|
+
{content}
|
|
137
|
+
</TooltipContent>
|
|
138
|
+
</Tooltip>
|
|
139
|
+
)
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
SimpleTooltip.displayName = "SimpleTooltip"
|
|
143
|
+
|
|
144
|
+
export {
|
|
145
|
+
Tooltip,
|
|
146
|
+
TooltipProvider,
|
|
147
|
+
TooltipTrigger,
|
|
148
|
+
TooltipContent,
|
|
149
|
+
TooltipArrow,
|
|
150
|
+
SimpleTooltip,
|
|
151
|
+
tooltipVariants,
|
|
152
|
+
}
|