@nccirtu/tablefy 0.8.4 → 0.9.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 (243) hide show
  1. package/README.md +224 -200
  2. package/dist/columns/forms/builders/actions-builder.d.ts +14 -0
  3. package/dist/columns/forms/builders/form-schema.d.ts +35 -0
  4. package/dist/columns/forms/builders/index.d.ts +5 -0
  5. package/dist/columns/forms/builders/section-builder.d.ts +16 -0
  6. package/dist/columns/forms/builders/tab-builder.d.ts +16 -0
  7. package/dist/columns/forms/builders/wizard-builder.d.ts +17 -0
  8. package/dist/columns/forms/components/field-renderer.d.ts +12 -0
  9. package/dist/columns/forms/components/form-actions.d.ts +9 -0
  10. package/dist/columns/forms/components/form-renderer.d.ts +14 -0
  11. package/dist/columns/forms/components/grid-layout.d.ts +7 -0
  12. package/dist/columns/forms/components/index.d.ts +9 -0
  13. package/dist/columns/forms/components/section-renderer.d.ts +14 -0
  14. package/dist/columns/forms/components/tab-renderer.d.ts +15 -0
  15. package/dist/columns/forms/components/wizard-renderer.d.ts +17 -0
  16. package/dist/columns/forms/fields/base-field.d.ts +24 -0
  17. package/dist/columns/forms/fields/checkbox-group.d.ts +12 -0
  18. package/dist/columns/forms/fields/checkbox.d.ts +9 -0
  19. package/dist/columns/forms/fields/date-picker.d.ts +15 -0
  20. package/dist/columns/forms/fields/file-upload.d.ts +17 -0
  21. package/dist/columns/forms/fields/hidden.d.ts +9 -0
  22. package/dist/columns/forms/fields/index.d.ts +12 -0
  23. package/dist/columns/forms/fields/radio-group.d.ts +14 -0
  24. package/dist/columns/forms/fields/repeater.d.ts +21 -0
  25. package/dist/columns/forms/fields/select.d.ts +16 -0
  26. package/dist/columns/forms/fields/text-input.d.ts +20 -0
  27. package/dist/columns/forms/fields/textarea.d.ts +14 -0
  28. package/dist/columns/forms/fields/toggle.d.ts +11 -0
  29. package/dist/columns/forms/index.d.ts +26 -0
  30. package/dist/columns/forms/types/actions.d.ts +11 -0
  31. package/dist/columns/forms/types/field.d.ts +98 -0
  32. package/dist/columns/forms/types/form.d.ts +37 -0
  33. package/dist/columns/forms/types/index.d.ts +4 -0
  34. package/dist/columns/forms/types/layout.d.ts +31 -0
  35. package/dist/columns/index.d.ts +2 -0
  36. package/dist/columns/inertia/index.d.ts +4 -0
  37. package/dist/columns/inertia/precognition.d.ts +6 -0
  38. package/dist/columns/inertia/types.d.ts +63 -0
  39. package/dist/columns/inertia/use-inertia-form.d.ts +2 -0
  40. package/dist/columns/inertia/use-server-table.d.ts +2 -0
  41. package/dist/forms/builders/actions-builder.d.ts +14 -0
  42. package/dist/forms/builders/empty-state.d.ts +46 -0
  43. package/dist/forms/builders/form-schema.d.ts +35 -0
  44. package/dist/forms/builders/index.d.ts +2 -0
  45. package/dist/forms/builders/section-builder.d.ts +16 -0
  46. package/dist/forms/builders/tab-builder.d.ts +16 -0
  47. package/dist/forms/builders/table-schema.d.ts +45 -0
  48. package/dist/forms/builders/wizard-builder.d.ts +17 -0
  49. package/dist/forms/columns/actions-column.d.ts +26 -0
  50. package/dist/forms/columns/avatar-group-column.d.ts +38 -0
  51. package/dist/forms/columns/badge-column.d.ts +29 -0
  52. package/dist/forms/columns/base-column.d.ts +21 -0
  53. package/dist/forms/columns/button-column.d.ts +14 -0
  54. package/dist/forms/columns/checkbox-column.d.ts +5 -0
  55. package/dist/forms/columns/date-column.d.ts +24 -0
  56. package/dist/forms/columns/dropdown-column.d.ts +17 -0
  57. package/dist/forms/columns/enum-column.d.ts +52 -0
  58. package/dist/forms/columns/icon-column.d.ts +58 -0
  59. package/dist/forms/columns/image-column.d.ts +21 -0
  60. package/dist/forms/columns/index.d.ts +18 -0
  61. package/dist/forms/columns/input-column.d.ts +27 -0
  62. package/dist/forms/columns/link-column.d.ts +27 -0
  63. package/dist/forms/columns/number-column.d.ts +23 -0
  64. package/dist/forms/columns/progress-column.d.ts +30 -0
  65. package/dist/forms/columns/select-column.d.ts +24 -0
  66. package/dist/forms/columns/text-column.d.ts +14 -0
  67. package/dist/forms/columns/types.d.ts +46 -0
  68. package/dist/forms/components/field-renderer.d.ts +12 -0
  69. package/dist/forms/components/form-actions.d.ts +9 -0
  70. package/dist/forms/components/form-renderer.d.ts +14 -0
  71. package/dist/forms/components/grid-layout.d.ts +7 -0
  72. package/dist/forms/components/index.d.ts +9 -0
  73. package/dist/forms/components/section-renderer.d.ts +14 -0
  74. package/dist/forms/components/tab-renderer.d.ts +15 -0
  75. package/dist/forms/components/wizard-renderer.d.ts +17 -0
  76. package/dist/forms/confirm/ConfirmProvider.d.ts +6 -0
  77. package/dist/forms/confirm/confirm.d.ts +3 -0
  78. package/dist/forms/confirm/index.d.ts +3 -0
  79. package/dist/forms/confirm/types.d.ts +10 -0
  80. package/dist/forms/fields/base-field.d.ts +24 -0
  81. package/dist/forms/fields/checkbox-group.d.ts +12 -0
  82. package/dist/forms/fields/checkbox.d.ts +9 -0
  83. package/dist/forms/fields/date-picker.d.ts +15 -0
  84. package/dist/forms/fields/file-upload.d.ts +17 -0
  85. package/dist/forms/fields/hidden.d.ts +9 -0
  86. package/dist/forms/fields/index.d.ts +12 -0
  87. package/dist/forms/fields/radio-group.d.ts +14 -0
  88. package/dist/forms/fields/repeater.d.ts +21 -0
  89. package/dist/forms/fields/select.d.ts +16 -0
  90. package/dist/forms/fields/text-input.d.ts +20 -0
  91. package/dist/forms/fields/textarea.d.ts +14 -0
  92. package/dist/forms/fields/toggle.d.ts +11 -0
  93. package/dist/forms/forms/builders/actions-builder.d.ts +14 -0
  94. package/dist/forms/forms/builders/form-schema.d.ts +35 -0
  95. package/dist/forms/forms/builders/index.d.ts +5 -0
  96. package/dist/forms/forms/builders/section-builder.d.ts +16 -0
  97. package/dist/forms/forms/builders/tab-builder.d.ts +16 -0
  98. package/dist/forms/forms/builders/wizard-builder.d.ts +17 -0
  99. package/dist/forms/forms/components/field-renderer.d.ts +12 -0
  100. package/dist/forms/forms/components/form-actions.d.ts +9 -0
  101. package/dist/forms/forms/components/form-renderer.d.ts +14 -0
  102. package/dist/forms/forms/components/grid-layout.d.ts +7 -0
  103. package/dist/forms/forms/components/index.d.ts +9 -0
  104. package/dist/forms/forms/components/section-renderer.d.ts +14 -0
  105. package/dist/forms/forms/components/tab-renderer.d.ts +15 -0
  106. package/dist/forms/forms/components/wizard-renderer.d.ts +17 -0
  107. package/dist/forms/forms/fields/base-field.d.ts +24 -0
  108. package/dist/forms/forms/fields/checkbox-group.d.ts +12 -0
  109. package/dist/forms/forms/fields/checkbox.d.ts +9 -0
  110. package/dist/forms/forms/fields/date-picker.d.ts +15 -0
  111. package/dist/forms/forms/fields/file-upload.d.ts +17 -0
  112. package/dist/forms/forms/fields/hidden.d.ts +9 -0
  113. package/dist/forms/forms/fields/index.d.ts +12 -0
  114. package/dist/forms/forms/fields/radio-group.d.ts +14 -0
  115. package/dist/forms/forms/fields/repeater.d.ts +21 -0
  116. package/dist/forms/forms/fields/select.d.ts +16 -0
  117. package/dist/forms/forms/fields/text-input.d.ts +20 -0
  118. package/dist/forms/forms/fields/textarea.d.ts +14 -0
  119. package/dist/forms/forms/fields/toggle.d.ts +11 -0
  120. package/dist/forms/forms/index.d.ts +26 -0
  121. package/dist/forms/forms/types/actions.d.ts +11 -0
  122. package/dist/forms/forms/types/field.d.ts +98 -0
  123. package/dist/forms/forms/types/form.d.ts +37 -0
  124. package/dist/forms/forms/types/index.d.ts +4 -0
  125. package/dist/forms/forms/types/layout.d.ts +31 -0
  126. package/dist/forms/index.d.ts +25 -0
  127. package/dist/forms/index.esm.js +1052 -0
  128. package/dist/forms/index.esm.js.map +1 -0
  129. package/dist/forms/index.js +1077 -0
  130. package/dist/forms/index.js.map +1 -0
  131. package/dist/forms/inertia/index.d.ts +4 -0
  132. package/dist/forms/inertia/precognition.d.ts +6 -0
  133. package/dist/forms/inertia/types.d.ts +63 -0
  134. package/dist/forms/inertia/use-inertia-form.d.ts +2 -0
  135. package/dist/forms/inertia/use-server-table.d.ts +2 -0
  136. package/dist/forms/tablefy/avatar-list.d.ts +15 -0
  137. package/dist/forms/tablefy/data-table-empty.d.ts +8 -0
  138. package/dist/forms/tablefy/data-table-header.d.ts +17 -0
  139. package/dist/forms/tablefy/data-table-pagination.d.ts +9 -0
  140. package/dist/forms/tablefy/data-table-schema.d.ts +1 -0
  141. package/dist/forms/tablefy/data-table.d.ts +13 -0
  142. package/dist/forms/tablefy/index.d.ts +6 -0
  143. package/dist/forms/types/actions.d.ts +21 -0
  144. package/dist/forms/types/empty-state.d.ts +18 -0
  145. package/dist/forms/types/field.d.ts +98 -0
  146. package/dist/forms/types/filters.d.ts +25 -0
  147. package/dist/forms/types/form.d.ts +37 -0
  148. package/dist/forms/types/index.d.ts +4 -0
  149. package/dist/forms/types/layout.d.ts +31 -0
  150. package/dist/forms/types/table.d.ts +42 -0
  151. package/dist/forms/utils.d.ts +1 -0
  152. package/dist/index.d.ts +2 -0
  153. package/dist/index.esm.js +1050 -9
  154. package/dist/index.esm.js.map +1 -1
  155. package/dist/index.js +1058 -0
  156. package/dist/index.js.map +1 -1
  157. package/dist/inertia/builders/empty-state.d.ts +46 -0
  158. package/dist/inertia/builders/index.d.ts +2 -0
  159. package/dist/inertia/builders/table-schema.d.ts +45 -0
  160. package/dist/inertia/columns/actions-column.d.ts +26 -0
  161. package/dist/inertia/columns/avatar-group-column.d.ts +38 -0
  162. package/dist/inertia/columns/badge-column.d.ts +29 -0
  163. package/dist/inertia/columns/base-column.d.ts +21 -0
  164. package/dist/inertia/columns/button-column.d.ts +14 -0
  165. package/dist/inertia/columns/checkbox-column.d.ts +5 -0
  166. package/dist/inertia/columns/date-column.d.ts +24 -0
  167. package/dist/inertia/columns/dropdown-column.d.ts +17 -0
  168. package/dist/inertia/columns/enum-column.d.ts +52 -0
  169. package/dist/inertia/columns/icon-column.d.ts +58 -0
  170. package/dist/inertia/columns/image-column.d.ts +21 -0
  171. package/dist/inertia/columns/index.d.ts +18 -0
  172. package/dist/inertia/columns/input-column.d.ts +27 -0
  173. package/dist/inertia/columns/link-column.d.ts +27 -0
  174. package/dist/inertia/columns/number-column.d.ts +23 -0
  175. package/dist/inertia/columns/progress-column.d.ts +30 -0
  176. package/dist/inertia/columns/select-column.d.ts +24 -0
  177. package/dist/inertia/columns/text-column.d.ts +14 -0
  178. package/dist/inertia/columns/types.d.ts +46 -0
  179. package/dist/inertia/confirm/ConfirmProvider.d.ts +6 -0
  180. package/dist/inertia/confirm/confirm.d.ts +3 -0
  181. package/dist/inertia/confirm/index.d.ts +3 -0
  182. package/dist/inertia/confirm/types.d.ts +10 -0
  183. package/dist/inertia/forms/builders/actions-builder.d.ts +14 -0
  184. package/dist/inertia/forms/builders/form-schema.d.ts +35 -0
  185. package/dist/inertia/forms/builders/index.d.ts +5 -0
  186. package/dist/inertia/forms/builders/section-builder.d.ts +16 -0
  187. package/dist/inertia/forms/builders/tab-builder.d.ts +16 -0
  188. package/dist/inertia/forms/builders/wizard-builder.d.ts +17 -0
  189. package/dist/inertia/forms/components/field-renderer.d.ts +12 -0
  190. package/dist/inertia/forms/components/form-actions.d.ts +9 -0
  191. package/dist/inertia/forms/components/form-renderer.d.ts +14 -0
  192. package/dist/inertia/forms/components/grid-layout.d.ts +7 -0
  193. package/dist/inertia/forms/components/index.d.ts +9 -0
  194. package/dist/inertia/forms/components/section-renderer.d.ts +14 -0
  195. package/dist/inertia/forms/components/tab-renderer.d.ts +15 -0
  196. package/dist/inertia/forms/components/wizard-renderer.d.ts +17 -0
  197. package/dist/inertia/forms/fields/base-field.d.ts +24 -0
  198. package/dist/inertia/forms/fields/checkbox-group.d.ts +12 -0
  199. package/dist/inertia/forms/fields/checkbox.d.ts +9 -0
  200. package/dist/inertia/forms/fields/date-picker.d.ts +15 -0
  201. package/dist/inertia/forms/fields/file-upload.d.ts +17 -0
  202. package/dist/inertia/forms/fields/hidden.d.ts +9 -0
  203. package/dist/inertia/forms/fields/index.d.ts +12 -0
  204. package/dist/inertia/forms/fields/radio-group.d.ts +14 -0
  205. package/dist/inertia/forms/fields/repeater.d.ts +21 -0
  206. package/dist/inertia/forms/fields/select.d.ts +16 -0
  207. package/dist/inertia/forms/fields/text-input.d.ts +20 -0
  208. package/dist/inertia/forms/fields/textarea.d.ts +14 -0
  209. package/dist/inertia/forms/fields/toggle.d.ts +11 -0
  210. package/dist/inertia/forms/index.d.ts +26 -0
  211. package/dist/inertia/forms/types/actions.d.ts +11 -0
  212. package/dist/inertia/forms/types/field.d.ts +98 -0
  213. package/dist/inertia/forms/types/form.d.ts +37 -0
  214. package/dist/inertia/forms/types/index.d.ts +4 -0
  215. package/dist/inertia/forms/types/layout.d.ts +31 -0
  216. package/dist/inertia/index.d.ts +25 -0
  217. package/dist/inertia/index.esm.js +149 -0
  218. package/dist/inertia/index.esm.js.map +1 -0
  219. package/dist/inertia/index.js +153 -0
  220. package/dist/inertia/index.js.map +1 -0
  221. package/dist/inertia/inertia/index.d.ts +4 -0
  222. package/dist/inertia/inertia/precognition.d.ts +6 -0
  223. package/dist/inertia/inertia/types.d.ts +63 -0
  224. package/dist/inertia/inertia/use-inertia-form.d.ts +2 -0
  225. package/dist/inertia/inertia/use-server-table.d.ts +2 -0
  226. package/dist/inertia/precognition.d.ts +6 -0
  227. package/dist/inertia/tablefy/avatar-list.d.ts +15 -0
  228. package/dist/inertia/tablefy/data-table-empty.d.ts +8 -0
  229. package/dist/inertia/tablefy/data-table-header.d.ts +17 -0
  230. package/dist/inertia/tablefy/data-table-pagination.d.ts +9 -0
  231. package/dist/inertia/tablefy/data-table-schema.d.ts +1 -0
  232. package/dist/inertia/tablefy/data-table.d.ts +13 -0
  233. package/dist/inertia/tablefy/index.d.ts +6 -0
  234. package/dist/inertia/types/actions.d.ts +21 -0
  235. package/dist/inertia/types/empty-state.d.ts +18 -0
  236. package/dist/inertia/types/filters.d.ts +25 -0
  237. package/dist/inertia/types/index.d.ts +4 -0
  238. package/dist/inertia/types/table.d.ts +42 -0
  239. package/dist/inertia/types.d.ts +63 -0
  240. package/dist/inertia/use-inertia-form.d.ts +2 -0
  241. package/dist/inertia/use-server-table.d.ts +2 -0
  242. package/dist/inertia/utils.d.ts +1 -0
  243. package/package.json +39 -13
