@auto-engineer/generate-react-client 1.63.0 → 1.65.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 (144) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/dist/starter/.storybook/main.ts +17 -22
  3. package/dist/starter/.storybook/manager-head.html +31 -31
  4. package/dist/starter/.storybook/manager.ts +133 -133
  5. package/dist/starter/.storybook/preview-head.html +12 -12
  6. package/dist/starter/.storybook/preview.tsx +79 -79
  7. package/dist/starter/biome.json +126 -0
  8. package/dist/starter/codegen.ts +11 -11
  9. package/dist/starter/components.json +27 -27
  10. package/dist/starter/package.json +86 -80
  11. package/dist/starter/public/mockServiceWorker.js +261 -261
  12. package/dist/starter/scripts/build-component-db.ts +17 -20
  13. package/dist/starter/src/App.tsx +15 -17
  14. package/dist/starter/src/components/ui/Accordion.stories.tsx +35 -35
  15. package/dist/starter/src/components/ui/Accordion.tsx +33 -33
  16. package/dist/starter/src/components/ui/Alert.stories.tsx +15 -15
  17. package/dist/starter/src/components/ui/Alert.tsx +32 -32
  18. package/dist/starter/src/components/ui/AlertDialog.stories.tsx +50 -50
  19. package/dist/starter/src/components/ui/AlertDialog.tsx +114 -115
  20. package/dist/starter/src/components/ui/AspectRatio.stories.tsx +20 -20
  21. package/dist/starter/src/components/ui/AspectRatio.tsx +1 -1
  22. package/dist/starter/src/components/ui/Avatar.stories.tsx +27 -27
  23. package/dist/starter/src/components/ui/Avatar.tsx +63 -63
  24. package/dist/starter/src/components/ui/Badge.stories.tsx +14 -14
  25. package/dist/starter/src/components/ui/Badge.tsx +27 -27
  26. package/dist/starter/src/components/ui/Breadcrumb.stories.tsx +38 -38
  27. package/dist/starter/src/components/ui/Breadcrumb.tsx +63 -62
  28. package/dist/starter/src/components/ui/Button.stories.tsx +55 -55
  29. package/dist/starter/src/components/ui/Button.tsx +49 -49
  30. package/dist/starter/src/components/ui/ButtonGroup.stories.tsx +17 -17
  31. package/dist/starter/src/components/ui/ButtonGroup.tsx +52 -53
  32. package/dist/starter/src/components/ui/Calendar.stories.tsx +20 -19
  33. package/dist/starter/src/components/ui/Calendar.tsx +142 -143
  34. package/dist/starter/src/components/ui/Card.stories.tsx +29 -29
  35. package/dist/starter/src/components/ui/Card.tsx +31 -31
  36. package/dist/starter/src/components/ui/Carousel.stories.tsx +41 -41
  37. package/dist/starter/src/components/ui/Carousel.tsx +171 -172
  38. package/dist/starter/src/components/ui/Chart.stories.tsx +21 -21
  39. package/dist/starter/src/components/ui/Chart.tsx +244 -247
  40. package/dist/starter/src/components/ui/Checkbox.stories.tsx +11 -11
  41. package/dist/starter/src/components/ui/Checkbox.tsx +18 -18
  42. package/dist/starter/src/components/ui/Collapsible.stories.tsx +40 -40
  43. package/dist/starter/src/components/ui/Collapsible.tsx +3 -3
  44. package/dist/starter/src/components/ui/Combobox.stories.tsx +48 -48
  45. package/dist/starter/src/components/ui/Combobox.tsx +204 -205
  46. package/dist/starter/src/components/ui/Command.stories.tsx +55 -55
  47. package/dist/starter/src/components/ui/Command.tsx +102 -103
  48. package/dist/starter/src/components/ui/ContextMenu.stories.tsx +52 -52
  49. package/dist/starter/src/components/ui/ContextMenu.tsx +151 -151
  50. package/dist/starter/src/components/ui/DesignSystem-Colors.stories.tsx +92 -92
  51. package/dist/starter/src/components/ui/DesignSystem-Layout.stories.tsx +139 -139
  52. package/dist/starter/src/components/ui/DesignSystem-Overview.stories.tsx +676 -657
  53. package/dist/starter/src/components/ui/DesignSystem-Typography.stories.tsx +59 -59
  54. package/dist/starter/src/components/ui/Dialog.stories.tsx +56 -56
  55. package/dist/starter/src/components/ui/Dialog.tsx +97 -98
  56. package/dist/starter/src/components/ui/Direction.stories.tsx +20 -20
  57. package/dist/starter/src/components/ui/Direction.tsx +7 -7
  58. package/dist/starter/src/components/ui/Drawer.stories.tsx +54 -54
  59. package/dist/starter/src/components/ui/Drawer.tsx +70 -70
  60. package/dist/starter/src/components/ui/DropdownMenu.stories.tsx +58 -58
  61. package/dist/starter/src/components/ui/DropdownMenu.tsx +157 -157
  62. package/dist/starter/src/components/ui/Empty.stories.tsx +22 -22
  63. package/dist/starter/src/components/ui/Empty.tsx +58 -58
  64. package/dist/starter/src/components/ui/Field.stories.tsx +31 -31
  65. package/dist/starter/src/components/ui/Field.tsx +180 -181
  66. package/dist/starter/src/components/ui/Form.stories.tsx +29 -29
  67. package/dist/starter/src/components/ui/Form.tsx +93 -96
  68. package/dist/starter/src/components/ui/HoverCard.stories.tsx +34 -34
  69. package/dist/starter/src/components/ui/HoverCard.tsx +21 -21
  70. package/dist/starter/src/components/ui/Input.stories.tsx +18 -18
  71. package/dist/starter/src/components/ui/Input.tsx +14 -14
  72. package/dist/starter/src/components/ui/InputGroup.stories.tsx +34 -34
  73. package/dist/starter/src/components/ui/InputGroup.tsx +110 -111
  74. package/dist/starter/src/components/ui/InputOTP.stories.tsx +28 -28
  75. package/dist/starter/src/components/ui/InputOTP.tsx +43 -43
  76. package/dist/starter/src/components/ui/Item.stories.tsx +45 -45
  77. package/dist/starter/src/components/ui/Item.tsx +113 -114
  78. package/dist/starter/src/components/ui/Kbd.stories.tsx +31 -31
  79. package/dist/starter/src/components/ui/Kbd.tsx +11 -11
  80. package/dist/starter/src/components/ui/Label.stories.tsx +62 -62
  81. package/dist/starter/src/components/ui/Label.tsx +26 -25
  82. package/dist/starter/src/components/ui/Menubar.stories.tsx +62 -62
  83. package/dist/starter/src/components/ui/Menubar.tsx +173 -173
  84. package/dist/starter/src/components/ui/NativeSelect.stories.tsx +26 -26
  85. package/dist/starter/src/components/ui/NativeSelect.tsx +29 -29
  86. package/dist/starter/src/components/ui/NavigationMenu.stories.tsx +64 -64
  87. package/dist/starter/src/components/ui/NavigationMenu.tsx +103 -103
  88. package/dist/starter/src/components/ui/Pagination.stories.tsx +61 -61
  89. package/dist/starter/src/components/ui/Pagination.tsx +69 -71
  90. package/dist/starter/src/components/ui/Popover.stories.tsx +38 -38
  91. package/dist/starter/src/components/ui/Popover.tsx +25 -25
  92. package/dist/starter/src/components/ui/Progress.stories.tsx +9 -9
  93. package/dist/starter/src/components/ui/Progress.tsx +14 -14
  94. package/dist/starter/src/components/ui/RadioGroup.stories.tsx +35 -35
  95. package/dist/starter/src/components/ui/RadioGroup.tsx +19 -19
  96. package/dist/starter/src/components/ui/Resizable.stories.tsx +54 -54
  97. package/dist/starter/src/components/ui/Resizable.tsx +29 -29
  98. package/dist/starter/src/components/ui/ScrollArea.stories.tsx +27 -27
  99. package/dist/starter/src/components/ui/ScrollArea.tsx +34 -34
  100. package/dist/starter/src/components/ui/Select.stories.tsx +43 -43
  101. package/dist/starter/src/components/ui/Select.tsx +120 -120
  102. package/dist/starter/src/components/ui/Separator.stories.tsx +27 -27
  103. package/dist/starter/src/components/ui/Separator.tsx +17 -17
  104. package/dist/starter/src/components/ui/Sheet.stories.tsx +53 -53
  105. package/dist/starter/src/components/ui/Sheet.tsx +69 -69
  106. package/dist/starter/src/components/ui/Sidebar.stories.tsx +77 -77
  107. package/dist/starter/src/components/ui/Sidebar.tsx +563 -564
  108. package/dist/starter/src/components/ui/Skeleton.stories.tsx +25 -25
  109. package/dist/starter/src/components/ui/Skeleton.tsx +1 -1
  110. package/dist/starter/src/components/ui/Slider.stories.tsx +5 -5
  111. package/dist/starter/src/components/ui/Slider.tsx +45 -44
  112. package/dist/starter/src/components/ui/Sonner.stories.tsx +32 -32
  113. package/dist/starter/src/components/ui/Sonner.tsx +23 -23
  114. package/dist/starter/src/components/ui/Spinner.stories.tsx +8 -8
  115. package/dist/starter/src/components/ui/Spinner.tsx +1 -1
  116. package/dist/starter/src/components/ui/Switch.stories.tsx +16 -17
  117. package/dist/starter/src/components/ui/Switch.tsx +24 -24
  118. package/dist/starter/src/components/ui/Table.stories.tsx +50 -50
  119. package/dist/starter/src/components/ui/Table.tsx +45 -45
  120. package/dist/starter/src/components/ui/Tabs.stories.tsx +39 -39
  121. package/dist/starter/src/components/ui/Tabs.tsx +47 -47
  122. package/dist/starter/src/components/ui/Textarea.stories.tsx +9 -9
  123. package/dist/starter/src/components/ui/Textarea.tsx +11 -11
  124. package/dist/starter/src/components/ui/Toast.stories.tsx +77 -77
  125. package/dist/starter/src/components/ui/Toast.tsx +75 -75
  126. package/dist/starter/src/components/ui/Toaster.tsx +17 -19
  127. package/dist/starter/src/components/ui/Toggle.stories.tsx +20 -20
  128. package/dist/starter/src/components/ui/Toggle.tsx +26 -26
  129. package/dist/starter/src/components/ui/ToggleGroup.stories.tsx +41 -41
  130. package/dist/starter/src/components/ui/ToggleGroup.tsx +61 -62
  131. package/dist/starter/src/components/ui/Tooltip.stories.tsx +26 -26
  132. package/dist/starter/src/components/ui/Tooltip.tsx +24 -24
  133. package/dist/starter/src/gql/execute.ts +1 -1
  134. package/dist/starter/src/gql/fragment-masking.ts +1 -1
  135. package/dist/starter/src/gql/graphql.ts +3 -0
  136. package/dist/starter/src/hooks/use-mobile.ts +11 -11
  137. package/dist/starter/src/hooks/use-toast.ts +135 -135
  138. package/dist/starter/src/index.css +105 -105
  139. package/dist/starter/src/lib/utils.ts +1 -1
  140. package/dist/starter/src/main.tsx +4 -1
  141. package/dist/starter/tsconfig.app.json +24 -24
  142. package/dist/starter/tsconfig.json +8 -8
  143. package/dist/starter/vite.config.ts +38 -37
  144. package/package.json +3 -3
