create-auto-app 1.34.0 → 1.35.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 (157) hide show
  1. package/package.json +2 -2
  2. package/templates/typical/.context/components-db.json +4006 -0
  3. package/templates/typical/.gitignore +1 -1
  4. package/templates/typical/auto.config.ts +85 -80
  5. package/templates/typical/client/.gitignore +0 -4
  6. package/templates/typical/client/.storybook/main.ts +0 -66
  7. package/templates/typical/client/.storybook/manager-head.html +0 -154
  8. package/templates/typical/client/.storybook/manager.ts +0 -164
  9. package/templates/typical/client/.storybook/preview-head.html +0 -31
  10. package/templates/typical/client/.storybook/preview.tsx +0 -120
  11. package/templates/typical/client/codegen.ts +0 -17
  12. package/templates/typical/client/components.json +0 -29
  13. package/templates/typical/client/index.html +0 -12
  14. package/templates/typical/client/package.json +0 -69
  15. package/templates/typical/client/pnpm-lock.yaml +0 -7868
  16. package/templates/typical/client/public/blank.svg +0 -1
  17. package/templates/typical/client/public/mockServiceWorker.js +0 -336
  18. package/templates/typical/client/src/App.tsx +0 -29
  19. package/templates/typical/client/src/components/.gitkeep +0 -0
  20. package/templates/typical/client/src/components/ui/Accordion.stories.tsx +0 -49
  21. package/templates/typical/client/src/components/ui/Accordion.tsx +0 -55
  22. package/templates/typical/client/src/components/ui/Alert.stories.tsx +0 -29
  23. package/templates/typical/client/src/components/ui/Alert.tsx +0 -56
  24. package/templates/typical/client/src/components/ui/AlertDialog.stories.tsx +0 -67
  25. package/templates/typical/client/src/components/ui/AlertDialog.tsx +0 -178
  26. package/templates/typical/client/src/components/ui/AspectRatio.stories.tsx +0 -35
  27. package/templates/typical/client/src/components/ui/AspectRatio.tsx +0 -13
  28. package/templates/typical/client/src/components/ui/Avatar.stories.tsx +0 -45
  29. package/templates/typical/client/src/components/ui/Avatar.tsx +0 -98
  30. package/templates/typical/client/src/components/ui/Badge.stories.tsx +0 -41
  31. package/templates/typical/client/src/components/ui/Badge.tsx +0 -45
  32. package/templates/typical/client/src/components/ui/Breadcrumb.stories.tsx +0 -54
  33. package/templates/typical/client/src/components/ui/Breadcrumb.tsx +0 -104
  34. package/templates/typical/client/src/components/ui/Button.stories.tsx +0 -102
  35. package/templates/typical/client/src/components/ui/Button.tsx +0 -67
  36. package/templates/typical/client/src/components/ui/ButtonGroup.stories.tsx +0 -32
  37. package/templates/typical/client/src/components/ui/ButtonGroup.tsx +0 -81
  38. package/templates/typical/client/src/components/ui/Calendar.stories.tsx +0 -40
  39. package/templates/typical/client/src/components/ui/Calendar.tsx +0 -165
  40. package/templates/typical/client/src/components/ui/Card.stories.tsx +0 -44
  41. package/templates/typical/client/src/components/ui/Card.tsx +0 -66
  42. package/templates/typical/client/src/components/ui/Carousel.stories.tsx +0 -56
  43. package/templates/typical/client/src/components/ui/Carousel.tsx +0 -225
  44. package/templates/typical/client/src/components/ui/Chart.stories.tsx +0 -39
  45. package/templates/typical/client/src/components/ui/Chart.tsx +0 -305
  46. package/templates/typical/client/src/components/ui/Checkbox.stories.tsx +0 -35
  47. package/templates/typical/client/src/components/ui/Checkbox.tsx +0 -30
  48. package/templates/typical/client/src/components/ui/Collapsible.stories.tsx +0 -58
  49. package/templates/typical/client/src/components/ui/Collapsible.tsx +0 -18
  50. package/templates/typical/client/src/components/ui/Combobox.stories.tsx +0 -75
  51. package/templates/typical/client/src/components/ui/Combobox.tsx +0 -296
  52. package/templates/typical/client/src/components/ui/Command.stories.tsx +0 -71
  53. package/templates/typical/client/src/components/ui/Command.tsx +0 -157
  54. package/templates/typical/client/src/components/ui/ContextMenu.stories.tsx +0 -68
  55. package/templates/typical/client/src/components/ui/ContextMenu.tsx +0 -231
  56. package/templates/typical/client/src/components/ui/DesignSystem-Colors.mdx +0 -68
  57. package/templates/typical/client/src/components/ui/DesignSystem-Colors.stories.tsx +0 -117
  58. package/templates/typical/client/src/components/ui/DesignSystem-Layout.mdx +0 -64
  59. package/templates/typical/client/src/components/ui/DesignSystem-Layout.stories.tsx +0 -167
  60. package/templates/typical/client/src/components/ui/DesignSystem-Overview.stories.tsx +0 -748
  61. package/templates/typical/client/src/components/ui/DesignSystem-Typography.mdx +0 -31
  62. package/templates/typical/client/src/components/ui/DesignSystem-Typography.stories.tsx +0 -80
  63. package/templates/typical/client/src/components/ui/Dialog.stories.tsx +0 -74
  64. package/templates/typical/client/src/components/ui/Dialog.tsx +0 -154
  65. package/templates/typical/client/src/components/ui/Direction.stories.tsx +0 -38
  66. package/templates/typical/client/src/components/ui/Direction.tsx +0 -24
  67. package/templates/typical/client/src/components/ui/Drawer.stories.tsx +0 -70
  68. package/templates/typical/client/src/components/ui/Drawer.tsx +0 -124
  69. package/templates/typical/client/src/components/ui/DropdownMenu.stories.tsx +0 -74
  70. package/templates/typical/client/src/components/ui/DropdownMenu.tsx +0 -239
  71. package/templates/typical/client/src/components/ui/Empty.stories.tsx +0 -37
  72. package/templates/typical/client/src/components/ui/Empty.tsx +0 -98
  73. package/templates/typical/client/src/components/ui/Field.stories.tsx +0 -50
  74. package/templates/typical/client/src/components/ui/Field.tsx +0 -251
  75. package/templates/typical/client/src/components/ui/Form.stories.tsx +0 -45
  76. package/templates/typical/client/src/components/ui/Form.tsx +0 -148
  77. package/templates/typical/client/src/components/ui/HoverCard.stories.tsx +0 -49
  78. package/templates/typical/client/src/components/ui/HoverCard.tsx +0 -39
  79. package/templates/typical/client/src/components/ui/Input.stories.tsx +0 -42
  80. package/templates/typical/client/src/components/ui/Input.tsx +0 -22
  81. package/templates/typical/client/src/components/ui/InputGroup.stories.tsx +0 -53
  82. package/templates/typical/client/src/components/ui/InputGroup.tsx +0 -153
  83. package/templates/typical/client/src/components/ui/InputOTP.stories.tsx +0 -42
  84. package/templates/typical/client/src/components/ui/InputOTP.tsx +0 -72
  85. package/templates/typical/client/src/components/ui/Item.stories.tsx +0 -64
  86. package/templates/typical/client/src/components/ui/Item.tsx +0 -168
  87. package/templates/typical/client/src/components/ui/Kbd.stories.tsx +0 -59
  88. package/templates/typical/client/src/components/ui/Kbd.tsx +0 -22
  89. package/templates/typical/client/src/components/ui/Label.stories.tsx +0 -90
  90. package/templates/typical/client/src/components/ui/Label.tsx +0 -44
  91. package/templates/typical/client/src/components/ui/Menubar.stories.tsx +0 -78
  92. package/templates/typical/client/src/components/ui/Menubar.tsx +0 -251
  93. package/templates/typical/client/src/components/ui/NativeSelect.stories.tsx +0 -45
  94. package/templates/typical/client/src/components/ui/NativeSelect.tsx +0 -50
  95. package/templates/typical/client/src/components/ui/NavigationMenu.stories.tsx +0 -80
  96. package/templates/typical/client/src/components/ui/NavigationMenu.tsx +0 -152
  97. package/templates/typical/client/src/components/ui/Pagination.stories.tsx +0 -77
  98. package/templates/typical/client/src/components/ui/Pagination.tsx +0 -108
  99. package/templates/typical/client/src/components/ui/Popover.stories.tsx +0 -53
  100. package/templates/typical/client/src/components/ui/Popover.tsx +0 -57
  101. package/templates/typical/client/src/components/ui/Progress.stories.tsx +0 -32
  102. package/templates/typical/client/src/components/ui/Progress.tsx +0 -25
  103. package/templates/typical/client/src/components/ui/RadioGroup.stories.tsx +0 -50
  104. package/templates/typical/client/src/components/ui/RadioGroup.tsx +0 -36
  105. package/templates/typical/client/src/components/ui/Resizable.stories.tsx +0 -72
  106. package/templates/typical/client/src/components/ui/Resizable.tsx +0 -54
  107. package/templates/typical/client/src/components/ui/ScrollArea.stories.tsx +0 -45
  108. package/templates/typical/client/src/components/ui/ScrollArea.tsx +0 -51
  109. package/templates/typical/client/src/components/ui/Select.stories.tsx +0 -59
  110. package/templates/typical/client/src/components/ui/Select.tsx +0 -171
  111. package/templates/typical/client/src/components/ui/Separator.stories.tsx +0 -42
  112. package/templates/typical/client/src/components/ui/Separator.tsx +0 -27
  113. package/templates/typical/client/src/components/ui/Sheet.stories.tsx +0 -68
  114. package/templates/typical/client/src/components/ui/Sheet.tsx +0 -115
  115. package/templates/typical/client/src/components/ui/Sidebar.stories.tsx +0 -96
  116. package/templates/typical/client/src/components/ui/Sidebar.tsx +0 -695
  117. package/templates/typical/client/src/components/ui/Skeleton.stories.tsx +0 -40
  118. package/templates/typical/client/src/components/ui/Skeleton.tsx +0 -11
  119. package/templates/typical/client/src/components/ui/Slider.stories.tsx +0 -24
  120. package/templates/typical/client/src/components/ui/Slider.tsx +0 -55
  121. package/templates/typical/client/src/components/ui/Sonner.stories.tsx +0 -45
  122. package/templates/typical/client/src/components/ui/Sonner.tsx +0 -38
  123. package/templates/typical/client/src/components/ui/Spinner.stories.tsx +0 -26
  124. package/templates/typical/client/src/components/ui/Spinner.tsx +0 -13
  125. package/templates/typical/client/src/components/ui/Switch.stories.tsx +0 -39
  126. package/templates/typical/client/src/components/ui/Switch.tsx +0 -35
  127. package/templates/typical/client/src/components/ui/Table.stories.tsx +0 -67
  128. package/templates/typical/client/src/components/ui/Table.tsx +0 -86
  129. package/templates/typical/client/src/components/ui/Tabs.stories.tsx +0 -53
  130. package/templates/typical/client/src/components/ui/Tabs.tsx +0 -75
  131. package/templates/typical/client/src/components/ui/Textarea.stories.tsx +0 -27
  132. package/templates/typical/client/src/components/ui/Textarea.tsx +0 -22
  133. package/templates/typical/client/src/components/ui/Toast.stories.tsx +0 -116
  134. package/templates/typical/client/src/components/ui/Toast.tsx +0 -123
  135. package/templates/typical/client/src/components/ui/Toaster.tsx +0 -32
  136. package/templates/typical/client/src/components/ui/Toggle.stories.tsx +0 -44
  137. package/templates/typical/client/src/components/ui/Toggle.tsx +0 -42
  138. package/templates/typical/client/src/components/ui/ToggleGroup.stories.tsx +0 -61
  139. package/templates/typical/client/src/components/ui/ToggleGroup.tsx +0 -83
  140. package/templates/typical/client/src/components/ui/Tooltip.stories.tsx +0 -42
  141. package/templates/typical/client/src/components/ui/Tooltip.tsx +0 -48
  142. package/templates/typical/client/src/gql/execute.ts +0 -11
  143. package/templates/typical/client/src/gql/fragment-masking.ts +0 -83
  144. package/templates/typical/client/src/gql/gql.ts +0 -9
  145. package/templates/typical/client/src/gql/graphql.ts +0 -182
  146. package/templates/typical/client/src/gql/index.ts +0 -2
  147. package/templates/typical/client/src/graphql/mutations.ts +0 -0
  148. package/templates/typical/client/src/graphql/queries.ts +0 -0
  149. package/templates/typical/client/src/hooks/.gitkeep +0 -0
  150. package/templates/typical/client/src/hooks/use-mobile.ts +0 -19
  151. package/templates/typical/client/src/hooks/use-toast.ts +0 -186
  152. package/templates/typical/client/src/index.css +0 -121
  153. package/templates/typical/client/src/lib/utils.ts +0 -6
  154. package/templates/typical/client/src/main.tsx +0 -5
  155. package/templates/typical/client/tsconfig.app.json +0 -26
  156. package/templates/typical/client/tsconfig.json +0 -10
  157. package/templates/typical/client/vite.config.ts +0 -50
