@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,129 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import { motion, useMotionValue, useTransform, useSpring } from "framer-motion";
5
+ import { cn } from "@/lib/utils";
6
+ import { cva, type VariantProps } from "class-variance-authority";
7
+
8
+ const magneticButtonVariants = cva(
9
+ "relative inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium 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",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
14
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
15
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
16
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
17
+ ghost: "hover:bg-accent hover:text-accent-foreground",
18
+ link: "text-primary underline-offset-4 hover:underline",
19
+ },
20
+ size: {
21
+ default: "h-10 px-4 py-2",
22
+ sm: "h-9 rounded-md px-3",
23
+ lg: "h-11 rounded-md px-8",
24
+ icon: "h-10 w-10",
25
+ },
26
+ },
27
+ defaultVariants: {
28
+ variant: "default",
29
+ size: "default",
30
+ },
31
+ }
32
+ );
33
+
34
+ export interface MagneticButtonProps
35
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
36
+ VariantProps<typeof magneticButtonVariants> {
37
+ magnetStrength?: number;
38
+ magnetDistance?: number;
39
+ }
40
+
41
+ const MagneticButton = React.forwardRef<HTMLButtonElement, MagneticButtonProps>(
42
+ (
43
+ {
44
+ className,
45
+ variant,
46
+ size,
47
+ magnetStrength = 0.25,
48
+ magnetDistance = 100,
49
+ children,
50
+ onMouseMove,
51
+ onMouseLeave,
52
+ ...props
53
+ },
54
+ ref
55
+ ) => {
56
+ const buttonRef = React.useRef<HTMLDivElement>(null);
57
+ const mouseX = useMotionValue(0);
58
+ const mouseY = useMotionValue(0);
59
+
60
+ const springConfig = { damping: 15, stiffness: 150 };
61
+ const x = useSpring(useTransform(mouseX, (value) => value * magnetStrength), springConfig);
62
+ const y = useSpring(useTransform(mouseY, (value) => value * magnetStrength), springConfig);
63
+
64
+ const handleMouseMove = React.useCallback(
65
+ (e: React.MouseEvent<HTMLButtonElement>) => {
66
+ if (!buttonRef.current) return;
67
+
68
+ const rect = buttonRef.current.getBoundingClientRect();
69
+ const centerX = rect.left + rect.width / 2;
70
+ const centerY = rect.top + rect.height / 2;
71
+
72
+ const distanceX = e.clientX - centerX;
73
+ const distanceY = e.clientY - centerY;
74
+
75
+ const distance = Math.sqrt(distanceX * distanceX + distanceY * distanceY);
76
+
77
+ if (distance < magnetDistance) {
78
+ mouseX.set(distanceX);
79
+ mouseY.set(distanceY);
80
+ } else {
81
+ mouseX.set(0);
82
+ mouseY.set(0);
83
+ }
84
+
85
+ onMouseMove?.(e);
86
+ },
87
+ [magnetDistance, mouseX, mouseY, onMouseMove]
88
+ );
89
+
90
+ const handleMouseLeave = React.useCallback(
91
+ (e: React.MouseEvent<HTMLButtonElement>) => {
92
+ mouseX.set(0);
93
+ mouseY.set(0);
94
+ onMouseLeave?.(e);
95
+ },
96
+ [mouseX, mouseY, onMouseLeave]
97
+ );
98
+
99
+ return (
100
+ <motion.div
101
+ ref={buttonRef}
102
+ style={{ x, y }}
103
+ className="inline-block"
104
+ >
105
+ <motion.button
106
+ ref={ref}
107
+ className={cn(magneticButtonVariants({ variant, size, className }))}
108
+ onMouseMove={handleMouseMove}
109
+ onMouseLeave={handleMouseLeave}
110
+ whileHover={{ scale: 1.05 }}
111
+ whileTap={{ scale: 0.95 }}
112
+ transition={{ type: "spring", stiffness: 400, damping: 17 }}
113
+ {...props}
114
+ >
115
+ <motion.span
116
+ style={{ x: useTransform(x, (value) => value * 0.2), y: useTransform(y, (value) => value * 0.2) }}
117
+ className="relative z-10"
118
+ >
119
+ {children}
120
+ </motion.span>
121
+ </motion.button>
122
+ </motion.div>
123
+ );
124
+ }
125
+ );
126
+
127
+ MagneticButton.displayName = "MagneticButton";
128
+
129
+ export { MagneticButton, magneticButtonVariants };
@@ -0,0 +1,183 @@
1
+ "use client";
2
+
3
+ import * as React from "react";
4
+ import * as PopoverPrimitive from "@radix-ui/react-popover";
5
+ import { cva, type VariantProps } from "class-variance-authority";
6
+
7
+ import { cn } from "../../lib/utils";
8
+
9
+ /**
10
+ * Popover Bileşeni
11
+ *
12
+ * Tıklanabilir bir öğeden açılan, tema sistemiyle tam entegre bir içerik bileşeni.
13
+ * Filtreler, ayarlar ve diğer içerikler için kullanışlı ve erişilebilir bir arayüz sağlar.
14
+ */
15
+
16
+ const popoverContentVariants = cva(
17
+ "z-50 w-72 rounded-md border border-border bg-background p-4 shadow-md outline-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
18
+ {
19
+ variants: {
20
+ variant: {
21
+ default: "",
22
+ destructive: "border-destructive bg-destructive/5",
23
+ outline: "border-border bg-transparent",
24
+ subtle: "border-transparent bg-muted/50",
25
+ },
26
+ size: {
27
+ sm: "w-48 p-3",
28
+ default: "w-72 p-4",
29
+ lg: "w-96 p-5",
30
+ },
31
+ side: {
32
+ top: "data-[side=top]:slide-in-from-bottom-2",
33
+ right: "data-[side=right]:slide-in-from-left-2",
34
+ bottom: "data-[side=bottom]:slide-in-from-top-2",
35
+ left: "data-[side=left]:slide-in-from-right-2",
36
+ },
37
+ position: {
38
+ pointerEventsNone: "pointer-events-none",
39
+ default: "",
40
+ },
41
+ radius: {
42
+ none: "rounded-none",
43
+ sm: "rounded-sm",
44
+ default: "rounded-md",
45
+ lg: "rounded-lg",
46
+ full: "rounded-full",
47
+ },
48
+ shadow: {
49
+ none: "shadow-none",
50
+ sm: "shadow-sm",
51
+ default: "shadow-md",
52
+ md: "shadow-md",
53
+ lg: "shadow-lg",
54
+ xl: "shadow-xl",
55
+ },
56
+ },
57
+ defaultVariants: {
58
+ variant: "default",
59
+ size: "default",
60
+ radius: "default",
61
+ shadow: "default",
62
+ },
63
+ }
64
+ );
65
+
66
+ const Popover = PopoverPrimitive.Root;
67
+ const PopoverTrigger = PopoverPrimitive.Trigger;
68
+ const PopoverAnchor = PopoverPrimitive.Anchor;
69
+
70
+ export interface PopoverContentProps
71
+ extends Omit<React.ComponentPropsWithoutRef<typeof PopoverPrimitive.Content>, 'side'>,
72
+ VariantProps<typeof popoverContentVariants> {
73
+ /**
74
+ * Popover'ın arka plan bulanıklığı etkin mi
75
+ */
76
+ backdrop?: boolean;
77
+ /**
78
+ * Popover'a tıklandığında kapanıp kapanmayacağı
79
+ */
80
+ closeOnInteractOutside?: boolean;
81
+ /**
82
+ * Popover açıkken arka planın karartılması
83
+ */
84
+ overlayBackdrop?: boolean;
85
+ }
86
+
87
+ const PopoverContent = React.forwardRef<
88
+ React.ElementRef<typeof PopoverPrimitive.Content>,
89
+ PopoverContentProps
90
+ >(({
91
+ className,
92
+ variant,
93
+ size,
94
+ side,
95
+ position,
96
+ radius,
97
+ shadow,
98
+ backdrop = false,
99
+ closeOnInteractOutside = true,
100
+ overlayBackdrop = false,
101
+ sideOffset = 4,
102
+ ...props
103
+ }, ref) => (
104
+ <>
105
+ {overlayBackdrop && (
106
+ <div className="fixed inset-0 z-40 bg-overlay opacity-30" />
107
+ )}
108
+ <PopoverPrimitive.Content
109
+ ref={ref}
110
+ sideOffset={sideOffset}
111
+ collisionPadding={8}
112
+ onInteractOutside={(e) => {
113
+ if (!closeOnInteractOutside) {
114
+ e.preventDefault();
115
+ }
116
+ }}
117
+ className={cn(
118
+ popoverContentVariants({
119
+ variant,
120
+ size,
121
+ side,
122
+ position,
123
+ radius,
124
+ shadow
125
+ }),
126
+ backdrop && "backdrop-blur-md bg-opacity-80",
127
+ className
128
+ )}
129
+ {...props}
130
+ />
131
+ </>
132
+ ));
133
+ PopoverContent.displayName = PopoverPrimitive.Content.displayName;
134
+
135
+ /**
136
+ * PopoverClose bileşeni
137
+ */
138
+ const PopoverClose = PopoverPrimitive.Close;
139
+
140
+ /**
141
+ * Popover için bölüm ayırıcı
142
+ */
143
+ const PopoverSeparator = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
144
+ <div
145
+ className={cn("my-2 h-px bg-border", className)}
146
+ {...props}
147
+ />
148
+ );
149
+ PopoverSeparator.displayName = "PopoverSeparator";
150
+
151
+ /**
152
+ * Popover başlık bileşeni
153
+ */
154
+ const PopoverHeader = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
155
+ <div
156
+ className={cn("-mx-4 -mt-4 mb-3 px-4 pt-4 pb-3 border-b border-border", className)}
157
+ {...props}
158
+ />
159
+ );
160
+ PopoverHeader.displayName = "PopoverHeader";
161
+
162
+ /**
163
+ * Popover footer bileşeni
164
+ */
165
+ const PopoverFooter = ({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) => (
166
+ <div
167
+ className={cn("-mx-4 -mb-4 mt-3 px-4 pt-3 pb-4 border-t border-border", className)}
168
+ {...props}
169
+ />
170
+ );
171
+ PopoverFooter.displayName = "PopoverFooter";
172
+
173
+ export {
174
+ Popover,
175
+ PopoverTrigger,
176
+ PopoverContent,
177
+ PopoverAnchor,
178
+ PopoverClose,
179
+ PopoverSeparator,
180
+ PopoverHeader,
181
+ PopoverFooter,
182
+ popoverContentVariants,
183
+ };
@@ -0,0 +1,273 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import * as SelectPrimitive from "@radix-ui/react-select"
5
+ import { Check, ChevronDown, ChevronUp, Loader2 } from "lucide-react"
6
+
7
+ import { cn } from "../../lib/utils"
8
+
9
+ /**
10
+ * Premium Select Component
11
+ *
12
+ * Advanced dropdown/select component with variants, sizes and accessibility features.
13
+ * Compatible with dark and light mode, providing a modern user experience with smooth animations.
14
+ */
15
+
16
+ // Directly re-exporting the Root component
17
+ const Select = SelectPrimitive.Root
18
+ Select.displayName = "Select"
19
+
20
+ const SelectGroup = SelectPrimitive.Group
21
+
22
+ const SelectValue = SelectPrimitive.Value
23
+
24
+ type SelectTriggerVariant = "standard" | "outline" | "ghost" | "underline";
25
+ type SelectTriggerSize = "sm" | "md" | "lg";
26
+
27
+ interface SelectTriggerProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger> {
28
+ /** Visual variant */
29
+ variant?: SelectTriggerVariant;
30
+ /** Size */
31
+ size?: SelectTriggerSize;
32
+ /** Error state */
33
+ error?: boolean | string;
34
+ /** Success state */
35
+ success?: boolean;
36
+ /** Loading state */
37
+ loading?: boolean;
38
+ /** Icon displayed on the left */
39
+ leftIcon?: React.ReactNode;
40
+ /** Icon displayed on the right (instead of default chevron) */
41
+ rightIcon?: React.ReactNode;
42
+ }
43
+
44
+ const SelectTrigger = React.forwardRef<
45
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
46
+ SelectTriggerProps
47
+ >(({ className, children, variant = "standard", size = "md", error, success, loading, leftIcon, rightIcon, ...props }, ref) => (
48
+ <SelectPrimitive.Trigger
49
+ ref={ref}
50
+ className={cn(
51
+ /* Base styles */
52
+ "flex w-full items-center justify-between gap-1 rounded-md transition-all duration-200",
53
+ "disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
54
+ "focus-visible:outline-none",
55
+ /* Error state */
56
+ error && "border-error focus-visible:ring-error/30 focus-visible:border-error",
57
+ /* Success state */
58
+ success && "border-success focus-visible:ring-success/30 focus-visible:border-success",
59
+ /* Size variants */
60
+ size === "sm" && "h-8 text-xs px-2",
61
+ size === "md" && "h-10 text-sm px-3",
62
+ size === "lg" && "h-12 text-base px-4",
63
+ /* Visual variants */
64
+ variant === "standard" && "border border-gray-300 dark:border-gray-700 bg-background dark:bg-gray-900 hover:border-gray-400 dark:hover:border-gray-600 focus-visible:ring-2 focus-visible:ring-primary/30 dark:focus-visible:ring-primary/50 focus-visible:border-primary dark:focus-visible:border-primary",
65
+ variant === "outline" && "border border-gray-300 dark:border-gray-700 bg-transparent hover:border-gray-400 dark:hover:border-gray-600 focus-visible:ring-2 focus-visible:ring-primary/30 dark:focus-visible:ring-primary/50 focus-visible:border-primary dark:focus-visible:border-primary",
66
+ variant === "ghost" && "border-none bg-gray-100 dark:bg-gray-800 hover:bg-gray-200 dark:hover:bg-gray-700 focus-visible:ring-2 focus-visible:ring-primary/30 dark:focus-visible:ring-primary/50",
67
+ variant === "underline" && "border-t-0 border-l-0 border-r-0 border-b border-gray-300 dark:border-gray-600 rounded-none px-0 hover:border-gray-400 dark:hover:border-gray-500 focus-visible:ring-0 focus-visible:border-b-2 focus-visible:border-primary dark:focus-visible:border-primary",
68
+ className
69
+ )}
70
+ {...props}
71
+ disabled={props.disabled || loading}
72
+ data-error={error ? true : undefined}
73
+ data-success={success ? true : undefined}
74
+ data-loading={loading ? true : undefined}
75
+ >
76
+ <div className="flex items-center flex-1 gap-2">
77
+ {leftIcon && (
78
+ <span className="flex items-center justify-center text-gray-500 dark:text-gray-400">
79
+ {leftIcon}
80
+ </span>
81
+ )}
82
+ {loading ? (
83
+ <div className="flex items-center gap-2">
84
+ <Loader2 className="h-3.5 w-3.5 animate-spin text-muted-foreground dark:text-gray-400" />
85
+ <span className="text-muted-foreground dark:text-gray-400">Loading...</span>
86
+ </div>
87
+ ) : children}
88
+ </div>
89
+ {!loading && (
90
+ <SelectPrimitive.Icon asChild>
91
+ {rightIcon ? (
92
+ <span className="opacity-60">{rightIcon}</span>
93
+ ) : (
94
+ <ChevronDown className="h-4 w-4 opacity-60 transition-transform duration-200 ease-out group-data-[state=open]:rotate-180" />
95
+ )}
96
+ </SelectPrimitive.Icon>
97
+ )}
98
+ </SelectPrimitive.Trigger>
99
+ ))
100
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName
101
+
102
+ /**
103
+ * Scroll Up Button Component
104
+ */
105
+ const SelectScrollUpButton = React.forwardRef<
106
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
107
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
108
+ >(({ className, ...props }, ref) => (
109
+ <SelectPrimitive.ScrollUpButton
110
+ ref={ref}
111
+ className={cn(
112
+ "flex cursor-default items-center justify-center py-1 text-muted-foreground hover:text-foreground transition-colors",
113
+ className
114
+ )}
115
+ {...props}
116
+ >
117
+ <ChevronUp className="h-4 w-4" />
118
+ </SelectPrimitive.ScrollUpButton>
119
+ ))
120
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName
121
+
122
+ /**
123
+ * Scroll Down Button Component
124
+ */
125
+ const SelectScrollDownButton = React.forwardRef<
126
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
127
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
128
+ >(({ className, ...props }, ref) => (
129
+ <SelectPrimitive.ScrollDownButton
130
+ ref={ref}
131
+ className={cn(
132
+ "flex cursor-default items-center justify-center py-1 text-muted-foreground hover:text-foreground transition-colors",
133
+ className
134
+ )}
135
+ {...props}
136
+ >
137
+ <ChevronDown className="h-4 w-4" />
138
+ </SelectPrimitive.ScrollDownButton>
139
+ ))
140
+ SelectScrollDownButton.displayName =
141
+ SelectPrimitive.ScrollDownButton.displayName
142
+
143
+ const SelectContent = React.forwardRef<
144
+ React.ElementRef<typeof SelectPrimitive.Content>,
145
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
146
+ >(({ className, children, position = "item-aligned", ...props }, ref) => (
147
+ <SelectPrimitive.Portal>
148
+ <SelectPrimitive.Content
149
+ ref={ref}
150
+ className={cn(
151
+ ["relative z-50 max-h-96 min-w-[8rem] overflow-hidden",
152
+ "rounded-md border border-gray-200 dark:border-gray-700",
153
+ "bg-white dark:bg-gray-900 text-foreground",
154
+ "shadow-lg dark:shadow-gray-900/20",
155
+ "data-[state=open]:animate-in data-[state=closed]:animate-out",
156
+ "data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
157
+ "data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95",
158
+ "data-[state=open]:duration-150"],
159
+ position === "popper" &&
160
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
161
+ className
162
+ )}
163
+ position={position}
164
+ {...props}
165
+ >
166
+ <SelectScrollUpButton />
167
+ <SelectPrimitive.Viewport
168
+ className={cn(
169
+ "p-1",
170
+ position === "popper" &&
171
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
172
+ )}
173
+ >
174
+ {children}
175
+ </SelectPrimitive.Viewport>
176
+ <SelectScrollDownButton />
177
+ </SelectPrimitive.Content>
178
+ </SelectPrimitive.Portal>
179
+ ))
180
+ SelectContent.displayName = SelectPrimitive.Content.displayName
181
+
182
+ const SelectLabel = React.forwardRef<
183
+ React.ElementRef<typeof SelectPrimitive.Label>,
184
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
185
+ >(({ className, ...props }, ref) => (
186
+ <SelectPrimitive.Label
187
+ ref={ref}
188
+ className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
189
+ {...props}
190
+ />
191
+ ))
192
+ SelectLabel.displayName = SelectPrimitive.Label.displayName
193
+
194
+ type SelectItemVariant = "default" | "subtle" | "destructive" | "success" | "warning";
195
+ type SelectItemSize = "sm" | "md" | "lg";
196
+
197
+ interface SelectItemProps extends React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item> {
198
+ /** Visual variant */
199
+ variant?: SelectItemVariant;
200
+ /** Size */
201
+ size?: SelectItemSize;
202
+ /** Icon displayed on the right */
203
+ rightIcon?: React.ReactNode;
204
+ /** Custom indicator icon (instead of default check) */
205
+ customIndicator?: React.ReactNode;
206
+ }
207
+
208
+ const SelectItem = React.forwardRef<
209
+ React.ElementRef<typeof SelectPrimitive.Item>,
210
+ SelectItemProps
211
+ >(({ className, children, variant = "default", size = "md", rightIcon, customIndicator, ...props }, ref) => (
212
+ <SelectPrimitive.Item
213
+ ref={ref}
214
+ className={cn(
215
+ "relative flex w-full cursor-default select-none items-center rounded-sm outline-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
216
+
217
+ /* Size variants */
218
+ size === "sm" && "py-1 pl-7 pr-2 text-xs",
219
+ size === "md" && "py-1.5 pl-8 pr-2 text-sm",
220
+ size === "lg" && "py-2 pl-9 pr-3 text-base",
221
+
222
+ /* Color variants */
223
+ variant === "default" && "focus:bg-accent focus:text-accent-foreground dark:focus:bg-gray-700 dark:focus:text-gray-100",
224
+ variant === "subtle" && "focus:bg-gray-100 dark:focus:bg-gray-800 focus:text-foreground dark:focus:text-gray-200",
225
+ variant === "destructive" && "text-error dark:text-red-400 focus:bg-error/10 dark:focus:bg-red-900/20 focus:text-error dark:focus:text-red-300",
226
+ variant === "success" && "text-success dark:text-green-400 focus:bg-success/10 dark:focus:bg-green-900/20 focus:text-success dark:focus:text-green-300",
227
+ variant === "warning" && "text-warning dark:text-yellow-400 focus:bg-warning/10 dark:focus:bg-yellow-900/20 focus:text-warning dark:focus:text-yellow-300",
228
+
229
+ className
230
+ )}
231
+ {...props}
232
+ >
233
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
234
+ <SelectPrimitive.ItemIndicator>
235
+ {customIndicator || <Check className="h-4 w-4" />}
236
+ </SelectPrimitive.ItemIndicator>
237
+ </span>
238
+
239
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
240
+
241
+ {rightIcon && (
242
+ <span className="ml-auto pl-2 opacity-70">
243
+ {rightIcon}
244
+ </span>
245
+ )}
246
+ </SelectPrimitive.Item>
247
+ ))
248
+ SelectItem.displayName = SelectPrimitive.Item.displayName
249
+
250
+ const SelectSeparator = React.forwardRef<
251
+ React.ElementRef<typeof SelectPrimitive.Separator>,
252
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
253
+ >(({ className, ...props }, ref) => (
254
+ <SelectPrimitive.Separator
255
+ ref={ref}
256
+ className={cn("-mx-1 my-1 h-px bg-muted dark:bg-gray-700", className)}
257
+ {...props}
258
+ />
259
+ ))
260
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName
261
+
262
+ export {
263
+ Select,
264
+ SelectGroup,
265
+ SelectValue,
266
+ SelectTrigger,
267
+ SelectContent,
268
+ SelectLabel,
269
+ SelectItem,
270
+ SelectSeparator,
271
+ SelectScrollUpButton,
272
+ SelectScrollDownButton,
273
+ }