@@ -1,50 +1,50 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Field, FieldLabel, FieldDescription, FieldError, FieldContent } from '@/components/ui/Field';
2
+ import { Field, FieldContent, FieldDescription, FieldError, FieldLabel } from '@/components/ui/Field';
3
3
  import { Input } from '@/components/ui/Input';
4
4
 
5
5
  const meta: Meta<typeof Field> = {
6
- title: 'UI Components/Field',
7
- component: Field,
6
+ title: 'UI Components/Field',
7
+ component: Field,
8
8
  };
9
9
  export default meta;
10
10
  type Story = StoryObj<typeof Field>;
11
11
 
12
12
  /** Shows a vertical form field with label, input, and helper description text. */
13
13
  export const Default: Story = {
14
- render: () => (
15
- <Field>
16
- <FieldLabel htmlFor="email">Email</FieldLabel>
17
- <FieldContent>
18
- <Input id="email" type="email" placeholder="you@example.com" />
19
- <FieldDescription>We will never share your email.</FieldDescription>
20
- </FieldContent>
21
- </Field>
22
- ),
14
+ render: () => (
15
+ <Field>
16
+ <FieldLabel htmlFor="email">Email</FieldLabel>
17
+ <FieldContent>
18
+ <Input id="email" type="email" placeholder="you@example.com" />
19
+ <FieldDescription>We will never share your email.</FieldDescription>
20
+ </FieldContent>
21
+ </Field>
22
+ ),
23
23
  };
