@wealthx/shadcn 0.0.1 → 1.0.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 (186) hide show
  1. package/.turbo/turbo-build.log +160 -0
  2. package/CHANGELOG.md +13 -0
  3. package/CHANGES.md +345 -0
  4. package/dist/chunk-2WZVSBAY.mjs +232 -0
  5. package/dist/chunk-2Y7YJKPE.mjs +47 -0
  6. package/dist/chunk-3U7SD3MS.mjs +55 -0
  7. package/dist/chunk-3VQNJ235.mjs +114 -0
  8. package/dist/chunk-55CEW76V.mjs +35 -0
  9. package/dist/chunk-6AFMNC42.mjs +146 -0
  10. package/dist/chunk-6OJF6XRN.mjs +117 -0
  11. package/dist/chunk-7LDIMXGM.mjs +181 -0
  12. package/dist/chunk-AMJ23O53.mjs +122 -0
  13. package/dist/chunk-BBJBJSXQ.mjs +44 -0
  14. package/dist/chunk-BGP2N52Z.mjs +126 -0
  15. package/dist/chunk-BMFN37JH.mjs +41 -0
  16. package/dist/chunk-CGOKTPXU.mjs +79 -0
  17. package/dist/chunk-CZ3BW5GL.mjs +81 -0
  18. package/dist/chunk-DBHJ5KC3.mjs +55 -0
  19. package/dist/chunk-DDPA2XXS.mjs +97 -0
  20. package/dist/chunk-DS2AMHN2.mjs +30 -0
  21. package/dist/chunk-E3K6O4FZ.mjs +57 -0
  22. package/dist/chunk-FWCSY2DS.mjs +37 -0
  23. package/dist/chunk-GPRJQ24C.mjs +28 -0
  24. package/dist/chunk-HS7TFG7V.mjs +24 -0
  25. package/dist/chunk-HUVTPUV2.mjs +256 -0
  26. package/dist/chunk-IAOOZCUY.mjs +90 -0
  27. package/dist/chunk-JF4PHPD5.mjs +111 -0
  28. package/dist/chunk-JU2RUWHF.mjs +123 -0
  29. package/dist/chunk-KKHTJNMM.mjs +86 -0
  30. package/dist/chunk-MJIEMGRD.mjs +266 -0
  31. package/dist/chunk-MKFL5MNH.mjs +372 -0
  32. package/dist/chunk-MQ72DIBH.mjs +105 -0
  33. package/dist/chunk-NGYG2EA6.mjs +148 -0
  34. package/dist/chunk-NWZ46DJL.mjs +213 -0
  35. package/dist/chunk-OXQQNQZI.mjs +75 -0
  36. package/dist/chunk-PMKODV6M.mjs +161 -0
  37. package/dist/chunk-QOJ2DQD6.mjs +57 -0
  38. package/dist/chunk-RL772EH7.mjs +126 -0
  39. package/dist/chunk-SLWCCURD.mjs +99 -0
  40. package/dist/chunk-V7CNWJT3.mjs +10 -0
  41. package/dist/chunk-VG6UF6UT.mjs +68 -0
  42. package/dist/chunk-VYMHBV6D.mjs +123 -0
  43. package/dist/chunk-VZ2NR7L3.mjs +195 -0
  44. package/dist/chunk-YN5SYTOO.mjs +117 -0
  45. package/dist/chunk-Z3MK2KKZ.mjs +83 -0
  46. package/dist/chunk-ZN2QKLF6.mjs +187 -0
  47. package/dist/chunk-ZZV5JVNW.mjs +34 -0
  48. package/dist/components/ui/accordion.js +142 -0
  49. package/dist/components/ui/accordion.mjs +14 -0
  50. package/dist/components/ui/alert-dialog.js +413 -0
  51. package/dist/components/ui/alert-dialog.mjs +34 -0
  52. package/dist/components/ui/alert.js +134 -0
  53. package/dist/components/ui/alert.mjs +12 -0
  54. package/dist/components/ui/avatar.js +173 -0
  55. package/dist/components/ui/avatar.mjs +18 -0
  56. package/dist/components/ui/badge.js +163 -0
  57. package/dist/components/ui/badge.mjs +11 -0
  58. package/dist/components/ui/button.js +198 -0
  59. package/dist/components/ui/button.mjs +11 -0
  60. package/dist/components/ui/calendar.js +408 -0
  61. package/dist/components/ui/calendar.mjs +12 -0
  62. package/dist/components/ui/card.js +156 -0
  63. package/dist/components/ui/card.mjs +20 -0
  64. package/dist/components/ui/checkbox.js +166 -0
  65. package/dist/components/ui/checkbox.mjs +11 -0
  66. package/dist/components/ui/chip.js +199 -0
  67. package/dist/components/ui/chip.mjs +10 -0
  68. package/dist/components/ui/data-table.js +925 -0
  69. package/dist/components/ui/data-table.mjs +29 -0
  70. package/dist/components/ui/date-picker.js +561 -0
  71. package/dist/components/ui/date-picker.mjs +15 -0
  72. package/dist/components/ui/dialog.js +378 -0
  73. package/dist/components/ui/dialog.mjs +30 -0
  74. package/dist/components/ui/drawer.js +213 -0
  75. package/dist/components/ui/drawer.mjs +28 -0
  76. package/dist/components/ui/dropdown-menu.js +338 -0
  77. package/dist/components/ui/dropdown-menu.mjs +38 -0
  78. package/dist/components/ui/empty.js +173 -0
  79. package/dist/components/ui/empty.mjs +18 -0
  80. package/dist/components/ui/field.js +359 -0
  81. package/dist/components/ui/field.mjs +28 -0
  82. package/dist/components/ui/input-group.js +406 -0
  83. package/dist/components/ui/input-group.mjs +22 -0
  84. package/dist/components/ui/input-otp.js +149 -0
  85. package/dist/components/ui/input-otp.mjs +14 -0
  86. package/dist/components/ui/input.js +81 -0
  87. package/dist/components/ui/input.mjs +8 -0
  88. package/dist/components/ui/label.js +85 -0
  89. package/dist/components/ui/label.mjs +8 -0
  90. package/dist/components/ui/pagination.js +333 -0
  91. package/dist/components/ui/pagination.mjs +22 -0
  92. package/dist/components/ui/popover.js +167 -0
  93. package/dist/components/ui/popover.mjs +22 -0
  94. package/dist/components/ui/progress.js +97 -0
  95. package/dist/components/ui/progress.mjs +8 -0
  96. package/dist/components/ui/radio-group.js +178 -0
  97. package/dist/components/ui/radio-group.mjs +12 -0
  98. package/dist/components/ui/select.js +262 -0
  99. package/dist/components/ui/select.mjs +28 -0
  100. package/dist/components/ui/separator.js +86 -0
  101. package/dist/components/ui/separator.mjs +8 -0
  102. package/dist/components/ui/sheet.js +227 -0
  103. package/dist/components/ui/sheet.mjs +26 -0
  104. package/dist/components/ui/skeleton.js +75 -0
  105. package/dist/components/ui/skeleton.mjs +8 -0
  106. package/dist/components/ui/sonner.js +86 -0
  107. package/dist/components/ui/sonner.mjs +7 -0
  108. package/dist/components/ui/spinner.js +93 -0
  109. package/dist/components/ui/spinner.mjs +10 -0
  110. package/dist/components/ui/switch.js +178 -0
  111. package/dist/components/ui/switch.mjs +11 -0
  112. package/dist/components/ui/table.js +184 -0
  113. package/dist/components/ui/table.mjs +22 -0
  114. package/dist/components/ui/tabs.js +181 -0
  115. package/dist/components/ui/tabs.mjs +16 -0
  116. package/dist/components/ui/textarea.js +79 -0
  117. package/dist/components/ui/textarea.mjs +8 -0
  118. package/dist/components/ui/toggle-group.js +184 -0
  119. package/dist/components/ui/toggle-group.mjs +12 -0
  120. package/dist/components/ui/toggle.js +108 -0
  121. package/dist/components/ui/toggle.mjs +11 -0
  122. package/dist/components/ui/tooltip.js +140 -0
  123. package/dist/components/ui/tooltip.mjs +16 -0
  124. package/dist/index.js +4409 -0
  125. package/dist/index.mjs +462 -0
  126. package/dist/lib/colors.js +84 -0
  127. package/dist/lib/colors.mjs +13 -0
  128. package/dist/lib/theme-provider.js +150 -0
  129. package/dist/lib/theme-provider.mjs +13 -0
  130. package/dist/lib/typography.js +157 -0
  131. package/dist/lib/typography.mjs +25 -0
  132. package/dist/lib/utils.js +34 -0
  133. package/dist/lib/utils.mjs +7 -0
  134. package/dist/styles.css +2 -0
  135. package/package.json +228 -11
  136. package/scripts/build-css.ts +15 -9
  137. package/src/components/index.tsx +443 -0
  138. package/src/components/ui/accordion.tsx +99 -0
  139. package/src/components/ui/alert-dialog.tsx +239 -0
  140. package/src/components/ui/alert.tsx +81 -0
  141. package/src/components/ui/avatar.tsx +130 -0
  142. package/src/components/ui/badge.tsx +57 -0
  143. package/src/components/ui/button.tsx +69 -37
  144. package/src/components/ui/calendar.tsx +252 -0
  145. package/src/components/ui/card.tsx +106 -0
  146. package/src/components/ui/checkbox.tsx +111 -0
  147. package/src/components/ui/chip.tsx +65 -0
  148. package/src/components/ui/data-table.tsx +490 -0
  149. package/src/components/ui/date-picker.tsx +133 -0
  150. package/src/components/ui/dialog.tsx +195 -0
  151. package/src/components/ui/drawer.tsx +169 -0
  152. package/src/components/ui/dropdown-menu.tsx +315 -0
  153. package/src/components/ui/empty.tsx +128 -0
  154. package/src/components/ui/field.tsx +273 -0
  155. package/src/components/ui/input-group.tsx +190 -0
  156. package/src/components/ui/input-otp.tsx +90 -0
  157. package/src/components/ui/input.tsx +28 -0
  158. package/src/components/ui/label.tsx +24 -0
  159. package/src/components/ui/pagination.tsx +148 -0
  160. package/src/components/ui/popover.tsx +112 -0
  161. package/src/components/ui/progress.tsx +40 -0
  162. package/src/components/ui/radio-group.tsx +129 -0
  163. package/src/components/ui/select.tsx +201 -0
  164. package/src/components/ui/separator.tsx +26 -0
  165. package/src/components/ui/sheet.tsx +182 -0
  166. package/src/components/ui/skeleton.tsx +22 -0
  167. package/src/components/ui/sonner.tsx +48 -0
  168. package/src/components/ui/spinner.tsx +41 -0
  169. package/src/components/ui/switch.tsx +126 -0
  170. package/src/components/ui/table.tsx +143 -0
  171. package/src/components/ui/tabs.tsx +119 -0
  172. package/src/components/ui/textarea.tsx +28 -0
  173. package/src/components/ui/toggle-group.tsx +94 -0
  174. package/src/components/ui/toggle.tsx +59 -0
  175. package/src/components/ui/tooltip.tsx +80 -0
  176. package/src/index.ts +15 -3
  177. package/src/lib/colors.ts +74 -0
  178. package/src/lib/slot.tsx +68 -0
  179. package/src/lib/theme-provider.tsx +134 -0
  180. package/src/lib/typography.ts +153 -0
  181. package/src/lib/utils.ts +1 -1
  182. package/src/styles/globals.css +377 -107
  183. package/src/styles/styles-css.ts +1 -1
  184. package/tsup.config.ts +48 -2
  185. package/src/provider/ShadcnProvider.tsx +0 -89
  186. package/src/provider/index.ts +0 -2
