@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,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 }
@@ -0,0 +1,322 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+
4
+ import { cn } from "../../lib/utils";
5
+
6
+ export type SortDirection = "asc" | "desc" | null;
7
+
8
+ const tableVariants = cva(
9
+ "w-full caption-bottom text-sm",
10
+ {
11
+ variants: {
12
+ variant: {
13
+ default: "dark:text-gray-200",
14
+ bordered: "border border-border dark:border-gray-700",
15
+ striped: "dark:text-gray-200",
16
+ card: "border border-border dark:border-gray-700 rounded-md shadow-sm dark:shadow-gray-900/20",
17
+ minimal: "border-none dark:text-gray-200",
18
+ },
19
+ size: {
20
+ sm: "text-xs",
21
+ default: "text-sm",
22
+ lg: "text-base",
23
+ },
24
+ },
25
+ defaultVariants: {
26
+ variant: "default",
27
+ size: "default",
28
+ },
29
+ }
30
+ );
31
+
32
+ export interface ColumnDefinition<T> {
33
+ id: string;
34
+ header: React.ReactNode;
35
+ accessorKey?: keyof T;
36
+ cell?: (row: T) => React.ReactNode;
37
+ sortable?: boolean;
38
+ }
39
+
40
+ interface TableProps<T>
41
+ extends React.HTMLAttributes<HTMLTableElement>,
42
+ VariantProps<typeof tableVariants> {
43
+ /** Veri yükleniyor durumunu gösterir */
44
+ loading?: boolean;
45
+ /** Sıralama için kullanılan sütun */
46
+ sortColumn?: string;
47
+ /** Sıralama yönü */
48
+ sortDirection?: SortDirection;
49
+ /** Sıralama değiştiğinde çağrılacak fonksiyon */
50
+ onSortChange?: (column: string, direction: SortDirection) => void;
51
+ /** Boş durum için özel içerik */
52
+ emptyContent?: React.ReactNode;
53
+ /** Seçili satır id'leri */
54
+ selectedRowIds?: string[];
55
+ /** Satır seçim değiştiğinde çağrılacak fonksiyon */
56
+ onRowSelectionChange?: (selectedRowIds: string[]) => void;
57
+ /** Satır seçim devre dışı */
58
+ disableRowSelection?: boolean;
59
+ /** Her satır için benzersiz id çıkarma fonksiyonu */
60
+ getRowId?: (row: T) => string;
61
+ }
62
+
63
+ // Tip parametresiz Table bileşeni için varsayılan tip, herhangi bir veri tipini kabul edebilmesi için
64
+ const Table = React.forwardRef<
65
+ HTMLTableElement,
66
+ TableProps<unknown>
67
+ >(({
68
+ className,
69
+ variant,
70
+ size,
71
+ loading,
72
+ emptyContent,
73
+ // Kullanılmayan özellikleri yoruma alarak lint uyarılarını önlüyoruz
74
+ // sortColumn,
75
+ // sortDirection,
76
+ // onSortChange,
77
+ // selectedRowIds,
78
+ // onRowSelectionChange,
79
+ // disableRowSelection,
80
+ // getRowId,
81
+ ...props
82
+ }, ref) => {
83
+ // Apply striped styles to the tbody via a class name
84
+ const striped = variant === "striped";
85
+
86
+ // Çocukları güvenli bir şekilde işle ve tip kontrollerini doğru yap
87
+ const childrenWithProps = React.Children.map(props.children, (child) => {
88
+ if (React.isValidElement(child)) {
89
+ if (child.type === "tbody") {
90
+ // Tip güvenliği için props'ları düzgün şekilde ele alıyoruz
91
+ const tbodyProps = child.props as Record<string, unknown>;
92
+ return React.cloneElement(child as React.ReactElement<React.HTMLAttributes<HTMLTableSectionElement>>, {
93
+ className: cn(tbodyProps.className as string | undefined, striped && "even:[&>tr]:bg-muted/50")
94
+ });
95
+ }
96
+ }
97
+ return child;
98
+ });
99
+
100
+ return (
101
+ <div className="relative w-full overflow-auto">
102
+ {/* Yükleniyor durumu için overlay */}
103
+ {loading && (
104
+ <div className="absolute inset-0 bg-background/60 flex items-center justify-center z-10">
105
+ <div className="h-8 w-8 animate-spin rounded-full border-2 border-primary border-t-transparent"></div>
106
+ </div>
107
+ )}
108
+
109
+ <table
110
+ ref={ref}
111
+ className={cn(
112
+ tableVariants({ variant, size }),
113
+ loading && "opacity-70",
114
+ className
115
+ )}
116
+ {...props}
117
+ >
118
+ {childrenWithProps}
119
+ </table>
120
+
121
+ </div>
122
+ );
123
+ });
124
+ Table.displayName = "Table";
125
+
126
+ const TableHeader = React.forwardRef<
127
+ HTMLTableSectionElement,
128
+ React.HTMLAttributes<HTMLTableSectionElement>
129
+ >(({ className, ...props }, ref) => (
130
+ <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
131
+ ));
132
+ TableHeader.displayName = "TableHeader";
133
+
134
+ interface TableBodyProps extends React.HTMLAttributes<HTMLTableSectionElement> {
135
+ /** Veri yoksa gösterilecek boş durum içeriği */
136
+ emptyContent?: React.ReactNode;
137
+ /** Varsayılan boş durum mesajı */
138
+ emptyMessage?: string;
139
+ }
140
+
141
+ const TableBody = React.forwardRef<
142
+ HTMLTableSectionElement,
143
+ TableBodyProps
144
+ >(({ className, emptyContent, emptyMessage = "No data available", children, ...props }, ref) => {
145
+ // Çocuk elementlerin varlığını kontrol et
146
+ const hasChildren = React.Children.count(children) > 0;
147
+
148
+ return (
149
+ <tbody
150
+ ref={ref}
151
+ className={cn("[&_tr:last-child]:border-0", className)}
152
+ {...props}
153
+ >
154
+ {hasChildren ? (
155
+ children
156
+ ) : (
157
+ <tr>
158
+ <td colSpan={100} className="h-24 text-center">
159
+ {emptyContent || (
160
+ <div className="py-6 text-muted-foreground">
161
+ <div className="text-sm">
162
+ {emptyMessage}
163
+ </div>
164
+ </div>
165
+ )}
166
+ </td>
167
+ </tr>
168
+ )}
169
+ </tbody>
170
+ )
171
+ });
172
+ TableBody.displayName = "TableBody";
173
+
174
+ const TableFooter = React.forwardRef<
175
+ HTMLTableSectionElement,
176
+ React.HTMLAttributes<HTMLTableSectionElement>
177
+ >(({ className, ...props }, ref) => (
178
+ <tfoot
179
+ ref={ref}
180
+ className={cn("bg-primary text-primary-foreground font-medium", className)}
181
+ {...props}
182
+ />
183
+ ));
184
+ TableFooter.displayName = "TableFooter";
185
+
186
+ const TableRow = React.forwardRef<
187
+ HTMLTableRowElement,
188
+ React.HTMLAttributes<HTMLTableRowElement>
189
+ >(({ className, ...props }, ref) => (
190
+ <tr
191
+ ref={ref}
192
+ className={cn(
193
+ "border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
194
+ className
195
+ )}
196
+ {...props}
197
+ />
198
+ ));
199
+ TableRow.displayName = "TableRow";
200
+
201
+ interface TableHeadProps extends React.ThHTMLAttributes<HTMLTableCellElement> {
202
+ /** Bu sütun için sıralama durumu */
203
+ sortable?: boolean;
204
+ /** Bu sütunun sıralanma durumu */
205
+ sorted?: SortDirection;
206
+ /** Sıralama değiştiğinde çağrılacak fonksiyon */
207
+ onSort?: () => void;
208
+ }
209
+
210
+ const TableHead = React.forwardRef<HTMLTableCellElement, TableHeadProps>(
211
+ ({ className, sortable, sorted, onSort, children, ...props }, ref) => {
212
+ // Sıralama için simgeler
213
+ const renderSortIcon = () => {
214
+ if (!sortable) return null;
215
+
216
+ if (sorted === "asc") {
217
+ return (
218
+ <svg
219
+ xmlns="http://www.w3.org/2000/svg"
220
+ className="ml-1 h-4 w-4 inline-block"
221
+ viewBox="0 0 20 20"
222
+ fill="currentColor"
223
+ >
224
+ <path
225
+ fillRule="evenodd"
226
+ d="M14.707 10.293a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L9 12.586V5a1 1 0 012 0v7.586l2.293-2.293a1 1 0 011.414 0z"
227
+ clipRule="evenodd"
228
+ />
229
+ </svg>
230
+ );
231
+ }
232
+
233
+ if (sorted === "desc") {
234
+ return (
235
+ <svg
236
+ xmlns="http://www.w3.org/2000/svg"
237
+ className="ml-1 h-4 w-4 inline-block"
238
+ viewBox="0 0 20 20"
239
+ fill="currentColor"
240
+ >
241
+ <path
242
+ fillRule="evenodd"
243
+ d="M5.293 9.707a1 1 0 010-1.414l4-4a1 1 0 011.414 0l4 4a1 1 0 01-1.414 1.414L11 7.414V15a1 1 0 11-2 0V7.414L6.707 9.707a1 1 0 01-1.414 0z"
244
+ clipRule="evenodd"
245
+ />
246
+ </svg>
247
+ );
248
+ }
249
+
250
+ return (
251
+ <svg
252
+ xmlns="http://www.w3.org/2000/svg"
253
+ className="ml-1 h-4 w-4 inline-block opacity-30"
254
+ viewBox="0 0 20 20"
255
+ fill="currentColor"
256
+ >
257
+ <path
258
+ fillRule="evenodd"
259
+ d="M10 3a1 1 0 01.707.293l3 3a1 1 0 01-1.414 1.414L10 5.414 7.707 7.707a1 1 0 01-1.414-1.414l3-3A1 1 0 0110 3zm-3.707 9.293a1 1 0 011.414 0L10 14.586l2.293-2.293a1 1 0 011.414 1.414l-3 3a1 1 0 01-1.414 0l-3-3a1 1 0 010-1.414z"
260
+ clipRule="evenodd"
261
+ />
262
+ </svg>
263
+ );
264
+ };
265
+
266
+ return (
267
+ <th
268
+ ref={ref}
269
+ className={cn(
270
+ "h-10 px-4 text-left align-middle font-medium text-muted-foreground [&:has([role=checkbox])]:pr-0",
271
+ sortable && "cursor-pointer hover:text-foreground select-none",
272
+ className
273
+ )}
274
+ onClick={sortable ? onSort : undefined}
275
+ {...props}
276
+ >
277
+ <div className="flex items-center">
278
+ {children}
279
+ {sortable && renderSortIcon()}
280
+ </div>
281
+ </th>
282
+ );
283
+ }
284
+ );
285
+ TableHead.displayName = "TableHead";
286
+
287
+ const TableCell = React.forwardRef<
288
+ HTMLTableCellElement,
289
+ React.TdHTMLAttributes<HTMLTableCellElement>
290
+ >(({ className, ...props }, ref) => (
291
+ <td
292
+ ref={ref}
293
+ className={cn("p-4 align-middle [&:has([role=checkbox])]:pr-0", className)}
294
+ {...props}
295
+ />
296
+ ));
297
+ TableCell.displayName = "TableCell";
298
+
299
+ const TableCaption = React.forwardRef<
300
+ HTMLTableCaptionElement,
301
+ React.HTMLAttributes<HTMLTableCaptionElement>
302
+ >(({ className, ...props }, ref) => (
303
+ <caption
304
+ ref={ref}
305
+ className={cn("mt-4 text-sm text-muted-foreground", className)}
306
+ {...props}
307
+ />
308
+ ));
309
+ TableCaption.displayName = "TableCaption";
310
+
311
+ export {
312
+ Table,
313
+ TableHeader,
314
+ TableBody,
315
+ TableFooter,
316
+ TableHead,
317
+ TableRow,
318
+ TableCell,
319
+ TableCaption,
320
+ };
321
+
322
+ export type { TableBodyProps };
@@ -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,46 @@
1
+ import * as React from "react"
2
+
3
+ import { cn } from "../../lib/utils"
4
+
5
+ export interface TextareaProps
6
+ extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
7
+ // Enhanced props that shouldn't be passed to DOM
8
+ variant?: "default" | "outline" | "ghost" | "underline"
9
+ size?: "sm" | "md" | "lg"
10
+ error?: boolean | string
11
+ success?: boolean
12
+ loading?: boolean
13
+ autoResize?: boolean
14
+ maxHeight?: number
15
+ characterCount?: boolean
16
+ }
17
+
18
+ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
19
+ ({
20
+ className,
21
+ // Extract enhanced props to prevent them from being passed to DOM
22
+ variant,
23
+ size,
24
+ error,
25
+ success,
26
+ loading,
27
+ autoResize,
28
+ maxHeight,
29
+ characterCount,
30
+ ...props
31
+ }, ref) => {
32
+ return (
33
+ <textarea
34
+ className={cn(
35
+ "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",
36
+ className
37
+ )}
38
+ ref={ref}
39
+ {...props}
40
+ />
41
+ )
42
+ }
43
+ )
44
+ Textarea.displayName = "Textarea"
45
+
46
+ export { Textarea }