24
24
 
25
25
  /** Shows a field in an invalid state with error styling and a validation error message. */
26
26
  export const WithError: Story = {
27
- render: () => (
28
- <Field data-invalid="true">
29
- <FieldLabel htmlFor="email-err">Email</FieldLabel>
30
- <FieldContent>
31
- <Input id="email-err" type="email" defaultValue="invalid-email" aria-invalid="true" />
32
- <FieldDescription>Enter a valid email address.</FieldDescription>
33
- <FieldError>Please enter a valid email address.</FieldError>
34
- </FieldContent>
35
- </Field>
36
- ),
27
+ render: () => (
28
+ <Field data-invalid="true">
29
+ <FieldLabel htmlFor="email-err">Email</FieldLabel>
30
+ <FieldContent>
31
+ <Input id="email-err" type="email" defaultValue="invalid-email" aria-invalid="true" />
32
+ <FieldDescription>Enter a valid email address.</FieldDescription>
33
+ <FieldError>Please enter a valid email address.</FieldError>
34
+ </FieldContent>
35
+ </Field>
36
+ ),
37
37
  };
38
38
 
39
39
  /** Shows a field with horizontal orientation where the label and input are side by side. */
40
40
  export const Horizontal: Story = {
41
- render: () => (
42
- <Field orientation="horizontal">
43
- <FieldLabel htmlFor="name-h">Full Name</FieldLabel>
44
- <FieldContent>
45
- <Input id="name-h" placeholder="John Doe" />
46
- <FieldDescription>Your first and last name.</FieldDescription>
47
- </FieldContent>
48
- </Field>
49
- ),
41
+ render: () => (
42
+ <Field orientation="horizontal">
43
+ <FieldLabel htmlFor="name-h">Full Name</FieldLabel>
44
+ <FieldContent>
45
+ <Input id="name-h" placeholder="John Doe" />
46
+ <FieldDescription>Your first and last name.</FieldDescription>
47
+ </FieldContent>
48
+ </Field>
49
+ ),
50
50
  };
