@codefast/ui 0.3.16-canary.1 → 0.3.16-canary.3

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 (223) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/README.md +1 -1
  3. package/dist/components/accordion.mjs +2 -2
  4. package/dist/components/alert-dialog.d.mts +1 -1
  5. package/dist/components/alert-dialog.mjs +4 -4
  6. package/dist/components/alert.d.mts +3 -13
  7. package/dist/components/alert.mjs +3 -23
  8. package/dist/components/badge.d.mts +3 -15
  9. package/dist/components/badge.mjs +2 -44
  10. package/dist/components/breadcrumb.mjs +1 -1
  11. package/dist/components/button-group.d.mts +3 -13
  12. package/dist/components/button-group.mjs +3 -24
  13. package/dist/components/button.d.mts +3 -25
  14. package/dist/components/button.mjs +2 -72
  15. package/dist/components/calendar.mjs +2 -1
  16. package/dist/components/carousel.d.mts +1 -2
  17. package/dist/components/chart.d.mts +2 -4
  18. package/dist/components/checkbox.mjs +2 -2
  19. package/dist/components/context-menu.mjs +2 -2
  20. package/dist/components/dialog.d.mts +1 -1
  21. package/dist/components/dialog.mjs +4 -4
  22. package/dist/components/drawer.d.mts +1 -1
  23. package/dist/components/drawer.mjs +2 -2
  24. package/dist/components/dropdown-menu.mjs +2 -2
  25. package/dist/components/empty.d.mts +3 -13
  26. package/dist/components/empty.mjs +3 -18
  27. package/dist/components/field.d.mts +3 -14
  28. package/dist/components/field.mjs +3 -32
  29. package/dist/components/form.d.mts +2 -4
  30. package/dist/components/hover-card.mjs +1 -1
  31. package/dist/components/input-group.d.mts +4 -31
  32. package/dist/components/input-group.mjs +3 -90
  33. package/dist/components/input-number.mjs +4 -4
  34. package/dist/components/input-otp.mjs +2 -2
  35. package/dist/components/input.mjs +1 -1
  36. package/dist/components/item.d.mts +4 -29
  37. package/dist/components/item.mjs +3 -56
  38. package/dist/components/menubar.mjs +2 -2
  39. package/dist/components/native-select.mjs +1 -1
  40. package/dist/components/navigation-menu.d.mts +1 -6
  41. package/dist/components/navigation-menu.mjs +8 -15
  42. package/dist/components/pagination.d.mts +1 -1
  43. package/dist/components/pagination.mjs +1 -1
  44. package/dist/components/popover.mjs +1 -1
  45. package/dist/components/progress-circle.d.mts +3 -47
  46. package/dist/components/progress-circle.mjs +2 -47
  47. package/dist/components/progress.mjs +1 -1
  48. package/dist/components/radio-group.mjs +1 -1
  49. package/dist/components/radio.mjs +1 -1
  50. package/dist/components/scroll-area.d.mts +3 -19
  51. package/dist/components/scroll-area.mjs +4 -61
  52. package/dist/components/select.d.mts +1 -1
  53. package/dist/components/select.mjs +3 -3
  54. package/dist/components/separator.d.mts +3 -18
  55. package/dist/components/separator.mjs +3 -23
  56. package/dist/components/sheet.d.mts +6 -18
  57. package/dist/components/sheet.mjs +6 -49
  58. package/dist/components/sidebar.d.mts +4 -19
  59. package/dist/components/sidebar.mjs +10 -46
  60. package/dist/components/skeleton.mjs +1 -1
  61. package/dist/components/slider.mjs +1 -1
  62. package/dist/components/spinner.mjs +1 -1
  63. package/dist/components/switch.mjs +2 -2
  64. package/dist/components/table.mjs +1 -1
  65. package/dist/components/tabs.mjs +1 -1
  66. package/dist/components/textarea.mjs +1 -1
  67. package/dist/components/toggle-group.d.mts +3 -2
  68. package/dist/components/toggle-group.mjs +1 -1
  69. package/dist/components/toggle.d.mts +2 -21
  70. package/dist/components/toggle.mjs +2 -39
  71. package/dist/components/tooltip.mjs +1 -1
  72. package/dist/index.d.mts +31 -16
  73. package/dist/index.mjs +30 -15
  74. package/dist/lib/utils.d.mts +1 -12
  75. package/dist/lib/utils.mjs +1 -9
  76. package/dist/primitives/checkbox-group.d.mts +1 -2
  77. package/dist/primitives/input-number.d.mts +1 -2
  78. package/dist/primitives/input.d.mts +1 -2
  79. package/dist/primitives/progress-circle.d.mts +1 -2
  80. package/dist/variants/alert.d.mts +18 -0
  81. package/dist/variants/alert.mjs +25 -0
  82. package/dist/variants/badge.d.mts +20 -0
  83. package/dist/variants/badge.mjs +46 -0
  84. package/dist/variants/button-group.d.mts +18 -0
  85. package/dist/variants/button-group.mjs +26 -0
  86. package/dist/variants/button.d.mts +30 -0
  87. package/dist/variants/button.mjs +76 -0
  88. package/dist/variants/empty.d.mts +18 -0
  89. package/dist/variants/empty.mjs +20 -0
  90. package/dist/variants/field.d.mts +19 -0
  91. package/dist/variants/field.mjs +34 -0
  92. package/dist/variants/input-group.d.mts +43 -0
  93. package/dist/variants/input-group.mjs +93 -0
  94. package/dist/variants/item.d.mts +37 -0
  95. package/dist/variants/item.mjs +60 -0
  96. package/dist/variants/navigation-menu.d.mts +13 -0
  97. package/dist/variants/navigation-menu.mjs +12 -0
  98. package/dist/variants/progress-circle.d.mts +52 -0
  99. package/dist/variants/progress-circle.mjs +49 -0
  100. package/dist/variants/scroll-area.d.mts +24 -0
  101. package/dist/variants/scroll-area.mjs +63 -0
  102. package/dist/variants/separator.d.mts +23 -0
  103. package/dist/variants/separator.mjs +25 -0
  104. package/dist/variants/sheet.d.mts +20 -0
  105. package/dist/variants/sheet.mjs +50 -0
  106. package/dist/variants/sidebar.d.mts +23 -0
  107. package/dist/variants/sidebar.mjs +42 -0
  108. package/dist/variants/toggle.d.mts +23 -0
  109. package/dist/variants/toggle.mjs +43 -0
  110. package/package.json +169 -21
  111. package/src/components/accordion.tsx +156 -0
  112. package/src/components/alert-dialog.tsx +314 -0
  113. package/src/components/alert.tsx +86 -0
  114. package/src/components/aspect-ratio.tsx +28 -0
  115. package/src/components/avatar.tsx +84 -0
  116. package/src/components/badge.tsx +38 -0
  117. package/src/components/breadcrumb.tsx +197 -0
  118. package/src/components/button-group.tsx +107 -0
  119. package/src/components/button.tsx +66 -0
  120. package/src/components/calendar.tsx +277 -0
  121. package/src/components/card.tsx +175 -0
  122. package/src/components/carousel.tsx +367 -0
  123. package/src/components/chart.tsx +587 -0
  124. package/src/components/checkbox-cards.tsx +92 -0
  125. package/src/components/checkbox-group.tsx +83 -0
  126. package/src/components/checkbox.tsx +65 -0
  127. package/src/components/collapsible.tsx +60 -0
  128. package/src/components/command.tsx +311 -0
  129. package/src/components/context-menu.tsx +489 -0
  130. package/src/components/dialog.tsx +295 -0
  131. package/src/components/drawer.tsx +271 -0
  132. package/src/components/dropdown-menu.tsx +498 -0
  133. package/src/components/empty.tsx +169 -0
  134. package/src/components/field.tsx +362 -0
  135. package/src/components/form.tsx +300 -0
  136. package/src/components/hover-card.tsx +116 -0
  137. package/src/components/input-group.tsx +224 -0
  138. package/src/components/input-number.tsx +161 -0
  139. package/src/components/input-otp.tsx +151 -0
  140. package/src/components/input-password.tsx +74 -0
  141. package/src/components/input-search.tsx +98 -0
  142. package/src/components/input.tsx +52 -0
  143. package/src/components/item.tsx +280 -0
  144. package/src/components/kbd.tsx +59 -0
  145. package/src/components/label.tsx +44 -0
  146. package/src/components/menubar.tsx +531 -0
  147. package/src/components/native-select.tsx +96 -0
  148. package/src/components/navigation-menu.tsx +295 -0
  149. package/src/components/pagination.tsx +204 -0
  150. package/src/components/popover.tsx +139 -0
  151. package/src/components/progress-circle.tsx +203 -0
  152. package/src/components/progress.tsx +54 -0
  153. package/src/components/radio-cards.tsx +85 -0
  154. package/src/components/radio-group.tsx +79 -0
  155. package/src/components/radio.tsx +61 -0
  156. package/src/components/resizable.tsx +99 -0
  157. package/src/components/scroll-area.tsx +115 -0
  158. package/src/components/select.tsx +319 -0
  159. package/src/components/separator.tsx +74 -0
  160. package/src/components/sheet.tsx +278 -0
  161. package/src/components/sidebar.tsx +1056 -0
  162. package/src/components/skeleton.tsx +37 -0
  163. package/src/components/slider.tsx +95 -0
  164. package/src/components/sonner.tsx +47 -0
  165. package/src/components/spinner.tsx +75 -0
  166. package/src/components/switch.tsx +66 -0
  167. package/src/components/table.tsx +200 -0
  168. package/src/components/tabs.tsx +128 -0
  169. package/src/components/textarea.tsx +49 -0
  170. package/src/components/toggle-group.tsx +141 -0
  171. package/src/components/toggle.tsx +39 -0
  172. package/src/components/tooltip.tsx +141 -0
  173. package/src/css/amber.css +59 -22
  174. package/src/css/blue.css +59 -22
  175. package/src/css/cyan.css +59 -22
  176. package/src/css/emerald.css +59 -22
  177. package/src/css/fuchsia.css +59 -22
  178. package/src/css/gray.css +59 -22
  179. package/src/css/green.css +59 -22
  180. package/src/css/indigo.css +59 -22
  181. package/src/css/lime.css +59 -22
  182. package/src/css/neutral.css +59 -22
  183. package/src/css/orange.css +59 -22
  184. package/src/css/pink.css +59 -22
  185. package/src/css/preset.css +32 -13
  186. package/src/css/purple.css +59 -22
  187. package/src/css/red.css +59 -22
  188. package/src/css/rose.css +59 -22
  189. package/src/css/sky.css +59 -22
  190. package/src/css/slate.css +59 -22
  191. package/src/css/stone.css +59 -22
  192. package/src/css/teal.css +59 -22
  193. package/src/css/violet.css +59 -22
  194. package/src/css/yellow.css +59 -22
  195. package/src/css/zinc.css +59 -22
  196. package/src/hooks/use-animated-value.ts +97 -0
  197. package/src/hooks/use-copy-to-clipboard.ts +63 -0
  198. package/src/hooks/use-is-mobile.ts +27 -0
  199. package/src/hooks/use-media-query.ts +71 -0
  200. package/src/hooks/use-mutation-observer.ts +54 -0
  201. package/src/hooks/use-pagination.ts +166 -0
  202. package/src/index.ts +720 -0
  203. package/src/lib/utils.ts +5 -0
  204. package/src/primitives/checkbox-group.tsx +360 -0
  205. package/src/primitives/input-number.tsx +1013 -0
  206. package/src/primitives/input.tsx +243 -0
  207. package/src/primitives/progress-circle.tsx +537 -0
  208. package/src/variants/alert.ts +45 -0
  209. package/src/variants/badge.ts +66 -0
  210. package/src/variants/button-group.ts +49 -0
  211. package/src/variants/button.ts +93 -0
  212. package/src/variants/empty.ts +43 -0
  213. package/src/variants/field.ts +50 -0
  214. package/src/variants/input-group.ts +132 -0
  215. package/src/variants/item.ts +90 -0
  216. package/src/variants/navigation-menu.ts +32 -0
  217. package/src/variants/progress-circle.ts +47 -0
  218. package/src/variants/scroll-area.ts +79 -0
  219. package/src/variants/separator.ts +41 -0
  220. package/src/variants/sheet.ts +70 -0
  221. package/src/variants/sidebar.ts +61 -0
  222. package/src/variants/toggle.ts +59 -0
  223. package/dist/node_modules/.pnpm/clsx@2.1.1/node_modules/clsx/clsx.d.mts +0 -6
