@ancatag/at-design 0.3.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 (166) hide show
  1. package/README.md +537 -0
  2. package/dist/components/Sheet.d.ts +20 -0
  3. package/dist/components/Sheet.d.ts.map +1 -0
  4. package/dist/components/Sheet.js +26 -0
  5. package/dist/components/Sheet.js.map +1 -0
  6. package/dist/components/alert-dialog.d.ts +21 -0
  7. package/dist/components/alert-dialog.d.ts.map +1 -0
  8. package/dist/components/alert-dialog.js +27 -0
  9. package/dist/components/alert-dialog.js.map +1 -0
  10. package/dist/components/alert.d.ts +9 -0
  11. package/dist/components/alert.d.ts.map +1 -0
  12. package/dist/components/alert.js +23 -0
  13. package/dist/components/alert.js.map +1 -0
  14. package/dist/components/avatar.d.ts +7 -0
  15. package/dist/components/avatar.d.ts.map +1 -0
  16. package/dist/components/avatar.js +12 -0
  17. package/dist/components/avatar.js.map +1 -0
  18. package/dist/components/badge.d.ts +10 -0
  19. package/dist/components/badge.d.ts.map +1 -0
  20. package/dist/components/badge.js +21 -0
  21. package/dist/components/badge.js.map +1 -0
  22. package/dist/components/bottom-sheet.d.ts +16 -0
  23. package/dist/components/bottom-sheet.d.ts.map +1 -0
  24. package/dist/components/bottom-sheet.js +13 -0
  25. package/dist/components/bottom-sheet.js.map +1 -0
  26. package/dist/components/button.d.ts +12 -0
  27. package/dist/components/button.d.ts.map +1 -0
  28. package/dist/components/button.js +34 -0
  29. package/dist/components/button.js.map +1 -0
  30. package/dist/components/card.d.ts +9 -0
  31. package/dist/components/card.d.ts.map +1 -0
  32. package/dist/components/card.js +17 -0
  33. package/dist/components/card.js.map +1 -0
  34. package/dist/components/checkbox.d.ts +5 -0
  35. package/dist/components/checkbox.d.ts.map +1 -0
  36. package/dist/components/checkbox.js +10 -0
  37. package/dist/components/checkbox.js.map +1 -0
  38. package/dist/components/combobox.d.ts +19 -0
  39. package/dist/components/combobox.d.ts.map +1 -0
  40. package/dist/components/combobox.js +101 -0
  41. package/dist/components/combobox.js.map +1 -0
  42. package/dist/components/dialog.d.ts +20 -0
  43. package/dist/components/dialog.d.ts.map +1 -0
  44. package/dist/components/dialog.js +24 -0
  45. package/dist/components/dialog.js.map +1 -0
  46. package/dist/components/dropdown-menu.d.ts +28 -0
  47. package/dist/components/dropdown-menu.d.ts.map +1 -0
  48. package/dist/components/dropdown-menu.js +36 -0
  49. package/dist/components/dropdown-menu.js.map +1 -0
  50. package/dist/components/error-page.d.ts +105 -0
  51. package/dist/components/error-page.d.ts.map +1 -0
  52. package/dist/components/error-page.js +99 -0
  53. package/dist/components/error-page.js.map +1 -0
  54. package/dist/components/glass-card.d.ts +12 -0
  55. package/dist/components/glass-card.d.ts.map +1 -0
  56. package/dist/components/glass-card.js +15 -0
  57. package/dist/components/glass-card.js.map +1 -0
  58. package/dist/components/icon-badge.d.ts +15 -0
  59. package/dist/components/icon-badge.d.ts.map +1 -0
  60. package/dist/components/icon-badge.js +77 -0
  61. package/dist/components/icon-badge.js.map +1 -0
  62. package/dist/components/input.d.ts +6 -0
  63. package/dist/components/input.d.ts.map +1 -0
  64. package/dist/components/input.js +9 -0
  65. package/dist/components/input.js.map +1 -0
  66. package/dist/components/label.d.ts +6 -0
  67. package/dist/components/label.d.ts.map +1 -0
  68. package/dist/components/label.js +11 -0
  69. package/dist/components/label.js.map +1 -0
  70. package/dist/components/progress.d.ts +5 -0
  71. package/dist/components/progress.d.ts.map +1 -0
  72. package/dist/components/progress.js +9 -0
  73. package/dist/components/progress.js.map +1 -0
  74. package/dist/components/radio-group.d.ts +6 -0
  75. package/dist/components/radio-group.d.ts.map +1 -0
  76. package/dist/components/radio-group.js +15 -0
  77. package/dist/components/radio-group.js.map +1 -0
  78. package/dist/components/responsive-table.d.ts +21 -0
  79. package/dist/components/responsive-table.d.ts.map +1 -0
  80. package/dist/components/responsive-table.js +48 -0
  81. package/dist/components/responsive-table.js.map +1 -0
  82. package/dist/components/select.d.ts +14 -0
  83. package/dist/components/select.d.ts.map +1 -0
  84. package/dist/components/select.js +27 -0
  85. package/dist/components/select.js.map +1 -0
  86. package/dist/components/skeleton.d.ts +3 -0
  87. package/dist/components/skeleton.d.ts.map +1 -0
  88. package/dist/components/skeleton.js +7 -0
  89. package/dist/components/skeleton.js.map +1 -0
  90. package/dist/components/slider.d.ts +5 -0
  91. package/dist/components/slider.d.ts.map +1 -0
  92. package/dist/components/slider.js +9 -0
  93. package/dist/components/slider.js.map +1 -0
  94. package/dist/components/stat-card.d.ts +17 -0
  95. package/dist/components/stat-card.d.ts.map +1 -0
  96. package/dist/components/stat-card.js +23 -0
  97. package/dist/components/stat-card.js.map +1 -0
  98. package/dist/components/switch.d.ts +5 -0
  99. package/dist/components/switch.d.ts.map +1 -0
  100. package/dist/components/switch.js +9 -0
  101. package/dist/components/switch.js.map +1 -0
  102. package/dist/components/table.d.ts +11 -0
  103. package/dist/components/table.d.ts.map +1 -0
  104. package/dist/components/table.js +21 -0
  105. package/dist/components/table.js.map +1 -0
  106. package/dist/components/tabs.d.ts +8 -0
  107. package/dist/components/tabs.d.ts.map +1 -0
  108. package/dist/components/tabs.js +14 -0
  109. package/dist/components/tabs.js.map +1 -0
  110. package/dist/components/textarea.d.ts +6 -0
  111. package/dist/components/textarea.d.ts.map +1 -0
  112. package/dist/components/textarea.js +9 -0
  113. package/dist/components/textarea.js.map +1 -0
  114. package/dist/components/toast.d.ts +6 -0
  115. package/dist/components/toast.d.ts.map +1 -0
  116. package/dist/components/toast.js +14 -0
  117. package/dist/components/toast.js.map +1 -0
  118. package/dist/components/tooltip.d.ts +8 -0
  119. package/dist/components/tooltip.d.ts.map +1 -0
  120. package/dist/components/tooltip.js +12 -0
  121. package/dist/components/tooltip.js.map +1 -0
  122. package/dist/index.d.ts +32 -0
  123. package/dist/index.d.ts.map +1 -0
  124. package/dist/index.js +40 -0
  125. package/dist/index.js.map +1 -0
  126. package/dist/utils/cn.d.ts +3 -0
  127. package/dist/utils/cn.d.ts.map +1 -0
  128. package/dist/utils/cn.js +6 -0
  129. package/dist/utils/cn.js.map +1 -0
  130. package/package.json +96 -0
  131. package/src/components/Sheet.tsx +117 -0
  132. package/src/components/alert-dialog.tsx +138 -0
  133. package/src/components/alert.tsx +58 -0
  134. package/src/components/avatar.tsx +48 -0
  135. package/src/components/badge.tsx +36 -0
  136. package/src/components/bottom-sheet.tsx +68 -0
  137. package/src/components/button.tsx +56 -0
  138. package/src/components/card.tsx +85 -0
  139. package/src/components/checkbox.tsx +30 -0
  140. package/src/components/combobox.tsx +246 -0
  141. package/src/components/dialog.tsx +122 -0
  142. package/src/components/dropdown-menu.tsx +198 -0
  143. package/src/components/error-page.tsx +368 -0
  144. package/src/components/glass-card.tsx +37 -0
  145. package/src/components/icon-badge.tsx +126 -0
  146. package/src/components/input.tsx +25 -0
  147. package/src/components/label.tsx +26 -0
  148. package/src/components/progress.tsx +28 -0
  149. package/src/components/radio-group.tsx +42 -0
  150. package/src/components/responsive-table.tsx +191 -0
  151. package/src/components/select.tsx +158 -0
  152. package/src/components/skeleton.tsx +15 -0
  153. package/src/components/slider.tsx +27 -0
  154. package/src/components/stat-card.tsx +69 -0
  155. package/src/components/switch.tsx +29 -0
  156. package/src/components/table.tsx +117 -0
  157. package/src/components/tabs.tsx +55 -0
  158. package/src/components/textarea.tsx +23 -0
  159. package/src/components/toast.tsx +26 -0
  160. package/src/components/tooltip.tsx +34 -0
  161. package/src/index.ts +136 -0
  162. package/src/styles/animations.css +55 -0
  163. package/src/styles/base.css +51 -0
  164. package/src/styles/tokens.css +37 -0
  165. package/src/utils/cn.ts +6 -0
  166. package/tsconfig.json +22 -0