@@ -1,28 +1,27 @@
1
1
  'use client';
2
2
 
3
- import { useMemo } from 'react';
4
3
  import { cva, type VariantProps } from 'class-variance-authority';
5
-
6
- import { cn } from '@/lib/utils';
4
+ import { useMemo } from 'react';
7
5
  import { Label } from '@/components/ui/Label';
8
6
  import { Separator } from '@/components/ui/Separator';
7
+ import { cn } from '@/lib/utils';
9
8
 
10
9
  /**
11
10
  * A semantic fieldset container for grouping related form fields.
12
11
  * Use as a top-level wrapper around FieldGroup or multiple Field components.
13
12
  */
14
13
  function FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {
15
- return (
16
- <fieldset
17
- data-slot="field-set"
18
- className={cn(
19
- 'flex flex-col gap-6',
20
- 'has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
21
- className,
22
- )}
23
- {...props}
24
- />
25
- );
14
+ return (
15
+ <fieldset
16
+ data-slot="field-set"
17
+ className={cn(
18
+ 'flex flex-col gap-6',
19
+ 'has-[>[data-slot=checkbox-group]]:gap-3 has-[>[data-slot=radio-group]]:gap-3',
20
+ className,
21
+ )}
22
+ {...props}
23
+ />
24
+ );
26
25
  }
