azamat-ui-kit-cli 0.2.2

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 (213) hide show
  1. package/README.md +8 -0
  2. package/dist/index.js +432 -0
  3. package/package.json +34 -0
  4. package/vendor/package.json +4 -0
  5. package/vendor/src/components/actions/action-bar.tsx +35 -0
  6. package/vendor/src/components/actions/action-menu.tsx +120 -0
  7. package/vendor/src/components/actions/button-group.tsx +47 -0
  8. package/vendor/src/components/actions/copy-button.tsx +91 -0
  9. package/vendor/src/components/actions/copy-field.tsx +31 -0
  10. package/vendor/src/components/actions/floating-action-button.tsx +33 -0
  11. package/vendor/src/components/actions/index.ts +7 -0
  12. package/vendor/src/components/actions/public.ts +5 -0
  13. package/vendor/src/components/actions/quick-action-grid.tsx +162 -0
  14. package/vendor/src/components/calendar/calendar.tsx +328 -0
  15. package/vendor/src/components/calendar/date-picker.tsx +78 -0
  16. package/vendor/src/components/calendar/date-range-picker.tsx +96 -0
  17. package/vendor/src/components/calendar/date-utils.ts +89 -0
  18. package/vendor/src/components/calendar/index.ts +4 -0
  19. package/vendor/src/components/charts/charts.tsx +275 -0
  20. package/vendor/src/components/charts/horizontal-bar-chart.tsx +46 -0
  21. package/vendor/src/components/charts/index.ts +4 -0
  22. package/vendor/src/components/charts/kpi.tsx +68 -0
  23. package/vendor/src/components/charts/progress-ring.tsx +45 -0
  24. package/vendor/src/components/charts/public.ts +1 -0
  25. package/vendor/src/components/command/command-palette.tsx +375 -0
  26. package/vendor/src/components/command/index.ts +1 -0
  27. package/vendor/src/components/data-table/data-table-actions-column.tsx +58 -0
  28. package/vendor/src/components/data-table/data-table-bulk-actions.tsx +84 -0
  29. package/vendor/src/components/data-table/data-table-column-visibility-menu.tsx +79 -0
  30. package/vendor/src/components/data-table/data-table-pagination.tsx +91 -0
  31. package/vendor/src/components/data-table/data-table-row-actions.tsx +48 -0
  32. package/vendor/src/components/data-table/data-table-select-column.tsx +59 -0
  33. package/vendor/src/components/data-table/data-table-sortable-header.tsx +45 -0
  34. package/vendor/src/components/data-table/data-table-toolbar.tsx +76 -0
  35. package/vendor/src/components/data-table/data-table-view-presets.tsx +128 -0
  36. package/vendor/src/components/data-table/data-table.tsx +507 -0
  37. package/vendor/src/components/data-table/index.ts +12 -0
  38. package/vendor/src/components/data-table/public.ts +10 -0
  39. package/vendor/src/components/data-table/table-export-menu.tsx +56 -0
  40. package/vendor/src/components/data-table/table-import-button.tsx +43 -0
  41. package/vendor/src/components/display/activity-feed.tsx +97 -0
  42. package/vendor/src/components/display/avatar.tsx +131 -0
  43. package/vendor/src/components/display/code-block.tsx +33 -0
  44. package/vendor/src/components/display/data-state.tsx +63 -0
  45. package/vendor/src/components/display/description-list.tsx +119 -0
  46. package/vendor/src/components/display/descriptions.tsx +83 -0
  47. package/vendor/src/components/display/entity-card.tsx +53 -0
  48. package/vendor/src/components/display/file-card.tsx +54 -0
  49. package/vendor/src/components/display/index.ts +30 -0
  50. package/vendor/src/components/display/kanban.tsx +104 -0
  51. package/vendor/src/components/display/keyboard-shortcut.tsx +31 -0
  52. package/vendor/src/components/display/list.tsx +100 -0
  53. package/vendor/src/components/display/metric-grid.tsx +86 -0
  54. package/vendor/src/components/display/progress.tsx +162 -0
  55. package/vendor/src/components/display/property-grid.tsx +54 -0
  56. package/vendor/src/components/display/result.tsx +90 -0
  57. package/vendor/src/components/display/smart-card.tsx +168 -0
  58. package/vendor/src/components/display/statistic.tsx +107 -0
  59. package/vendor/src/components/display/status-legend.tsx +108 -0
  60. package/vendor/src/components/display/tag-list.tsx +52 -0
  61. package/vendor/src/components/display/timeline.tsx +132 -0
  62. package/vendor/src/components/display/tree-view.tsx +116 -0
  63. package/vendor/src/components/feedback/alert.tsx +69 -0
  64. package/vendor/src/components/feedback/empty-state.tsx +56 -0
  65. package/vendor/src/components/feedback/index.ts +5 -0
  66. package/vendor/src/components/feedback/loading-state.tsx +39 -0
  67. package/vendor/src/components/feedback/page-state.tsx +69 -0
  68. package/vendor/src/components/feedback/status-badge.tsx +62 -0
  69. package/vendor/src/components/filters/filter-bar.tsx +89 -0
  70. package/vendor/src/components/filters/filter-chips.tsx +69 -0
  71. package/vendor/src/components/filters/index.ts +2 -0
  72. package/vendor/src/components/form/form-actions.tsx +53 -0
  73. package/vendor/src/components/form/form-async-select.tsx +26 -0
  74. package/vendor/src/components/form/form-date-input.tsx +19 -0
  75. package/vendor/src/components/form/form-date-picker.tsx +54 -0
  76. package/vendor/src/components/form/form-date-range-input.tsx +79 -0
  77. package/vendor/src/components/form/form-date-range-picker.tsx +57 -0
  78. package/vendor/src/components/form/form-field-shell.tsx +191 -0
  79. package/vendor/src/components/form/form-input.tsx +480 -0
  80. package/vendor/src/components/form/form-number-input.tsx +19 -0
  81. package/vendor/src/components/form/form-password-input.tsx +19 -0
  82. package/vendor/src/components/form/form-phone-input.tsx +22 -0
  83. package/vendor/src/components/form/form-search-input.tsx +19 -0
  84. package/vendor/src/components/form/form-section.tsx +29 -0
  85. package/vendor/src/components/form/form-select.tsx +194 -0
  86. package/vendor/src/components/form/form-switch.tsx +145 -0
  87. package/vendor/src/components/form/form-textarea.tsx +103 -0
  88. package/vendor/src/components/form/index.ts +17 -0
  89. package/vendor/src/components/form/public.ts +14 -0
  90. package/vendor/src/components/form/smart-form-shell.tsx +59 -0
  91. package/vendor/src/components/inputs/async-select.tsx +1143 -0
  92. package/vendor/src/components/inputs/clearable-input.tsx +78 -0
  93. package/vendor/src/components/inputs/color-input.tsx +47 -0
  94. package/vendor/src/components/inputs/combobox.tsx +89 -0
  95. package/vendor/src/components/inputs/date-input.tsx +32 -0
  96. package/vendor/src/components/inputs/date-range-input.tsx +67 -0
  97. package/vendor/src/components/inputs/index.ts +19 -0
  98. package/vendor/src/components/inputs/input-chrome.tsx +37 -0
  99. package/vendor/src/components/inputs/input-decorator.tsx +64 -0
  100. package/vendor/src/components/inputs/input-value.ts +42 -0
  101. package/vendor/src/components/inputs/masked-input.tsx +51 -0
  102. package/vendor/src/components/inputs/money-input.tsx +73 -0
  103. package/vendor/src/components/inputs/number-input.tsx +87 -0
  104. package/vendor/src/components/inputs/numeric-value.ts +39 -0
  105. package/vendor/src/components/inputs/otp-input.tsx +102 -0
  106. package/vendor/src/components/inputs/password-input.tsx +85 -0
  107. package/vendor/src/components/inputs/phone-input.tsx +46 -0
  108. package/vendor/src/components/inputs/quantity-input.tsx +116 -0
  109. package/vendor/src/components/inputs/quantity-stepper.tsx +49 -0
  110. package/vendor/src/components/inputs/rating.tsx +98 -0
  111. package/vendor/src/components/inputs/search-input.tsx +26 -0
  112. package/vendor/src/components/inputs/simple-select.tsx +72 -0
  113. package/vendor/src/components/inputs/slider.tsx +149 -0
  114. package/vendor/src/components/inputs/tag-input.tsx +104 -0
  115. package/vendor/src/components/layout/app-header.tsx +46 -0
  116. package/vendor/src/components/layout/app-shell.tsx +243 -0
  117. package/vendor/src/components/layout/app-sidebar.tsx +179 -0
  118. package/vendor/src/components/layout/breadcrumbs.tsx +72 -0
  119. package/vendor/src/components/layout/index.ts +11 -0
  120. package/vendor/src/components/layout/page-container.tsx +30 -0
  121. package/vendor/src/components/layout/page-header.tsx +60 -0
  122. package/vendor/src/components/layout/public.ts +10 -0
  123. package/vendor/src/components/layout/section.tsx +76 -0
  124. package/vendor/src/components/layout/sidebar-nav.tsx +147 -0
  125. package/vendor/src/components/layout/stat-card.tsx +88 -0
  126. package/vendor/src/components/layout/sticky-footer-bar.tsx +23 -0
  127. package/vendor/src/components/layout/workspace-shell.tsx +50 -0
  128. package/vendor/src/components/navigation/anchor-nav.tsx +44 -0
  129. package/vendor/src/components/navigation/index.ts +4 -0
  130. package/vendor/src/components/navigation/page-tabs.tsx +67 -0
  131. package/vendor/src/components/navigation/pagination.tsx +179 -0
  132. package/vendor/src/components/navigation/stepper-tabs.tsx +67 -0
  133. package/vendor/src/components/notifications/index.ts +1 -0
  134. package/vendor/src/components/notifications/toast.tsx +259 -0
  135. package/vendor/src/components/overlay/confirm-dialog.tsx +66 -0
  136. package/vendor/src/components/overlay/dialog-actions.tsx +68 -0
  137. package/vendor/src/components/overlay/index.ts +4 -0
  138. package/vendor/src/components/overlay/modal-shell.tsx +93 -0
  139. package/vendor/src/components/overlay/sheet-shell.tsx +212 -0
  140. package/vendor/src/components/patterns/action-system.tsx +116 -0
  141. package/vendor/src/components/patterns/crud-system.tsx +53 -0
  142. package/vendor/src/components/patterns/data-view.tsx +84 -0
  143. package/vendor/src/components/patterns/entity-details.tsx +66 -0
  144. package/vendor/src/components/patterns/filter-builder.tsx +113 -0
  145. package/vendor/src/components/patterns/form-builder-presets.ts +131 -0
  146. package/vendor/src/components/patterns/form-builder.tsx +334 -0
  147. package/vendor/src/components/patterns/index.ts +12 -0
  148. package/vendor/src/components/patterns/public.ts +4 -0
  149. package/vendor/src/components/patterns/resource-detail-page.tsx +160 -0
  150. package/vendor/src/components/patterns/resource-page.tsx +159 -0
  151. package/vendor/src/components/patterns/resource-system.tsx +61 -0
  152. package/vendor/src/components/patterns/settings-section.tsx +46 -0
  153. package/vendor/src/components/patterns/status-system.tsx +89 -0
  154. package/vendor/src/components/theme-provider.tsx +51 -0
  155. package/vendor/src/components/ui/badge.tsx +52 -0
  156. package/vendor/src/components/ui/button.tsx +61 -0
  157. package/vendor/src/components/ui/card.tsx +103 -0
  158. package/vendor/src/components/ui/checkbox.tsx +82 -0
  159. package/vendor/src/components/ui/collapse.tsx +126 -0
  160. package/vendor/src/components/ui/command.tsx +194 -0
  161. package/vendor/src/components/ui/dialog.tsx +160 -0
  162. package/vendor/src/components/ui/divider.tsx +46 -0
  163. package/vendor/src/components/ui/dropdown-menu.tsx +266 -0
  164. package/vendor/src/components/ui/input-group.tsx +158 -0
  165. package/vendor/src/components/ui/input.tsx +20 -0
  166. package/vendor/src/components/ui/popover.tsx +90 -0
  167. package/vendor/src/components/ui/segmented-control.tsx +78 -0
  168. package/vendor/src/components/ui/select.tsx +201 -0
  169. package/vendor/src/components/ui/skeleton.tsx +75 -0
  170. package/vendor/src/components/ui/spinner.tsx +50 -0
  171. package/vendor/src/components/ui/switch.tsx +71 -0
  172. package/vendor/src/components/ui/table.tsx +114 -0
  173. package/vendor/src/components/ui/tabs.tsx +55 -0
  174. package/vendor/src/components/ui/textarea.tsx +18 -0
  175. package/vendor/src/components/ui/tooltip.tsx +38 -0
  176. package/vendor/src/components/upload/file-upload.tsx +483 -0
  177. package/vendor/src/components/upload/image-upload.tsx +118 -0
  178. package/vendor/src/components/upload/index.ts +2 -0
  179. package/vendor/src/components/wizard/index.ts +2 -0
  180. package/vendor/src/components/wizard/stepper.tsx +53 -0
  181. package/vendor/src/components/wizard/wizard.tsx +60 -0
  182. package/vendor/src/families/card-family.ts +28 -0
  183. package/vendor/src/families/catalog.ts +96 -0
  184. package/vendor/src/families/data-table-family.ts +31 -0
  185. package/vendor/src/families/docs-adoption.ts +103 -0
  186. package/vendor/src/families/docs-groups.ts +209 -0
  187. package/vendor/src/families/docs-queries.ts +84 -0
  188. package/vendor/src/families/docs-routing.ts +89 -0
  189. package/vendor/src/families/form-family.ts +45 -0
  190. package/vendor/src/families/index.ts +17 -0
  191. package/vendor/src/families/input-family.ts +61 -0
  192. package/vendor/src/families/member-metadata.ts +466 -0
  193. package/vendor/src/families/member-queries.ts +28 -0
  194. package/vendor/src/families/member-snippet-queries.ts +54 -0
  195. package/vendor/src/families/member-snippets.ts +673 -0
  196. package/vendor/src/families/migration-map.ts +79 -0
  197. package/vendor/src/families/queries.ts +63 -0
  198. package/vendor/src/families/select-family.ts +33 -0
  199. package/vendor/src/families/views.ts +81 -0
  200. package/vendor/src/hooks/index.ts +6 -0
  201. package/vendor/src/hooks/use-before-unload-when-dirty.ts +21 -0
  202. package/vendor/src/hooks/use-data-table-view-state.ts +122 -0
  203. package/vendor/src/hooks/use-debounce.ts +52 -0
  204. package/vendor/src/hooks/use-disclosure.ts +38 -0
  205. package/vendor/src/hooks/use-is-mobile.ts +28 -0
  206. package/vendor/src/hooks/use-session-storage-state.ts +85 -0
  207. package/vendor/src/index.ts +38 -0
  208. package/vendor/src/lib/utils.ts +6 -0
  209. package/vendor/templates/components/button.tsx +0 -0
  210. package/vendor/templates/components/data-table.tsx +0 -0
  211. package/vendor/templates/components/input.tsx +0 -0
  212. package/vendor/templates/lib/utils.ts +0 -0
  213. package/vendor/templates/styles/globals.css +0 -0