@@ -1,239 +0,0 @@
1
- 'use client';
2
-
3
- import * as React from 'react';
4
- import { CheckIcon, ChevronRightIcon, CircleIcon } from 'lucide-react';
5
- import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui';
6
-
7
- import { cn } from '@/lib/utils';
8
-
9
- /** Action menu triggered by a button click. Supports submenus, checkbox/radio items, and keyboard shortcuts. */
10
- function DropdownMenu({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Root>) {
11
- return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />;
12
- }
13
-
14
- /** Renders dropdown menu content into a React portal. */
15
- function DropdownMenuPortal({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
16
- return <DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />;
17
- }
18
-
19
- /** Button or element that toggles the dropdown menu open/closed. */
20
- function DropdownMenuTrigger({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
21
- return <DropdownMenuPrimitive.Trigger data-slot="dropdown-menu-trigger" {...props} />;
22
- }
23
-
24
- /** The positioned popup container for dropdown menu items. Portals to the document body. */
25
- function DropdownMenuContent({
26
- className,
27
- sideOffset = 4,
28
- ...props
29
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
30
- return (
31
- <DropdownMenuPrimitive.Portal>
32
- <DropdownMenuPrimitive.Content
33
- data-slot="dropdown-menu-content"
34
- sideOffset={sideOffset}
35
- className={cn(
36
- 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md',
37
- className,
38
- )}
39
- {...props}
40
- />
41
- </DropdownMenuPrimitive.Portal>
42
- );
43
- }
44
-
45
- /** Groups related dropdown menu items for accessibility. */
46
- function DropdownMenuGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
47
- return <DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />;
48
- }
49
-
50
- /**
51
- * A selectable action within the dropdown menu.
52
- * Use variant="destructive" for dangerous actions like delete or logout.
53
- */
54
- function DropdownMenuItem({
55
- className,
56
- /** Adds left padding to align with items that have icons or indicators. */
57
- inset,
58
- /** Use "destructive" for dangerous actions. */
59
- variant = 'default',
60
- ...props
61
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
62
- inset?: boolean;
63
- variant?: 'default' | 'destructive';
64
- }) {
65
- return (
66
- <DropdownMenuPrimitive.Item
67
- data-slot="dropdown-menu-item"
68
- data-inset={inset}
69
- data-variant={variant}
70
- className={cn(
71
- "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
72
- className,
73
- )}
74
- {...props}
75
- />
76
- );
77
- }
78
-
79
- /** A toggleable checkbox item within the dropdown menu. Shows a check indicator when active. */
80
- function DropdownMenuCheckboxItem({
81
- className,
82
- children,
83
- checked,
84
- ...props
85
- }: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
86
- return (
87
- <DropdownMenuPrimitive.CheckboxItem
88
- data-slot="dropdown-menu-checkbox-item"
89
- className={cn(
90
- "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
91
- className,
92
- )}
93
- checked={checked}
94
- {...props}
95
- >
96
- <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
97
- <DropdownMenuPrimitive.ItemIndicator>
98
- <CheckIcon className="size-4" />
99
- </DropdownMenuPrimitive.ItemIndicator>
100
- </span>
101
- {children}
102
- </DropdownMenuPrimitive.CheckboxItem>
103
- );
104
- }
105
-
106
- /** Groups radio items for single-selection within the dropdown menu. */
107
- function DropdownMenuRadioGroup({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
108
- return <DropdownMenuPrimitive.RadioGroup data-slot="dropdown-menu-radio-group" {...props} />;
109
- }
110
-
111
- /** A radio-selectable item within a DropdownMenuRadioGroup. Shows a dot indicator when selected. */
112
- function DropdownMenuRadioItem({
113
- className,
114
- children,
115
- ...props
116
- }: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
117
- return (
118
- <DropdownMenuPrimitive.RadioItem
119
- data-slot="dropdown-menu-radio-item"
120
- className={cn(
121
- "focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
122
- className,
123
- )}
124
- {...props}
125
- >
126
- <span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
127
- <DropdownMenuPrimitive.ItemIndicator>
128
- <CircleIcon className="size-2 fill-current" />
129
- </DropdownMenuPrimitive.ItemIndicator>
130
- </span>
131
- {children}
132
- </DropdownMenuPrimitive.RadioItem>
133
- );
134
- }
135
-
136
- /** Non-interactive label within a dropdown menu, used for section headings. */
137
- function DropdownMenuLabel({
138
- className,
139
- inset,
140
- ...props
141
- }: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
142
- inset?: boolean;
143
- }) {
144
- return (
145
- <DropdownMenuPrimitive.Label
146
- data-slot="dropdown-menu-label"
147
- data-inset={inset}
148
- className={cn('px-2 py-1.5 text-sm font-medium data-[inset]:pl-8', className)}
149
- {...props}
150
- />
151
- );
152
- }
153
-
154
- /** Visual divider between groups of dropdown menu items. */
155
- function DropdownMenuSeparator({ className, ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
156
- return (
157
- <DropdownMenuPrimitive.Separator
158
- data-slot="dropdown-menu-separator"
159
- className={cn('bg-border -mx-1 my-1 h-px', className)}
160
- {...props}
161
- />
162
- );
163
- }
164
-
165
- /** Displays a keyboard shortcut hint aligned to the right side of a dropdown menu item. */
166
- function DropdownMenuShortcut({ className, ...props }: React.ComponentProps<'span'>) {
167
- return (
168
- <span
169
- data-slot="dropdown-menu-shortcut"
170
- className={cn('text-muted-foreground ml-auto text-xs tracking-widest', className)}
171
- {...props}
172
- />
173
- );
174
- }
175
-
176
- /** Container for a submenu within the dropdown menu. */
177
- function DropdownMenuSub({ ...props }: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
178
- return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />;
179
- }
180
-
181
- /** Menu item that opens a nested submenu on hover or keyboard navigation. Shows a chevron indicator. */
182
- function DropdownMenuSubTrigger({
183
- className,
184
- inset,
185
- children,
186
- ...props
187
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
188
- inset?: boolean;
189
- }) {
190
- return (
191
- <DropdownMenuPrimitive.SubTrigger
192
- data-slot="dropdown-menu-sub-trigger"
193
- data-inset={inset}
194
- className={cn(
195
- "focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
196
- className,
197
- )}
198
- {...props}
199
- >
200
- {children}
201
- <ChevronRightIcon className="ml-auto size-4" />
202
- </DropdownMenuPrimitive.SubTrigger>
203
- );
204
- }
205
-
206
- /** Popup content for a nested submenu. */
207
- function DropdownMenuSubContent({
208
- className,
209
- ...props
210
- }: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
211
- return (
212
- <DropdownMenuPrimitive.SubContent
213
- data-slot="dropdown-menu-sub-content"
214
- className={cn(
215
- 'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg',
216
- className,
217
- )}
218
- {...props}
219
- />
220
- );
221
- }
222
-
223
- export {
224
- DropdownMenu,
225
- DropdownMenuPortal,
226
- DropdownMenuTrigger,
227
- DropdownMenuContent,
228
- DropdownMenuGroup,
229
- DropdownMenuLabel,
230
- DropdownMenuItem,
231
- DropdownMenuCheckboxItem,
232
- DropdownMenuRadioGroup,
233
- DropdownMenuRadioItem,
234
- DropdownMenuSeparator,
235
- DropdownMenuShortcut,
236
- DropdownMenuSub,
237
- DropdownMenuSubTrigger,
238
- DropdownMenuSubContent,
239
- };
@@ -1,37 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyMedia } from '@/components/ui/Empty';
3
- import { InboxIcon } from 'lucide-react';
4
-
5
- const meta: Meta<typeof Empty> = {
6
- title: 'UI Components/Empty',
7
- component: Empty,
8
- };
9
- export default meta;
10
- type Story = StoryObj<typeof Empty>;
11
-
12
- /** Shows a basic empty state with a title and description text. */
13
- export const Default: Story = {
14
- render: () => (
15
- <Empty>
16
- <EmptyHeader>
17
- <EmptyTitle>No results found</EmptyTitle>
18
- <EmptyDescription>Try adjusting your search or filter to find what you are looking for.</EmptyDescription>
19
- </EmptyHeader>
20
- </Empty>
21
- ),
22
- };
23
-
24
- /** Shows an empty state with a styled icon container above the title and description. */
25
- export const WithIcon: Story = {
26
- render: () => (
27
- <Empty>
28
- <EmptyHeader>
29
- <EmptyMedia variant="icon">
30
- <InboxIcon />
31
- </EmptyMedia>
32
- <EmptyTitle>Your inbox is empty</EmptyTitle>
33
- <EmptyDescription>New messages will appear here when you receive them.</EmptyDescription>
34
- </EmptyHeader>
35
- </Empty>
36
- ),
37
- };
@@ -1,98 +0,0 @@
1
- import { cva, type VariantProps } from 'class-variance-authority';
2
-
3
- import { cn } from '@/lib/utils';
4
-
5
- /**
6
- * A placeholder component for empty states, such as no search results or empty inboxes.
7
- * Compose with EmptyHeader, EmptyMedia, EmptyTitle, EmptyDescription, and EmptyContent
8
- * to build structured empty state layouts with icons and call-to-action areas.
9
- */
10
- function Empty({ className, ...props }: React.ComponentProps<'div'>) {
11
- return (
12
- <div
13
- data-slot="empty"
14
- className={cn(
15
- 'flex min-w-0 flex-1 flex-col items-center justify-center gap-6 rounded-lg border-dashed p-6 text-center text-balance md:p-12',
16
- className,
17
- )}
18
- {...props}
19
- />
20
- );
21
- }
22
-
23
- /** Container for the title, description, and optional media in an empty state. */
24
- function EmptyHeader({ className, ...props }: React.ComponentProps<'div'>) {
25
- return (
26
- <div
27
- data-slot="empty-header"
28
- className={cn('flex max-w-sm flex-col items-center gap-2 text-center', className)}
29
- {...props}
30
- />
31
- );
32
- }
33
-
34
- const emptyMediaVariants = cva(
35
- 'flex shrink-0 items-center justify-center mb-2 [&_svg]:pointer-events-none [&_svg]:shrink-0',
36
- {
37
- variants: {
38
- variant: {
39
- default: 'bg-transparent',
40
- icon: "bg-muted text-foreground flex size-10 shrink-0 items-center justify-center rounded-lg [&_svg:not([class*='size-'])]:size-6",
41
- },
42
- },
43
- defaultVariants: {
44
- variant: 'default',
45
- },
46
- },
47
- );
48
-
49
- /**
50
- * Container for an icon or illustration in the empty state.
51
- * Use variant="icon" for a styled icon container with a muted background.
52
- */
53
- function EmptyMedia({
54
- className,
55
- variant = 'default',
56
- ...props
57
- }: React.ComponentProps<'div'> & VariantProps<typeof emptyMediaVariants>) {
58
- return (
59
- <div
60
- data-slot="empty-icon"
61
- data-variant={variant}
62
- className={cn(emptyMediaVariants({ variant, className }))}
63
- {...props}
64
- />
65
- );
66
- }
67
-
68
- /** Title text for the empty state message. */
69
- function EmptyTitle({ className, ...props }: React.ComponentProps<'div'>) {
70
- return <div data-slot="empty-title" className={cn('text-lg font-medium tracking-tight', className)} {...props} />;
71
- }
72
-
73
- /** Descriptive text explaining why the content is empty or what the user can do. */
74
- function EmptyDescription({ className, ...props }: React.ComponentProps<'p'>) {
75
- return (
76
- <div
77
- data-slot="empty-description"
78
- className={cn(
79
- 'text-muted-foreground [&>a:hover]:text-primary text-sm/relaxed [&>a]:underline [&>a]:underline-offset-4',
80
- className,
81
- )}
82
- {...props}
83
- />
84
- );
85
- }
86
-
87
- /** Container for action buttons or additional content below the empty state message. */
88
- function EmptyContent({ className, ...props }: React.ComponentProps<'div'>) {
89
- return (
90
- <div
91
- data-slot="empty-content"
92
- className={cn('flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance', className)}
93
- {...props}
94
- />
95
- );
96
- }
97
-
98
- export { Empty, EmptyHeader, EmptyTitle, EmptyDescription, EmptyContent, EmptyMedia };
@@ -1,50 +0,0 @@
1
- import type { Meta, StoryObj } from '@storybook/react-vite';
2
- import { Field, FieldLabel, FieldDescription, FieldError, FieldContent } from '@/components/ui/Field';
3
- import { Input } from '@/components/ui/Input';
4
-
5
- const meta: Meta<typeof Field> = {
6
- title: 'UI Components/Field',
7
- component: Field,
8
- };
9
- export default meta;
10
- type Story = StoryObj<typeof Field>;
11
-
12
- /** Shows a vertical form field with label, input, and helper description text. */
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
- ),
23
- };
24
-
25
- /** Shows a field in an invalid state with error styling and a validation error message. */
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
- ),
37
- };
38
-
39
- /** Shows a field with horizontal orientation where the label and input are side by side. */
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
- ),
50
- };
@@ -1,251 +0,0 @@
1
- 'use client';
2
-
3
- import { useMemo } from 'react';
4
- import { cva, type VariantProps } from 'class-variance-authority';
5
-
6
- import { cn } from '@/lib/utils';
7
- import { Label } from '@/components/ui/Label';
8
- import { Separator } from '@/components/ui/Separator';
9
-
10
- /**
11
- * A semantic fieldset container for grouping related form fields.
12
- * Use as a top-level wrapper around FieldGroup or multiple Field components.
13
- */
14
- 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
- );
26
- }
27
-
28
- /**
29
- * A legend element for a FieldSet, displayed as the group heading.
30
- * Use variant="label" for a smaller label-sized heading.
31
- */
32
- function FieldLegend({
33
- className,
34
- variant = 'legend',
35
- ...props
36
- }: 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
- );
45
- }
46
-
47
- /** Container for a group of Field components. Provides container query context for responsive layouts. */
48
- 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
- );
59
- }
60
-
61
- 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
- },
80
- });
81
-
82
- /**
83
- * A form field wrapper that pairs a label with an input and optional description/error.
84
- * Supports vertical, horizontal, and responsive orientations.
85
- * Set data-invalid="true" to apply error styling.
86
- */
87
- 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
92
- }: 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
- );
102
- }
103
-
104
- /** Container for the input element plus optional description and error within a Field. */
105
- 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
- );
113
- }
114
-
115
- /** Label for a form field. Supports wrapping checkbox/radio inputs for card-style selection. */
116
- 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
- );
129
- }
130
-
131
- /** Non-interactive title text for a field, used when a label association is not needed. */
132
- 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
- );
143
- }
144
-
145
- /** Helper text displayed below the input to provide additional guidance. */
146
- 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
- );
159
- }
160
-
161
- /** A visual separator between fields within a FieldGroup. Optionally displays centered text. */
162
- function FieldSeparator({
163
- children,
164
- className,
165
- ...props
166
- }: React.ComponentProps<'div'> & {
167
- children?: React.ReactNode;
168
- }) {
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
- );
187
- }
188
-
189
- /**
190
- * Displays validation error messages for a form field.
191
- * Accepts either children or an errors array; deduplicates and renders as a list if multiple.
192
- */
193
- function FieldError({
194
- className,
195
- children,
196
- /** Array of error objects with message strings. Automatically deduplicated. */
197
- errors,
198
- ...props
199
- }: React.ComponentProps<'div'> & {
200
- errors?: Array<{ message?: string } | undefined>;
201
- }) {
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
- );
238
- }
239
-
240
- export {
241
- Field,
242
- FieldLabel,
243
- FieldDescription,
244
- FieldError,
245
- FieldGroup,
246
- FieldLegend,
247
- FieldSeparator,
248
- FieldSet,
249
- FieldContent,
250
- FieldTitle,
251
- };