27
26
 
28
27
  /**
@@ -30,53 +29,53 @@ function FieldSet({ className, ...props }: React.ComponentProps<'fieldset'>) {
30
29
  * Use variant="label" for a smaller label-sized heading.
31
30
  */
32
31
  function FieldLegend({
33
- className,
34
- variant = 'legend',
35
- ...props
32
+ className,
33
+ variant = 'legend',
34
+ ...props
36
35
  }: React.ComponentProps<'legend'> & { variant?: 'legend' | 'label' }) {
37
- return (
38
- <legend
39
- data-slot="field-legend"
40
- data-variant={variant}
41
- className={cn('mb-3 font-medium', 'data-[variant=legend]:text-base', 'data-[variant=label]:text-sm', className)}
42
- {...props}
43
- />
44
- );
36
+ return (
37
+ <legend
38
+ data-slot="field-legend"
39
+ data-variant={variant}
40
+ className={cn('mb-3 font-medium', 'data-[variant=legend]:text-base', 'data-[variant=label]:text-sm', className)}
41
+ {...props}
42
+ />
43
+ );
45
44
  }
46
45
 
47
46
  /** Container for a group of Field components. Provides container query context for responsive layouts. */
48
47
  function FieldGroup({ className, ...props }: React.ComponentProps<'div'>) {
49
- return (
50
- <div
51
- data-slot="field-group"
52
- className={cn(
53
- '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',
54
- className,
55
- )}
56
- {...props}
57
- />
58
- );
48
+ return (
49
+ <div
50
+ data-slot="field-group"
51
+ className={cn(
52
+ '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',
53
+ className,
54
+ )}
55
+ {...props}
56
+ />
57
+ );
59
58
  }
60
59
 
61
60
  const fieldVariants = cva('group/field flex w-full gap-3 data-[invalid=true]:text-destructive', {
62
- variants: {
63
- orientation: {
64
- vertical: ['flex-col [&>*]:w-full [&>.sr-only]:w-auto'],
65
- horizontal: [
66
- 'flex-row items-center',
67
- '[&>[data-slot=field-label]]:flex-auto',
68
- 'has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
69
- ],
70
- responsive: [
71
- 'flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto',
72
- '@md/field-group:[&>[data-slot=field-label]]:flex-auto',
73
- '@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
74
- ],
75
- },
76
- },
77
- defaultVariants: {
78
- orientation: 'vertical',
79
- },
61
+ variants: {
62
+ orientation: {
63
+ vertical: ['flex-col [&>*]:w-full [&>.sr-only]:w-auto'],
64
+ horizontal: [
65
+ 'flex-row items-center',
66
+ '[&>[data-slot=field-label]]:flex-auto',
67
+ 'has-[>[data-slot=field-content]]:items-start has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
68
+ ],
69
+ responsive: [
70
+ 'flex-col [&>*]:w-full [&>.sr-only]:w-auto @md/field-group:flex-row @md/field-group:items-center @md/field-group:[&>*]:w-auto',
71
+ '@md/field-group:[&>[data-slot=field-label]]:flex-auto',
72
+ '@md/field-group:has-[>[data-slot=field-content]]:items-start @md/field-group:has-[>[data-slot=field-content]]:[&>[role=checkbox],[role=radio]]:mt-px',
73
+ ],
74
+ },
75
+ },
76
+ defaultVariants: {
77
+ orientation: 'vertical',
78
+ },
80
79
  });
81
80
 
