@moontra/moonui-pro 2.0.22 → 2.0.23

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 (96) hide show
  1. package/package.json +2 -1
  2. package/src/__tests__/use-intersection-observer.test.tsx +216 -0
  3. package/src/__tests__/use-local-storage.test.tsx +174 -0
  4. package/src/__tests__/use-pro-access.test.tsx +183 -0
  5. package/src/components/advanced-chart/advanced-chart.test.tsx +281 -0
  6. package/src/components/advanced-chart/index.tsx +412 -0
  7. package/src/components/advanced-forms/index.tsx +431 -0
  8. package/src/components/animated-button/index.tsx +202 -0
  9. package/src/components/calendar/event-dialog.tsx +372 -0
  10. package/src/components/calendar/index.tsx +531 -0
  11. package/src/components/color-picker/index.tsx +434 -0
  12. package/src/components/dashboard/index.tsx +334 -0
  13. package/src/components/data-table/data-table.test.tsx +187 -0
  14. package/src/components/data-table/index.tsx +368 -0
  15. package/src/components/draggable-list/index.tsx +100 -0
  16. package/src/components/enhanced/button.tsx +360 -0
  17. package/src/components/enhanced/card.tsx +272 -0
  18. package/src/components/enhanced/dialog.tsx +248 -0
  19. package/src/components/enhanced/index.ts +3 -0
  20. package/src/components/error-boundary/index.tsx +111 -0
  21. package/src/components/file-upload/file-upload.test.tsx +242 -0
  22. package/src/components/file-upload/index.tsx +362 -0
  23. package/src/components/floating-action-button/index.tsx +209 -0
  24. package/src/components/github-stars/index.tsx +414 -0
  25. package/src/components/health-check/index.tsx +441 -0
  26. package/src/components/hover-card-3d/index.tsx +170 -0
  27. package/src/components/index.ts +76 -0
  28. package/src/components/kanban/index.tsx +436 -0
  29. package/src/components/lazy-component/index.tsx +342 -0
  30. package/src/components/magnetic-button/index.tsx +170 -0
  31. package/src/components/memory-efficient-data/index.tsx +352 -0
  32. package/src/components/optimized-image/index.tsx +427 -0
  33. package/src/components/performance-debugger/index.tsx +591 -0
  34. package/src/components/performance-monitor/index.tsx +775 -0
  35. package/src/components/pinch-zoom/index.tsx +172 -0
  36. package/src/components/rich-text-editor/index-old-backup.tsx +443 -0
  37. package/src/components/rich-text-editor/index.tsx +1537 -0
  38. package/src/components/rich-text-editor/slash-commands-extension.ts +220 -0
  39. package/src/components/rich-text-editor/slash-commands.css +35 -0
  40. package/src/components/rich-text-editor/table-styles.css +65 -0
  41. package/src/components/spotlight-card/index.tsx +194 -0
  42. package/src/components/swipeable-card/index.tsx +100 -0
  43. package/src/components/timeline/index.tsx +333 -0
  44. package/src/components/ui/animated-button.tsx +185 -0
  45. package/src/components/ui/avatar.tsx +135 -0
  46. package/src/components/ui/badge.tsx +225 -0
  47. package/src/components/ui/button.tsx +221 -0
  48. package/src/components/ui/card.tsx +141 -0
  49. package/src/components/ui/checkbox.tsx +256 -0
  50. package/src/components/ui/color-picker.tsx +95 -0
  51. package/src/components/ui/dialog.tsx +332 -0
  52. package/src/components/ui/dropdown-menu.tsx +200 -0
  53. package/src/components/ui/hover-card-3d.tsx +103 -0
  54. package/src/components/ui/index.ts +33 -0
  55. package/src/components/ui/input.tsx +219 -0
  56. package/src/components/ui/label.tsx +26 -0
  57. package/src/components/ui/magnetic-button.tsx +129 -0
  58. package/src/components/ui/popover.tsx +183 -0
  59. package/src/components/ui/select.tsx +273 -0
  60. package/src/components/ui/separator.tsx +140 -0
  61. package/src/components/ui/slider.tsx +351 -0
  62. package/src/components/ui/spotlight-card.tsx +119 -0
  63. package/src/components/ui/switch.tsx +83 -0
  64. package/src/components/ui/tabs.tsx +195 -0
  65. package/src/components/ui/textarea.tsx +25 -0
  66. package/src/components/ui/toast.tsx +313 -0
  67. package/src/components/ui/tooltip.tsx +152 -0
  68. package/src/components/virtual-list/index.tsx +369 -0
  69. package/src/hooks/use-chart.ts +205 -0
  70. package/src/hooks/use-data-table.ts +182 -0
  71. package/src/hooks/use-docs-pro-access.ts +13 -0
  72. package/src/hooks/use-license-check.ts +65 -0
  73. package/src/hooks/use-subscription.ts +19 -0
  74. package/src/index.ts +11 -0
  75. package/src/lib/micro-interactions.ts +255 -0
  76. package/src/lib/utils.ts +6 -0
  77. package/src/patterns/login-form/index.tsx +276 -0
  78. package/src/patterns/login-form/types.ts +67 -0
  79. package/src/setupTests.ts +41 -0
  80. package/src/styles/design-system.css +365 -0
  81. package/src/styles/index.css +4 -0
  82. package/src/styles/tailwind.css +6 -0
  83. package/src/styles/tokens.css +453 -0
  84. package/src/types/moonui.d.ts +22 -0
  85. package/src/use-intersection-observer.tsx +154 -0
  86. package/src/use-local-storage.tsx +71 -0
  87. package/src/use-paddle.ts +138 -0
  88. package/src/use-performance-optimizer.ts +379 -0
  89. package/src/use-pro-access.ts +141 -0
  90. package/src/use-scroll-animation.ts +221 -0
  91. package/src/use-subscription.ts +37 -0
  92. package/src/use-toast.ts +32 -0
  93. package/src/utils/chart-helpers.ts +257 -0
  94. package/src/utils/cn.ts +69 -0
  95. package/src/utils/data-processing.ts +151 -0
  96. package/src/utils/license-validator.tsx +183 -0