@@ -0,0 +1,368 @@
1
+ import { ArrowLeft, Home, Search } from "lucide-react";
2
+ import * as React from "react";
3
+ import { cn } from "../utils/cn";
4
+ import { Button } from "./button";
5
+ import {
6
+ Card,
7
+ CardContent,
8
+ CardDescription,
9
+ CardHeader,
10
+ CardTitle,
11
+ } from "./card";
12
+
13
+ export interface ErrorPageProps {
14
+ /**
15
+ * Error code (e.g., "404", "500", "403")
16
+ */
17
+ code?: string;
18
+ /**
19
+ * Main error title
20
+ */
21
+ title?: string;
22
+ /**
23
+ * Error description/message
24
+ */
25
+ description?: string;
26
+ /**
27
+ * Show home button
28
+ */
29
+ showHomeButton?: boolean;
30
+ /**
31
+ * Show back button
32
+ */
33
+ showBackButton?: boolean;
34
+ /**
35
+ * Show search button
36
+ */
37
+ showSearchButton?: boolean;
38
+ /**
39
+ * Custom home button label
40
+ */
41
+ homeButtonLabel?: string;
42
+ /**
43
+ * Custom back button label
44
+ */
45
+ backButtonLabel?: string;
46
+ /**
47
+ * Custom search button label
48
+ */
49
+ searchButtonLabel?: string;
50
+ /**
51
+ * Home button action
52
+ */
53
+ onHomeClick?: () => void;
54
+ /**
55
+ * Back button action
56
+ */
57
+ onBackClick?: () => void;
58
+ /**
59
+ * Search button action
60
+ */
61
+ onSearchClick?: () => void;
62
+ /**
63
+ * Custom icon component
64
+ */
65
+ icon?: React.ComponentType<{ className?: string }>;
66
+ /**
67
+ * Additional CSS classes
68
+ */
69
+ className?: string;
70
+ /**
71
+ * Show as card or full page
72
+ */
73
+ variant?: "card" | "full";
74
+ }
75
+
76
+ /**
77
+ * ErrorPage - A beautiful, customizable error page component
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * <ErrorPage
82
+ * code="404"
83
+ * title="Page Not Found"
84
+ * description="The page you're looking for doesn't exist or has been moved."
85
+ * showHomeButton
86
+ * showBackButton
87
+ * />
88
+ * ```
89
+ */
90
+ export const ErrorPage = React.forwardRef<HTMLDivElement, ErrorPageProps>(
91
+ (
92
+ {
93
+ code = "404",
94
+ title = "Page Not Found",
95
+ description = "The page you're looking for doesn't exist or has been moved.",
96
+ showHomeButton = true,
97
+ showBackButton = true,
98
+ showSearchButton = false,
99
+ homeButtonLabel = "Go Home",
100
+ backButtonLabel = "Go Back",
101
+ searchButtonLabel = "Search",
102
+ onHomeClick,
103
+ onBackClick,
104
+ onSearchClick,
105
+ icon: Icon,
106
+ className,
107
+ variant = "full",
108
+ },
109
+ ref,
110
+ ) => {
111
+ const handleBack = () => {
112
+ if (onBackClick) {
113
+ onBackClick();
114
+ } else {
115
+ window.history.back();
116
+ }
117
+ };
118
+
119
+ const handleHome = () => {
120
+ if (onHomeClick) {
121
+ onHomeClick();
122
+ } else {
123
+ window.location.href = "/";
124
+ }
125
+ };
126
+
127
+ const handleSearch = () => {
128
+ if (onSearchClick) {
129
+ onSearchClick();
130
+ }
131
+ };
132
+
133
+ const content = (
134
+ <div
135
+ ref={ref}
136
+ className={cn(
137
+ "flex flex-col items-center justify-center text-center",
138
+ variant === "card"
139
+ ? "p-6"
140
+ : "min-h-screen px-4 py-16 sm:py-20 lg:py-24 sm:px-6 lg:px-8",
141
+ className,
142
+ )}
143
+ >
144
+ {/* Error Code - Much Larger with Gradient */}
145
+ <div className="mb-8 sm:mb-12 relative">
146
+ <div
147
+ className={cn(
148
+ "font-bold select-none",
149
+ "text-[120px] sm:text-[180px] lg:text-[240px] xl:text-[280px]",
150
+ "leading-none tracking-tight",
151
+ "bg-gradient-to-br from-primary via-primary/80 to-primary/40",
152
+ "bg-clip-text text-transparent",
153
+ "drop-shadow-2xl",
154
+ )}
155
+ style={{
156
+ animation: "fade-in 0.8s ease-out forwards",
157
+ }}
158
+ >
159
+ {code}
160
+ </div>
161
+ {/* Decorative glow effect */}
162
+ <div
163
+ className="absolute inset-0 -z-10 blur-3xl opacity-20"
164
+ style={{
165
+ background:
166
+ "radial-gradient(circle, hsl(var(--primary)) 0%, transparent 70%)",
167
+ }}
168
+ />
169
+ </div>
170
+
171
+ {/* Icon */}
172
+ {Icon && (
173
+ <div
174
+ className="mb-8 sm:mb-10"
175
+ style={{
176
+ animation: "fade-in 0.8s ease-out 0.2s forwards",
177
+ opacity: 0,
178
+ }}
179
+ >
180
+ <Icon className="h-20 w-20 sm:h-24 sm:w-24 lg:h-28 lg:w-28 text-muted-foreground/60" />
181
+ </div>
182
+ )}
183
+
184
+ {/* Title - Larger */}
185
+ <h1
186
+ className={cn(
187
+ "font-bold text-foreground mb-6 sm:mb-8",
188
+ "text-4xl sm:text-5xl lg:text-6xl xl:text-7xl",
189
+ "leading-tight tracking-tight",
190
+ )}
191
+ style={{
192
+ animation: "fade-in 0.8s ease-out 0.3s forwards",
193
+ opacity: 0,
194
+ }}
195
+ >
196
+ {title}
197
+ </h1>
198
+
199
+ {/* Description - Larger with better spacing */}
200
+ <p
201
+ className={cn(
202
+ "text-muted-foreground max-w-lg sm:max-w-xl lg:max-w-2xl mb-10 sm:mb-12",
203
+ "text-lg sm:text-xl lg:text-2xl",
204
+ "leading-relaxed",
205
+ )}
206
+ style={{
207
+ animation: "fade-in 0.8s ease-out 0.4s forwards",
208
+ opacity: 0,
209
+ }}
210
+ >
211
+ {description}
212
+ </p>
213
+
214
+ {/* Action Buttons - Larger with better spacing */}
215
+ <div
216
+ className={cn("flex flex-col sm:flex-row gap-4 sm:gap-5")}
217
+ style={{
218
+ animation: "fade-in 0.8s ease-out 0.5s forwards",
219
+ opacity: 0,
220
+ }}
221
+ >
222
+ {showBackButton && (
223
+ <Button
224
+ variant="outline"
225
+ size="lg"
226
+ onClick={handleBack}
227
+ className="min-w-[160px] sm:min-w-[180px] h-12 sm:h-14 text-base sm:text-lg"
228
+ >
229
+ <ArrowLeft className="mr-2 h-5 w-5" />
230
+ {backButtonLabel}
231
+ </Button>
232
+ )}
233
+ {showHomeButton && (
234
+ <Button
235
+ variant="default"
236
+ size="lg"
237
+ onClick={handleHome}
238
+ className="min-w-[160px] sm:min-w-[180px] h-12 sm:h-14 text-base sm:text-lg"
239
+ >
240
+ <Home className="mr-2 h-5 w-5" />
241
+ {homeButtonLabel}
242
+ </Button>
243
+ )}
244
+ {showSearchButton && (
245
+ <Button
246
+ variant="secondary"
247
+ size="lg"
248
+ onClick={handleSearch}
249
+ className="min-w-[160px] sm:min-w-[180px] h-12 sm:h-14 text-base sm:text-lg"
250
+ >
251
+ <Search className="mr-2 h-5 w-5" />
252
+ {searchButtonLabel}
253
+ </Button>
254
+ )}
255
+ </div>
256
+ </div>
257
+ );
258
+
259
+ if (variant === "card") {
260
+ return (
261
+ <Card className="max-w-2xl mx-auto">
262
+ <CardHeader>
263
+ <CardTitle className="text-center">{title}</CardTitle>
264
+ <CardDescription className="text-center">
265
+ {description}
266
+ </CardDescription>
267
+ </CardHeader>
268
+ <CardContent>{content}</CardContent>
269
+ </Card>
270
+ );
271
+ }
272
+
273
+ return content;
274
+ },
275
+ );
276
+
277
+ ErrorPage.displayName = "ErrorPage";
278
+
279
+ /**
280
+ * Pre-configured 404 error page
281
+ */
282
+ export const NotFoundPage = React.forwardRef<
283
+ HTMLDivElement,
284
+ Omit<ErrorPageProps, "code" | "title"> & { description?: string }
285
+ >((props, ref) => {
286
+ return (
287
+ <ErrorPage
288
+ ref={ref}
289
+ code="404"
290
+ title="Page Not Found"
291
+ description={
292
+ props.description ||
293
+ "The page you're looking for doesn't exist or has been moved."
294
+ }
295
+ {...props}
296
+ />
297
+ );
298
+ });
299
+
300
+ NotFoundPage.displayName = "NotFoundPage";
301
+
302
+ /**
303
+ * Pre-configured 500 error page
304
+ */
305
+ export const ServerErrorPage = React.forwardRef<
306
+ HTMLDivElement,
307
+ Omit<ErrorPageProps, "code" | "title"> & { description?: string }
308
+ >((props, ref) => {
309
+ return (
310
+ <ErrorPage
311
+ ref={ref}
312
+ code="500"
313
+ title="Server Error"
314
+ description={
315
+ props.description ||
316
+ "Something went wrong on our end. We're working to fix it."
317
+ }
318
+ {...props}
319
+ />
320
+ );
321
+ });
322
+
323
+ ServerErrorPage.displayName = "ServerErrorPage";
324
+
325
+ /**
326
+ * Pre-configured 403 error page
327
+ */
328
+ export const ForbiddenPage = React.forwardRef<
329
+ HTMLDivElement,
330
+ Omit<ErrorPageProps, "code" | "title"> & { description?: string }
331
+ >((props, ref) => {
332
+ return (
333
+ <ErrorPage
334
+ ref={ref}
335
+ code="403"
336
+ title="Access Forbidden"
337
+ description={
338
+ props.description ||
339
+ "You don't have permission to access this resource."
340
+ }
341
+ {...props}
342
+ />
343
+ );
344
+ });
345
+
346
+ ForbiddenPage.displayName = "ForbiddenPage";
347
+
348
+ /**
349
+ * Pre-configured 401 error page
350
+ */
351
+ export const UnauthorizedPage = React.forwardRef<
352
+ HTMLDivElement,
353
+ Omit<ErrorPageProps, "code" | "title"> & { description?: string }
354
+ >((props, ref) => {
355
+ return (
356
+ <ErrorPage
357
+ ref={ref}
358
+ code="401"
359
+ title="Unauthorized"
360
+ description={
361
+ props.description || "You need to be logged in to access this page."
362
+ }
363
+ {...props}
364
+ />
365
+ );
366
+ });
367
+
368
+ UnauthorizedPage.displayName = "UnauthorizedPage";
@@ -0,0 +1,37 @@
1
+ import React from "react";
2
+
3
+ export interface GlassCardProps {
4
+ children: React.ReactNode;
5
+ className?: string;
6
+ gradient?: "blue" | "purple" | "pink" | "none";
7
+ }
8
+
9
+ const gradientClasses = {
10
+ blue: "from-blue-500/5 to-cyan-500/5",
11
+ purple: "from-purple-500/5 to-pink-500/5",
12
+ pink: "from-pink-500/5 to-rose-500/5",
13
+ none: "",
14
+ };
15
+
16
+ /**
17
+ * Apple-inspired glass morphism card
18
+ * Frosted glass effect with subtle gradients
19
+ */
20
+ export function GlassCard({
21
+ children,
22
+ className = "",
23
+ gradient = "none",
24
+ }: GlassCardProps) {
25
+ return (
26
+ <div
27
+ className={`relative overflow-hidden rounded-3xl border border-white/10 bg-white/5 p-8 shadow-2xl backdrop-blur-2xl dark:bg-black/20 ${className}`}
28
+ >
29
+ {gradient !== "none" && (
30
+ <div
31
+ className={`absolute inset-0 bg-gradient-to-br ${gradientClasses[gradient]}`}
32
+ />
33
+ )}
34
+ <div className="relative z-10">{children}</div>
35
+ </div>
36
+ );
37
+ }
@@ -0,0 +1,126 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+
3
+ export interface IconBadgeProps {
4
+ icon: LucideIcon;
5
+ value: string | number;
6
+ label?: string;
7
+ size?: "sm" | "md" | "lg";
8
+ variant?: "blue" | "yellow" | "green" | "red" | "purple";
9
+ onClick?: () => void;
10
+ }
11
+
12
+ const sizeClasses = {
13
+ sm: {
14
+ container: "gap-2 px-3 py-1.5",
15
+ icon: "h-4 w-4",
16
+ value: "text-sm",
17
+ label: "text-xs",
18
+ },
19
+ md: {
20
+ container: "gap-2.5 px-3.5 py-2",
21
+ icon: "h-4 w-4",
22
+ value: "text-sm",
23
+ label: "text-xs",
24
+ },
25
+ lg: {
26
+ container: "gap-3 px-4 py-2.5",
27
+ icon: "h-5 w-5",
28
+ value: "text-base",
29
+ label: "text-sm",
30
+ },
31
+ };
32
+
33
+ const variantClasses = {
34
+ blue: {
35
+ container:
36
+ "border-blue-200/50 bg-blue-50/50 dark:border-blue-900/30 dark:bg-blue-950/20",
37
+ icon: "text-blue-600 dark:text-blue-400",
38
+ value: "text-blue-700 dark:text-blue-300",
39
+ label: "text-blue-600/70 dark:text-blue-400/70",
40
+ },
41
+ yellow: {
42
+ container:
43
+ "border-yellow-200/50 bg-yellow-50/50 dark:border-yellow-900/30 dark:bg-yellow-950/20",
44
+ icon: "text-yellow-600 dark:text-yellow-400",
45
+ value: "text-yellow-700 dark:text-yellow-300",
46
+ label: "text-yellow-600/70 dark:text-yellow-400/70",
47
+ },
48
+ green: {
49
+ container:
50
+ "border-green-200/50 bg-green-50/50 dark:border-green-900/30 dark:bg-green-950/20",
51
+ icon: "text-green-600 dark:text-green-400",
52
+ value: "text-green-700 dark:text-green-300",
53
+ label: "text-green-600/70 dark:text-green-400/70",
54
+ },
55
+ red: {
56
+ container:
57
+ "border-red-200/50 bg-red-50/50 dark:border-red-900/30 dark:bg-red-950/20",
58
+ icon: "text-red-600 dark:text-red-400",
59
+ value: "text-red-700 dark:text-red-300",
60
+ label: "text-red-600/70 dark:text-red-400/70",
61
+ },
62
+ purple: {
63
+ container:
64
+ "border-purple-200/50 bg-purple-50/50 dark:border-purple-900/30 dark:bg-purple-950/20",
65
+ icon: "text-purple-600 dark:text-purple-400",
66
+ value: "text-purple-700 dark:text-purple-300",
67
+ label: "text-purple-600/70 dark:text-purple-400/70",
68
+ },
69
+ };
70
+
71
+ /**
72
+ * Apple-inspired icon badge for displaying token balances and stats
73
+ * Subtle borders, clean rounded design
74
+ */
75
+ export function IconBadge({
76
+ icon: Icon,
77
+ value,
78
+ label,
79
+ size = "md",
80
+ variant = "blue",
81
+ onClick,
82
+ }: IconBadgeProps) {
83
+ const sizeStyle = sizeClasses[size];
84
+ const variantStyle = variantClasses[variant];
85
+ const isClickable = !!onClick;
86
+
87
+ return (
88
+ <div
89
+ className={`
90
+ flex items-center rounded-xl border transition-all duration-200
91
+ ${sizeStyle.container} ${variantStyle.container}
92
+ ${isClickable ? "cursor-pointer hover:shadow-md active:scale-95" : ""}
93
+ `}
94
+ onClick={onClick}
95
+ {...(isClickable && {
96
+ role: "button",
97
+ tabIndex: 0,
98
+ onKeyDown: (e) => {
99
+ if (e.key === "Enter" || e.key === " ") {
100
+ e.preventDefault();
101
+ onClick?.();
102
+ }
103
+ },
104
+ })}
105
+ >
106
+ <Icon
107
+ className={`${sizeStyle.icon} ${variantStyle.icon}`}
108
+ strokeWidth={2}
109
+ />
110
+ <div className="flex flex-col">
111
+ <div
112
+ className={`font-semibold tabular-nums ${sizeStyle.value} ${variantStyle.value}`}
113
+ >
114
+ {value}
115
+ </div>
116
+ {label && (
117
+ <div
118
+ className={`font-medium ${sizeStyle.label} ${variantStyle.label}`}
119
+ >
120
+ {label}
121
+ </div>
122
+ )}
123
+ </div>
124
+ </div>
125
+ );
126
+ }
@@ -0,0 +1,25 @@
1
+ import * as React from "react";
2
+
3
+ import { cn } from "../utils/cn";
4
+
5
+ export interface InputProps
6
+ extends React.InputHTMLAttributes<HTMLInputElement> {}
7
+
8
+ const Input = React.forwardRef<HTMLInputElement, InputProps>(
9
+ ({ className, type, ...props }, ref) => {
10
+ return (
11
+ <input
12
+ type={type}
13
+ className={cn(
14
+ "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
15
+ className,
16
+ )}
17
+ ref={ref}
18
+ {...props}
19
+ />
20
+ );
21
+ },
22
+ );
23
+ Input.displayName = "Input";
24
+
25
+ export { Input };
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ import * as LabelPrimitive from "@radix-ui/react-label";
4
+ import { cva, type VariantProps } from "class-variance-authority";
5
+ import * as React from "react";
6
+
7
+ import { cn } from "../utils/cn";
8
+
9
+ const labelVariants = cva(
10
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
11
+ );
12
+
13
+ const Label = React.forwardRef<
14
+ React.ElementRef<typeof LabelPrimitive.Root>,
15
+ React.ComponentPropsWithoutRef<typeof LabelPrimitive.Root> &
16
+ VariantProps<typeof labelVariants>
17
+ >(({ className, ...props }, ref) => (
18
+ <LabelPrimitive.Root
19
+ ref={ref}
20
+ className={cn(labelVariants(), className)}
21
+ {...props}
22
+ />
23
+ ));
24
+ Label.displayName = LabelPrimitive.Root.displayName;
25
+
26
+ export { Label };
@@ -0,0 +1,28 @@
1
+ "use client";
2
+
3
+ import * as ProgressPrimitive from "@radix-ui/react-progress";
4
+ import * as React from "react";
5
+
6
+ import { cn } from "../utils/cn";
7
+
8
+ const Progress = React.forwardRef<
9
+ React.ElementRef<typeof ProgressPrimitive.Root>,
10
+ React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
11
+ >(({ className, value, ...props }, ref) => (
12
+ <ProgressPrimitive.Root
13
+ ref={ref}
14
+ className={cn(
15
+ "relative h-4 w-full overflow-hidden rounded-full bg-secondary",
16
+ className,
17
+ )}
18
+ {...props}
19
+ >
20
+ <ProgressPrimitive.Indicator
21
+ className="h-full w-full flex-1 bg-primary transition-all"
22
+ style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
23
+ />
24
+ </ProgressPrimitive.Root>
25
+ ));
26
+ Progress.displayName = ProgressPrimitive.Root.displayName;
27
+
28
+ export { Progress };
@@ -0,0 +1,42 @@
1
+ import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
2
+ import { Circle } from "lucide-react";
3
+ import * as React from "react";
4
+
5
+ import { cn } from "../utils/cn";
6
+
7
+ const RadioGroup = React.forwardRef<
8
+ React.ElementRef<typeof RadioGroupPrimitive.Root>,
9
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Root>
10
+ >(({ className, ...props }, ref) => {
11
+ return (
12
+ <RadioGroupPrimitive.Root
13
+ className={cn("grid gap-2", className)}
14
+ {...props}
15
+ ref={ref}
16
+ />
17
+ );
18
+ });
19
+ RadioGroup.displayName = RadioGroupPrimitive.Root.displayName;
20
+
21
+ const RadioGroupItem = React.forwardRef<
22
+ React.ElementRef<typeof RadioGroupPrimitive.Item>,
23
+ React.ComponentPropsWithoutRef<typeof RadioGroupPrimitive.Item>
24
+ >(({ className, ...props }, ref) => {
25
+ return (
26
+ <RadioGroupPrimitive.Item
27
+ ref={ref}
28
+ className={cn(
29
+ "aspect-square h-4 w-4 rounded-full border border-primary text-primary ring-offset-background focus:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
30
+ className,
31
+ )}
32
+ {...props}
33
+ >
34
+ <RadioGroupPrimitive.Indicator className="flex items-center justify-center">
35
+ <Circle className="h-2.5 w-2.5 fill-current text-current" />
36
+ </RadioGroupPrimitive.Indicator>
37
+ </RadioGroupPrimitive.Item>
38
+ );
39
+ });
40
+ RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName;
41
+
42
+ export { RadioGroup, RadioGroupItem };