82
81
  /**
@@ -85,105 +84,105 @@ const fieldVariants = cva('group/field flex w-full gap-3 data-[invalid=true]:tex
85
84
  * Set data-invalid="true" to apply error styling.
86
85
  */
87
86
  function Field({
88
- className,
89
- /** Layout direction: "vertical" stacks label above input, "horizontal" places them side by side, "responsive" switches at the md breakpoint. */
90
- orientation = 'vertical',
91
- ...props
87
+ className,
88
+ /** Layout direction: "vertical" stacks label above input, "horizontal" places them side by side, "responsive" switches at the md breakpoint. */
89
+ orientation = 'vertical',
90
+ ...props
92
91
  }: React.ComponentProps<'div'> & VariantProps<typeof fieldVariants>) {
93
- return (
94
- <div
95
- role="group"
96
- data-slot="field"
97
- data-orientation={orientation}
98
- className={cn(fieldVariants({ orientation }), className)}
99
- {...props}
100
- />
101
- );
92
+ return (
93
+ <div
94
+ role="group"
95
+ data-slot="field"
96
+ data-orientation={orientation}
97
+ className={cn(fieldVariants({ orientation }), className)}
98
+ {...props}
99
+ />
100
+ );
102
101
  }
103
102
 
104
103
  /** Container for the input element plus optional description and error within a Field. */
105
104
  function FieldContent({ className, ...props }: React.ComponentProps<'div'>) {
106
- return (
107
- <div
108
- data-slot="field-content"
109
- className={cn('group/field-content flex flex-1 flex-col gap-1.5 leading-snug', className)}
110
- {...props}
111
- />
112
- );
105
+ return (
106
+ <div
107
+ data-slot="field-content"
108
+ className={cn('group/field-content flex flex-1 flex-col gap-1.5 leading-snug', className)}
109
+ {...props}
110
+ />
111
+ );
113
112
  }
114
113
 
115
114
  /** Label for a form field. Supports wrapping checkbox/radio inputs for card-style selection. */
116
115
  function FieldLabel({ className, ...props }: React.ComponentProps<typeof Label>) {
117
- return (
118
- <Label
119
- data-slot="field-label"
120
- className={cn(
121
- 'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50',
122
- 'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4',
123
- 'has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10',
124
- className,
125
- )}
126
- {...props}
127
- />
128
- );
116
+ return (
117
+ <Label
118
+ data-slot="field-label"
119
+ className={cn(
120
+ 'group/field-label peer/field-label flex w-fit gap-2 leading-snug group-data-[disabled=true]/field:opacity-50',
121
+ 'has-[>[data-slot=field]]:w-full has-[>[data-slot=field]]:flex-col has-[>[data-slot=field]]:rounded-md has-[>[data-slot=field]]:border [&>*]:data-[slot=field]:p-4',
122
+ 'has-data-[state=checked]:bg-primary/5 has-data-[state=checked]:border-primary dark:has-data-[state=checked]:bg-primary/10',
123
+ className,
124
+ )}
125
+ {...props}
126
+ />
127
+ );
129
128
  }
130
129
 
131
130
  /** Non-interactive title text for a field, used when a label association is not needed. */
132
131
  function FieldTitle({ className, ...props }: React.ComponentProps<'div'>) {
133
- return (
134
- <div
135
- data-slot="field-label"
136
- className={cn(
137
- 'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
138
- className,
139
- )}
140
- {...props}
141
- />
142
- );
132
+ return (
133
+ <div
134
+ data-slot="field-label"
135
+ className={cn(
136
+ 'flex w-fit items-center gap-2 text-sm leading-snug font-medium group-data-[disabled=true]/field:opacity-50',
137
+ className,
138
+ )}
139
+ {...props}
140
+ />
141
+ );
143
142
  }
144
143
 
145
144
  /** Helper text displayed below the input to provide additional guidance. */
