@moontra/moonui 2.0.7 → 2.0.9

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 (94) hide show
  1. package/package.json +26 -19
  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/ui/__tests__/accordion.test.tsx +571 -0
  6. package/src/components/ui/__tests__/alert.test.tsx +484 -0
  7. package/src/components/ui/__tests__/aspect-ratio.test.tsx +492 -0
  8. package/src/components/ui/__tests__/avatar.test.tsx +382 -0
  9. package/src/components/ui/__tests__/badge.test.tsx +364 -0
  10. package/src/components/ui/__tests__/breadcrumb.test.tsx +687 -0
  11. package/src/components/ui/__tests__/button.test.tsx +91 -0
  12. package/src/components/ui/__tests__/card.test.tsx +104 -0
  13. package/src/components/ui/__tests__/checkbox.test.tsx +591 -0
  14. package/src/components/ui/__tests__/collapsible.test.tsx +592 -0
  15. package/src/components/ui/__tests__/color-picker.test.tsx +365 -0
  16. package/src/components/ui/__tests__/command.test.tsx +677 -0
  17. package/src/components/ui/__tests__/data-table.test.tsx +256 -0
  18. package/src/components/ui/__tests__/date-picker.test.tsx +320 -0
  19. package/src/components/ui/__tests__/dialog.test.tsx +764 -0
  20. package/src/components/ui/__tests__/input.test.tsx +318 -0
  21. package/src/components/ui/__tests__/label.test.tsx +103 -0
  22. package/src/components/ui/__tests__/popover.test.tsx +637 -0
  23. package/src/components/ui/__tests__/progress.test.tsx +382 -0
  24. package/src/components/ui/__tests__/radio-group.test.tsx +555 -0
  25. package/src/components/ui/__tests__/select.test.tsx +705 -0
  26. package/src/components/ui/__tests__/skeleton.test.tsx +253 -0
  27. package/src/components/ui/__tests__/slider.test.tsx +493 -0
  28. package/src/components/ui/__tests__/switch.test.tsx +372 -0
  29. package/src/components/ui/__tests__/table.test.tsx +824 -0
  30. package/src/components/ui/__tests__/tabs.test.tsx +887 -0
  31. package/src/components/ui/__tests__/toast.test.tsx +458 -0
  32. package/src/components/ui/__tests__/tooltip.test.tsx +583 -0
  33. package/src/components/ui/accordion.tsx +63 -0
  34. package/src/components/ui/alert.tsx +130 -0
  35. package/src/components/ui/aspect-ratio.tsx +72 -0
  36. package/src/components/ui/avatar.tsx +135 -0
  37. package/src/components/ui/badge.tsx +225 -0
  38. package/src/components/ui/breadcrumb.tsx +207 -0
  39. package/src/components/ui/button.stories.tsx +162 -0
  40. package/src/components/ui/button.tsx +221 -0
  41. package/src/components/ui/calendar.tsx +262 -0
  42. package/src/components/ui/card.stories.tsx +208 -0
  43. package/src/components/ui/card.tsx +141 -0
  44. package/src/components/ui/checkbox.tsx +256 -0
  45. package/src/components/ui/collapsible.tsx +129 -0
  46. package/src/components/ui/color-picker.tsx +664 -0
  47. package/src/components/ui/command.tsx +218 -0
  48. package/src/components/ui/data-table.stories.tsx +509 -0
  49. package/src/components/ui/data-table.tsx +623 -0
  50. package/src/components/ui/date-picker.stories.tsx +321 -0
  51. package/src/components/ui/date-picker.tsx +603 -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/file-upload.tsx +400 -0
  55. package/src/components/ui/github-stars.tsx +201 -0
  56. package/src/components/ui/index.ts +12 -0
  57. package/src/components/ui/input.stories.tsx +255 -0
  58. package/src/components/ui/input.tsx +219 -0
  59. package/src/components/ui/label.tsx +26 -0
  60. package/src/components/ui/locked-component.tsx +215 -0
  61. package/src/components/ui/moon-logo.tsx +97 -0
  62. package/src/components/ui/pagination.tsx +116 -0
  63. package/src/components/ui/popover.tsx +183 -0
  64. package/src/components/ui/progress.tsx +182 -0
  65. package/src/components/ui/radio-group.tsx +243 -0
  66. package/src/components/ui/select.tsx +273 -0
  67. package/src/components/ui/separator.tsx +140 -0
  68. package/src/components/ui/simple-editor.tsx +652 -0
  69. package/src/components/ui/skeleton.tsx +351 -0
  70. package/src/components/ui/slider.tsx +351 -0
  71. package/src/components/ui/switch.tsx +83 -0
  72. package/src/components/ui/table.tsx +322 -0
  73. package/src/components/ui/tabs.tsx +195 -0
  74. package/src/components/ui/textarea.tsx +46 -0
  75. package/src/components/ui/toast.tsx +313 -0
  76. package/src/components/ui/toggle.tsx +47 -0
  77. package/src/components/ui/tooltip.tsx +152 -0
  78. package/src/docs/theme-system.md +1194 -0
  79. package/src/index.tsx +39 -0
  80. package/src/lib/micro-interactions.ts +255 -0
  81. package/src/lib/theme.tsx +398 -0
  82. package/src/lib/utils.ts +69 -0
  83. package/src/styles/design-system.css +365 -0
  84. package/src/styles/index.css +4 -0
  85. package/src/styles/tailwind.css +6 -0
  86. package/src/styles/tokens.css +453 -0
  87. package/src/use-intersection-observer.tsx +154 -0
  88. package/src/use-local-storage.tsx +71 -0
  89. package/src/use-paddle.ts +138 -0
  90. package/src/use-performance-optimizer.ts +379 -0
  91. package/src/use-pro-access.ts +141 -0
  92. package/src/use-scroll-animation.ts +221 -0
  93. package/src/use-subscription.ts +37 -0
  94. package/src/use-toast.ts +32 -0