@@ -0,0 +1,128 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+ import { cn } from "@/lib/utils"
5
+
6
+ /**
7
+ * Empty — WealthX DS (shadcn registry base)
8
+ *
9
+ * Composable empty-state component for no-data / blank-state screens.
10
+ * WealthX overrides:
11
+ * - `rounded-lg` removed — sharp corners per WealthX DS
12
+ */
13
+
14
+ export type EmptyProps = React.ComponentProps<"div">
15
+
16
+ function Empty({ className, ...props }: EmptyProps): ReactElement {
17
+ return (
18
+ <div
19
+ className={cn(
20
+ "flex min-w-0 flex-1 flex-col items-center justify-center gap-6 border border-dashed p-6 text-center text-balance md:p-12",
21
+ className
22
+ )}
23
+ data-slot="empty"
24
+ {...props}
25
+ />
26
+ )
27
+ }
28
+
29
+ export type EmptyHeaderProps = React.ComponentProps<"div">
30
+
31
+ function EmptyHeader({ className, ...props }: EmptyHeaderProps): ReactElement {
32
+ return (
33
+ <div
34
+ className={cn(
35
+ "flex max-w-sm flex-col items-center gap-2 text-center",
36
+ className
37
+ )}
38
+ data-slot="empty-header"
39
+ {...props}
40
+ />
41
+ )
42
+ }
43
+
44
+ const emptyMediaVariants = cva(
45
+ "mb-2 flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0",
46
+ {
47
+ variants: {
48
+ variant: {
49
+ default: "[&_svg:not([class*='size-'])]:size-10",
50
+ icon: "flex size-10 shrink-0 items-center justify-center text-muted-foreground [&_svg:not([class*='size-'])]:size-6",
51
+ },
52
+ },
53
+ defaultVariants: {
54
+ variant: "default",
55
+ },
56
+ }
57
+ )
58
+
59
+ export type EmptyMediaProps = React.ComponentProps<"div"> & VariantProps<typeof emptyMediaVariants>
60
+
61
+ function EmptyMedia({
62
+ className,
63
+ variant = "default",
64
+ ...props
65
+ }: EmptyMediaProps): ReactElement {
66
+ return (
67
+ <div
68
+ className={cn(emptyMediaVariants({ variant, className }))}
69
+ data-slot="empty-icon"
70
+ data-variant={variant}
71
+ {...props}
72
+ />
73
+ )
74
+ }
75
+
76
+ export type EmptyTitleProps = React.ComponentProps<"div">
77
+
78
+ function EmptyTitle({ className, ...props }: EmptyTitleProps): ReactElement {
79
+ return (
80
+ <div
81
+ className={cn("text-lg font-medium tracking-tight", className)}
82
+ data-slot="empty-title"
83
+ {...props}
84
+ />
85
+ )
86
+ }
87
+
88
+ export type EmptyDescriptionProps = React.ComponentProps<"div">
89
+
90
+ function EmptyDescription({
91
+ className,
92
+ ...props
93
+ }: EmptyDescriptionProps): ReactElement {
94
+ return (
95
+ <div
96
+ className={cn(
97
+ "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
98
+ className
99
+ )}
100
+ data-slot="empty-description"
101
+ {...props}
102
+ />
103
+ )
104
+ }
105
+
106
+ export type EmptyContentProps = React.ComponentProps<"div">
107
+
108
+ function EmptyContent({ className, ...props }: EmptyContentProps): ReactElement {
109
+ return (
110
+ <div
111
+ className={cn(
112
+ "flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance",
113
+ className
114
+ )}
115
+ data-slot="empty-content"
116
+ {...props}
117
+ />
118
+ )
119
+ }
120
+
121
+ export {
122
+ Empty,
123
+ EmptyHeader,
124
+ EmptyTitle,
125
+ EmptyDescription,
126
+ EmptyContent,
127
+ EmptyMedia,
128
+ }
@@ -0,0 +1,273 @@
1
+ import { type ReactElement, useMemo } from "react"
2
+ import { cva, type VariantProps } from "class-variance-authority"
3
+ import { cn } from "@/lib/utils"
4
+ import { Label } from "@/components/ui/label"
5
+ import { Separator } from "@/components/ui/separator"
6
+
7
+ /**
8
+ * Field — shadcn/WealthX
9
+ * Base: npx shadcn\@latest add field
10
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=1188-4205
11
+ *
12
+ * WealthX additions:
13
+ * - FieldDescription/FieldError: text-xs (12px) per Figma helper text spec
14
+ * - FieldError.uniqueErrors: Array.from() instead of spread on Map iterator (es5 compat)
15
+ */
16
+
17
+ export type FieldSetProps = React.ComponentProps<"fieldset">
18
+
19
+ function FieldSet({ className, ...props }: FieldSetProps): ReactElement {
20
+ return (
21
+ <fieldset
22
+ className={cn(
23
+ "flex flex-col gap-6",
24
+ "has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3",
25
+ className
26
+ )}
27
+ data-slot="field-set"
28
+ {...props}
29
+ />
30
+ )
31
+ }
32
+
33
+ export type FieldLegendProps = React.ComponentProps<"legend"> & { variant?: "legend" | "label" }
34
+
35
+ function FieldLegend({
36
+ className,
37
+ variant = "legend",
38
+ ...props
39
+ }: FieldLegendProps): ReactElement {
40
+ return (
41
+ <legend
42
+ className={cn(
43
+ "mb-3 font-medium",
44
+ "data-[variant=legend]:text-base",
45
+ "data-[variant=label]:text-sm",
46
+ className
47
+ )}
48
+ data-slot="field-legend"
49
+ data-variant={variant}
50
+ {...props}
51
+ />
52
+ )
53
+ }
54
+
55
+ export type FieldGroupProps = React.ComponentProps<"div">
56
+
57
+ function FieldGroup({ className, ...props }: FieldGroupProps): ReactElement {
58
+ return (
59
+ <div
60
+ className={cn(
61
+ "group/field-group @container/field-group flex w-full flex-col gap-7 data-[slot=checkbox-group]:gap-3 [&>[data-slot=field-group]]:gap-4",
62
+ className
63
+ )}
64
+ data-slot="field-group"
65
+ {...props}
66
+ />
67
+ )
68
+ }
69
+
70
+ const fieldVariants = cva(
71
+ "group/field flex w-full gap-3 data-[invalid=true]:text-destructive",
72
+ {
73
+ variants: {
74
+ orientation: {
75
+ vertical: ["flex-col [&>*]:w-full [&>.sr-only]:w-auto"],
76
+ horizontal: [
77
+ "flex-row items-center",
78
+ "[&>[data-slot=field-label]]:flex-auto",
79
+ "has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
80
+ ],
81
+ responsive: [
82
+ "flex-col @md/field-group:flex-row @md/field-group:items-center [&>*]:w-full @md/field-group:[&>*]:w-auto [&>.sr-only]:w-auto",
83
+ "@md/field-group:[&>[data-slot=field-label]]:flex-auto",
84
+ "@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px",
85
+ ],
86
+ },
87
+ },
88
+ defaultVariants: {
89
+ orientation: "vertical",
90
+ },
91
+ }
92
+ )
93
+
94
+ export type FieldProps = React.ComponentProps<"div"> & VariantProps<typeof fieldVariants>
95
+
96
+ function Field({
97
+ className,
98
+ orientation = "vertical",
99
+ ...props
100
+ }: FieldProps): ReactElement {
101
+ return (
102
+ <div
103
+ className={cn(fieldVariants({ orientation }), className)}
104
+ data-orientation={orientation}
105
+ data-slot="field"
106
+ role="group"
107
+ {...props}
108
+ />
109
+ )
110
+ }
111
+
112
+ export type FieldContentProps = React.ComponentProps<"div">
113
+
114
+ function FieldContent({ className, ...props }: FieldContentProps): ReactElement {
115
+ return (
116
+ <div
117
+ className={cn(
118
+ "group/field-content flex flex-1 flex-col gap-1.5 leading-snug",
119
+ className
120
+ )}
121
+ data-slot="field-content"
122
+ {...props}
123
+ />
124
+ )
125
+ }
126
+
127
+ export type FieldLabelProps = React.ComponentProps<typeof Label>
128
+
129
+ function FieldLabel({
130
+ className,
131
+ ...props
132
+ }: FieldLabelProps): ReactElement {
133
+ return (
134
+ <Label
135
+ className={cn(
136
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50",
137
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4",
138
+ "has-data-checked:border-primary has-data-checked:bg-primary/5 dark:has-data-checked:bg-primary/10",
139
+ className
140
+ )}
141
+ data-slot="field-label"
142
+ {...props}
143
+ />
144
+ )
145
+ }
146
+
147
+ export type FieldTitleProps = React.ComponentProps<"div">
148
+
149
+ function FieldTitle({ className, ...props }: FieldTitleProps): ReactElement {
150
+ return (
151
+ <div
152
+ className={cn(
153
+ "flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50",
154
+ className
155
+ )}
156
+ data-slot="field-label"
157
+ {...props}
158
+ />
159
+ )
160
+ }
161
+
162
+ export type FieldDescriptionProps = React.ComponentProps<"p">
163
+
164
+ function FieldDescription({ className, ...props }: FieldDescriptionProps): ReactElement {
165
+ return (
166
+ <p
167
+ className={cn(
168
+ "text-xs leading-normal font-normal text-muted-foreground group-has-[[data-orientation=horizontal]]/field:text-balance",
169
+ "last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5",
170
+ "[&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
171
+ className
172
+ )}
173
+ data-slot="field-description"
174
+ {...props}
175
+ />
176
+ )
177
+ }
178
+
179
+ export type FieldSeparatorProps = React.ComponentProps<"div"> & {
180
+ children?: React.ReactNode
181
+ }
182
+
183
+ function FieldSeparator({
184
+ children,
185
+ className,
186
+ ...props
187
+ }: FieldSeparatorProps): ReactElement {
188
+ return (
189
+ <div
190
+ className={cn(
191
+ "relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2",
192
+ className
193
+ )}
194
+ data-content={Boolean(children)}
195
+ data-slot="field-separator"
196
+ {...props}
197
+ >
198
+ <Separator className="absolute inset-0 top-1/2" />
199
+ {children ? <span
200
+ className="relative mx-auto block w-fit bg-background px-2 text-muted-foreground"
201
+ data-slot="field-separator-content"
202
+ >
203
+ {children}
204
+ </span> : null}
205
+ </div>
206
+ )
207
+ }
208
+
209
+ export type FieldErrorProps = React.ComponentProps<"div"> & {
210
+ errors?: ({ message?: string } | undefined)[]
211
+ }
212
+
213
+ function FieldError({
214
+ className,
215
+ children,
216
+ errors,
217
+ ...props
218
+ }: FieldErrorProps): ReactElement | null {
219
+ const content = useMemo(() => {
220
+ if (children) {
221
+ return children
222
+ }
223
+
224
+ if (!errors?.length) {
225
+ return null
226
+ }
227
+
228
+ const uniqueErrors = Array.from(
229
+ new Map(errors.map((error) => [error?.message, error])).values()
230
+ )
231
+
232
+ if (uniqueErrors.length === 1) {
233
+ return uniqueErrors[0]?.message
234
+ }
235
+
236
+ return (
237
+ <ul className="ml-4 flex list-disc flex-col gap-1">
238
+ {uniqueErrors.map(
239
+ (error) =>
240
+ error?.message && <li key={error.message}>{error.message}</li>
241
+ )}
242
+ </ul>
243
+ )
244
+ }, [children, errors])
245
+
246
+ if (!content) {
247
+ return null
248
+ }
249
+
250
+ return (
251
+ <div
252
+ className={cn("text-xs font-normal text-destructive", className)}
253
+ data-slot="field-error"
254
+ role="alert"
255
+ {...props}
256
+ >
257
+ {content}
258
+ </div>
259
+ )
260
+ }
261
+
262
+ export {
263
+ Field,
264
+ FieldLabel,
265
+ FieldDescription,
266
+ FieldError,
267
+ FieldGroup,
268
+ FieldLegend,
269
+ FieldSeparator,
270
+ FieldSet,
271
+ FieldContent,
272
+ FieldTitle,
273
+ }
@@ -0,0 +1,190 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { cva, type VariantProps } from "class-variance-authority"
4
+ import { cn } from "@/lib/utils"
5
+ import { Button } from "@/components/ui/button"
6
+ import { Input } from "@/components/ui/input"
7
+ import { Textarea } from "@/components/ui/textarea"
8
+
9
+ /**
10
+ * InputGroup — shadcn/WealthX
11
+ * Base: npx shadcn\@latest add input-group
12
+ * WealthX: removed rounded-md (square corners per brand)
13
+ */
14
+ export type InputGroupProps = React.ComponentProps<"div">
15
+
16
+ function InputGroup({ className, ...props }: InputGroupProps): ReactElement {
17
+ return (
18
+ <div
19
+ className={cn(
20
+ "group/input-group relative flex w-full items-center border border-input shadow-xs transition-[color,box-shadow] outline-none dark:bg-input/30",
21
+ "h-9 min-w-0 has-[>textarea]:h-auto",
22
+
23
+ // Variants based on alignment.
24
+ "has-[>[data-align=inline-start]]:[&>input]:pl-2",
25
+ "has-[>[data-align=inline-end]]:[&>input]:pr-2",
26
+ "has-[>[data-align=block-start]]:h-auto has-[>[data-align=block-start]]:flex-col has-[>[data-align=block-start]]:[&>input]:pb-3",
27
+ "has-[>[data-align=block-end]]:h-auto has-[>[data-align=block-end]]:flex-col has-[>[data-align=block-end]]:[&>input]:pt-3",
28
+
29
+ // Focus state.
30
+ "has-[[data-slot=input-group-control]:focus-visible]:border-primary has-[[data-slot=input-group-control]:focus-visible]:ring-[3px] has-[[data-slot=input-group-control]:focus-visible]:ring-primary/20",
31
+
32
+ // Error state.
33
+ "has-[[data-slot][aria-invalid=true]]:border-destructive has-[[data-slot][aria-invalid=true]]:ring-destructive/20 dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40",
34
+
35
+ className
36
+ )}
37
+ data-slot="input-group"
38
+ role="group"
39
+ {...props}
40
+ />
41
+ )
42
+ }
43
+
44
+ const inputGroupAddonVariants = cva(
45
+ "flex h-auto cursor-text items-center justify-center gap-2 py-1.5 text-sm font-medium text-muted-foreground select-none group-data-[disabled=true]/input-group:opacity-50 [&>kbd]:rounded-[calc(var(--radius)-5px)] [&>svg:not([class*='size-'])]:size-4",
46
+ {
47
+ variants: {
48
+ align: {
49
+ "inline-start":
50
+ "order-first pl-3 has-[>button]:ml-[-0.45rem] has-[>kbd]:ml-[-0.35rem]",
51
+ "inline-end":
52
+ "order-last pr-3 has-[>button]:mr-[-0.45rem] has-[>kbd]:mr-[-0.35rem]",
53
+ "block-start":
54
+ "order-first w-full justify-start px-3 pt-3 group-has-[>input]/input-group:pt-2.5 [.border-b]:pb-3",
55
+ "block-end":
56
+ "order-last w-full justify-start px-3 pb-3 group-has-[>input]/input-group:pb-2.5 [.border-t]:pt-3",
57
+ },
58
+ },
59
+ defaultVariants: {
60
+ align: "inline-start",
61
+ },
62
+ }
63
+ )
64
+
65
+ export type InputGroupAddonProps = React.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>
66
+
67
+ function InputGroupAddon({
68
+ className,
69
+ align = "inline-start",
70
+ ...props
71
+ }: InputGroupAddonProps): ReactElement {
72
+ return (
73
+ // eslint-disable-next-line jsx-a11y/no-noninteractive-element-interactions -- click only forwards focus to the sibling input; the div is not truly interactive
74
+ <div
75
+ className={cn(inputGroupAddonVariants({ align }), className)}
76
+ data-align={align}
77
+ data-slot="input-group-addon"
78
+ onClick={(e) => {
79
+ if ((e.target as HTMLElement).closest("button")) {
80
+ return
81
+ }
82
+ e.currentTarget.parentElement?.querySelector("input")?.focus()
83
+ }}
84
+ onKeyDown={(e) => {
85
+ if (e.key === "Enter" || e.key === " ") {
86
+ e.currentTarget.parentElement?.querySelector("input")?.focus()
87
+ }
88
+ }}
89
+ role="group"
90
+ {...props}
91
+ />
92
+ )
93
+ }
94
+
95
+ const inputGroupButtonVariants = cva(
96
+ "flex items-center gap-2 text-sm shadow-none",
97
+ {
98
+ variants: {
99
+ size: {
100
+ xs: "h-6 gap-1 px-2 has-[>svg]:px-2 [&>svg:not([class*='size-'])]:size-3.5",
101
+ sm: "h-8 gap-1.5 px-2.5 has-[>svg]:px-2.5",
102
+ "icon-xs": "size-6 p-0 has-[>svg]:p-0",
103
+ "icon-sm": "size-8 p-0 has-[>svg]:p-0",
104
+ },
105
+ },
106
+ defaultVariants: {
107
+ size: "xs",
108
+ },
109
+ }
110
+ )
111
+
112
+ export type InputGroupButtonProps = Omit<React.ComponentProps<typeof Button>, "size"> &
113
+ VariantProps<typeof inputGroupButtonVariants>
114
+
115
+ function InputGroupButton({
116
+ className,
117
+ type = "button",
118
+ variant = "ghost",
119
+ size = "xs",
120
+ ...props
121
+ }: InputGroupButtonProps): ReactElement {
122
+ return (
123
+ <Button
124
+ className={cn(inputGroupButtonVariants({ size }), className)}
125
+ data-size={size}
126
+ type={type}
127
+ variant={variant}
128
+ {...props}
129
+ />
130
+ )
131
+ }
132
+
133
+ export type InputGroupTextProps = React.ComponentProps<"span">
134
+
135
+ function InputGroupText({ className, ...props }: InputGroupTextProps): ReactElement {
136
+ return (
137
+ <span
138
+ className={cn(
139
+ "flex items-center gap-2 text-sm text-muted-foreground [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4",
140
+ className
141
+ )}
142
+ {...props}
143
+ />
144
+ )
145
+ }
146
+
147
+ export type InputGroupInputProps = React.ComponentProps<"input">
148
+
149
+ function InputGroupInput({
150
+ className,
151
+ ...props
152
+ }: InputGroupInputProps): ReactElement {
153
+ return (
154
+ <Input
155
+ className={cn(
156
+ "flex-1 rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0 dark:bg-transparent",
157
+ className
158
+ )}
159
+ data-slot="input-group-control"
160
+ {...props}
161
+ />
162
+ )
163
+ }
164
+
165
+ export type InputGroupTextareaProps = React.ComponentProps<"textarea">
166
+
167
+ function InputGroupTextarea({
168
+ className,
169
+ ...props
170
+ }: InputGroupTextareaProps): ReactElement {
171
+ return (
172
+ <Textarea
173
+ className={cn(
174
+ "flex-1 resize-none rounded-none border-0 bg-transparent py-3 shadow-none focus-visible:ring-0 dark:bg-transparent",
175
+ className
176
+ )}
177
+ data-slot="input-group-control"
178
+ {...props}
179
+ />
180
+ )
181
+ }
182
+
183
+ export {
184
+ InputGroup,
185
+ InputGroupAddon,
186
+ InputGroupButton,
187
+ InputGroupText,
188
+ InputGroupInput,
189
+ InputGroupTextarea,
190
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * InputOTP — shadcn/WealthX
3
+ * Base: npx shadcn\@latest add input-otp
4
+ * Figma: https://www.figma.com/design/9V9F0NGVsif8LGmEhVjOcT/Design-System---shadcn?node-id=101-698
5
+ *
6
+ * WealthX overrides:
7
+ * - InputOTPSlot: removed first:rounded-l-md last:rounded-r-md — sharp corners (radius-none)
8
+ */
9
+
10
+ import { type ReactElement } from "react"
11
+ import * as React from "react"
12
+ import { OTPInput, OTPInputContext } from "input-otp"
13
+ import { MinusIcon } from "lucide-react"
14
+ import { cn } from "@/lib/utils"
15
+
16
+ export type InputOTPProps = React.ComponentProps<typeof OTPInput> & {
17
+ containerClassName?: string
18
+ }
19
+
20
+ function InputOTP({
21
+ className,
22
+ containerClassName,
23
+ ...props
24
+ }: InputOTPProps): ReactElement {
25
+ return (
26
+ <OTPInput
27
+ className={cn("disabled:cursor-not-allowed", className)}
28
+ containerClassName={cn(
29
+ "flex items-center gap-2 has-disabled:opacity-50",
30
+ containerClassName
31
+ )}
32
+ data-slot="input-otp"
33
+ {...props}
34
+ />
35
+ )
36
+ }
37
+
38
+ export type InputOTPGroupProps = React.ComponentProps<"div">
39
+
40
+ function InputOTPGroup({ className, ...props }: InputOTPGroupProps): ReactElement {
41
+ return (
42
+ <div
43
+ className={cn("flex items-center", className)}
44
+ data-slot="input-otp-group"
45
+ {...props}
46
+ />
47
+ )
48
+ }
49
+
50
+ export type InputOTPSlotProps = React.ComponentProps<"div"> & {
51
+ index: number
52
+ }
53
+
54
+ function InputOTPSlot({
55
+ index,
56
+ className,
57
+ ...props
58
+ }: InputOTPSlotProps): ReactElement {
59
+ const inputOTPContext = React.useContext(OTPInputContext)
60
+ const { char, hasFakeCaret, isActive } = inputOTPContext.slots[index] ?? {}
61
+
62
+ return (
63
+ <div
64
+ className={cn(
65
+ "relative flex h-9 w-9 items-center justify-center border-y border-r border-input text-sm shadow-xs transition-all outline-none first:border-l aria-invalid:border-destructive data-[active=true]:z-10 data-[active=true]:border-ring data-[active=true]:ring-[3px] data-[active=true]:ring-ring/50 data-[active=true]:aria-invalid:border-destructive data-[active=true]:aria-invalid:ring-destructive/20 dark:bg-input/30 dark:data-[active=true]:aria-invalid:ring-destructive/40",
66
+ className
67
+ )}
68
+ data-active={isActive}
69
+ data-slot="input-otp-slot"
70
+ {...props}
71
+ >
72
+ {char}
73
+ {hasFakeCaret ? <div className="pointer-events-none absolute inset-0 flex items-center justify-center">
74
+ <div className="h-4 w-px animate-caret-blink bg-foreground duration-1000" />
75
+ </div> : null}
76
+ </div>
77
+ )
78
+ }
79
+
80
+ export type InputOTPSeparatorProps = React.ComponentProps<"div">
81
+
82
+ function InputOTPSeparator({ ...props }: InputOTPSeparatorProps): ReactElement {
83
+ return (
84
+ <div data-slot="input-otp-separator" role="separator" {...props}>
85
+ <MinusIcon />
86
+ </div>
87
+ )
88
+ }
89
+
90
+ export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator }
@@ -0,0 +1,28 @@
1
+ import { type ReactElement } from "react"
2
+ import * as React from "react"
3
+ import { cn } from "@/lib/utils"
4
+
5
+ /**
6
+ * Input — shadcn/WealthX
7
+ * Base: npx shadcn\@latest add input
8
+ * WealthX: removed rounded-md (square corners), added font-sans.
9
+ */
10
+ export type InputProps = React.ComponentProps<"input">
11
+
12
+ function Input({ className, type, ...props }: InputProps): ReactElement {
13
+ return (
14
+ <input
15
+ className={cn(
16
+ "h-9 w-full min-w-0 border border-input bg-transparent px-3 py-1 text-base font-sans shadow-xs transition-[color,box-shadow] outline-none selection:bg-primary selection:text-primary-foreground file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm dark:bg-input/30",
17
+ "focus-visible:border-primary focus-visible:ring-[3px] focus-visible:ring-primary/20",
18
+ "aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40",
19
+ className
20
+ )}
21
+ data-slot="input"
22
+ type={type}
23
+ {...props}
24
+ />
25
+ )
26
+ }
27
+
28
+ export { Input }