146
145
  function FieldDescription({ className, ...props }: React.ComponentProps<'p'>) {
147
- return (
148
- <p
149
- data-slot="field-description"
150
- className={cn(
151
- 'text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance',
152
- 'last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5',
153
- '[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
154
- className,
155
- )}
156
- {...props}
157
- />
158
- );
146
+ return (
147
+ <p
148
+ data-slot="field-description"
149
+ className={cn(
150
+ 'text-muted-foreground text-sm leading-normal font-normal group-has-[[data-orientation=horizontal]]/field:text-balance',
151
+ 'last:mt-0 nth-last-2:-mt-1 [[data-variant=legend]+&]:-mt-1.5',
152
+ '[&>a:hover]:text-primary [&>a]:underline [&>a]:underline-offset-4',
153
+ className,
154
+ )}
155
+ {...props}
156
+ />
157
+ );
159
158
  }
160
159
 
161
160
  /** A visual separator between fields within a FieldGroup. Optionally displays centered text. */
162
161
  function FieldSeparator({
163
- children,
164
- className,
165
- ...props
162
+ children,
163
+ className,
164
+ ...props
166
165
  }: React.ComponentProps<'div'> & {
167
- children?: React.ReactNode;
166
+ children?: React.ReactNode;
168
167
  }) {
169
- return (
170
- <div
171
- data-slot="field-separator"
172
- data-content={!!children}
173
- className={cn('relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2', className)}
174
- {...props}
175
- >
176
- <Separator className="absolute inset-0 top-1/2" />
177
- {children && (
178
- <span
179
- className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
180
- data-slot="field-separator-content"
181
- >
182
- {children}
183
- </span>
184
- )}
185
- </div>
186
- );
168
+ return (
169
+ <div
170
+ data-slot="field-separator"
171
+ data-content={!!children}
172
+ className={cn('relative -my-2 h-5 text-sm group-data-[variant=outline]/field-group:-mb-2', className)}
173
+ {...props}
174
+ >
175
+ <Separator className="absolute inset-0 top-1/2" />
176
+ {children && (
177
+ <span
178
+ className="bg-background text-muted-foreground relative mx-auto block w-fit px-2"
179
+ data-slot="field-separator-content"
180
+ >
181
+ {children}
182
+ </span>
183
+ )}
184
+ </div>
185
+ );
187
186
  }
188
187
 
189
188
  /**
@@ -191,61 +190,61 @@ function FieldSeparator({
191
190
  * Accepts either children or an errors array; deduplicates and renders as a list if multiple.
192
191
  */
193
192
  function FieldError({
194
- className,
195
- children,
196
- /** Array of error objects with message strings. Automatically deduplicated. */
197
- errors,
198
- ...props
193
+ className,
194
+ children,
195
+ /** Array of error objects with message strings. Automatically deduplicated. */
196
+ errors,
197
+ ...props
199
198
  }: React.ComponentProps<'div'> & {
200
- errors?: Array<{ message?: string } | undefined>;
199
+ errors?: Array<{ message?: string } | undefined>;
201
200
  }) {
202
- const content = useMemo(() => {
203
- if (children) {
204
- return children;
205
- }
206
-
207
- if (!errors?.length) {
208
- return null;
209
- }
210
-
211
- const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
212
-
213
- if (uniqueErrors?.length == 1) {
214
- return uniqueErrors[0]?.message;
215
- }
216
-
217
- return (
218
- <ul className="ml-4 flex list-disc flex-col gap-1">
219
- {uniqueErrors.map((error, index) => error?.message && <li key={index}>{error.message}</li>)}
220
- </ul>
221
- );
222
- }, [children, errors]);
223
-
224
- if (!content) {
225
- return null;
226
- }
227
-
228
- return (
229
- <div
230
- role="alert"
231
- data-slot="field-error"
232
- className={cn('text-destructive text-sm font-normal', className)}
233
- {...props}
234
- >
235
- {content}
236
- </div>
237
- );
201
+ const content = useMemo(() => {
202
+ if (children) {
203
+ return children;
204
+ }
205
+
206
+ if (!errors?.length) {
207
+ return null;
208
+ }
209
+
210
+ const uniqueErrors = [...new Map(errors.map((error) => [error?.message, error])).values()];
211
+
212
+ if (uniqueErrors?.length === 1) {
213
+ return uniqueErrors[0]?.message;
214
+ }
215
+
216
+ return (
217
+ <ul className="ml-4 flex list-disc flex-col gap-1">
218
+ {uniqueErrors.map((error) => error?.message && <li key={error.message}>{error.message}</li>)}
219
+ </ul>
220
+ );
221
+ }, [children, errors]);
222
+
223
+ if (!content) {
224
+ return null;
225
+ }
226
+
227
+ return (
228
+ <div
229
+ role="alert"
230
+ data-slot="field-error"
231
+ className={cn('text-destructive text-sm font-normal', className)}
232
+ {...props}
233
+ >
234
+ {content}
235
+ </div>
236
+ );
238
237
  }