@@ -0,0 +1,362 @@
1
+ "use client";
2
+
3
+ import type { FieldVariants } from "#/variants/field";
4
+ import type { ComponentProps, JSX, ReactNode } from "react";
5
+
6
+ import { cn } from "#/lib/utils";
7
+
8
+ import { fieldVariants } from "#/variants/field";
9
+ import { useMemo } from "react";
10
+
11
+ import { Label } from "#/components/label";
12
+ import { Separator } from "#/components/separator";
13
+
14
+ /* -----------------------------------------------------------------------------
15
+ * Component: FieldSet
16
+ * -------------------------------------------------------------------------- */
17
+
18
+ /**
19
+ * @since 0.3.16-canary.0
20
+ */
21
+ type FieldSetProps = ComponentProps<"fieldset">;
22
+
23
+ /**
24
+ * @since 0.3.16-canary.0
25
+ */
26
+ function FieldSet({ className, ...props }: FieldSetProps): JSX.Element {
27
+ return (
28
+ <fieldset
29
+ className={cn(
30
+ "flex flex-col gap-6",
31
+ "has-[>[data-slot=checkbox-group]]:gap-3",
32
+ "has-[>[data-slot=radio-group]]:gap-3",
33
+ className,
34
+ )}
35
+ data-slot="field-set"
36
+ {...props}
37
+ />
38
+ );
39
+ }
40
+
41
+ /* -----------------------------------------------------------------------------
42
+ * Component: FieldLegend
43
+ * -------------------------------------------------------------------------- */
44
+
45
+ /**
46
+ * @since 0.3.16-canary.0
47
+ */
48
+ type FieldLegendProps = ComponentProps<"legend"> & {
49
+ variant?: "label" | "legend";
50
+ };
51
+
52
+ /**
53
+ * @since 0.3.16-canary.0
54
+ */
55
+ function FieldLegend({ className, variant = "legend", ...props }: FieldLegendProps): JSX.Element {
56
+ return (
57
+ <legend
58
+ className={cn("mb-3", "text-base font-medium", "data-[variant=label]:text-sm", className)}
59
+ data-slot="field-legend"
60
+ data-variant={variant}
61
+ {...props}
62
+ />
63
+ );
64
+ }
65
+
66
+ /* -----------------------------------------------------------------------------
67
+ * Component: FieldGroup
68
+ * -------------------------------------------------------------------------- */
69
+
70
+ /**
71
+ * @since 0.3.16-canary.0
72
+ */
73
+ type FieldGroupProps = ComponentProps<"div">;
74
+
75
+ /**
76
+ * @since 0.3.16-canary.0
77
+ */
78
+ function FieldGroup({ className, ...props }: FieldGroupProps): JSX.Element {
79
+ return (
80
+ <div
81
+ className={cn(
82
+ "group/field-group @container/field-group flex w-full flex-col gap-7",
83
+ "data-[slot=checkbox-group]:gap-3",
84
+ "[&>[data-slot=field-group]]:gap-4",
85
+ className,
86
+ )}
87
+ data-slot="field-group"
88
+ {...props}
89
+ />
90
+ );
91
+ }
92
+
93
+ /* -----------------------------------------------------------------------------
94
+ * Component: Field
95
+ * -------------------------------------------------------------------------- */
96
+
97
+ /**
98
+ * @since 0.3.16-canary.0
99
+ */
100
+ type FieldProps = ComponentProps<"div"> & FieldVariants;
101
+
102
+ /**
103
+ * @since 0.3.16-canary.0
104
+ */
105
+ function Field({ className, orientation = "vertical", ...props }: FieldProps): JSX.Element {
106
+ return (
107
+ <div
108
+ className={fieldVariants({ className, orientation })}
109
+ data-orientation={orientation}
110
+ data-slot="field"
111
+ role="group"
112
+ {...props}
113
+ />
114
+ );
115
+ }
116
+
117
+ /* -----------------------------------------------------------------------------
118
+ * Component: FieldContent
119
+ * -------------------------------------------------------------------------- */
120
+
121
+ /**
122
+ * @since 0.3.16-canary.0
123
+ */
124
+ type FieldContentProps = ComponentProps<"div">;
125
+
126
+ /**
127
+ * @since 0.3.16-canary.0
128
+ */
129
+ function FieldContent({ className, ...props }: FieldContentProps): JSX.Element {
130
+ return (
131
+ <div
132
+ className={cn("group/field-content flex flex-1 flex-col gap-1.5", "leading-snug", className)}
133
+ data-slot="field-content"
134
+ {...props}
135
+ />
136
+ );
137
+ }
138
+
139
+ /* -----------------------------------------------------------------------------
140
+ * Component: FieldLabel
141
+ * -------------------------------------------------------------------------- */
142
+
143
+ /**
144
+ * @since 0.3.16-canary.0
145
+ */
146
+ type FieldLabelProps = ComponentProps<typeof Label>;
147
+
148
+ /**
149
+ * @since 0.3.16-canary.0
150
+ */
151
+ function FieldLabel({ className, ...props }: FieldLabelProps): JSX.Element {
152
+ return (
153
+ <Label
154
+ className={cn(
155
+ "group/field-label peer/field-label flex w-fit gap-2 leading-snug",
156
+ "group-data-disabled/field:opacity-50",
157
+ "has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-lg has-[>[data-slot=field]]:border",
158
+ "has-data-checked:border-primary has-data-checked:bg-primary/5",
159
+ "dark:has-data-checked:bg-primary/10",
160
+ "[&>*]:data-[slot=field]:p-4",
161
+ className,
162
+ )}
163
+ data-slot="field-label"
164
+ {...props}
165
+ />
166
+ );
167
+ }
168
+
169
+ /* -----------------------------------------------------------------------------
170
+ * Component: FieldTitle
171
+ * -------------------------------------------------------------------------- */
172
+
173
+ /**
174
+ * @since 0.3.16-canary.0
175
+ */
176
+ type FieldTitleProps = ComponentProps<"div">;
177
+
178
+ /**
179
+ * @since 0.3.16-canary.0
180
+ */
181
+ function FieldTitle({ className, ...props }: FieldTitleProps): JSX.Element {
182
+ return (
183
+ <div
184
+ className={cn(
185
+ "flex w-fit items-center gap-2 text-sm leading-snug font-medium",
186
+ "group-data-disabled/field:opacity-50",
187
+ className,
188
+ )}
189
+ data-slot="field-label"
190
+ {...props}
191
+ />
192
+ );
193
+ }
194
+
195
+ /* -----------------------------------------------------------------------------
196
+ * Component: FieldDescription
197
+ * -------------------------------------------------------------------------- */
198
+
199
+ /**
200
+ * @since 0.3.16-canary.0
201
+ */
202
+ type FieldDescriptionProps = ComponentProps<"p">;
203
+
204
+ /**
205
+ * @since 0.3.16-canary.0
206
+ */
207
+ function FieldDescription({ className, ...props }: FieldDescriptionProps): JSX.Element {
208
+ return (
209
+ <p
210
+ className={cn(
211
+ "text-sm leading-normal font-normal text-muted-foreground",
212
+ "group-has-data-[orientation=horizontal]/field:text-balance",
213
+ "last:mt-0",
214
+ "nth-last-2:-mt-1",
215
+ "[&>a]:underline [&>a]:underline-offset-4",
216
+ "[&>a:hover]:text-primary",
217
+ "[[data-variant=legend]+&]:-mt-1.5",
218
+ className,
219
+ )}
220
+ data-slot="field-description"
221
+ {...props}
222
+ />
223
+ );
224
+ }
225
+
226
+ /* -----------------------------------------------------------------------------
227
+ * Component: FieldSeparator
228
+ * -------------------------------------------------------------------------- */
229
+
230
+ /**
231
+ * @since 0.3.16-canary.0
232
+ */
233
+ type FieldSeparatorProps = ComponentProps<"div"> & {
234
+ children?: ComponentProps<"span">["children"];
235
+ };
236
+
237
+ /**
238
+ * @since 0.3.16-canary.0
239
+ */
240
+ function FieldSeparator({ children, className, ...props }: FieldSeparatorProps): JSX.Element {
241
+ const hasContent = Boolean(children);
242
+
243
+ return (
244
+ <div
245
+ className={cn(
246
+ "relative",
247
+ "-my-2 h-5 text-sm",
248
+ "group-data-[variant=outline]/field-group:-mb-2",
249
+ className,
250
+ )}
251
+ data-content={hasContent}
252
+ data-slot="field-separator"
253
+ {...props}
254
+ >
255
+ <Separator className={cn("absolute", "inset-0 top-1/2")} />
256
+ {hasContent ? (
257
+ <span
258
+ className={cn(
259
+ "relative block",
260
+ "mx-auto w-fit px-2",
261
+ "bg-background text-muted-foreground",
262
+ )}
263
+ data-slot="field-separator-content"
264
+ >
265
+ {children}
266
+ </span>
267
+ ) : null}
268
+ </div>
269
+ );
270
+ }
271
+
272
+ /* -----------------------------------------------------------------------------
273
+ * Component: FieldError
274
+ * -------------------------------------------------------------------------- */
275
+
276
+ interface FieldErrorMessage {
277
+ message?: string;
278
+ }
279
+
280
+ /**
281
+ * @since 0.3.16-canary.0
282
+ */
283
+ interface FieldErrorProps extends ComponentProps<"div"> {
284
+ errors?: Array<FieldErrorMessage | undefined>;
285
+ }
286
+
287
+ /**
288
+ * @since 0.3.16-canary.0
289
+ */
290
+ function FieldError({
291
+ children,
292
+ className,
293
+ errors,
294
+ ...props
295
+ }: FieldErrorProps): JSX.Element | null {
296
+ const fallbackContent = useMemo<null | ReactNode>(() => {
297
+ if (!errors?.length) {
298
+ return null;
299
+ }
300
+
301
+ const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
302
+
303
+ if (uniqueErrors.length === 1) {
304
+ return uniqueErrors[0]?.message ?? null;
305
+ }
306
+
307
+ return (
308
+ <ul className={cn("flex flex-col gap-1", "ml-4", "list-disc")}>
309
+ {uniqueErrors.map((error) =>
310
+ error?.message ? <li key={error.message}>{error.message}</li> : null,
311
+ )}
312
+ </ul>
313
+ );
314
+ }, [errors]);
315
+
316
+ const content = children ?? fallbackContent;
317
+
318
+ if (content === null || content === undefined || content === false) {
319
+ return null;
320
+ }
321
+
322
+ return (
323
+ <div
324
+ className={cn("text-sm font-normal text-destructive", className)}
325
+ data-slot="field-error"
326
+ role="alert"
327
+ {...props}
328
+ >
329
+ {content}
330
+ </div>
331
+ );
332
+ }
333
+
334
+ /* -----------------------------------------------------------------------------
335
+ * Exports
336
+ * -------------------------------------------------------------------------- */
337
+
338
+ export {
339
+ Field,
340
+ FieldContent,
341
+ FieldDescription,
342
+ FieldError,
343
+ FieldGroup,
344
+ FieldLabel,
345
+ FieldLegend,
346
+ FieldSeparator,
347
+ FieldSet,
348
+ FieldTitle,
349
+ };
350
+
351
+ export type {
352
+ FieldContentProps,
353
+ FieldDescriptionProps,
354
+ FieldErrorProps,
355
+ FieldGroupProps,
356
+ FieldLabelProps,
357
+ FieldLegendProps,
358
+ FieldProps,
359
+ FieldSeparatorProps,
360
+ FieldSetProps,
361
+ FieldTitleProps,
362
+ };
@@ -0,0 +1,300 @@
1
+ "use client";
2
+
3
+ import type { Scope } from "@radix-ui/react-context";
4
+ import type * as LabelPrimitive from "@radix-ui/react-label";
5
+ import type { ComponentProps, JSX, ReactNode } from "react";
6
+ import type { ControllerProps, FieldError, FieldPath, FieldValues } from "react-hook-form";
7
+
8
+ import { cn } from "#/lib/utils";
9
+ import { createContextScope } from "@radix-ui/react-context";
10
+ import { Slot } from "@radix-ui/react-slot";
11
+ import { useId } from "react";
12
+ import { Controller, FormProvider, useFormContext, useFormState } from "react-hook-form";
13
+
14
+ import { Label } from "#/components/label";
15
+
16
+ /* -----------------------------------------------------------------------------
17
+ * Component: Form
18
+ * -------------------------------------------------------------------------- */
19
+
20
+ /**
21
+ * @since 0.3.16-canary.0
22
+ */
23
+ type FormProps = ComponentProps<typeof FormProvider>;
24
+
25
+ /**
26
+ * @since 0.3.16-canary.0
27
+ */
28
+ const Form: typeof FormProvider = FormProvider;
29
+
30
+ /* -----------------------------------------------------------------------------
31
+ * Context: FormField
32
+ * -------------------------------------------------------------------------- */
33
+
34
+ const FORM_FIELD_NAME = "FormField";
35
+
36
+ type ScopedProps<P> = P & { __scopeFormField?: Scope };
37
+
38
+ const [createFormFieldContext, createFormFieldScope] = createContextScope(FORM_FIELD_NAME);
39
+
40
+ interface FormFieldContextValue<
41
+ TFieldValues extends FieldValues = FieldValues,
42
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
43
+ > {
44
+ name: TName;
45
+ }
46
+
47
+ const [FormFieldContextProvider, useFormFieldContext] =
48
+ createFormFieldContext<FormFieldContextValue>(FORM_FIELD_NAME);
49
+
50
+ function useFormItem(
51
+ consumerName: string,
52
+ scope: Scope,
53
+ ): {
54
+ formDescriptionId: string;
55
+ formItemId: string;
56
+ formMessageId: string;
57
+ id: string;
58
+ invalid: boolean;
59
+ isDirty: boolean;
60
+ isTouched: boolean;
61
+ isValidating: boolean;
62
+ name: string;
63
+ error?: FieldError;
64
+ } {
65
+ const { id } = useFormItemContext(consumerName, scope);
66
+ const { name } = useFormFieldContext(consumerName, scope);
67
+ const { getFieldState } = useFormContext();
68
+ const formState = useFormState({ name });
69
+ const fieldState = getFieldState(name, formState);
70
+
71
+ return {
72
+ formDescriptionId: `form-item-description-${id}`,
73
+ formItemId: `form-item-${id}`,
74
+ formMessageId: `form-item-message-${id}`,
75
+ id,
76
+ name,
77
+ ...fieldState,
78
+ };
79
+ }
80
+
81
+ /* -----------------------------------------------------------------------------
82
+ * Component: FormField
83
+ * -------------------------------------------------------------------------- */
84
+
85
+ /**
86
+ * @since 0.3.16-canary.0
87
+ */
88
+ type FormFieldProps<
89
+ TFieldValues extends FieldValues = FieldValues,
90
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
91
+ > = ControllerProps<TFieldValues, TName>;
92
+
93
+ /**
94
+ * @since 0.3.16-canary.0
95
+ */
96
+ function FormField<
97
+ TFieldValues extends FieldValues = FieldValues,
98
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
99
+ >(formFieldProps: ScopedProps<FormFieldProps<TFieldValues, TName>>): JSX.Element {
100
+ const { __scopeFormField, ...props } = formFieldProps;
101
+
102
+ return (
103
+ <FormFieldContextProvider name={props.name} scope={__scopeFormField}>
104
+ <Controller {...props} />
105
+ </FormFieldContextProvider>
106
+ );
107
+ }
108
+
109
+ /* -----------------------------------------------------------------------------
110
+ * Context: FormItem
111
+ * -------------------------------------------------------------------------- */
112
+
113
+ const FORM_ITEM_NAME = "FormItem";
114
+
115
+ interface FormItemContextValue {
116
+ id: string;
117
+ }
118
+
119
+ const [FormItemContextProvider, useFormItemContext] =
120
+ createFormFieldContext<FormItemContextValue>(FORM_ITEM_NAME);
121
+
122
+ /* -----------------------------------------------------------------------------
123
+ * Component: FormItem
124
+ * -------------------------------------------------------------------------- */
125
+
126
+ /**
127
+ * @since 0.3.16-canary.0
128
+ */
129
+ type FormItemProps = ComponentProps<"div">;
130
+
131
+ /**
132
+ * @since 0.3.16-canary.0
133
+ */
134
+ function FormItem({
135
+ __scopeFormField,
136
+ className,
137
+ ...props
138
+ }: ScopedProps<FormItemProps>): JSX.Element {
139
+ const id = useId();
140
+
141
+ return (
142
+ <FormItemContextProvider id={id} scope={__scopeFormField}>
143
+ <div className={cn("grid gap-2", className)} data-slot="form-item" {...props} />
144
+ </FormItemContextProvider>
145
+ );
146
+ }
147
+
148
+ /* -----------------------------------------------------------------------------
149
+ * Component: FormLabel
150
+ * -------------------------------------------------------------------------- */
151
+
152
+ const FORM_LABEL_NAME = "FormLabel";
153
+
154
+ /**
155
+ * @since 0.3.16-canary.0
156
+ */
157
+ type FormLabelProps = ComponentProps<typeof LabelPrimitive.Root>;
158
+
159
+ /**
160
+ * @since 0.3.16-canary.0
161
+ */
162
+ function FormLabel({ __scopeFormField, ...props }: ScopedProps<FormLabelProps>): JSX.Element {
163
+ const { error, formItemId } = useFormItem(FORM_LABEL_NAME, __scopeFormField);
164
+
165
+ return (
166
+ <Label
167
+ data-invalid={error ? true : undefined}
168
+ data-slot="form-label"
169
+ htmlFor={formItemId}
170
+ {...props}
171
+ />
172
+ );
173
+ }
174
+
175
+ /* -----------------------------------------------------------------------------
176
+ * Component: FormControl
177
+ * -------------------------------------------------------------------------- */
178
+
179
+ const FORM_CONTROL_NAME = "FormControl";
180
+
181
+ /**
182
+ * @since 0.3.16-canary.0
183
+ */
184
+ type FormControlProps = ComponentProps<typeof Slot>;
185
+
186
+ /**
187
+ * @since 0.3.16-canary.0
188
+ */
189
+ function FormControl({ __scopeFormField, ...props }: ScopedProps<FormControlProps>): JSX.Element {
190
+ const { error, formDescriptionId, formItemId, formMessageId } = useFormItem(
191
+ FORM_CONTROL_NAME,
192
+ __scopeFormField,
193
+ );
194
+
195
+ return (
196
+ <Slot
197
+ aria-describedby={error ? `${formDescriptionId} ${formMessageId}` : formDescriptionId}
198
+ aria-invalid={Boolean(error)}
199
+ data-slot="form-control"
200
+ id={formItemId}
201
+ {...props}
202
+ />
203
+ );
204
+ }
205
+
206
+ /* -----------------------------------------------------------------------------
207
+ * Component: FormDescription
208
+ * -------------------------------------------------------------------------- */
209
+
210
+ /**
211
+ * @since 0.3.16-canary.0
212
+ */
213
+ type FormDescriptionProps = ComponentProps<"p">;
214
+
215
+ /**
216
+ * @since 0.3.16-canary.0
217
+ */
218
+ function FormDescription({
219
+ __scopeFormField,
220
+ className,
221
+ ...props
222
+ }: ScopedProps<FormDescriptionProps>): JSX.Element {
223
+ const { formDescriptionId } = useFormItem(FORM_MESSAGE_NAME, __scopeFormField);
224
+
225
+ return (
226
+ <p
227
+ className={cn("text-xs text-muted-foreground", className)}
228
+ data-slot="form-description"
229
+ id={formDescriptionId}
230
+ {...props}
231
+ />
232
+ );
233
+ }
234
+
235
+ /* -----------------------------------------------------------------------------
236
+ * Component: FormMessage
237
+ * -------------------------------------------------------------------------- */
238
+
239
+ const FORM_MESSAGE_NAME = "FormMessage";
240
+
241
+ /**
242
+ * @since 0.3.16-canary.0
243
+ */
244
+ type FormMessageProps = ComponentProps<"p">;
245
+
246
+ /**
247
+ * @since 0.3.16-canary.0
248
+ */
249
+ function FormMessage({
250
+ __scopeFormField,
251
+ children,
252
+ className,
253
+ ...props
254
+ }: ScopedProps<FormMessageProps>): ReactNode {
255
+ const { error, formMessageId } = useFormItem(FORM_MESSAGE_NAME, __scopeFormField);
256
+ const body = error?.message ?? children;
257
+
258
+ if (!body) {
259
+ return null;
260
+ }
261
+
262
+ return (
263
+ <p
264
+ className={cn(
265
+ "text-xs",
266
+ error?.message ? "font-medium text-destructive" : "text-muted-foreground",
267
+ className,
268
+ )}
269
+ data-slot="form-message"
270
+ id={formMessageId}
271
+ {...props}
272
+ >
273
+ {body}
274
+ </p>
275
+ );
276
+ }
277
+
278
+ /* -----------------------------------------------------------------------------
279
+ * Exports
280
+ * -------------------------------------------------------------------------- */
281
+
282
+ export type {
283
+ FormControlProps,
284
+ FormDescriptionProps,
285
+ FormFieldProps,
286
+ FormItemProps,
287
+ FormLabelProps,
288
+ FormMessageProps,
289
+ FormProps,
290
+ };
291
+ export {
292
+ createFormFieldScope,
293
+ Form,
294
+ FormControl,
295
+ FormDescription,
296
+ FormField,
297
+ FormItem,
298
+ FormLabel,
299
+ FormMessage,
300
+ };