@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.
Files changed (99) hide show
  1. package/dist/index.mjs +215 -214
  2. package/package.json +4 -2
  3. package/src/__tests__/use-intersection-observer.test.tsx +216 -0
  4. package/src/__tests__/use-local-storage.test.tsx +174 -0
  5. package/src/__tests__/use-pro-access.test.tsx +183 -0
  6. package/src/components/advanced-chart/advanced-chart.test.tsx +281 -0
  7. package/src/components/advanced-chart/index.tsx +412 -0
  8. package/src/components/advanced-forms/index.tsx +431 -0
  9. package/src/components/animated-button/index.tsx +202 -0
  10. package/src/components/calendar/event-dialog.tsx +372 -0
  11. package/src/components/calendar/index.tsx +557 -0
  12. package/src/components/color-picker/index.tsx +434 -0
  13. package/src/components/dashboard/index.tsx +334 -0
  14. package/src/components/data-table/data-table.test.tsx +187 -0
  15. package/src/components/data-table/index.tsx +368 -0
  16. package/src/components/draggable-list/index.tsx +100 -0
  17. package/src/components/enhanced/button.tsx +360 -0
  18. package/src/components/enhanced/card.tsx +272 -0
  19. package/src/components/enhanced/dialog.tsx +248 -0
  20. package/src/components/enhanced/index.ts +3 -0
  21. package/src/components/error-boundary/index.tsx +111 -0
  22. package/src/components/file-upload/file-upload.test.tsx +242 -0
  23. package/src/components/file-upload/index.tsx +362 -0
  24. package/src/components/floating-action-button/index.tsx +209 -0
  25. package/src/components/github-stars/index.tsx +414 -0
  26. package/src/components/health-check/index.tsx +441 -0
  27. package/src/components/hover-card-3d/index.tsx +170 -0
  28. package/src/components/index.ts +76 -0
  29. package/src/components/kanban/index.tsx +436 -0
  30. package/src/components/lazy-component/index.tsx +342 -0
  31. package/src/components/magnetic-button/index.tsx +170 -0
  32. package/src/components/memory-efficient-data/index.tsx +352 -0
  33. package/src/components/optimized-image/index.tsx +427 -0
  34. package/src/components/performance-debugger/index.tsx +591 -0
  35. package/src/components/performance-monitor/index.tsx +775 -0
  36. package/src/components/pinch-zoom/index.tsx +172 -0
  37. package/src/components/rich-text-editor/index-old-backup.tsx +443 -0
  38. package/src/components/rich-text-editor/index.tsx +1537 -0
  39. package/src/components/rich-text-editor/slash-commands-extension.ts +220 -0
  40. package/src/components/rich-text-editor/slash-commands.css +35 -0
  41. package/src/components/rich-text-editor/table-styles.css +65 -0
  42. package/src/components/spotlight-card/index.tsx +194 -0
  43. package/src/components/swipeable-card/index.tsx +100 -0
  44. package/src/components/timeline/index.tsx +333 -0
  45. package/src/components/ui/animated-button.tsx +185 -0
  46. package/src/components/ui/avatar.tsx +135 -0
  47. package/src/components/ui/badge.tsx +225 -0
  48. package/src/components/ui/button.tsx +221 -0
  49. package/src/components/ui/card.tsx +141 -0
  50. package/src/components/ui/checkbox.tsx +256 -0
  51. package/src/components/ui/color-picker.tsx +95 -0
  52. package/src/components/ui/dialog.tsx +332 -0
  53. package/src/components/ui/dropdown-menu.tsx +200 -0
  54. package/src/components/ui/hover-card-3d.tsx +103 -0
  55. package/src/components/ui/index.ts +33 -0
  56. package/src/components/ui/input.tsx +219 -0
  57. package/src/components/ui/label.tsx +26 -0
  58. package/src/components/ui/magnetic-button.tsx +129 -0
  59. package/src/components/ui/popover.tsx +183 -0
  60. package/src/components/ui/select.tsx +273 -0
  61. package/src/components/ui/separator.tsx +140 -0
  62. package/src/components/ui/slider.tsx +351 -0
  63. package/src/components/ui/spotlight-card.tsx +119 -0
  64. package/src/components/ui/switch.tsx +83 -0
  65. package/src/components/ui/tabs.tsx +195 -0
  66. package/src/components/ui/textarea.tsx +25 -0
  67. package/src/components/ui/toast.tsx +313 -0
  68. package/src/components/ui/tooltip.tsx +152 -0
  69. package/src/components/virtual-list/index.tsx +369 -0
  70. package/src/hooks/use-chart.ts +205 -0
  71. package/src/hooks/use-data-table.ts +182 -0
  72. package/src/hooks/use-docs-pro-access.ts +13 -0
  73. package/src/hooks/use-license-check.ts +65 -0
  74. package/src/hooks/use-subscription.ts +19 -0
  75. package/src/index.ts +14 -0
  76. package/src/lib/micro-interactions.ts +255 -0
  77. package/src/lib/utils.ts +6 -0
  78. package/src/patterns/login-form/index.tsx +276 -0
  79. package/src/patterns/login-form/types.ts +67 -0
  80. package/src/setupTests.ts +41 -0
  81. package/src/styles/design-system.css +365 -0
  82. package/src/styles/index.css +4 -0
  83. package/src/styles/tailwind.css +6 -0
  84. package/src/styles/tokens.css +453 -0
  85. package/src/types/moonui.d.ts +22 -0
  86. package/src/use-intersection-observer.tsx +154 -0
  87. package/src/use-local-storage.tsx +71 -0
  88. package/src/use-paddle.ts +138 -0
  89. package/src/use-performance-optimizer.ts +379 -0
  90. package/src/use-pro-access.ts +141 -0
  91. package/src/use-scroll-animation.ts +221 -0
  92. package/src/use-subscription.ts +37 -0
  93. package/src/use-toast.ts +32 -0
  94. package/src/utils/chart-helpers.ts +257 -0
  95. package/src/utils/cn.ts +69 -0
  96. package/src/utils/data-processing.ts +151 -0
  97. package/src/utils/license-guard.tsx +177 -0
  98. package/src/utils/license-validator.tsx +183 -0
  99. 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
+ }