239
238
 
240
239
  export {
241
- Field,
242
- FieldLabel,
243
- FieldDescription,
244
- FieldError,
245
- FieldGroup,
246
- FieldLegend,
247
- FieldSeparator,
248
- FieldSet,
249
- FieldContent,
250
- FieldTitle,
240
+ Field,
241
+ FieldLabel,
242
+ FieldDescription,
243
+ FieldError,
244
+ FieldGroup,
245
+ FieldLegend,
246
+ FieldSeparator,
247
+ FieldSet,
248
+ FieldContent,
249
+ FieldTitle,
251
250
  };
@@ -1,45 +1,45 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { useForm } from 'react-hook-form';
2
+ import { type FieldValues, useForm } from 'react-hook-form';
3
+ import { Button } from '@/components/ui/Button';
3
4
  import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from '@/components/ui/Form';
4
5
  import { Input } from '@/components/ui/Input';
5
- import { Button } from '@/components/ui/Button';
6
6
 
7
7
  const meta: Meta<typeof Form> = {
8
- title: 'UI Components/Form',
9
- component: Form,
8
+ title: 'UI Components/Form',
9
+ component: Form,
10
10
  };
11
11
  export default meta;
12
12
  type Story = StoryObj<typeof Form>;
13
13
 
14
14
  function FormDemo() {
15
- const form = useForm({
16
- defaultValues: { username: '' },
17
- });
15
+ const form = useForm<FieldValues>({
16
+ defaultValues: { username: '' },
17
+ });
18
18
 
19
- return (
20
- <Form {...form}>
21
- <form onSubmit={form.handleSubmit(() => {})} className="w-full max-w-sm space-y-6">
22
- <FormField
23
- control={form.control}
24
- name="username"
25
- render={({ field }) => (
26
- <FormItem>
27
- <FormLabel>Username</FormLabel>
28
- <FormControl>
29
- <Input placeholder="Enter username" {...field} />
30
- </FormControl>
31
- <FormDescription>This is your public display name.</FormDescription>
32
- <FormMessage />
33
- </FormItem>
34
- )}
35
- />
36
- <Button type="submit">Submit</Button>
37
- </form>
38
- </Form>
39
- );
19
+ return (
20
+ <Form {...form}>
21
+ <form onSubmit={form.handleSubmit(() => {})} className="w-full max-w-sm space-y-6">
22
+ <FormField
23
+ control={form.control}
24
+ name="username"
25
+ render={({ field }) => (
26
+ <FormItem>
27
+ <FormLabel>Username</FormLabel>
28
+ <FormControl>
29
+ <Input placeholder="Enter username" {...field} />
30
+ </FormControl>
31
+ <FormDescription>This is your public display name.</FormDescription>
32
+ <FormMessage />
33
+ </FormItem>
34
+ )}
35
+ />
36
+ <Button type="submit">Submit</Button>
37
+ </form>
38
+ </Form>
39
+ );
40
40
  }
41
41
 
42
42
  /** Demonstrates a complete form field with label, input, description, and validation message wiring. */
43
43
  export const Default: Story = {
44
- render: () => <FormDemo />,
44
+ render: () => <FormDemo />,
45
45
  };