@@ -0,0 +1,194 @@
1
+ /* eslint-disable @typescript-eslint/no-unused-vars */
2
+ import { Controller, type Control, type FieldPath, type FieldValues } from "react-hook-form"
3
+
4
+ import {
5
+ AsyncSelect,
6
+ type AsyncSelectOption,
7
+ type AsyncSelectProps,
8
+ } from "@/components/inputs/async-select"
9
+ import { SimpleSelect, type SimpleSelectProps } from "@/components/inputs/simple-select"
10
+ import {
11
+ FormFieldShell,
12
+ type FormFieldShellControlProps,
13
+ } from "@/components/form/form-field-shell"
14
+
15
+ export type FormSelectKind = "simple" | "async"
16
+
17
+ type FormSelectBaseProps<
18
+ TFieldValues extends FieldValues,
19
+ TName extends FieldPath<TFieldValues>,
20
+ > = FormFieldShellControlProps & {
21
+ control: Control<TFieldValues>
22
+ name: TName
23
+ fieldClassName?: string
24
+ }
25
+
26
+ export type FormSimpleSelectProps<
27
+ TFieldValues extends FieldValues = FieldValues,
28
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
29
+ > = Omit<SimpleSelectProps, "value" | "onValueChange" | "disabled"> &
30
+ FormSelectBaseProps<TFieldValues, TName> & {
31
+ kind?: "simple"
32
+ emptyValue?: unknown
33
+ onValueChange?: (value: string) => void
34
+ }
35
+
36
+ export type FormSelectAsyncVariantProps<
37
+ TFieldValues extends FieldValues = FieldValues,
38
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
39
+ TValue extends string = string,
40
+ TData = unknown,
41
+ TOption extends AsyncSelectOption<TValue, TData> = AsyncSelectOption<TValue, TData>,
42
+ > = Omit<AsyncSelectProps<TValue, TData, TOption>, "value" | "onValueChange" | "disabled"> &
43
+ FormSelectBaseProps<TFieldValues, TName> & {
44
+ kind: "async"
45
+ emptyValue?: unknown
46
+ onValueChange?: (value: TValue | undefined, option?: TOption) => void
47
+ }
48
+
49
+ export type FormSelectProps<
50
+ TFieldValues extends FieldValues = FieldValues,
51
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
52
+ TValue extends string = string,
53
+ TData = unknown,
54
+ TOption extends AsyncSelectOption<TValue, TData> = AsyncSelectOption<TValue, TData>,
55
+ > =
56
+ | FormSimpleSelectProps<TFieldValues, TName>
57
+ | FormSelectAsyncVariantProps<TFieldValues, TName, TValue, TData, TOption>
58
+
59
+ function buildShellProps<
60
+ TFieldValues extends FieldValues,
61
+ TName extends FieldPath<TFieldValues>,
62
+ TValue extends string,
63
+ TData,
64
+ TOption extends AsyncSelectOption<TValue, TData>,
65
+ >(props: FormSelectProps<TFieldValues, TName, TValue, TData, TOption>) {
66
+ return {
67
+ label: props.label,
68
+ description: props.description,
69
+ required: props.required,
70
+ className: props.className,
71
+ layout: props.layout,
72
+ descriptionPosition: props.descriptionPosition,
73
+ labelAction: props.labelAction,
74
+ requiredIndicator: props.requiredIndicator,
75
+ errorIcon: props.errorIcon,
76
+ showErrorIcon: props.showErrorIcon,
77
+ disabled: props.disabled,
78
+ readOnly: props.readOnly,
79
+ labelClassName: props.labelClassName,
80
+ labelRowClassName: props.labelRowClassName,
81
+ descriptionClassName: props.descriptionClassName,
82
+ errorClassName: props.errorClassName,
83
+ contentClassName: props.contentClassName,
84
+ } satisfies FormFieldShellControlProps
85
+ }
86
+
87
+ function FormSelect<
88
+ TFieldValues extends FieldValues = FieldValues,
89
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
90
+ TValue extends string = string,
91
+ TData = unknown,
92
+ TOption extends AsyncSelectOption<TValue, TData> = AsyncSelectOption<TValue, TData>,
93
+ >(props: FormSelectProps<TFieldValues, TName, TValue, TData, TOption>) {
94
+ const shellProps = buildShellProps(props)
95
+ const kind = props.kind ?? "simple"
96
+
97
+ return (
98
+ <Controller
99
+ control={props.control}
100
+ name={props.name}
101
+ render={({ field, fieldState }) => {
102
+ const error = fieldState.error?.message
103
+
104
+ if (kind === "async") {
105
+ const {
106
+ control: _control,
107
+ name: _name,
108
+ label: _label,
109
+ description: _description,
110
+ required: _required,
111
+ className: _className,
112
+ layout: _layout,
113
+ descriptionPosition: _descriptionPosition,
114
+ labelAction: _labelAction,
115
+ requiredIndicator: _requiredIndicator,
116
+ errorIcon: _errorIcon,
117
+ showErrorIcon: _showErrorIcon,
118
+ disabled,
119
+ readOnly,
120
+ labelClassName: _labelClassName,
121
+ labelRowClassName: _labelRowClassName,
122
+ descriptionClassName: _descriptionClassName,
123
+ errorClassName: _errorClassName,
124
+ contentClassName: _contentClassName,
125
+ fieldClassName,
126
+ emptyValue,
127
+ kind: _kind,
128
+ onValueChange,
129
+ ...asyncProps
130
+ } = props as FormSelectAsyncVariantProps<TFieldValues, TName, TValue, TData, TOption>
131
+
132
+ return (
133
+ <FormFieldShell {...shellProps} error={error}>
134
+ <AsyncSelect<TValue, TData, TOption>
135
+ {...asyncProps}
136
+ value={field.value == null || field.value === "" ? undefined : (String(field.value) as TValue)}
137
+ disabled={disabled || readOnly}
138
+ onValueChange={(nextValue, option) => {
139
+ field.onChange(nextValue ?? emptyValue)
140
+ onValueChange?.(nextValue, option)
141
+ }}
142
+ triggerClassName={fieldClassName}
143
+ />
144
+ </FormFieldShell>
145
+ )
146
+ }
147
+
148
+ const {
149
+ control: _control,
150
+ name: _name,
151
+ label: _label,
152
+ description: _description,
153
+ required: _required,
154
+ className: _className,
155
+ layout: _layout,
156
+ descriptionPosition: _descriptionPosition,
157
+ labelAction: _labelAction,
158
+ requiredIndicator: _requiredIndicator,
159
+ errorIcon: _errorIcon,
160
+ showErrorIcon: _showErrorIcon,
161
+ disabled,
162
+ readOnly,
163
+ labelClassName: _labelClassName,
164
+ labelRowClassName: _labelRowClassName,
165
+ descriptionClassName: _descriptionClassName,
166
+ errorClassName: _errorClassName,
167
+ contentClassName: _contentClassName,
168
+ fieldClassName,
169
+ emptyValue,
170
+ kind: _kind,
171
+ onValueChange,
172
+ ...simpleProps
173
+ } = props as FormSimpleSelectProps<TFieldValues, TName>
174
+
175
+ return (
176
+ <FormFieldShell {...shellProps} error={error}>
177
+ <SimpleSelect
178
+ {...simpleProps}
179
+ value={field.value == null ? "" : String(field.value)}
180
+ disabled={disabled || readOnly}
181
+ onValueChange={(nextValue) => {
182
+ field.onChange(nextValue || emptyValue)
183
+ onValueChange?.(nextValue)
184
+ }}
185
+ triggerClassName={fieldClassName}
186
+ />
187
+ </FormFieldShell>
188
+ )
189
+ }}
190
+ />
191
+ )
192
+ }
193
+
194
+ export { FormSelect }
@@ -0,0 +1,145 @@
1
+ import * as React from "react"
2
+ import { Controller, type Control, type FieldPath, type FieldValues } from "react-hook-form"
3
+
4
+ import { FormFieldShell, type FormFieldShellControlProps } from "@/components/form/form-field-shell"
5
+ import { Switch } from "@/components/ui/switch"
6
+ import { cn } from "@/lib/utils"
7
+
8
+ export type FormSwitchLabelPlacement = "start" | "end"
9
+
10
+ export type FormSwitchProps<
11
+ TFieldValues extends FieldValues = FieldValues,
12
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
13
+ > = Omit<React.ComponentProps<typeof Switch>, "name" | "checked" | "defaultChecked" | "disabled"> &
14
+ FormFieldShellControlProps & {
15
+ control: Control<TFieldValues>
16
+ name: TName
17
+ fieldClassName?: string
18
+ labelPlacement?: FormSwitchLabelPlacement
19
+ onCheckedChange?: (checked: boolean) => void
20
+ }
21
+
22
+ function FormSwitch<
23
+ TFieldValues extends FieldValues = FieldValues,
24
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
25
+ >({
26
+ control,
27
+ name,
28
+ label,
29
+ description,
30
+ required,
31
+ className,
32
+ layout,
33
+ descriptionPosition,
34
+ labelAction,
35
+ requiredIndicator,
36
+ errorIcon,
37
+ showErrorIcon,
38
+ disabled,
39
+ readOnly,
40
+ labelClassName,
41
+ labelRowClassName,
42
+ descriptionClassName,
43
+ errorClassName,
44
+ contentClassName,
45
+ fieldClassName,
46
+ labelPlacement = "end",
47
+ id,
48
+ onCheckedChange,
49
+ ...props
50
+ }: FormSwitchProps<TFieldValues, TName>) {
51
+ const switchId = id ?? name
52
+ const labelContent = label ? (
53
+ <label
54
+ htmlFor={switchId}
55
+ className={cn("text-sm font-medium leading-none", disabled && "opacity-60", labelClassName)}
56
+ >
57
+ {label}
58
+ {required && (requiredIndicator ?? <span className="ml-1 text-destructive">*</span>)}
59
+ </label>
60
+ ) : null
61
+
62
+ return (
63
+ <Controller
64
+ control={control}
65
+ name={name}
66
+ render={({ field, fieldState }) => {
67
+ const switchControl = (
68
+ <Switch
69
+ id={switchId}
70
+ ref={field.ref}
71
+ checked={Boolean(field.value)}
72
+ disabled={disabled || readOnly}
73
+ aria-invalid={fieldState.invalid || undefined}
74
+ className={fieldClassName}
75
+ onBlur={field.onBlur}
76
+ onCheckedChange={(checked) => {
77
+ field.onChange(checked)
78
+ onCheckedChange?.(checked)
79
+ }}
80
+ {...props}
81
+ />
82
+ )
83
+
84
+ return labelPlacement === "start" ? (
85
+ <FormFieldShell
86
+ label={label}
87
+ description={description}
88
+ required={required}
89
+ error={fieldState.error?.message}
90
+ htmlFor={switchId}
91
+ className={className}
92
+ layout={layout}
93
+ descriptionPosition={descriptionPosition}
94
+ labelAction={labelAction}
95
+ requiredIndicator={requiredIndicator}
96
+ errorIcon={errorIcon}
97
+ showErrorIcon={showErrorIcon}
98
+ disabled={disabled}
99
+ readOnly={readOnly}
100
+ labelClassName={labelClassName}
101
+ labelRowClassName={labelRowClassName}
102
+ descriptionClassName={descriptionClassName}
103
+ errorClassName={errorClassName}
104
+ contentClassName={contentClassName}
105
+ >
106
+ {switchControl}
107
+ </FormFieldShell>
108
+ ) : (
109
+ <FormFieldShell
110
+ error={fieldState.error?.message}
111
+ className={className}
112
+ errorIcon={errorIcon}
113
+ showErrorIcon={showErrorIcon}
114
+ disabled={disabled}
115
+ readOnly={readOnly}
116
+ errorClassName={errorClassName}
117
+ contentClassName={contentClassName}
118
+ >
119
+ <div className="flex items-start gap-3">
120
+ {switchControl}
121
+
122
+ {(label || description || labelAction) && (
123
+ <div className="grid min-w-0 gap-1">
124
+ {(label || labelAction) && (
125
+ <div className={cn("flex items-center justify-between gap-2", labelRowClassName)}>
126
+ {labelContent}
127
+ {labelAction && <div className="shrink-0 text-sm">{labelAction}</div>}
128
+ </div>
129
+ )}
130
+ {description && (
131
+ <p className={cn("text-xs text-muted-foreground", descriptionClassName)}>
132
+ {description}
133
+ </p>
134
+ )}
135
+ </div>
136
+ )}
137
+ </div>
138
+ </FormFieldShell>
139
+ )
140
+ }}
141
+ />
142
+ )
143
+ }
144
+
145
+ export { FormSwitch }
@@ -0,0 +1,103 @@
1
+ import * as React from "react"
2
+ import { Controller, type Control, type FieldPath, type FieldValues } from "react-hook-form"
3
+
4
+ import { FormFieldShell, type FormFieldShellControlProps } from "@/components/form/form-field-shell"
5
+ import { Textarea } from "@/components/ui/textarea"
6
+
7
+ export type FormTextareaProps<
8
+ TFieldValues extends FieldValues = FieldValues,
9
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
10
+ > = Omit<React.ComponentProps<typeof Textarea>, "name" | "value" | "defaultValue"> &
11
+ FormFieldShellControlProps & {
12
+ control: Control<TFieldValues>
13
+ name: TName
14
+ fieldClassName?: string
15
+ transformIn?: (value: unknown) => string | number | readonly string[] | undefined
16
+ transformOut?: (event: React.ChangeEvent<HTMLTextAreaElement>) => unknown
17
+ }
18
+
19
+ function FormTextarea<
20
+ TFieldValues extends FieldValues = FieldValues,
21
+ TName extends FieldPath<TFieldValues> = FieldPath<TFieldValues>,
22
+ >({
23
+ control,
24
+ name,
25
+ label,
26
+ description,
27
+ required,
28
+ className,
29
+ layout,
30
+ descriptionPosition,
31
+ labelAction,
32
+ requiredIndicator,
33
+ errorIcon,
34
+ showErrorIcon,
35
+ disabled,
36
+ readOnly,
37
+ labelClassName,
38
+ labelRowClassName,
39
+ descriptionClassName,
40
+ errorClassName,
41
+ contentClassName,
42
+ fieldClassName,
43
+ transformIn,
44
+ transformOut,
45
+ id,
46
+ onChange,
47
+ onBlur,
48
+ ...props
49
+ }: FormTextareaProps<TFieldValues, TName>) {
50
+ const textareaId = id ?? name
51
+
52
+ return (
53
+ <Controller
54
+ control={control}
55
+ name={name}
56
+ render={({ field, fieldState }) => (
57
+ <FormFieldShell
58
+ label={label}
59
+ description={description}
60
+ required={required}
61
+ error={fieldState.error?.message}
62
+ htmlFor={textareaId}
63
+ className={className}
64
+ layout={layout}
65
+ descriptionPosition={descriptionPosition}
66
+ labelAction={labelAction}
67
+ requiredIndicator={requiredIndicator}
68
+ errorIcon={errorIcon}
69
+ showErrorIcon={showErrorIcon}
70
+ disabled={disabled}
71
+ readOnly={readOnly}
72
+ labelClassName={labelClassName}
73
+ labelRowClassName={labelRowClassName}
74
+ descriptionClassName={descriptionClassName}
75
+ errorClassName={errorClassName}
76
+ contentClassName={contentClassName}
77
+ >
78
+ <Textarea
79
+ id={textareaId}
80
+ ref={field.ref}
81
+ name={field.name}
82
+ value={transformIn ? transformIn(field.value) : field.value ?? ""}
83
+ disabled={disabled}
84
+ readOnly={readOnly}
85
+ onBlur={(event) => {
86
+ field.onBlur()
87
+ onBlur?.(event)
88
+ }}
89
+ onChange={(event) => {
90
+ field.onChange(transformOut ? transformOut(event) : event)
91
+ onChange?.(event)
92
+ }}
93
+ aria-invalid={fieldState.invalid || undefined}
94
+ className={fieldClassName}
95
+ {...props}
96
+ />
97
+ </FormFieldShell>
98
+ )}
99
+ />
100
+ )
101
+ }
102
+
103
+ export { FormTextarea }
@@ -0,0 +1,17 @@
1
+ export * from "./form-field-shell"
2
+ export * from "./form-input"
3
+ export * from "./form-select"
4
+ export * from "./form-async-select"
5
+ export * from "./form-textarea"
6
+ export * from "./form-switch"
7
+ export * from "./form-search-input"
8
+ export * from "./form-password-input"
9
+ export * from "./form-number-input"
10
+ export * from "./form-phone-input"
11
+ export * from "./form-date-input"
12
+ export * from "./form-date-range-input"
13
+ export * from "./form-date-picker"
14
+ export * from "./form-date-range-picker"
15
+ export * from "./form-actions"
16
+ export * from "./form-section"
17
+ export * from "./smart-form-shell"
@@ -0,0 +1,14 @@
1
+ export * from "./form-field-shell"
2
+ export * from "./form-input"
3
+ export * from "./form-select"
4
+ export * from "./form-async-select"
5
+ export * from "./form-textarea"
6
+ export * from "./form-switch"
7
+ export * from "./form-search-input"
8
+ export * from "./form-password-input"
9
+ export * from "./form-number-input"
10
+ export * from "./form-phone-input"
11
+ export * from "./form-date-input"
12
+ export * from "./form-date-range-input"
13
+ export * from "./form-date-picker"
14
+ export * from "./form-date-range-picker"
@@ -0,0 +1,59 @@
1
+ import * as React from "react"
2
+
3
+ import { FormActions } from "@/components/form/form-actions"
4
+ import { FormSection } from "@/components/form/form-section"
5
+ import { cn } from "@/lib/utils"
6
+
7
+ /** @deprecated Prefer `FormFamily.Section`/`FormFamily.Actions` composition or `FormFamily.Builder` for new public usage. */
8
+ export type SmartFormSection = {
9
+ key: string
10
+ title?: React.ReactNode
11
+ description?: React.ReactNode
12
+ fields?: React.ReactNode
13
+ content?: React.ReactNode
14
+ columns?: 1 | 2 | 3
15
+ hidden?: boolean
16
+ }
17
+
18
+ /** @deprecated Prefer `FormFamily` entry members for new public usage. */
19
+ export type SmartFormShellProps = React.ComponentProps<"form"> & {
20
+ title?: React.ReactNode
21
+ description?: React.ReactNode
22
+ sections?: SmartFormSection[]
23
+ actions?: React.ReactNode
24
+ loading?: boolean
25
+ disabled?: boolean
26
+ submitLabel?: React.ReactNode
27
+ cancelLabel?: React.ReactNode
28
+ onCancel?: () => void
29
+ renderHeader?: () => React.ReactNode
30
+ renderSection?: (section: SmartFormSection) => React.ReactNode
31
+ contentClassName?: string
32
+ }
33
+
34
+ /** @deprecated Prefer `FormFamily.Shell` access and migrate new docs usage toward family-first form composition. */
35
+ function SmartFormShell({ title, description, sections, actions, loading = false, disabled = false, submitLabel, cancelLabel, onCancel, renderHeader, renderSection, contentClassName, className, children, ...props }: SmartFormShellProps) {
36
+ const visibleSections = sections?.filter((section) => !section.hidden)
37
+
38
+ return (
39
+ <form data-slot="smart-form-shell" className={cn("grid gap-5", className)} {...props}>
40
+ {renderHeader?.() ?? ((title || description) && (
41
+ <div className="grid gap-1">
42
+ {title && <h2 className="text-xl font-semibold tracking-tight text-foreground">{title}</h2>}
43
+ {description && <p className="text-sm text-muted-foreground">{description}</p>}
44
+ </div>
45
+ ))}
46
+ <div className={cn("grid gap-4", contentClassName)}>
47
+ {visibleSections?.map((section) => renderSection?.(section) ?? (
48
+ <FormSection key={section.key} title={section.title} description={section.description} columns={section.columns}>
49
+ {section.content ?? section.fields}
50
+ </FormSection>
51
+ ))}
52
+ {children}
53
+ </div>
54
+ {actions ?? <FormActions loading={loading} disabled={disabled} submitLabel={submitLabel} cancelLabel={cancelLabel} onCancel={onCancel} />}
55
+ </form>
56
+ )
57
+ }
58
+
59
+ export { SmartFormShell }