@@ -0,0 +1,140 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SeparatorPrimitive from "@radix-ui/react-separator"
5
+ import { cva, type VariantProps } from "class-variance-authority"
6
+
7
+ import { cn } from "../../lib/utils"
8
+
9
+ const separatorVariants = cva(
10
+ "shrink-0 bg-border",
11
+ {
12
+ variants: {
13
+ orientation: {
14
+ horizontal: "h-[1px] w-full",
15
+ vertical: "h-full w-[1px]",
16
+ },
17
+ variant: {
18
+ default: "bg-border",
19
+ dashed: "border-dashed border-border bg-transparent",
20
+ dotted: "border-dotted border-border bg-transparent",
21
+ gradient: "bg-gradient-to-r from-transparent via-border to-transparent",
22
+ },
23
+ size: {
24
+ sm: "",
25
+ md: "",
26
+ lg: "",
27
+ },
28
+ },
29
+ compoundVariants: [
30
+ {
31
+ orientation: "horizontal",
32
+ variant: "dashed",
33
+ className: "border-t h-0",
34
+ },
35
+ {
36
+ orientation: "vertical",
37
+ variant: "dashed",
38
+ className: "border-l w-0",
39
+ },
40
+ {
41
+ orientation: "horizontal",
42
+ variant: "dotted",
43
+ className: "border-t h-0",
44
+ },
45
+ {
46
+ orientation: "vertical",
47
+ variant: "dotted",
48
+ className: "border-l w-0",
49
+ },
50
+ {
51
+ orientation: "horizontal",
52
+ size: "sm",
53
+ variant: ["dashed", "dotted"],
54
+ className: "border-t-[0.5px]",
55
+ },
56
+ {
57
+ orientation: "horizontal",
58
+ size: "lg",
59
+ variant: ["dashed", "dotted"],
60
+ className: "border-t-2",
61
+ },
62
+ {
63
+ orientation: "vertical",
64
+ size: "sm",
65
+ variant: ["dashed", "dotted"],
66
+ className: "border-l-[0.5px]",
67
+ },
68
+ {
69
+ orientation: "vertical",
70
+ size: "lg",
71
+ variant: ["dashed", "dotted"],
72
+ className: "border-l-2",
73
+ },
74
+ {
75
+ orientation: "horizontal",
76
+ size: "sm",
77
+ variant: ["default", "gradient"],
78
+ className: "h-[0.5px]",
79
+ },
80
+ {
81
+ orientation: "horizontal",
82
+ size: "lg",
83
+ variant: ["default", "gradient"],
84
+ className: "h-[2px]",
85
+ },
86
+ {
87
+ orientation: "vertical",
88
+ size: "sm",
89
+ variant: ["default", "gradient"],
90
+ className: "w-[0.5px]",
91
+ },
92
+ {
93
+ orientation: "vertical",
94
+ size: "lg",
95
+ variant: ["default", "gradient"],
96
+ className: "w-[2px]",
97
+ },
98
+ ],
99
+ defaultVariants: {
100
+ orientation: "horizontal",
101
+ variant: "default",
102
+ size: "md",
103
+ },
104
+ }
105
+ )
106
+
107
+ export interface SeparatorProps
108
+ extends React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>,
109
+ VariantProps<typeof separatorVariants> {}
110
+
111
+ const Separator = React.forwardRef<
112
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
113
+ SeparatorProps
114
+ >(
115
+ (
116
+ {
117
+ className,
118
+ orientation = "horizontal",
119
+ variant = "default",
120
+ size = "md",
121
+ decorative = true,
122
+ ...props
123
+ },
124
+ ref
125
+ ) => (
126
+ <SeparatorPrimitive.Root
127
+ ref={ref}
128
+ decorative={decorative}
129
+ orientation={orientation}
130
+ className={cn(
131
+ separatorVariants({ orientation, variant, size }),
132
+ className
133
+ )}
134
+ {...props}
135
+ />
136
+ )
137
+ )
138
+ Separator.displayName = SeparatorPrimitive.Root.displayName
139
+
140
+ export { Separator, separatorVariants }
@@ -0,0 +1,351 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { cva, type VariantProps } from "class-variance-authority"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ /**
9
+ * Slider Component
10
+ *
11
+ * Accessible, customizable slider component fully integrated with the theme system.
12
+ * Used for value ranges like volume, brightness, price ranges.
13
+ */
14
+
15
+ const sliderVariants = cva(
16
+ "relative flex w-full touch-none select-none items-center",
17
+ {
18
+ variants: {
19
+ size: {
20
+ sm: "h-5",
21
+ default: "h-6",
22
+ md: "h-8",
23
+ lg: "h-10",
24
+ },
25
+ },
26
+ defaultVariants: {
27
+ size: "default",
28
+ },
29
+ }
30
+ )
31
+
32
+ const sliderTrackVariants = cva(
33
+ "relative h-1.5 w-full grow overflow-hidden rounded-full",
34
+ {
35
+ variants: {
36
+ variant: {
37
+ default: "bg-muted",
38
+ primary: "bg-primary/20",
39
+ secondary: "bg-secondary/20",
40
+ accent: "bg-accent/20",
41
+ success: "bg-success/20",
42
+ warning: "bg-warning/20",
43
+ error: "bg-error/20",
44
+ },
45
+ },
46
+ defaultVariants: {
47
+ variant: "default",
48
+ },
49
+ }
50
+ )
51
+
52
+ const sliderRangeVariants = cva(
53
+ "absolute h-full",
54
+ {
55
+ variants: {
56
+ variant: {
57
+ default: "bg-foreground",
58
+ primary: "bg-primary",
59
+ secondary: "bg-secondary",
60
+ accent: "bg-accent",
61
+ success: "bg-success",
62
+ warning: "bg-warning",
63
+ error: "bg-error",
64
+ },
65
+ },
66
+ defaultVariants: {
67
+ variant: "primary",
68
+ },
69
+ }
70
+ )
71
+
72
+ const sliderThumbVariants = cva(
73
+ "block rounded-full border-2 ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
74
+ {
75
+ variants: {
76
+ variant: {
77
+ default: "border-foreground bg-background",
78
+ primary: "border-primary bg-background",
79
+ secondary: "border-secondary bg-background",
80
+ accent: "border-accent bg-background",
81
+ success: "border-success bg-background",
82
+ warning: "border-warning bg-background",
83
+ error: "border-error bg-background",
84
+ },
85
+ size: {
86
+ sm: "h-3 w-3",
87
+ default: "h-4 w-4",
88
+ md: "h-5 w-5",
89
+ lg: "h-6 w-6",
90
+ },
91
+ },
92
+ defaultVariants: {
93
+ variant: "primary",
94
+ size: "default",
95
+ },
96
+ }
97
+ )
98
+
99
+ // Custom type definition for component properties
100
+ type SliderBaseProps = {
101
+ /**
102
+ * Track variant
103
+ */
104
+ trackVariant?: VariantProps<typeof sliderTrackVariants>["variant"];
105
+ /**
106
+ * Range variant
107
+ */
108
+ rangeVariant?: VariantProps<typeof sliderRangeVariants>["variant"];
109
+ /**
110
+ * Thumb variant
111
+ */
112
+ thumbVariant?: VariantProps<typeof sliderThumbVariants>["variant"];
113
+ /**
114
+ * Thumb size
115
+ */
116
+ thumbSize?: VariantProps<typeof sliderThumbVariants>["size"];
117
+ /**
118
+ * Show value label
119
+ */
120
+ showValueLabel?: boolean;
121
+ /**
122
+ * Value label format function
123
+ */
124
+ valueLabelFormat?: (value: number) => string;
125
+ /**
126
+ * Value label class name
127
+ */
128
+ valueLabelClassName?: string;
129
+ /**
130
+ * Slider value
131
+ */
132
+ value?: number[];
133
+ /**
134
+ * Default value
135
+ */
136
+ defaultValue?: number[];
137
+ /**
138
+ * Function called when value changes
139
+ */
140
+ onValueChange?: (value: number[]) => void;
141
+ /**
142
+ * Minimum value
143
+ */
144
+ min?: number;
145
+ /**
146
+ * Maximum value
147
+ */
148
+ max?: number;
149
+ /**
150
+ * Step value
151
+ */
152
+ step?: number;
153
+ /**
154
+ * Slider size
155
+ */
156
+ size?: VariantProps<typeof sliderVariants>["size"];
157
+ /**
158
+ * Disabled state
159
+ */
160
+ disabled?: boolean;
161
+ }
162
+
163
+ // Merge HTML properties without defaultValue conflicts
164
+ type SliderProps = SliderBaseProps & Omit<React.HTMLAttributes<HTMLDivElement>, 'defaultValue'>
165
+
166
+ const Slider = React.forwardRef<
167
+ HTMLDivElement,
168
+ SliderProps
169
+ >(({
170
+ className,
171
+ size,
172
+ trackVariant,
173
+ rangeVariant,
174
+ thumbVariant,
175
+ thumbSize,
176
+ showValueLabel = false,
177
+ valueLabelFormat,
178
+ valueLabelClassName,
179
+ value,
180
+ defaultValue = [0],
181
+ onValueChange,
182
+ disabled,
183
+ ...props
184
+ }, ref) => {
185
+ // Value management
186
+ const [sliderValue, setSliderValue] = React.useState<number[]>(
187
+ value as number[] || defaultValue as number[] || [0]
188
+ );
189
+
190
+ React.useEffect(() => {
191
+ if (value !== undefined) {
192
+ setSliderValue(value as number[]);
193
+ }
194
+ }, [value]);
195
+
196
+ // Format value label with proper precision handling
197
+ const formatValue = (val: number) => {
198
+ if (valueLabelFormat) {
199
+ return valueLabelFormat(val);
200
+ }
201
+ // Round to avoid floating point precision issues
202
+ const roundedValue = Math.round(val * 100) / 100;
203
+ return `${roundedValue}`;
204
+ };
205
+
206
+ // Calculate percentage for thumb position
207
+ const calculateThumbPercent = (value: number, min: number, max: number) => {
208
+ return ((value - min) / (max - min)) * 100;
209
+ };
210
+
211
+ // Handle mouse/touch events
212
+ const trackRef = React.useRef<HTMLDivElement>(null);
213
+
214
+ const min = props.min || 0;
215
+ const max = props.max || 100;
216
+ const step = props.step || 1;
217
+
218
+ // Handle value change with proper precision
219
+ const handleValueChange = (newValues: number[]) => {
220
+ // Round values to avoid floating point precision issues
221
+ const roundedValues = newValues.map(val => {
222
+ if (step < 1) {
223
+ // For decimal steps, round to appropriate decimal places
224
+ const decimals = step.toString().split('.')[1]?.length || 0;
225
+ return Math.round(val * Math.pow(10, decimals)) / Math.pow(10, decimals);
226
+ }
227
+ return Math.round(val);
228
+ });
229
+
230
+ setSliderValue(roundedValues);
231
+ if (onValueChange) {
232
+ onValueChange(roundedValues);
233
+ }
234
+ };
235
+
236
+ // Handle track click
237
+ const handleTrackClick = (event: React.MouseEvent<HTMLDivElement>) => {
238
+ if (disabled) return;
239
+
240
+ const track = trackRef.current;
241
+ if (!track) return;
242
+
243
+ const rect = track.getBoundingClientRect();
244
+ const percent = (event.clientX - rect.left) / rect.width;
245
+ const rawValue = min + percent * (max - min);
246
+ const steppedValue = Math.round(rawValue / step) * step;
247
+ const boundedValue = Math.max(min, Math.min(max, steppedValue));
248
+
249
+ const newValues = [...sliderValue];
250
+ // Just update the first thumb for simplicity
251
+ newValues[0] = boundedValue;
252
+ handleValueChange(newValues);
253
+ };
254
+
255
+ const handleThumbMouseDown = (index: number) => (event: React.MouseEvent) => {
256
+ if (disabled) return;
257
+ event.preventDefault();
258
+
259
+ const handleMouseMove = (moveEvent: MouseEvent) => {
260
+ const track = trackRef.current;
261
+ if (!track) return;
262
+
263
+ const rect = track.getBoundingClientRect();
264
+ const percent = (moveEvent.clientX - rect.left) / rect.width;
265
+ const rawValue = min + percent * (max - min);
266
+ const steppedValue = Math.round(rawValue / step) * step;
267
+ const boundedValue = Math.max(min, Math.min(max, steppedValue));
268
+
269
+ const newValues = [...sliderValue];
270
+ newValues[index] = boundedValue;
271
+ handleValueChange(newValues);
272
+ };
273
+
274
+ const handleMouseUp = () => {
275
+ document.removeEventListener('mousemove', handleMouseMove);
276
+ document.removeEventListener('mouseup', handleMouseUp);
277
+ };
278
+
279
+ document.addEventListener('mousemove', handleMouseMove);
280
+ document.addEventListener('mouseup', handleMouseUp);
281
+ };
282
+
283
+ return (
284
+ <div className="w-full" ref={ref} {...props}>
285
+ {showValueLabel && (
286
+ <div className={cn(
287
+ "flex justify-end mb-1 text-sm text-muted-foreground",
288
+ valueLabelClassName
289
+ )}>
290
+ {sliderValue.length === 1 ? (
291
+ <span>{formatValue(sliderValue[0])}</span>
292
+ ) : (
293
+ <span>{formatValue(sliderValue[0])} - {formatValue(sliderValue[sliderValue.length - 1])}</span>
294
+ )}
295
+ </div>
296
+ )}
297
+
298
+ <div
299
+ className={cn(sliderVariants({ size }), className)}
300
+ data-disabled={disabled ? true : undefined}
301
+ >
302
+ {/* Track */}
303
+ <div
304
+ ref={trackRef}
305
+ className={cn(sliderTrackVariants({ variant: trackVariant || thumbVariant || "default" }))}
306
+ onClick={handleTrackClick}
307
+ >
308
+ {/* Range */}
309
+ <div
310
+ className={cn(sliderRangeVariants({ variant: rangeVariant || thumbVariant || "primary" }))}
311
+ style={{
312
+ left: '0%',
313
+ width: `${calculateThumbPercent(sliderValue[0], min, max)}%`,
314
+ }}
315
+ />
316
+ </div>
317
+
318
+ {/* Thumbs */}
319
+ {sliderValue.map((value, i) => (
320
+ <div
321
+ key={i}
322
+ className={cn(sliderThumbVariants({
323
+ variant: thumbVariant || "primary",
324
+ size: thumbSize
325
+ }))}
326
+ style={{
327
+ left: `${calculateThumbPercent(value, min, max)}%`,
328
+ transform: 'translateX(-50%)',
329
+ position: 'absolute',
330
+ top: '50%',
331
+ marginTop: thumbSize === "sm" ? '-6px' : thumbSize === "lg" ? '-12px' : '-8px',
332
+ cursor: disabled ? 'not-allowed' : 'pointer',
333
+ }}
334
+ onMouseDown={handleThumbMouseDown(i)}
335
+ aria-label={`Thumb ${i + 1}`}
336
+ tabIndex={disabled ? -1 : 0}
337
+ role="slider"
338
+ aria-valuemin={min}
339
+ aria-valuemax={max}
340
+ aria-valuenow={value}
341
+ aria-disabled={disabled}
342
+ />
343
+ ))}
344
+ </div>
345
+ </div>
346
+ )
347
+ })
348
+
349
+ Slider.displayName = "Slider"
350
+
351
+ export { Slider }
@@ -0,0 +1,119 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { motion, useMotionValue, useSpring } from "framer-motion";
5
+ import { cn } from "@/lib/utils";
6
+
7
+ export interface SpotlightCardProps extends React.HTMLAttributes<HTMLDivElement> {
8
+ children: React.ReactNode;
9
+ spotlightColor?: string;
10
+ spotlightSize?: number;
11
+ spotlightIntensity?: number;
12
+ borderRadius?: string;
13
+ }
14
+
15
+ const SpotlightCard = React.forwardRef<HTMLDivElement, SpotlightCardProps>(
16
+ (
17
+ {
18
+ children,
19
+ className,
20
+ spotlightColor = "rgba(255, 255, 255, 0.15)",
21
+ spotlightSize = 400,
22
+ spotlightIntensity = 0.5,
23
+ borderRadius = "0.5rem",
24
+ style,
25
+ ...props
26
+ },
27
+ ref
28
+ ) => {
29
+ const cardRef = React.useRef<HTMLDivElement>(null);
30
+ const mouseX = useMotionValue(0);
31
+ const mouseY = useMotionValue(0);
32
+
33
+ const springConfig = { damping: 20, stiffness: 300 };
34
+ const spotlightX = useSpring(mouseX, springConfig);
35
+ const spotlightY = useSpring(mouseY, springConfig);
36
+
37
+ const handleMouseMove = React.useCallback(
38
+ (e: React.MouseEvent<HTMLDivElement>) => {
39
+ if (!cardRef.current) return;
40
+
41
+ const rect = cardRef.current.getBoundingClientRect();
42
+ const x = e.clientX - rect.left;
43
+ const y = e.clientY - rect.top;
44
+
45
+ mouseX.set(x);
46
+ mouseY.set(y);
47
+ },
48
+ [mouseX, mouseY]
49
+ );
50
+
51
+ const handleMouseLeave = React.useCallback(() => {
52
+ // Optionally fade out the spotlight on mouse leave
53
+ // For now, we'll keep it at the last position
54
+ }, []);
55
+
56
+ return (
57
+ <motion.div
58
+ ref={(el) => {
59
+ cardRef.current = el;
60
+ if (ref) {
61
+ if (typeof ref === "function") ref(el);
62
+ else ref.current = el;
63
+ }
64
+ }}
65
+ className={cn(
66
+ "relative overflow-hidden bg-card text-card-foreground",
67
+ className
68
+ )}
69
+ style={{
70
+ borderRadius,
71
+ ...style,
72
+ }}
73
+ onMouseMove={handleMouseMove}
74
+ onMouseLeave={handleMouseLeave}
75
+ whileHover={{ scale: 1.02 }}
76
+ transition={{ type: "spring", stiffness: 400, damping: 25 }}
77
+ {...props}
78
+ >
79
+ {/* Spotlight overlay */}
80
+ <motion.div
81
+ className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-300 hover:opacity-100"
82
+ style={{
83
+ background: `radial-gradient(${spotlightSize}px circle at var(--x) var(--y), ${spotlightColor}, transparent ${spotlightIntensity * 100}%)`,
84
+ "--x": spotlightX,
85
+ "--y": spotlightY,
86
+ } as React.CSSProperties}
87
+ />
88
+
89
+ {/* Border glow effect */}
90
+ <motion.div
91
+ className="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-300 hover:opacity-100"
92
+ style={{
93
+ background: `radial-gradient(${spotlightSize * 1.5}px circle at var(--x) var(--y), ${spotlightColor}, transparent 40%)`,
94
+ "--x": spotlightX,
95
+ "--y": spotlightY,
96
+ filter: "blur(40px)",
97
+ } as React.CSSProperties}
98
+ />
99
+
100
+ {/* Card border */}
101
+ <div
102
+ className="absolute inset-0 rounded-[inherit] border border-border/50"
103
+ style={{
104
+ background: `radial-gradient(${spotlightSize * 2}px circle at var(--x) var(--y), rgba(255,255,255,0.1), transparent 40%)`,
105
+ "--x": spotlightX,
106
+ "--y": spotlightY,
107
+ } as React.CSSProperties}
108
+ />
109
+
110
+ {/* Content */}
111
+ <div className="relative z-10">{children}</div>
112
+ </motion.div>
113
+ );
114
+ }
115
+ );
116
+
117
+ SpotlightCard.displayName = "SpotlightCard";
118
+
119
+ export { SpotlightCard };
@@ -0,0 +1,83 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SwitchPrimitives from "@radix-ui/react-switch"
5
+
6
+ import { cn } from "../../lib/utils"
7
+
8
+ type SwitchSize = "sm" | "md" | "lg";
9
+ type SwitchVariant = "primary" | "success" | "warning" | "danger" | "secondary";
10
+
11
+ export interface SwitchProps extends React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root> {
12
+ /** Switch boyutu */
13
+ size?: SwitchSize;
14
+ /** Switch renk varyantı */
15
+ variant?: SwitchVariant;
16
+ /** Yükleniyor durumunu gösterir */
17
+ loading?: boolean;
18
+ /** Sol tarafta gösterilecek ikon */
19
+ leftIcon?: React.ReactNode;
20
+ /** Sağ tarafta gösterilecek ikon */
21
+ rightIcon?: React.ReactNode;
22
+ /** Switch açıklaması */
23
+ description?: string;
24
+ }
25
+
26
+ const Switch = React.forwardRef<
27
+ React.ElementRef<typeof SwitchPrimitives.Root>,
28
+ SwitchProps
29
+ >(({ className, size = "md", variant = "primary", loading, leftIcon, rightIcon, description, ...props }, ref) => (
30
+ <div className="inline-flex items-center gap-2">
31
+ {leftIcon && <span className="text-muted-foreground">{leftIcon}</span>}
32
+ <SwitchPrimitives.Root
33
+ className={cn(
34
+ "peer relative inline-flex shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 dark:data-[state=unchecked]:bg-gray-700/70 dark:focus-visible:ring-primary/40 dark:focus-visible:ring-offset-gray-950",
35
+
36
+ // Boyutlar
37
+ size === "sm" && "h-4 w-8",
38
+ size === "md" && "h-6 w-11",
39
+ size === "lg" && "h-7 w-14",
40
+
41
+ // Varyantlar (checked durumunda)
42
+ variant === "primary" && "data-[state=checked]:bg-primary",
43
+ variant === "success" && "data-[state=checked]:bg-success",
44
+ variant === "warning" && "data-[state=checked]:bg-warning",
45
+ variant === "danger" && "data-[state=checked]:bg-error",
46
+ variant === "secondary" && "data-[state=checked]:bg-accent",
47
+
48
+ // Unchecked durumu
49
+ "data-[state=unchecked]:bg-input",
50
+
51
+ // Loading durumu
52
+ loading && "opacity-80 cursor-wait",
53
+
54
+ className
55
+ )}
56
+ disabled={loading || props.disabled}
57
+ {...props}
58
+ ref={ref}
59
+ >
60
+ {loading ? (
61
+ <div className="absolute inset-0 flex items-center justify-center">
62
+ <div className="h-3 w-3 animate-spin rounded-full border-2 border-gray-300 border-t-primary"></div>
63
+ </div>
64
+ ) : null}
65
+
66
+ <SwitchPrimitives.Thumb
67
+ className={cn(
68
+ "pointer-events-none block rounded-full bg-background shadow-lg ring-0 transition-transform",
69
+ // Boyuta göre thumb boyutları
70
+ size === "sm" && "h-3 w-3 data-[state=checked]:translate-x-4 data-[state=unchecked]:translate-x-0",
71
+ size === "md" && "h-5 w-5 data-[state=checked]:translate-x-5 data-[state=unchecked]:translate-x-0",
72
+ size === "lg" && "h-6 w-6 data-[state=checked]:translate-x-7 data-[state=unchecked]:translate-x-0",
73
+ )}
74
+ />
75
+ </SwitchPrimitives.Root>
76
+ {rightIcon && <span className="text-muted-foreground">{rightIcon}</span>}
77
+ {description && <span className="text-sm text-muted-foreground">{description}</span>}
78
+ </div>
79
+ ))
80
+ Switch.displayName = SwitchPrimitives.Root.displayName
81
+
82
+ export { Switch }
83
+ export type { SwitchSize, SwitchVariant }