@@ -0,0 +1,1052 @@
1
+ import { jsx, jsxs } from 'react/jsx-runtime';
2
+ import { Input } from '@/components/ui/input';
3
+ import { cn } from '@/lib/utils';
4
+ import { Textarea as Textarea$1 } from '@/components/ui/textarea';
5
+ import { Select as Select$1, SelectTrigger, SelectValue, SelectContent, SelectItem } from '@/components/ui/select';
6
+ import { Checkbox as Checkbox$1 } from '@/components/ui/checkbox';
7
+ import { Switch } from '@/components/ui/switch';
8
+ import { RadioGroup as RadioGroup$1, RadioGroupItem } from '@/components/ui/radio-group';
9
+ import { Label } from '@/components/ui/label';
10
+ import { Button } from '@/components/ui/button';
11
+ import { Calendar } from '@/components/ui/calendar';
12
+ import { Popover, PopoverTrigger, PopoverContent } from '@/components/ui/popover';
13
+ import { CalendarIcon, Upload, File, X, GripVertical, Trash2, Plus, ChevronDown, Check } from 'lucide-react';
14
+ import { useRef, useState, useCallback, useMemo } from 'react';
15
+ import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
16
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@/components/ui/tabs';
17
+
18
+ class ActionsBuilder {
19
+ actionsList = [];
20
+ submit(opts = {}) {
21
+ this.actionsList.push({
22
+ type: "submit",
23
+ label: opts.label || "Submit",
24
+ variant: opts.variant || "default",
25
+ ...opts,
26
+ });
27
+ return this;
28
+ }
29
+ cancel(opts = {}) {
30
+ this.actionsList.push({
31
+ type: "cancel",
32
+ label: opts.label || "Cancel",
33
+ variant: opts.variant || "outline",
34
+ ...opts,
35
+ });
36
+ return this;
37
+ }
38
+ custom(opts) {
39
+ this.actionsList.push({ ...opts, type: "custom" });
40
+ return this;
41
+ }
42
+ build() {
43
+ return [...this.actionsList];
44
+ }
45
+ }
46
+
47
+ /**
48
+ * Form Schema Builder
49
+ * Fluent API for building complete form configurations
50
+ */
51
+ class FormSchema {
52
+ fieldBuilders = [];
53
+ schemaConfig = {};
54
+ static make() {
55
+ return new FormSchema();
56
+ }
57
+ // --- Configuration ---
58
+ title(title) {
59
+ this.schemaConfig.title = title;
60
+ return this;
61
+ }
62
+ description(description) {
63
+ this.schemaConfig.description = description;
64
+ return this;
65
+ }
66
+ columns(columns) {
67
+ this.schemaConfig.columns = columns;
68
+ return this;
69
+ }
70
+ bordered(bordered = true) {
71
+ this.schemaConfig.bordered = bordered;
72
+ return this;
73
+ }
74
+ spacing(spacing) {
75
+ this.schemaConfig.spacing = spacing;
76
+ return this;
77
+ }
78
+ disabled(disabled) {
79
+ this.schemaConfig.disabled = disabled;
80
+ return this;
81
+ }
82
+ // --- Fields ---
83
+ fields(...builders) {
84
+ this.fieldBuilders.push(...builders);
85
+ return this;
86
+ }
87
+ // --- Layout ---
88
+ sections(...sections) {
89
+ this.schemaConfig.sections = sections.map((s) => s.build());
90
+ return this;
91
+ }
92
+ tabs(...tabs) {
93
+ this.schemaConfig.tabs = tabs.map((t) => t.build());
94
+ return this;
95
+ }
96
+ wizard(...steps) {
97
+ this.schemaConfig.wizardSteps = steps.map((s) => s.build());
98
+ return this;
99
+ }
100
+ // --- Actions ---
101
+ actions(fn) {
102
+ const builder = new ActionsBuilder();
103
+ this.schemaConfig.actions = fn(builder).build();
104
+ return this;
105
+ }
106
+ actionsPosition(position) {
107
+ this.schemaConfig.actionsPosition = position;
108
+ return this;
109
+ }
110
+ // --- Build ---
111
+ build() {
112
+ const fields = this.fieldBuilders.map((b) => b.build());
113
+ return {
114
+ fields,
115
+ config: {
116
+ ...this.schemaConfig,
117
+ fields,
118
+ },
119
+ };
120
+ }
121
+ }
122
+
123
+ class SectionBuilder {
124
+ sectionConfig;
125
+ constructor(title) {
126
+ this.sectionConfig = {
127
+ id: title.toLowerCase().replace(/\s+/g, "-"),
128
+ title,
129
+ fields: [],
130
+ columns: 1,
131
+ collapsible: false,
132
+ collapsed: false,
133
+ };
134
+ }
135
+ static make(title) {
136
+ return new SectionBuilder(title);
137
+ }
138
+ id(id) {
139
+ this.sectionConfig.id = id;
140
+ return this;
141
+ }
142
+ description(description) {
143
+ this.sectionConfig.description = description;
144
+ return this;
145
+ }
146
+ fields(fields) {
147
+ this.sectionConfig.fields = fields;
148
+ return this;
149
+ }
150
+ columns(columns) {
151
+ this.sectionConfig.columns = columns;
152
+ return this;
153
+ }
154
+ collapsible(collapsible = true) {
155
+ this.sectionConfig.collapsible = collapsible;
156
+ return this;
157
+ }
158
+ collapsed(collapsed = true) {
159
+ this.sectionConfig.collapsed = collapsed;
160
+ this.sectionConfig.collapsible = true;
161
+ return this;
162
+ }
163
+ icon(icon) {
164
+ this.sectionConfig.icon = icon;
165
+ return this;
166
+ }
167
+ hidden(fn) {
168
+ this.sectionConfig.hidden = fn;
169
+ return this;
170
+ }
171
+ build() {
172
+ return { ...this.sectionConfig };
173
+ }
174
+ }
175
+
176
+ class TabBuilder {
177
+ tabConfig;
178
+ constructor(label) {
179
+ this.tabConfig = {
180
+ id: label.toLowerCase().replace(/\s+/g, "-"),
181
+ label,
182
+ };
183
+ }
184
+ static make(label) {
185
+ return new TabBuilder(label);
186
+ }
187
+ id(id) {
188
+ this.tabConfig.id = id;
189
+ return this;
190
+ }
191
+ icon(icon) {
192
+ this.tabConfig.icon = icon;
193
+ return this;
194
+ }
195
+ fields(fields) {
196
+ this.tabConfig.fields = fields;
197
+ return this;
198
+ }
199
+ sections(...sections) {
200
+ this.tabConfig.sections = sections.map((s) => s.build());
201
+ return this;
202
+ }
203
+ badge(badge) {
204
+ this.tabConfig.badge = badge;
205
+ return this;
206
+ }
207
+ disabled(fn) {
208
+ this.tabConfig.disabled = fn;
209
+ return this;
210
+ }
211
+ build() {
212
+ return { ...this.tabConfig };
213
+ }
214
+ }
215
+
216
+ class WizardStep {
217
+ stepConfig;
218
+ constructor(label) {
219
+ this.stepConfig = {
220
+ id: label.toLowerCase().replace(/\s+/g, "-"),
221
+ label,
222
+ };
223
+ }
224
+ static make(label) {
225
+ return new WizardStep(label);
226
+ }
227
+ id(id) {
228
+ this.stepConfig.id = id;
229
+ return this;
230
+ }
231
+ description(description) {
232
+ this.stepConfig.description = description;
233
+ return this;
234
+ }
235
+ icon(icon) {
236
+ this.stepConfig.icon = icon;
237
+ return this;
238
+ }
239
+ fields(fields) {
240
+ this.stepConfig.fields = fields;
241
+ return this;
242
+ }
243
+ sections(...sections) {
244
+ this.stepConfig.sections = sections.map((s) => s.build());
245
+ return this;
246
+ }
247
+ canProceed(fn) {
248
+ this.stepConfig.canProceed = fn;
249
+ return this;
250
+ }
251
+ beforeNext(fn) {
252
+ this.stepConfig.beforeNext = fn;
253
+ return this;
254
+ }
255
+ build() {
256
+ return { ...this.stepConfig };
257
+ }
258
+ }
259
+
260
+ class BaseField {
261
+ config;
262
+ constructor(name) {
263
+ this.config = {
264
+ name,
265
+ required: false,
266
+ disabled: false,
267
+ readOnly: false,
268
+ hidden: false,
269
+ columnSpan: 1,
270
+ rules: [],
271
+ dependsOn: [],
272
+ };
273
+ }
274
+ // --- Fluent API ---
275
+ label(label) {
276
+ this.config.label = label;
277
+ return this;
278
+ }
279
+ placeholder(placeholder) {
280
+ this.config.placeholder = placeholder;
281
+ return this;
282
+ }
283
+ helperText(text) {
284
+ this.config.helperText = text;
285
+ return this;
286
+ }
287
+ required(required = true) {
288
+ this.config.required = required;
289
+ if (required &&
290
+ !this.config.rules?.some((r) => r.type === "required")) {
291
+ this.config.rules = [
292
+ ...(this.config.rules || []),
293
+ {
294
+ type: "required",
295
+ message: `${this.config.label || this.config.name} is required`,
296
+ },
297
+ ];
298
+ }
299
+ return this;
300
+ }
301
+ disabled(disabled = true) {
302
+ this.config.disabled = disabled;
303
+ return this;
304
+ }
305
+ readOnly(readOnly = true) {
306
+ this.config.readOnly = readOnly;
307
+ return this;
308
+ }
309
+ hidden(hidden = true) {
310
+ this.config.hidden = hidden;
311
+ return this;
312
+ }
313
+ default(value) {
314
+ this.config.defaultValue = value;
315
+ return this;
316
+ }
317
+ columnSpan(span) {
318
+ this.config.columnSpan = span;
319
+ return this;
320
+ }
321
+ className(className) {
322
+ this.config.className = className;
323
+ return this;
324
+ }
325
+ rules(rules) {
326
+ this.config.rules = [...(this.config.rules || []), ...rules];
327
+ return this;
328
+ }
329
+ zodSchema(schema) {
330
+ this.config.zodSchema = schema;
331
+ return this;
332
+ }
333
+ dependsOn(field, condition, effect = "show", effectValue) {
334
+ this.config.dependsOn = [
335
+ ...(this.config.dependsOn || []),
336
+ { field, condition, effect, effectValue },
337
+ ];
338
+ return this;
339
+ }
340
+ reactive(reactive = true) {
341
+ this.config.reactive = reactive;
342
+ return this;
343
+ }
344
+ // --- Build ---
345
+ build() {
346
+ return {
347
+ name: this.config.name,
348
+ type: this.fieldType,
349
+ config: { ...this.config },
350
+ render: (props) => this.renderField(props),
351
+ };
352
+ }
353
+ }
354
+
355
+ class TextInput extends BaseField {
356
+ fieldType = "text";
357
+ static make(name) {
358
+ return new TextInput(name);
359
+ }
360
+ type(type) {
361
+ this.config.type = type;
362
+ return this;
363
+ }
364
+ email() {
365
+ return this.type("email");
366
+ }
367
+ password() {
368
+ return this.type("password");
369
+ }
370
+ number() {
371
+ return this.type("number");
372
+ }
373
+ url() {
374
+ return this.type("url");
375
+ }
376
+ tel() {
377
+ return this.type("tel");
378
+ }
379
+ minLength(min) {
380
+ this.config.minLength = min;
381
+ this.config.rules = [
382
+ ...(this.config.rules || []),
383
+ {
384
+ type: "min",
385
+ value: min,
386
+ message: `${this.config.label || this.config.name} must be at least ${min} characters`,
387
+ },
388
+ ];
389
+ return this;
390
+ }
391
+ maxLength(max) {
392
+ this.config.maxLength = max;
393
+ this.config.rules = [
394
+ ...(this.config.rules || []),
395
+ {
396
+ type: "max",
397
+ value: max,
398
+ message: `${this.config.label || this.config.name} must be at most ${max} characters`,
399
+ },
400
+ ];
401
+ return this;
402
+ }
403
+ prefix(prefix) {
404
+ this.config.prefix = prefix;
405
+ return this;
406
+ }
407
+ suffix(suffix) {
408
+ this.config.suffix = suffix;
409
+ return this;
410
+ }
411
+ autocomplete(value) {
412
+ this.config.autocomplete = value;
413
+ return this;
414
+ }
415
+ renderField({ value, onChange, onBlur, error, disabled, }) {
416
+ const { type, placeholder, maxLength, readOnly, autocomplete, className, prefix, suffix, } = this.config;
417
+ const input = (jsx(Input, { type: type || "text", value: value ?? "", onChange: (e) => onChange(type === "number" ? Number(e.target.value) : e.target.value), onBlur: onBlur, placeholder: placeholder, maxLength: maxLength, disabled: disabled, readOnly: readOnly, autoComplete: autocomplete, className: cn(error && "border-destructive", className) }));
418
+ if (prefix || suffix) {
419
+ return (jsxs("div", { className: "flex items-center gap-2", children: [prefix && (jsx("span", { className: "text-sm text-muted-foreground", children: prefix })), input, suffix && (jsx("span", { className: "text-sm text-muted-foreground", children: suffix }))] }));
420
+ }
421
+ return input;
422
+ }
423
+ }
424
+
425
+ class Textarea extends BaseField {
426
+ fieldType = "textarea";
427
+ constructor(name) {
428
+ super(name);
429
+ this.config.rows = 3;
430
+ }
431
+ static make(name) {
432
+ return new Textarea(name);
433
+ }
434
+ rows(rows) {
435
+ this.config.rows = rows;
436
+ return this;
437
+ }
438
+ minLength(min) {
439
+ this.config.minLength = min;
440
+ this.config.rules = [
441
+ ...(this.config.rules || []),
442
+ {
443
+ type: "min",
444
+ value: min,
445
+ message: `${this.config.label || this.config.name} must be at least ${min} characters`,
446
+ },
447
+ ];
448
+ return this;
449
+ }
450
+ maxLength(max) {
451
+ this.config.maxLength = max;
452
+ this.config.rules = [
453
+ ...(this.config.rules || []),
454
+ {
455
+ type: "max",
456
+ value: max,
457
+ message: `${this.config.label || this.config.name} must be at most ${max} characters`,
458
+ },
459
+ ];
460
+ return this;
461
+ }
462
+ autoResize(autoResize = true) {
463
+ this.config.autoResize = autoResize;
464
+ return this;
465
+ }
466
+ renderField({ value, onChange, onBlur, error, disabled, }) {
467
+ const { placeholder, rows, maxLength, readOnly, className } = this.config;
468
+ return (jsx(Textarea$1, { value: value ?? "", onChange: (e) => onChange(e.target.value), onBlur: onBlur, placeholder: placeholder, rows: rows, maxLength: maxLength, disabled: disabled, readOnly: readOnly, className: cn(error && "border-destructive", className) }));
469
+ }
470
+ }
471
+
472
+ class Select extends BaseField {
473
+ fieldType = "select";
474
+ constructor(name) {
475
+ super(name);
476
+ this.config.options = [];
477
+ this.config.multiple = false;
478
+ this.config.searchable = false;
479
+ this.config.clearable = false;
480
+ }
481
+ static make(name) {
482
+ return new Select(name);
483
+ }
484
+ options(options) {
485
+ this.config.options = options;
486
+ return this;
487
+ }
488
+ multiple(multiple = true) {
489
+ this.config.multiple = multiple;
490
+ return this;
491
+ }
492
+ searchable(searchable = true) {
493
+ this.config.searchable = searchable;
494
+ return this;
495
+ }
496
+ clearable(clearable = true) {
497
+ this.config.clearable = clearable;
498
+ return this;
499
+ }
500
+ maxItems(max) {
501
+ this.config.maxItems = max;
502
+ return this;
503
+ }
504
+ loadOptions(fn) {
505
+ this.config.loadOptions = fn;
506
+ return this;
507
+ }
508
+ renderField({ value, onChange, error, disabled, data, }) {
509
+ const cfg = this.config;
510
+ const resolvedOptions = typeof cfg.options === "function" ? cfg.options(data) : cfg.options;
511
+ return (jsxs(Select$1, { value: value ?? "", onValueChange: onChange, disabled: disabled, children: [jsx(SelectTrigger, { className: cn(error && "border-destructive", cfg.className), children: jsx(SelectValue, { placeholder: cfg.placeholder || "Select..." }) }), jsx(SelectContent, { children: resolvedOptions.map((opt) => (jsx(SelectItem, { value: opt.value, disabled: opt.disabled, children: opt.label }, opt.value))) })] }));
512
+ }
513
+ }
514
+
515
+ class Checkbox extends BaseField {
516
+ fieldType = "checkbox";
517
+ static make(name) {
518
+ return new Checkbox(name);
519
+ }
520
+ renderField({ value, onChange, error, disabled, }) {
521
+ const { className } = this.config;
522
+ return (jsxs("div", { className: cn("flex items-center space-x-2", className), children: [jsx(Checkbox$1, { id: this.config.name, checked: !!value, onCheckedChange: (checked) => onChange(!!checked), disabled: disabled, className: cn(error && "border-destructive") }), this.config.label && (jsx("label", { htmlFor: this.config.name, className: cn("text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", error && "text-destructive"), children: this.config.label }))] }));
523
+ }
524
+ }
525
+
526
+ class Toggle extends BaseField {
527
+ fieldType = "toggle";
528
+ static make(name) {
529
+ return new Toggle(name);
530
+ }
531
+ onLabel(label) {
532
+ this.config.onLabel = label;
533
+ return this;
534
+ }
535
+ offLabel(label) {
536
+ this.config.offLabel = label;
537
+ return this;
538
+ }
539
+ renderField({ value, onChange, error, disabled, }) {
540
+ const { onLabel, offLabel, className } = this.config;
541
+ const displayLabel = value ? onLabel : offLabel;
542
+ return (jsxs("div", { className: cn("flex items-center space-x-2", className), children: [jsx(Switch, { id: this.config.name, checked: !!value, onCheckedChange: (checked) => onChange(checked), disabled: disabled, className: cn(error && "border-destructive") }), displayLabel && (jsx("label", { htmlFor: this.config.name, className: "text-sm text-muted-foreground", children: displayLabel }))] }));
543
+ }
544
+ }
545
+
546
+ class RadioGroup extends BaseField {
547
+ fieldType = "radio-group";
548
+ constructor(name) {
549
+ super(name);
550
+ this.config.options = [];
551
+ this.config.orientation = "vertical";
552
+ }
553
+ static make(name) {
554
+ return new RadioGroup(name);
555
+ }
556
+ options(options) {
557
+ this.config.options = options;
558
+ return this;
559
+ }
560
+ orientation(orientation) {
561
+ this.config.orientation = orientation;
562
+ return this;
563
+ }
564
+ horizontal() {
565
+ return this.orientation("horizontal");
566
+ }
567
+ vertical() {
568
+ return this.orientation("vertical");
569
+ }
570
+ renderField({ value, onChange, error, disabled, }) {
571
+ const cfg = this.config;
572
+ const isHorizontal = cfg.orientation === "horizontal";
573
+ return (jsx(RadioGroup$1, { value: value ?? "", onValueChange: onChange, disabled: disabled, className: cn(isHorizontal ? "flex flex-row gap-4" : "flex flex-col gap-2", cfg.className), children: cfg.options.map((opt) => (jsxs("div", { className: "flex items-center space-x-2", children: [jsx(RadioGroupItem, { value: opt.value, id: `${cfg.name}-${opt.value}`, disabled: opt.disabled, className: cn(error && "border-destructive") }), jsx(Label, { htmlFor: `${cfg.name}-${opt.value}`, className: "text-sm font-normal", children: opt.label })] }, opt.value))) }));
574
+ }
575
+ }
576
+
577
+ class DatePicker extends BaseField {
578
+ fieldType = "date-picker";
579
+ constructor(name) {
580
+ super(name);
581
+ this.config.format = "PPP";
582
+ this.config.includeTime = false;
583
+ this.config.locale = "en-US";
584
+ }
585
+ static make(name) {
586
+ return new DatePicker(name);
587
+ }
588
+ minDate(date) {
589
+ this.config.minDate = date;
590
+ return this;
591
+ }
592
+ maxDate(date) {
593
+ this.config.maxDate = date;
594
+ return this;
595
+ }
596
+ format(format) {
597
+ this.config.format = format;
598
+ return this;
599
+ }
600
+ includeTime(includeTime = true) {
601
+ this.config.includeTime = includeTime;
602
+ if (includeTime) {
603
+ this.fieldType = "date-time-picker";
604
+ }
605
+ return this;
606
+ }
607
+ locale(locale) {
608
+ this.config.locale = locale;
609
+ return this;
610
+ }
611
+ renderField({ value, onChange, error, disabled, data, }) {
612
+ const { placeholder, className, locale } = this.config;
613
+ const resolvedMinDate = typeof this.config.minDate === "function"
614
+ ? this.config.minDate(data)
615
+ : this.config.minDate;
616
+ const resolvedMaxDate = typeof this.config.maxDate === "function"
617
+ ? this.config.maxDate(data)
618
+ : this.config.maxDate;
619
+ const dateValue = value ? new Date(value) : undefined;
620
+ const formatDate = (date) => {
621
+ return date.toLocaleDateString(locale || "en-US", {
622
+ year: "numeric",
623
+ month: "long",
624
+ day: "numeric",
625
+ });
626
+ };
627
+ return (jsxs(Popover, { children: [jsx(PopoverTrigger, { asChild: true, children: jsxs(Button, { variant: "outline", disabled: disabled, className: cn("w-full justify-start text-left font-normal", !dateValue && "text-muted-foreground", error && "border-destructive", className), children: [jsx(CalendarIcon, { className: "mr-2 h-4 w-4" }), dateValue ? formatDate(dateValue) : placeholder || "Pick a date"] }) }), jsx(PopoverContent, { className: "w-auto p-0", align: "start", children: jsx(Calendar, { mode: "single", selected: dateValue, onSelect: (date) => onChange(date ? date.toISOString().split("T")[0] : ""), disabled: (date) => {
628
+ if (resolvedMinDate && date < resolvedMinDate)
629
+ return true;
630
+ if (resolvedMaxDate && date > resolvedMaxDate)
631
+ return true;
632
+ return false;
633
+ }, initialFocus: true }) })] }));
634
+ }
635
+ }
636
+
637
+ class Hidden extends BaseField {
638
+ fieldType = "hidden";
639
+ static make(name) {
640
+ return new Hidden(name);
641
+ }
642
+ renderField({ value }) {
643
+ return jsx("input", { type: "hidden", name: this.config.name, value: value ?? "" });
644
+ }
645
+ }
646
+
647
+ class FileUpload extends BaseField {
648
+ fieldType = "file-upload";
649
+ constructor(name) {
650
+ super(name);
651
+ this.config.multiple = false;
652
+ this.config.preview = true;
653
+ this.config.maxFiles = 1;
654
+ }
655
+ static make(name) {
656
+ return new FileUpload(name);
657
+ }
658
+ accept(accept) {
659
+ this.config.accept = accept;
660
+ return this;
661
+ }
662
+ maxSize(bytes) {
663
+ this.config.maxSize = bytes;
664
+ return this;
665
+ }
666
+ multiple(multiple = true) {
667
+ this.config.multiple = multiple;
668
+ return this;
669
+ }
670
+ maxFiles(max) {
671
+ this.config.maxFiles = max;
672
+ return this;
673
+ }
674
+ preview(preview = true) {
675
+ this.config.preview = preview;
676
+ return this;
677
+ }
678
+ image() {
679
+ return this.accept("image/*");
680
+ }
681
+ pdf() {
682
+ return this.accept("application/pdf");
683
+ }
684
+ renderField({ value, onChange, error, disabled, }) {
685
+ const cfg = this.config;
686
+ const inputRef = useRef(null);
687
+ const [dragOver, setDragOver] = useState(false);
688
+ const files = Array.isArray(value) ? value : value ? [value] : [];
689
+ const handleFiles = (fileList) => {
690
+ if (!fileList)
691
+ return;
692
+ const newFiles = Array.from(fileList);
693
+ if (cfg.maxSize) {
694
+ const oversized = newFiles.find((f) => f.size > cfg.maxSize);
695
+ if (oversized)
696
+ return;
697
+ }
698
+ if (cfg.multiple) {
699
+ const combined = [...files, ...newFiles].slice(0, cfg.maxFiles);
700
+ onChange(combined);
701
+ }
702
+ else {
703
+ onChange(newFiles[0] || null);
704
+ }
705
+ };
706
+ const removeFile = (index) => {
707
+ if (cfg.multiple) {
708
+ const updated = files.filter((_, i) => i !== index);
709
+ onChange(updated.length ? updated : null);
710
+ }
711
+ else {
712
+ onChange(null);
713
+ }
714
+ };
715
+ return (jsxs("div", { className: cn("space-y-2", cfg.className), children: [jsxs("div", { className: cn("flex flex-col items-center justify-center rounded-lg border-2 border-dashed p-6 transition-colors", dragOver
716
+ ? "border-primary bg-primary/5"
717
+ : "border-muted-foreground/25", error && "border-destructive", disabled && "cursor-not-allowed opacity-50"), onDragOver: (e) => {
718
+ e.preventDefault();
719
+ if (!disabled)
720
+ setDragOver(true);
721
+ }, onDragLeave: () => setDragOver(false), onDrop: (e) => {
722
+ e.preventDefault();
723
+ setDragOver(false);
724
+ if (!disabled)
725
+ handleFiles(e.dataTransfer.files);
726
+ }, children: [jsx(Upload, { className: "mb-2 h-8 w-8 text-muted-foreground" }), jsxs("p", { className: "text-sm text-muted-foreground", children: ["Drag & drop or", " ", jsx("button", { type: "button", className: "text-primary underline", onClick: () => inputRef.current?.click(), disabled: disabled, children: "browse" })] }), cfg.accept && (jsxs("p", { className: "mt-1 text-xs text-muted-foreground", children: ["Accepted: ", cfg.accept] })), jsx("input", { ref: inputRef, type: "file", accept: cfg.accept, multiple: cfg.multiple, onChange: (e) => handleFiles(e.target.files), className: "hidden", disabled: disabled })] }), files.length > 0 && (jsx("ul", { className: "space-y-1", children: files.map((file, index) => (jsxs("li", { className: "flex items-center justify-between rounded-md border px-3 py-2 text-sm", children: [jsxs("span", { className: "flex items-center gap-2 truncate", children: [jsx(File, { className: "h-4 w-4 text-muted-foreground" }), file.name] }), jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-6 w-6 p-0", onClick: () => removeFile(index), disabled: disabled, children: jsx(X, { className: "h-3 w-3" }) })] }, index))) }))] }));
727
+ }
728
+ }
729
+
730
+ class CheckboxGroup extends BaseField {
731
+ fieldType = "checkbox-group";
732
+ constructor(name) {
733
+ super(name);
734
+ this.config.options = [];
735
+ this.config.columns = 1;
736
+ }
737
+ static make(name) {
738
+ return new CheckboxGroup(name);
739
+ }
740
+ options(options) {
741
+ this.config.options = options;
742
+ return this;
743
+ }
744
+ columns(columns) {
745
+ this.config.columns = columns;
746
+ return this;
747
+ }
748
+ renderField({ value, onChange, error, disabled, }) {
749
+ const cfg = this.config;
750
+ const selectedValues = Array.isArray(value) ? value : [];
751
+ const gridColsClass = {
752
+ 1: "grid-cols-1",
753
+ 2: "grid-cols-2",
754
+ 3: "grid-cols-3",
755
+ 4: "grid-cols-4",
756
+ };
757
+ const handleToggle = (optionValue, checked) => {
758
+ if (checked) {
759
+ onChange([...selectedValues, optionValue]);
760
+ }
761
+ else {
762
+ onChange(selectedValues.filter((v) => v !== optionValue));
763
+ }
764
+ };
765
+ return (jsx("div", { className: cn("grid gap-2", gridColsClass[cfg.columns || 1] || gridColsClass[1], cfg.className), children: cfg.options.map((opt) => (jsxs("div", { className: "flex items-center space-x-2", children: [jsx(Checkbox$1, { id: `${cfg.name}-${opt.value}`, checked: selectedValues.includes(opt.value), onCheckedChange: (checked) => handleToggle(opt.value, !!checked), disabled: disabled || opt.disabled, className: cn(error && "border-destructive") }), jsx(Label, { htmlFor: `${cfg.name}-${opt.value}`, className: "text-sm font-normal", children: opt.label })] }, opt.value))) }));
766
+ }
767
+ }
768
+
769
+ class Repeater extends BaseField {
770
+ fieldType = "repeater";
771
+ fieldBuilders = [];
772
+ constructor(name) {
773
+ super(name);
774
+ this.config.minItems = 0;
775
+ this.config.maxItems = Infinity;
776
+ this.config.addLabel = "Add item";
777
+ this.config.collapsible = false;
778
+ this.config.orderable = false;
779
+ }
780
+ static make(name) {
781
+ return new Repeater(name);
782
+ }
783
+ fields(...builders) {
784
+ this.fieldBuilders = builders;
785
+ this.config.fields = builders.map((b) => b.build());
786
+ return this;
787
+ }
788
+ minItems(min) {
789
+ this.config.minItems = min;
790
+ return this;
791
+ }
792
+ maxItems(max) {
793
+ this.config.maxItems = max;
794
+ return this;
795
+ }
796
+ addLabel(label) {
797
+ this.config.addLabel = label;
798
+ return this;
799
+ }
800
+ collapsible(collapsible = true) {
801
+ this.config.collapsible = collapsible;
802
+ return this;
803
+ }
804
+ orderable(orderable = true) {
805
+ this.config.orderable = orderable;
806
+ return this;
807
+ }
808
+ renderField({ value, onChange, error, disabled, data, }) {
809
+ const cfg = this.config;
810
+ const items = Array.isArray(value) ? value : [];
811
+ const builtFields = cfg.fields;
812
+ const addItem = () => {
813
+ if (items.length >= (cfg.maxItems || Infinity))
814
+ return;
815
+ const defaults = {};
816
+ builtFields.forEach((f) => {
817
+ defaults[f.name] = f.config.defaultValue ?? "";
818
+ });
819
+ onChange([...items, defaults]);
820
+ };
821
+ const removeItem = (index) => {
822
+ if (items.length <= (cfg.minItems || 0))
823
+ return;
824
+ onChange(items.filter((_, i) => i !== index));
825
+ };
826
+ const updateItem = (index, fieldName, fieldValue) => {
827
+ const updated = items.map((item, i) => i === index ? { ...item, [fieldName]: fieldValue } : item);
828
+ onChange(updated);
829
+ };
830
+ const canAdd = items.length < (cfg.maxItems || Infinity);
831
+ const canRemove = items.length > (cfg.minItems || 0);
832
+ return (jsxs("div", { className: cn("space-y-3", cfg.className), children: [items.map((item, index) => (jsx(Card, { children: jsxs(CardContent, { className: "flex items-start gap-3 pt-4", children: [cfg.orderable && (jsx(GripVertical, { className: "mt-2 h-5 w-5 cursor-grab text-muted-foreground" })), jsx("div", { className: "grid flex-1 gap-3", children: builtFields.map((field) => (jsxs("div", { className: "space-y-1", children: [field.config.label && (jsx("label", { className: "text-sm font-medium", children: field.config.label })), field.render({
833
+ value: item[field.name],
834
+ onChange: (v) => updateItem(index, field.name, v),
835
+ disabled,
836
+ data,
837
+ })] }, field.name))) }), canRemove && (jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "mt-1 h-8 w-8 p-0 text-destructive", onClick: () => removeItem(index), disabled: disabled, children: jsx(Trash2, { className: "h-4 w-4" }) }))] }) }, index))), canAdd && (jsxs(Button, { type: "button", variant: "outline", size: "sm", onClick: addItem, disabled: disabled, className: "w-full", children: [jsx(Plus, { className: "mr-2 h-4 w-4" }), cfg.addLabel] }))] }));
838
+ }
839
+ }
840
+
841
+ function FieldRenderer({ field, value, error, disabled, data, onChange, onBlur, }) {
842
+ // Hidden fields render without wrapper
843
+ if (field.type === "hidden") {
844
+ return field.render({ value, onChange, onBlur, error, disabled, data });
845
+ }
846
+ // Checkbox and Toggle handle their own labels inline
847
+ const skipLabel = field.type === "checkbox" || field.type === "toggle";
848
+ return (jsxs("div", { className: cn("space-y-2", field.config.columnSpan && field.config.columnSpan > 1
849
+ ? `col-span-${field.config.columnSpan}`
850
+ : undefined, field.config.className), children: [field.config.label && !skipLabel && (jsxs(Label, { htmlFor: field.name, className: cn(error && "text-destructive"), children: [field.config.label, field.config.required && (jsx("span", { className: "text-destructive ml-1", children: "*" }))] })), field.render({
851
+ value,
852
+ onChange,
853
+ onBlur,
854
+ error,
855
+ disabled,
856
+ data,
857
+ }), error && jsx("p", { className: "text-sm text-destructive", children: error }), !error && field.config.helperText && (jsx("p", { className: "text-sm text-muted-foreground", children: field.config.helperText }))] }));
858
+ }
859
+
860
+ const gridColsClass = {
861
+ 1: "grid-cols-1",
862
+ 2: "grid-cols-1 sm:grid-cols-2",
863
+ 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
864
+ 4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4",
865
+ };
866
+ function GridLayout({ columns = 1, children, className, }) {
867
+ return (jsx("div", { className: cn("grid gap-4", gridColsClass[columns] || gridColsClass[1], className), children: children }));
868
+ }
869
+
870
+ const positionClass = {
871
+ start: "justify-start",
872
+ center: "justify-center",
873
+ between: "justify-between",
874
+ end: "justify-end",
875
+ };
876
+ function FormActions({ actions, position = "end", data, processing = false, }) {
877
+ if (!actions.length)
878
+ return null;
879
+ return (jsx("div", { className: cn("flex gap-2", positionClass[position]), children: actions.map((action, i) => {
880
+ const isDisabled = typeof action.disabled === "function"
881
+ ? action.disabled(data, processing)
882
+ : action.disabled;
883
+ if (action.type === "submit") {
884
+ return (jsxs(Button, { type: "submit", variant: action.variant || "default", disabled: isDisabled || processing, children: [action.icon && jsx("span", { className: "mr-2", children: action.icon }), processing ? "Processing..." : action.label] }, i));
885
+ }
886
+ if (action.type === "cancel" && action.href) {
887
+ return (jsx(Button, { type: "button", variant: action.variant || "outline", disabled: processing, asChild: true, children: jsxs("a", { href: action.href, children: [action.icon && jsx("span", { className: "mr-2", children: action.icon }), action.label] }) }, i));
888
+ }
889
+ return (jsxs(Button, { type: "button", variant: action.variant || "outline", onClick: action.onClick, disabled: isDisabled || (action.type === "cancel" && processing), children: [action.icon && jsx("span", { className: "mr-2", children: action.icon }), action.label] }, i));
890
+ }) }));
891
+ }
892
+
893
+ function SectionRenderer({ section, fields, data, errors, onChange, onBlur, isFieldVisible, isFieldDisabled, }) {
894
+ const [isCollapsed, setIsCollapsed] = useState(section.collapsed ?? false);
895
+ if (section.hidden && section.hidden(data))
896
+ return null;
897
+ const sectionFields = fields.filter((f) => section.fields.includes(f.name));
898
+ return (jsxs(Card, { children: [jsxs(CardHeader, { className: cn(section.collapsible && "cursor-pointer select-none"), onClick: section.collapsible ? () => setIsCollapsed(!isCollapsed) : undefined, children: [jsxs("div", { className: "flex items-center justify-between", children: [jsxs("div", { className: "flex items-center gap-2", children: [section.icon, jsx(CardTitle, { className: "text-lg", children: section.title })] }), section.collapsible && (jsx(Button, { variant: "ghost", size: "sm", className: "h-8 w-8 p-0", children: jsx(ChevronDown, { className: cn("h-4 w-4 transition-transform", isCollapsed && "-rotate-90") }) }))] }), section.description && (jsx(CardDescription, { children: section.description }))] }), !isCollapsed && (jsx(CardContent, { children: jsx(GridLayout, { columns: section.columns, children: sectionFields.map((field) => {
899
+ if (!isFieldVisible(field))
900
+ return null;
901
+ return (jsx(FieldRenderer, { field: field, value: data[field.name], error: errors[field.name], disabled: isFieldDisabled(field), data: data, onChange: (v) => onChange(field.name, v), onBlur: onBlur ? () => onBlur(field.name) : undefined }, field.name));
902
+ }) }) }))] }));
903
+ }
904
+
905
+ function TabRenderer({ tabs, fields, data, errors, onChange, onBlur, isFieldVisible, isFieldDisabled, columns, }) {
906
+ const firstTab = tabs[0]?.id;
907
+ return (jsxs(Tabs, { defaultValue: firstTab, children: [jsx(TabsList, { children: tabs.map((tab) => {
908
+ const isDisabled = tab.disabled ? tab.disabled(data) : false;
909
+ const badge = typeof tab.badge === "function" ? tab.badge(data) : tab.badge;
910
+ return (jsx(TabsTrigger, { value: tab.id, disabled: isDisabled, children: jsxs("span", { className: "flex items-center gap-2", children: [tab.icon, tab.label, badge !== undefined && badge !== null && (jsx("span", { className: "ml-1 rounded-full bg-muted px-2 py-0.5 text-xs", children: badge }))] }) }, tab.id));
911
+ }) }), tabs.map((tab) => {
912
+ const tabFields = tab.fields
913
+ ? fields.filter((f) => tab.fields.includes(f.name))
914
+ : [];
915
+ return (jsx(TabsContent, { value: tab.id, className: "mt-4", children: tab.sections ? (jsx("div", { className: "space-y-4", children: tab.sections.map((section) => (jsx(SectionRenderer, { section: section, fields: fields, data: data, errors: errors, onChange: onChange, onBlur: onBlur, isFieldVisible: isFieldVisible, isFieldDisabled: isFieldDisabled }, section.id))) })) : (jsx(GridLayout, { columns: columns, children: tabFields.map((field) => {
916
+ if (!isFieldVisible(field))
917
+ return null;
918
+ return (jsx(FieldRenderer, { field: field, value: data[field.name], error: errors[field.name], disabled: isFieldDisabled(field), data: data, onChange: (v) => onChange(field.name, v), onBlur: onBlur ? () => onBlur(field.name) : undefined }, field.name));
919
+ }) })) }, tab.id));
920
+ })] }));
921
+ }
922
+
923
+ function WizardRenderer({ steps, fields, data, errors, onChange, onBlur, isFieldVisible, isFieldDisabled, columns, onSubmit, processing = false, }) {
924
+ const [currentStep, setCurrentStep] = useState(0);
925
+ const currentStepConfig = steps[currentStep];
926
+ const isLastStep = currentStep === steps.length - 1;
927
+ const isFirstStep = currentStep === 0;
928
+ const canProceed = currentStepConfig?.canProceed
929
+ ? currentStepConfig.canProceed(data)
930
+ : true;
931
+ const handleNext = useCallback(async () => {
932
+ if (currentStepConfig?.beforeNext) {
933
+ const canContinue = await currentStepConfig.beforeNext(data);
934
+ if (!canContinue)
935
+ return;
936
+ }
937
+ if (isLastStep) {
938
+ onSubmit();
939
+ }
940
+ else {
941
+ setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1));
942
+ }
943
+ }, [currentStep, currentStepConfig, data, isLastStep, onSubmit, steps.length]);
944
+ const handlePrevious = useCallback(() => {
945
+ setCurrentStep((prev) => Math.max(prev - 1, 0));
946
+ }, []);
947
+ const stepFields = currentStepConfig?.fields
948
+ ? fields.filter((f) => currentStepConfig.fields.includes(f.name))
949
+ : [];
950
+ return (jsxs("div", { className: "space-y-6", children: [jsx("nav", { "aria-label": "Progress", children: jsx("ol", { className: "flex items-center", children: steps.map((step, index) => (jsxs("li", { className: cn("flex items-center", index < steps.length - 1 && "flex-1"), children: [jsxs("div", { className: "flex items-center gap-2", children: [jsx("div", { className: cn("flex h-8 w-8 items-center justify-center rounded-full border-2 text-sm font-medium", index < currentStep &&
951
+ "border-primary bg-primary text-primary-foreground", index === currentStep &&
952
+ "border-primary text-primary", index > currentStep &&
953
+ "border-muted-foreground/25 text-muted-foreground"), children: index < currentStep ? (jsx(Check, { className: "h-4 w-4" })) : (index + 1) }), jsxs("div", { className: "hidden sm:block", children: [jsx("p", { className: cn("text-sm font-medium", index <= currentStep
954
+ ? "text-foreground"
955
+ : "text-muted-foreground"), children: step.label }), step.description && (jsx("p", { className: "text-xs text-muted-foreground", children: step.description }))] })] }), index < steps.length - 1 && (jsx("div", { className: cn("mx-4 hidden h-0.5 flex-1 sm:block", index < currentStep ? "bg-primary" : "bg-muted") }))] }, step.id))) }) }), jsx("div", { children: currentStepConfig?.sections ? (jsx("div", { className: "space-y-4", children: currentStepConfig.sections.map((section) => (jsx(SectionRenderer, { section: section, fields: fields, data: data, errors: errors, onChange: onChange, onBlur: onBlur, isFieldVisible: isFieldVisible, isFieldDisabled: isFieldDisabled }, section.id))) })) : (jsx(GridLayout, { columns: columns, children: stepFields.map((field) => {
956
+ if (!isFieldVisible(field))
957
+ return null;
958
+ return (jsx(FieldRenderer, { field: field, value: data[field.name], error: errors[field.name], disabled: isFieldDisabled(field), data: data, onChange: (v) => onChange(field.name, v), onBlur: onBlur ? () => onBlur(field.name) : undefined }, field.name));
959
+ }) })) }), jsxs("div", { className: "flex justify-between", children: [jsx(Button, { type: "button", variant: "outline", onClick: handlePrevious, disabled: isFirstStep, children: "Previous" }), jsx(Button, { type: isLastStep ? "submit" : "button", onClick: handleNext, disabled: !canProceed || (isLastStep && processing), children: isLastStep
960
+ ? processing
961
+ ? "Processing..."
962
+ : "Submit"
963
+ : "Next" })] })] }));
964
+ }
965
+
966
+ function FormRenderer({ schema, data, errors, onChange, onSubmit, processing = false, className, disabled = false, onBlur, }) {
967
+ const { fields, config } = schema;
968
+ const resolvedTitle = useMemo(() => {
969
+ if (typeof config.title === "function")
970
+ return config.title(data);
971
+ return config.title;
972
+ }, [config.title, data]);
973
+ const resolvedDescription = useMemo(() => {
974
+ if (typeof config.description === "function")
975
+ return config.description(data);
976
+ return config.description;
977
+ }, [config.description, data]);
978
+ const isFieldVisible = useCallback((field) => {
979
+ if (typeof field.config.hidden === "function")
980
+ return !field.config.hidden(data);
981
+ if (typeof field.config.hidden === "boolean")
982
+ return !field.config.hidden;
983
+ for (const dep of field.config.dependsOn || []) {
984
+ const depValue = data[dep.field];
985
+ if (dep.effect === "show" && !dep.condition(depValue, data))
986
+ return false;
987
+ if (dep.effect === "hide" && dep.condition(depValue, data))
988
+ return false;
989
+ }
990
+ return true;
991
+ }, [data]);
992
+ const isFieldDisabled = useCallback((field) => {
993
+ if (disabled)
994
+ return true;
995
+ if (typeof config.disabled === "function" && config.disabled(data))
996
+ return true;
997
+ if (typeof config.disabled === "boolean" && config.disabled)
998
+ return true;
999
+ if (typeof field.config.disabled === "function")
1000
+ return field.config.disabled(data);
1001
+ if (typeof field.config.disabled === "boolean")
1002
+ return field.config.disabled;
1003
+ for (const dep of field.config.dependsOn || []) {
1004
+ const depValue = data[dep.field];
1005
+ if (dep.effect === "disable" && dep.condition(depValue, data))
1006
+ return true;
1007
+ if (dep.effect === "enable" &&
1008
+ !dep.condition(depValue, data))
1009
+ return true;
1010
+ }
1011
+ return false;
1012
+ }, [disabled, config.disabled, data]);
1013
+ const renderFieldsFlat = () => (jsx(GridLayout, { columns: config.columns, children: fields.map((field) => {
1014
+ if (!isFieldVisible(field))
1015
+ return null;
1016
+ return (jsx(FieldRenderer, { field: field, value: data[field.name], error: errors[field.name], disabled: isFieldDisabled(field), data: data, onChange: (v) => onChange(field.name, v), onBlur: onBlur ? () => onBlur(field.name) : undefined }, field.name));
1017
+ }) }));
1018
+ const renderContent = () => {
1019
+ // Wizard mode
1020
+ if (config.wizardSteps?.length) {
1021
+ return (jsx(WizardRenderer, { steps: config.wizardSteps, fields: fields, data: data, errors: errors, onChange: onChange, onBlur: onBlur, isFieldVisible: isFieldVisible, isFieldDisabled: isFieldDisabled, columns: config.columns, onSubmit: onSubmit, processing: processing }));
1022
+ }
1023
+ // Tabs mode
1024
+ if (config.tabs?.length) {
1025
+ return (jsx(TabRenderer, { tabs: config.tabs, fields: fields, data: data, errors: errors, onChange: onChange, onBlur: onBlur, isFieldVisible: isFieldVisible, isFieldDisabled: isFieldDisabled, columns: config.columns }));
1026
+ }
1027
+ // Sections mode
1028
+ if (config.sections?.length) {
1029
+ return (jsx("div", { className: "space-y-4", children: config.sections.map((section) => (jsx(SectionRenderer, { section: section, fields: fields, data: data, errors: errors, onChange: onChange, onBlur: onBlur, isFieldVisible: isFieldVisible, isFieldDisabled: isFieldDisabled }, section.id))) }));
1030
+ }
1031
+ // Flat mode (default)
1032
+ return renderFieldsFlat();
1033
+ };
1034
+ const spacingClass = {
1035
+ compact: "space-y-3",
1036
+ normal: "space-y-6",
1037
+ relaxed: "space-y-8",
1038
+ }[config.spacing || "normal"];
1039
+ // Wizard handles its own submit button
1040
+ const showActions = config.actions?.length && !config.wizardSteps?.length;
1041
+ const formBody = (jsxs("form", { onSubmit: (e) => {
1042
+ e.preventDefault();
1043
+ onSubmit();
1044
+ }, className: cn(spacingClass, className), children: [(resolvedTitle || resolvedDescription) && (jsxs("div", { className: "space-y-1", children: [resolvedTitle && (jsx("h2", { className: "text-2xl font-semibold tracking-tight", children: resolvedTitle })), resolvedDescription && (jsx("p", { className: "text-sm text-muted-foreground", children: resolvedDescription }))] })), renderContent(), showActions && (jsx(FormActions, { actions: config.actions, position: config.actionsPosition, data: data, processing: processing }))] }));
1045
+ if (config.bordered) {
1046
+ return (jsx(Card, { children: jsx(CardContent, { className: "pt-6", children: formBody }) }));
1047
+ }
1048
+ return formBody;
1049
+ }
1050
+
1051
+ export { ActionsBuilder, BaseField, Checkbox, CheckboxGroup, DatePicker, FieldRenderer, FileUpload, FormActions, FormRenderer, FormSchema, GridLayout, Hidden, RadioGroup, Repeater, SectionBuilder, SectionRenderer, Select, TabBuilder, TabRenderer, TextInput, Textarea, Toggle, WizardRenderer, WizardStep };
1052
+ //# sourceMappingURL=index.esm.js.map