@@ -0,0 +1,207 @@
1
+ import * as React from "react";
2
+ import { ChevronRight, MoreHorizontal } from "lucide-react";
3
+ import { cva, type VariantProps } from "class-variance-authority";
4
+
5
+ import { cn } from "../../lib/utils";
6
+
7
+ const breadcrumbVariants = cva(
8
+ "flex items-center gap-1.5 text-sm",
9
+ {
10
+ variants: {
11
+ variant: {
12
+ default: "dark:text-gray-100 transition-colors",
13
+ muted: "text-muted-foreground dark:text-gray-400 transition-colors",
14
+ ghost: "text-foreground/60 dark:text-gray-300/60 transition-colors",
15
+ },
16
+ size: {
17
+ default: "text-sm",
18
+ sm: "text-xs",
19
+ lg: "text-base",
20
+ },
21
+ },
22
+ defaultVariants: {
23
+ variant: "default",
24
+ size: "default",
25
+ },
26
+ }
27
+ );
28
+
29
+ interface BreadcrumbProps
30
+ extends React.HTMLAttributes<HTMLElement>,
31
+ VariantProps<typeof breadcrumbVariants> {
32
+ separator?: React.ReactNode;
33
+ showHomeIcon?: boolean;
34
+ }
35
+
36
+ interface BreadcrumbListProps extends React.HTMLAttributes<HTMLOListElement> {
37
+ collapsed?: boolean;
38
+ collapsedWidth?: number;
39
+ }
40
+
41
+ interface BreadcrumbItemProps extends React.HTMLAttributes<HTMLLIElement> {
42
+ isCurrent?: boolean;
43
+ href?: string;
44
+ asChild?: boolean;
45
+ }
46
+
47
+ interface BreadcrumbSeparatorProps extends React.HTMLAttributes<HTMLSpanElement> {
48
+ /** Custom separator icon or text */
49
+ icon?: React.ReactNode;
50
+ }
51
+
52
+ interface BreadcrumbEllipsisProps extends React.HTMLAttributes<HTMLSpanElement> {
53
+ /** Custom ellipsis icon */
54
+ icon?: React.ReactNode;
55
+ }
56
+
57
+ const Breadcrumb = React.forwardRef<HTMLElement, BreadcrumbProps>(
58
+ ({ className, variant, size, ...props }, ref) => (
59
+ <nav
60
+ ref={ref}
61
+ className={cn(breadcrumbVariants({ variant, size }), className)}
62
+ aria-label="breadcrumb"
63
+ {...props}
64
+ />
65
+ )
66
+ );
67
+ Breadcrumb.displayName = "Breadcrumb";
68
+
69
+ const BreadcrumbList = React.forwardRef<HTMLOListElement, BreadcrumbListProps>(
70
+ ({ className, collapsed, collapsedWidth = 3, ...props }, ref) => {
71
+ const childrenArray = React.Children.toArray(props.children).filter(Boolean);
72
+ const childCount = childrenArray.length;
73
+
74
+ if (collapsed && childCount > collapsedWidth) {
75
+ const firstItem = childrenArray[0];
76
+ // const middleItems = childrenArray.slice(1, -2); // Uncomment when needed
77
+ const lastTwoItems = childrenArray.slice(-2);
78
+
79
+ return (
80
+ <ol
81
+ ref={ref}
82
+ className={cn(
83
+ "flex flex-wrap items-center gap-1.5 sm:gap-2.5",
84
+ className
85
+ )}
86
+ {...props}
87
+ >
88
+ {firstItem}
89
+ <BreadcrumbEllipsis />
90
+ {lastTwoItems}
91
+ </ol>
92
+ );
93
+ }
94
+
95
+ return (
96
+ <ol
97
+ ref={ref}
98
+ className={cn(
99
+ "flex flex-wrap items-center gap-1.5 sm:gap-2.5",
100
+ className
101
+ )}
102
+ {...props}
103
+ />
104
+ );
105
+ }
106
+ );
107
+ BreadcrumbList.displayName = "BreadcrumbList";
108
+
109
+ const BreadcrumbItem = React.forwardRef<HTMLLIElement, BreadcrumbItemProps>(
110
+ ({ className, isCurrent, href, asChild = false, ...props }, ref) => {
111
+ const Comp = asChild ? React.Fragment : href ? "a" : "span";
112
+ const itemProps = asChild ? {} : href ? { href } : {};
113
+
114
+ return (
115
+ <li
116
+ ref={ref}
117
+ className={cn("inline-flex items-center gap-1.5", className)}
118
+ aria-current={isCurrent ? "page" : undefined}
119
+ {...props}
120
+ >
121
+ <Comp
122
+ className={cn(
123
+ "transition-colors duration-200 hover:text-foreground",
124
+ isCurrent
125
+ ? "font-medium text-foreground dark:text-white dark:text-opacity-95"
126
+ : "text-muted-foreground hover:text-foreground dark:text-gray-400 dark:hover:text-gray-200 dark:hover:text-opacity-95 hover:underline hover:underline-offset-4 hover:decoration-gray-300/30"
127
+ )}
128
+ {...itemProps}
129
+ >
130
+ {props.children}
131
+ </Comp>
132
+ </li>
133
+ );
134
+ }
135
+ );
136
+ BreadcrumbItem.displayName = "BreadcrumbItem";
137
+
138
+ const BreadcrumbSeparator = ({
139
+ children,
140
+ className,
141
+ ...props
142
+ }: BreadcrumbSeparatorProps) => (
143
+ <span
144
+ role="presentation"
145
+ aria-hidden="true"
146
+ className={cn("text-muted-foreground dark:text-gray-500 opacity-70", className)}
147
+ {...props}
148
+ >
149
+ {children || <ChevronRight className="h-3.5 w-3.5" />}
150
+ </span>
151
+ );
152
+ BreadcrumbSeparator.displayName = "BreadcrumbSeparator";
153
+
154
+ const BreadcrumbEllipsis = ({
155
+ className,
156
+ ...props
157
+ }: BreadcrumbEllipsisProps) => (
158
+ <span
159
+ role="presentation"
160
+ aria-hidden="true"
161
+ className={cn("flex items-center text-muted-foreground dark:text-gray-500 dark:hover:text-gray-400 transition-colors duration-200", className)}
162
+ {...props}
163
+ >
164
+ <MoreHorizontal className="h-4 w-4" />
165
+ <span className="sr-only">More pages</span>
166
+ </span>
167
+ );
168
+ BreadcrumbEllipsis.displayName = "BreadcrumbEllipsis";
169
+
170
+ const BreadcrumbLink = React.forwardRef<HTMLAnchorElement, React.AnchorHTMLAttributes<HTMLAnchorElement>>(
171
+ ({ className, href, ...props }, ref) => (
172
+ <a
173
+ ref={ref}
174
+ href={href}
175
+ className={cn(
176
+ "transition-colors duration-200 hover:text-foreground text-muted-foreground hover:underline hover:underline-offset-4 hover:decoration-gray-300/30 dark:text-gray-400 dark:hover:text-gray-200",
177
+ className
178
+ )}
179
+ {...props}
180
+ />
181
+ )
182
+ );
183
+ BreadcrumbLink.displayName = "BreadcrumbLink";
184
+
185
+ const BreadcrumbPage = React.forwardRef<HTMLSpanElement, React.HTMLAttributes<HTMLSpanElement>>(
186
+ ({ className, ...props }, ref) => (
187
+ <span
188
+ ref={ref}
189
+ className={cn(
190
+ "font-medium text-foreground dark:text-white dark:text-opacity-95",
191
+ className
192
+ )}
193
+ {...props}
194
+ />
195
+ )
196
+ );
197
+ BreadcrumbPage.displayName = "BreadcrumbPage";
198
+
199
+ export {
200
+ Breadcrumb,
201
+ BreadcrumbList,
202
+ BreadcrumbItem,
203
+ BreadcrumbLink,
204
+ BreadcrumbPage,
205
+ BreadcrumbSeparator,
206
+ BreadcrumbEllipsis,
207
+ };
@@ -0,0 +1,162 @@
1
+ import type { Meta, StoryObj } from '@storybook/react';
2
+ import { Button } from './button';
3
+ import { Mail, Plus, Loader2 } from 'lucide-react';
4
+
5
+ const meta: Meta<typeof Button> = {
6
+ title: 'Components/Button',
7
+ component: Button,
8
+ parameters: {
9
+ layout: 'centered',
10
+ docs: {
11
+ description: {
12
+ component: 'Displays a button or a component that looks like a button.',
13
+ },
14
+ },
15
+ },
16
+ tags: ['autodocs'],
17
+ argTypes: {
18
+ variant: {
19
+ control: 'select',
20
+ options: ['default', 'destructive', 'outline', 'secondary', 'ghost', 'link'],
21
+ description: 'The visual style of the button',
22
+ },
23
+ size: {
24
+ control: 'select',
25
+ options: ['default', 'sm', 'lg', 'icon'],
26
+ description: 'The size of the button',
27
+ },
28
+ disabled: {
29
+ control: 'boolean',
30
+ description: 'Whether the button is disabled',
31
+ },
32
+ children: {
33
+ control: 'text',
34
+ description: 'The content of the button',
35
+ },
36
+ },
37
+ };
38
+
39
+ export default meta;
40
+ type Story = StoryObj<typeof meta>;
41
+
42
+ export const Default: Story = {
43
+ args: {
44
+ children: 'Button',
45
+ },
46
+ };
47
+
48
+ export const Secondary: Story = {
49
+ args: {
50
+ variant: 'secondary',
51
+ children: 'Secondary',
52
+ },
53
+ };
54
+
55
+ export const Destructive: Story = {
56
+ args: {
57
+ variant: 'destructive',
58
+ children: 'Destructive',
59
+ },
60
+ };
61
+
62
+ export const Outline: Story = {
63
+ args: {
64
+ variant: 'outline',
65
+ children: 'Outline',
66
+ },
67
+ };
68
+
69
+ export const Ghost: Story = {
70
+ args: {
71
+ variant: 'ghost',
72
+ children: 'Ghost',
73
+ },
74
+ };
75
+
76
+ export const Link: Story = {
77
+ args: {
78
+ variant: 'link',
79
+ children: 'Link',
80
+ },
81
+ };
82
+
83
+ export const Small: Story = {
84
+ args: {
85
+ size: 'sm',
86
+ children: 'Small',
87
+ },
88
+ };
89
+
90
+ export const Large: Story = {
91
+ args: {
92
+ size: 'lg',
93
+ children: 'Large',
94
+ },
95
+ };
96
+
97
+ export const Icon: Story = {
98
+ args: {
99
+ size: 'icon',
100
+ children: <Plus className="h-4 w-4" />,
101
+ },
102
+ };
103
+
104
+ export const WithIcon: Story = {
105
+ args: {
106
+ children: (
107
+ <>
108
+ <Mail className="mr-2 h-4 w-4" />
109
+ Login with Email
110
+ </>
111
+ ),
112
+ },
113
+ };
114
+
115
+ export const Loading: Story = {
116
+ args: {
117
+ disabled: true,
118
+ children: (
119
+ <>
120
+ <Loader2 className="mr-2 h-4 w-4 animate-spin" />
121
+ Please wait
122
+ </>
123
+ ),
124
+ },
125
+ };
126
+
127
+ export const Disabled: Story = {
128
+ args: {
129
+ disabled: true,
130
+ children: 'Disabled',
131
+ },
132
+ };
133
+
134
+ export const AllVariants: Story = {
135
+ render: () => (
136
+ <div className="flex flex-col gap-4">
137
+ <div className="flex flex-wrap gap-2">
138
+ <Button variant="default">Default</Button>
139
+ <Button variant="secondary">Secondary</Button>
140
+ <Button variant="destructive">Destructive</Button>
141
+ <Button variant="outline">Outline</Button>
142
+ <Button variant="ghost">Ghost</Button>
143
+ <Button variant="link">Link</Button>
144
+ </div>
145
+ <div className="flex flex-wrap gap-2">
146
+ <Button size="sm">Small</Button>
147
+ <Button size="default">Default</Button>
148
+ <Button size="lg">Large</Button>
149
+ <Button size="icon">
150
+ <Plus className="h-4 w-4" />
151
+ </Button>
152
+ </div>
153
+ </div>
154
+ ),
155
+ parameters: {
156
+ docs: {
157
+ description: {
158
+ story: 'All button variants and sizes in one view.',
159
+ },
160
+ },
161
+ },
162
+ };
@@ -0,0 +1,221 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+ import { cn } from "../../lib/utils";
4
+ import { Loader2 } from "lucide-react";
5
+
6
+ /**
7
+ * Premium Button Component
8
+ *
9
+ * A world-class, accessible, and versatile button component.
10
+ * Pixel-perfect design with extensive variants and states.
11
+ */
12
+ const buttonVariants = cva(
13
+ ["inline-flex items-center justify-center gap-2 whitespace-nowrap",
14
+ "font-medium relative",
15
+ "transition-all duration-200 ease-out",
16
+ "premium-hover", /* Standart premium hover efekti */
17
+ "focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2",
18
+ "disabled:opacity-50 disabled:cursor-not-allowed disabled:pointer-events-none"],
19
+ {
20
+ variants: {
21
+ variant: {
22
+ primary: [
23
+ "bg-primary text-primary-foreground",
24
+ "hover:bg-primary-hover dark:hover:bg-primary/90",
25
+ "active:bg-primary-active dark:active:bg-primary/80",
26
+ "shadow-sm dark:shadow-primary/20 dark:shadow-md",
27
+ "focus-visible:ring-primary/50 dark:focus-visible:ring-primary/30",
28
+ "dark:bg-primary dark:text-white",
29
+ "transition-all duration-200",
30
+ ],
31
+ secondary: [
32
+ "bg-secondary text-secondary-foreground",
33
+ "hover:bg-gray-200 dark:hover:bg-gray-700/90",
34
+ "active:bg-gray-300 dark:active:bg-gray-600/90",
35
+ "border border-transparent dark:border-gray-700",
36
+ "focus-visible:ring-gray-400/30 dark:focus-visible:ring-gray-500/40",
37
+ "dark:bg-gray-800 dark:text-gray-100",
38
+ "dark:shadow-sm dark:shadow-gray-900/20",
39
+ ],
40
+ outline: [
41
+ "bg-background text-foreground",
42
+ "border border-gray-300 dark:border-gray-700",
43
+ "hover:border-gray-400 dark:hover:border-gray-500",
44
+ "hover:bg-gray-50 dark:hover:bg-gray-800/80",
45
+ "active:bg-gray-100 dark:active:bg-gray-800/90",
46
+ "focus-visible:ring-gray-400/30 dark:focus-visible:ring-gray-500/40",
47
+ "dark:text-gray-200 dark:bg-gray-900/50",
48
+ "dark:shadow-sm dark:shadow-gray-950/10",
49
+ ],
50
+ ghost: [
51
+ "text-foreground",
52
+ "hover:bg-gray-100 dark:hover:bg-gray-800/90",
53
+ "active:bg-gray-200 dark:active:bg-gray-700/90",
54
+ "focus-visible:ring-gray-400/30 dark:focus-visible:ring-gray-500/40",
55
+ "dark:text-gray-200",
56
+ "transition-colors duration-200",
57
+ ],
58
+ destructive: [
59
+ "bg-error text-white",
60
+ "hover:bg-error/90 dark:hover:bg-error/80",
61
+ "active:bg-error/80 dark:active:bg-error/70",
62
+ "shadow-sm dark:shadow-error/10 dark:shadow-md",
63
+ "focus-visible:ring-error/50 dark:focus-visible:ring-error/30",
64
+ "dark:text-white dark:border-error/20",
65
+ "transition-colors duration-200",
66
+ ],
67
+ success: [
68
+ "bg-success text-white",
69
+ "hover:bg-success/90 dark:hover:bg-success/80",
70
+ "active:bg-success/80 dark:active:bg-success/70",
71
+ "shadow-sm dark:shadow-success/10 dark:shadow-md",
72
+ "focus-visible:ring-success/50 dark:focus-visible:ring-success/30",
73
+ "dark:text-white dark:border-success/20",
74
+ "transition-colors duration-200",
75
+ ],
76
+ link: [
77
+ "text-primary bg-transparent",
78
+ "underline-offset-4 hover:underline",
79
+ "hover:text-primary-hover active:text-primary-active",
80
+ "p-0 h-auto",
81
+ "dark:text-blue-400 dark:hover:text-blue-300",
82
+ "dark:active:text-blue-500",
83
+ "transition-colors duration-200",
84
+ ],
85
+ gradient: [
86
+ "bg-gradient-to-r from-blue-600 to-purple-600 text-white",
87
+ "hover:from-blue-700 hover:to-purple-700",
88
+ "active:from-blue-800 active:to-purple-800",
89
+ "shadow-lg shadow-purple-500/25",
90
+ "focus-visible:ring-purple-500/50",
91
+ "transition-all duration-300",
92
+ ],
93
+ glow: [
94
+ "bg-primary text-primary-foreground",
95
+ "shadow-lg shadow-primary/50",
96
+ "hover:shadow-xl hover:shadow-primary/60",
97
+ "active:shadow-md active:shadow-primary/40",
98
+ "focus-visible:ring-primary/50",
99
+ "transition-all duration-300",
100
+ ],
101
+ soft: [
102
+ "bg-primary/10 text-primary",
103
+ "hover:bg-primary/20",
104
+ "active:bg-primary/30",
105
+ "focus-visible:ring-primary/50",
106
+ "dark:bg-primary/20 dark:text-primary-foreground",
107
+ "dark:hover:bg-primary/30 dark:active:bg-primary/40",
108
+ "transition-colors duration-200",
109
+ ],
110
+ glass: [
111
+ "bg-white/10 backdrop-blur-md text-foreground",
112
+ "border border-white/20",
113
+ "hover:bg-white/20 hover:border-white/30",
114
+ "active:bg-white/30",
115
+ "shadow-lg",
116
+ "focus-visible:ring-white/50",
117
+ "dark:bg-gray-800/10 dark:border-gray-700/20",
118
+ "dark:hover:bg-gray-800/20 dark:hover:border-gray-700/30",
119
+ "transition-all duration-200",
120
+ ],
121
+ neon: [
122
+ "bg-transparent text-primary",
123
+ "border-2 border-primary",
124
+ "hover:bg-primary hover:text-primary-foreground",
125
+ "hover:shadow-[0_0_20px_rgba(var(--primary),0.5)]",
126
+ "active:shadow-[0_0_10px_rgba(var(--primary),0.3)]",
127
+ "focus-visible:ring-primary/50",
128
+ "transition-all duration-300",
129
+ ],
130
+ },
131
+ size: {
132
+ xs: "h-7 px-2.5 text-xs rounded",
133
+ sm: "h-8 px-3 text-sm rounded",
134
+ md: "h-10 px-4 text-base rounded-md",
135
+ lg: "h-12 px-6 text-lg rounded-lg",
136
+ xl: "h-14 px-8 text-xl rounded-lg",
137
+ icon: "h-10 w-10 p-2 rounded-md",
138
+ "icon-sm": "h-8 w-8 p-1.5 rounded",
139
+ "icon-lg": "h-12 w-12 p-3 rounded-lg",
140
+ },
141
+ rounded: {
142
+ default: "",
143
+ full: "rounded-full",
144
+ none: "rounded-none",
145
+ },
146
+ fullWidth: {
147
+ true: "w-full",
148
+ },
149
+ },
150
+ defaultVariants: {
151
+ variant: "primary",
152
+ size: "md",
153
+ rounded: "default",
154
+ fullWidth: false,
155
+ },
156
+ }
157
+ );
158
+
159
+ // Button component props
160
+ export interface ButtonProps
161
+ extends React.ButtonHTMLAttributes<HTMLButtonElement>,
162
+ VariantProps<typeof buttonVariants> {
163
+ asChild?: boolean;
164
+ loading?: boolean;
165
+ leftIcon?: React.ReactNode;
166
+ rightIcon?: React.ReactNode;
167
+ }
168
+
169
+ /**
170
+ * Premium Button Component
171
+ *
172
+ * @param props - Button component properties
173
+ * @param props.variant - Visual variant (primary, secondary, ghost, etc.)
174
+ * @param props.size - Button size (xs, sm, md, lg, xl)
175
+ * @param props.loading - Loading state display
176
+ * @param props.leftIcon - Icon on the left side
177
+ * @param props.rightIcon - Icon on the right side
178
+ * @param props.fullWidth - Full width display
179
+ * @param props.rounded - Corner rounding style (default, full, none)
180
+ * @param props.asChild - For Radix UI polymorphic component
181
+ */
182
+ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
183
+ ({
184
+ className,
185
+ variant,
186
+ size,
187
+ rounded,
188
+ fullWidth,
189
+ asChild = false,
190
+ loading = false,
191
+ leftIcon,
192
+ rightIcon,
193
+ children,
194
+ disabled,
195
+ ...props
196
+ }, ref) => {
197
+ // Radix UI'ın asChild pattern'i için - "as" prop yerine
198
+ const Comp = asChild ? "button" : "button";
199
+
200
+ return (
201
+ <Comp
202
+ className={cn(buttonVariants({ variant, size, rounded, fullWidth, className }))}
203
+ ref={ref}
204
+ disabled={disabled || loading}
205
+ data-loading={loading ? "" : undefined}
206
+ {...props}
207
+ >
208
+ {loading && (
209
+ <Loader2 className="w-4 h-4 animate-spin" aria-hidden="true" />
210
+ )}
211
+ {!loading && leftIcon}
212
+ {children}
213
+ {!loading && rightIcon}
214
+ </Comp>
215
+ );
216
+ }
217
+ );
218
+
219
+ Button.displayName = "Button";
220
+
221
+ export { Button, buttonVariants };