@k3-universe/react-kit 0.0.13 → 0.0.15

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 (59) hide show
  1. package/dist/index.js +1773 -1739
  2. package/dist/kit/builder/data-table/types.d.ts +1 -1
  3. package/dist/kit/builder/data-table/types.d.ts.map +1 -1
  4. package/dist/kit/builder/form/components/FormBuilder.d.ts +3 -172
  5. package/dist/kit/builder/form/components/FormBuilder.d.ts.map +1 -1
  6. package/dist/kit/builder/form/components/FormBuilderContext.d.ts +18 -0
  7. package/dist/kit/builder/form/components/FormBuilderContext.d.ts.map +1 -0
  8. package/dist/kit/builder/form/components/FormBuilderField.d.ts +8 -8
  9. package/dist/kit/builder/form/components/FormBuilderField.d.ts.map +1 -1
  10. package/dist/kit/builder/form/components/fields/types.d.ts +3 -3
  11. package/dist/kit/builder/form/components/fields/types.d.ts.map +1 -1
  12. package/dist/kit/builder/form/components/sectionNodes.d.ts +17 -0
  13. package/dist/kit/builder/form/components/sectionNodes.d.ts.map +1 -0
  14. package/dist/kit/builder/form/index.d.ts +1 -0
  15. package/dist/kit/builder/form/index.d.ts.map +1 -1
  16. package/dist/kit/builder/form/types.d.ts +176 -0
  17. package/dist/kit/builder/form/types.d.ts.map +1 -0
  18. package/dist/kit/builder/form/utils/common-forms.d.ts +1 -1
  19. package/dist/kit/builder/form/utils/common-forms.d.ts.map +1 -1
  20. package/dist/kit/builder/form/utils/field-factories.d.ts +3 -3
  21. package/dist/kit/builder/form/utils/field-factories.d.ts.map +1 -1
  22. package/dist/kit/builder/form/utils/section-factories.d.ts +4 -4
  23. package/dist/kit/builder/form/utils/section-factories.d.ts.map +1 -1
  24. package/dist/kit/builder/stack-dialog/provider.d.ts.map +1 -1
  25. package/dist/kit/builder/stack-dialog/renderer.d.ts.map +1 -1
  26. package/dist/kit/components/autocomplete/Autocomplete.d.ts +8 -8
  27. package/dist/kit/components/autocomplete/Autocomplete.d.ts.map +1 -1
  28. package/dist/kit/components/autocomplete/types.d.ts +6 -4
  29. package/dist/kit/components/autocomplete/types.d.ts.map +1 -1
  30. package/dist/kit/themes/clean-slate.css +3 -3
  31. package/dist/kit/themes/default.css +4 -4
  32. package/dist/kit/themes/minimal-modern.css +3 -3
  33. package/dist/kit/themes/spotify.css +3 -3
  34. package/package.json +1 -1
  35. package/src/kit/builder/data-table/components/DataTable.tsx +1 -1
  36. package/src/kit/builder/data-table/types.ts +1 -1
  37. package/src/kit/builder/form/components/FormBuilder.tsx +113 -369
  38. package/src/kit/builder/form/components/FormBuilderContext.tsx +45 -0
  39. package/src/kit/builder/form/components/FormBuilderField.tsx +42 -34
  40. package/src/kit/builder/form/components/fields/AutocompleteField.tsx +2 -2
  41. package/src/kit/builder/form/components/fields/types.ts +3 -3
  42. package/src/kit/builder/form/components/sectionNodes.tsx +116 -0
  43. package/src/kit/builder/form/index.ts +1 -0
  44. package/src/kit/builder/form/types.ts +200 -0
  45. package/src/kit/builder/form/utils/common-forms.ts +1 -1
  46. package/src/kit/builder/form/utils/field-factories.ts +5 -5
  47. package/src/kit/builder/form/utils/section-factories.ts +10 -10
  48. package/src/kit/builder/stack-dialog/provider.tsx +2 -1
  49. package/src/kit/builder/stack-dialog/renderer.tsx +6 -7
  50. package/src/kit/components/autocomplete/Autocomplete.tsx +34 -26
  51. package/src/kit/components/autocomplete/types.ts +7 -5
  52. package/src/kit/themes/default.css +1 -1
  53. package/src/shadcn/ui/button.tsx +1 -1
  54. package/src/shadcn/ui/command.tsx +1 -1
  55. package/src/shadcn/ui/input.tsx +1 -1
  56. package/src/shadcn/ui/popover.tsx +1 -1
  57. package/src/shadcn/ui/select.tsx +1 -1
  58. package/src/shadcn/ui/textarea.tsx +1 -1
  59. package/src/stories/kit/builder/Form.MultipleFormBuilder.stories.tsx +335 -0
@@ -1,9 +1,9 @@
1
1
  import { useCallback } from 'react';
2
- import type { Control, FieldValues } from 'react-hook-form';
2
+ import type { Control, FieldValues, Path } from 'react-hook-form';
3
3
  import { useController } from 'react-hook-form';
4
4
  import { cn } from '../../../../shadcn/lib/utils';
5
5
  import { Label } from '../../../../shadcn/ui/label';
6
- import type { FormBuilderFieldConfig } from './FormBuilder';
6
+ import type { FormBuilderFieldConfig } from '../types';
7
7
  import {
8
8
  AutocompleteField,
9
9
  TextField,
@@ -27,29 +27,37 @@ import {
27
27
  ArrayField,
28
28
  } from './fields';
29
29
 
30
- export interface FormBuilderFieldProps {
31
- field: FormBuilderFieldConfig;
32
- control: Control<FieldValues>;
33
- onChange?: (value: unknown) => void;
34
- onFieldChange?: (name: string, value: unknown, allValues: Record<string, unknown>) => void;
30
+ export interface FormBuilderFieldProps<
31
+ TFieldValues extends FieldValues = FieldValues,
32
+ TName extends string | Path<TFieldValues> = Path<TFieldValues>
33
+ > {
34
+ field: FormBuilderFieldConfig<TFieldValues, TName>;
35
+ control: Control<TFieldValues>;
36
+ onChange?: (value: unknown, ...extras: unknown[]) => void;
37
+ onFieldChange?: (name: import('react-hook-form').Path<TFieldValues> | string, value: unknown, allValues: TFieldValues) => void;
35
38
  parentPath?: string;
36
39
  }
37
40
 
38
- export function FormBuilderField({ field, control, onChange, parentPath }: FormBuilderFieldProps) {
39
- const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
41
+ export function FormBuilderField<
42
+ TFieldValues extends FieldValues = FieldValues,
43
+ TName extends string | Path<TFieldValues> = Path<TFieldValues>
44
+ >({ field, control, onChange, parentPath }: FormBuilderFieldProps<TFieldValues, TName>) {
45
+ const fieldPath = parentPath ? `${parentPath}.${field.name}` : (field.name as string);
40
46
 
41
47
  const {
42
48
  field: controllerField,
43
49
  fieldState: { error },
44
- } = useController({
45
- name: fieldPath,
50
+ } = useController<TFieldValues>({
51
+ name: fieldPath as unknown as Path<TFieldValues>,
46
52
  control,
47
53
  disabled: field.disabled,
48
54
  });
49
55
 
50
- const handleChange = useCallback((value: unknown) => {
56
+ const handleChange = useCallback((value: unknown, ...extras: unknown[]) => {
57
+ // Only patch the RHF value with the first argument (the canonical value)
51
58
  controllerField.onChange(value);
52
- onChange?.(value);
59
+ // Forward any extra metadata upstream (e.g., option, raw)
60
+ onChange?.(value, ...extras);
53
61
  }, [controllerField, onChange]);
54
62
  const baseClassName = cn(
55
63
  error && 'border-destructive focus-visible:ring-destructive',
@@ -62,7 +70,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
62
70
  return (
63
71
  <AutocompleteField
64
72
  field={field}
65
- control={control}
73
+ control={control as unknown as Control<FieldValues>}
66
74
  fieldPath={fieldPath}
67
75
  value={controllerField.value}
68
76
  onChange={handleChange}
@@ -75,7 +83,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
75
83
  return (
76
84
  <TextField
77
85
  field={field}
78
- control={control}
86
+ control={control as unknown as Control<FieldValues>}
79
87
  fieldPath={fieldPath}
80
88
  value={controllerField.value}
81
89
  onChange={handleChange}
@@ -86,7 +94,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
86
94
  return (
87
95
  <NumberField
88
96
  field={field}
89
- control={control}
97
+ control={control as unknown as Control<FieldValues>}
90
98
  fieldPath={fieldPath}
91
99
  value={controllerField.value}
92
100
  onChange={handleChange}
@@ -97,7 +105,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
97
105
  return (
98
106
  <TextareaField
99
107
  field={field}
100
- control={control}
108
+ control={control as unknown as Control<FieldValues>}
101
109
  fieldPath={fieldPath}
102
110
  value={controllerField.value}
103
111
  onChange={handleChange}
@@ -108,7 +116,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
108
116
  return (
109
117
  <SelectField
110
118
  field={field}
111
- control={control}
119
+ control={control as unknown as Control<FieldValues>}
112
120
  fieldPath={fieldPath}
113
121
  value={controllerField.value}
114
122
  onChange={handleChange}
@@ -119,7 +127,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
119
127
  return (
120
128
  <CheckboxField
121
129
  field={field}
122
- control={control}
130
+ control={control as unknown as Control<FieldValues>}
123
131
  fieldPath={fieldPath}
124
132
  value={controllerField.value}
125
133
  onChange={handleChange}
@@ -130,7 +138,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
130
138
  return (
131
139
  <SwitchField
132
140
  field={field}
133
- control={control}
141
+ control={control as unknown as Control<FieldValues>}
134
142
  fieldPath={fieldPath}
135
143
  value={controllerField.value}
136
144
  onChange={handleChange}
@@ -141,7 +149,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
141
149
  return (
142
150
  <RadioField
143
151
  field={field}
144
- control={control}
152
+ control={control as unknown as Control<FieldValues>}
145
153
  fieldPath={fieldPath}
146
154
  value={controllerField.value}
147
155
  onChange={handleChange}
@@ -152,7 +160,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
152
160
  return (
153
161
  <DateField
154
162
  field={field}
155
- control={control}
163
+ control={control as unknown as Control<FieldValues>}
156
164
  fieldPath={fieldPath}
157
165
  value={controllerField.value}
158
166
  onChange={handleChange}
@@ -163,7 +171,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
163
171
  return (
164
172
  <DatePickerField
165
173
  field={field}
166
- control={control}
174
+ control={control as unknown as Control<FieldValues>}
167
175
  fieldPath={fieldPath}
168
176
  value={controllerField.value}
169
177
  onChange={handleChange}
@@ -174,7 +182,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
174
182
  return (
175
183
  <DateRangePickerField
176
184
  field={field}
177
- control={control}
185
+ control={control as unknown as Control<FieldValues>}
178
186
  fieldPath={fieldPath}
179
187
  value={controllerField.value}
180
188
  onChange={handleChange}
@@ -185,7 +193,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
185
193
  return (
186
194
  <MonthPickerField
187
195
  field={field}
188
- control={control}
196
+ control={control as unknown as Control<FieldValues>}
189
197
  fieldPath={fieldPath}
190
198
  value={controllerField.value}
191
199
  onChange={handleChange}
@@ -196,7 +204,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
196
204
  return (
197
205
  <MonthRangePickerField
198
206
  field={field}
199
- control={control}
207
+ control={control as unknown as Control<FieldValues>}
200
208
  fieldPath={fieldPath}
201
209
  value={controllerField.value}
202
210
  onChange={handleChange}
@@ -207,7 +215,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
207
215
  return (
208
216
  <TimePickerField
209
217
  field={field}
210
- control={control}
218
+ control={control as unknown as Control<FieldValues>}
211
219
  fieldPath={fieldPath}
212
220
  value={controllerField.value}
213
221
  onChange={handleChange}
@@ -218,7 +226,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
218
226
  return (
219
227
  <TimeRangePickerField
220
228
  field={field}
221
- control={control}
229
+ control={control as unknown as Control<FieldValues>}
222
230
  fieldPath={fieldPath}
223
231
  value={controllerField.value}
224
232
  onChange={handleChange}
@@ -229,7 +237,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
229
237
  return (
230
238
  <DateTimePickerField
231
239
  field={field}
232
- control={control}
240
+ control={control as unknown as Control<FieldValues>}
233
241
  fieldPath={fieldPath}
234
242
  value={controllerField.value}
235
243
  onChange={handleChange}
@@ -240,7 +248,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
240
248
  return (
241
249
  <DateTimeRangePickerField
242
250
  field={field}
243
- control={control}
251
+ control={control as unknown as Control<FieldValues>}
244
252
  fieldPath={fieldPath}
245
253
  value={controllerField.value}
246
254
  onChange={handleChange}
@@ -251,7 +259,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
251
259
  return (
252
260
  <FileField
253
261
  field={field}
254
- control={control}
262
+ control={control as unknown as Control<FieldValues>}
255
263
  fieldPath={fieldPath}
256
264
  value={controllerField.value}
257
265
  onChange={handleChange}
@@ -262,7 +270,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
262
270
  return (
263
271
  <ObjectField
264
272
  field={field}
265
- control={control}
273
+ control={control as unknown as Control<FieldValues>}
266
274
  fieldPath={fieldPath}
267
275
  value={controllerField.value}
268
276
  onChange={handleChange}
@@ -273,7 +281,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
273
281
  return (
274
282
  <ArrayField
275
283
  field={field}
276
- control={control}
284
+ control={control as unknown as Control<FieldValues>}
277
285
  fieldPath={fieldPath}
278
286
  value={controllerField.value}
279
287
  onChange={handleChange}
@@ -284,7 +292,7 @@ export function FormBuilderField({ field, control, onChange, parentPath }: FormB
284
292
  return (
285
293
  <TextField
286
294
  field={field}
287
- control={control}
295
+ control={control as unknown as Control<FieldValues>}
288
296
  fieldPath={fieldPath}
289
297
  value={controllerField.value}
290
298
  onChange={handleChange}
@@ -5,7 +5,7 @@ import type { FieldRenderProps } from './types'
5
5
  export function AutocompleteField({ field, value, onChange, className }: FieldRenderProps) {
6
6
  const options: AutocompleteOption[] = (field.options ?? [])
7
7
  .filter((o): o is { label: string; value: string | number } => o.value !== null && o.value !== undefined)
8
- .map(o => ({ label: o.label, value: o.value as string | number }))
8
+ .map(o => ({ label: o.label, value: o.value as string | number, raw: (o as unknown as AutocompleteOption).raw }))
9
9
 
10
10
  // Shape defaultValue according to single/multiple
11
11
  let defaultValueShaped: string | number | null | Array<string | number> | undefined = undefined
@@ -42,7 +42,7 @@ export function AutocompleteField({ field, value, onChange, className }: FieldRe
42
42
  initialSelectedOptions={field.initialSelectedOptions ?? null}
43
43
  loadSelected={field.loadSelected}
44
44
  value={field.multiple ? ((Array.isArray(value) ? value : (value ? [value] : [])) as Array<string | number>) : ((value as string | number | null) ?? null)}
45
- onChange={(val) => onChange(val)}
45
+ onChange={(val, option, raw) => onChange(val, option, raw)}
46
46
  placeholder={field.placeholder}
47
47
  searchPlaceholder={field.searchPlaceholder}
48
48
  renderOption={field.renderOption}
@@ -1,12 +1,12 @@
1
1
  import type { Control, FieldValues } from 'react-hook-form'
2
- import type { FormBuilderFieldConfig } from '../FormBuilder'
2
+ import type { FormBuilderFieldConfig } from '../../types'
3
3
 
4
4
  export interface FieldRenderProps {
5
- field: FormBuilderFieldConfig
5
+ field: FormBuilderFieldConfig<any, any>
6
6
  control: Control<FieldValues>
7
7
  fieldPath: string
8
8
  value: unknown
9
- onChange: (value: unknown) => void
9
+ onChange: (value: unknown, ...extras: unknown[]) => void
10
10
  className?: string
11
11
  disabled?: boolean
12
12
  errorMessage?: string
@@ -0,0 +1,116 @@
1
+ import type { FieldValues, Path, Control, UseFormGetValues } from 'react-hook-form'
2
+ import type { SectionNode } from '../../section/types'
3
+ import { FormBuilderField } from './FormBuilderField'
4
+ import type { FormBuilderFieldConfig, FormBuilderSectionConfig } from '../types'
5
+
6
+ interface BuildSectionNodesOptions<TFieldValues extends FieldValues> {
7
+ sections: Array<FormBuilderSectionConfig<TFieldValues>>
8
+ control: Control<TFieldValues>
9
+ handleFieldDependencies: (
10
+ field: FormBuilderFieldConfig<TFieldValues, string | Path<TFieldValues>>
11
+ ) => { disabled?: boolean; hidden?: boolean } | Record<string, never>
12
+ handleFieldChange: (
13
+ field: FormBuilderFieldConfig<TFieldValues, string | Path<TFieldValues>>,
14
+ value: unknown,
15
+ ...extras: unknown[]
16
+ ) => void
17
+ onFieldChange?: (
18
+ name: Path<TFieldValues> | string,
19
+ value: unknown,
20
+ allValues: TFieldValues
21
+ ) => void
22
+ getValues: UseFormGetValues<TFieldValues>
23
+ }
24
+
25
+ export function buildSectionNodes<TFieldValues extends FieldValues>(
26
+ options: BuildSectionNodesOptions<TFieldValues>
27
+ ): SectionNode[] {
28
+ const {
29
+ sections,
30
+ control,
31
+ handleFieldDependencies,
32
+ handleFieldChange,
33
+ onFieldChange,
34
+ getValues,
35
+ } = options
36
+
37
+ const buildLeavesFromFields = (
38
+ fields?: Array<FormBuilderFieldConfig<TFieldValues, string | Path<TFieldValues>>>
39
+ ): SectionNode['children'] =>
40
+ (fields ?? [])
41
+ .map((field) => {
42
+ const fieldState = handleFieldDependencies(field)
43
+ if (field.hidden || fieldState.hidden) return null
44
+
45
+ const spanMd = Math.max(1, Math.min(12, field.gridCols ?? 1))
46
+
47
+ return {
48
+ key: field.name,
49
+ span: { base: 1, md: spanMd },
50
+ className: field.wrapperClassName,
51
+ hidden: field.hidden,
52
+ content: (
53
+ <FormBuilderField
54
+ key={field.name}
55
+ field={{
56
+ ...field,
57
+ disabled: field.disabled || fieldState.disabled,
58
+ }}
59
+ control={control}
60
+ onChange={(value, ...extras) => {
61
+ handleFieldChange(field, value, ...extras)
62
+ onFieldChange?.(field.name as Path<TFieldValues> | string, value, getValues())
63
+ }}
64
+ onFieldChange={onFieldChange}
65
+ />
66
+ ),
67
+ }
68
+ })
69
+ .filter(Boolean) as SectionNode['children']
70
+
71
+ const buildSectionNode = (
72
+ section: FormBuilderSectionConfig<TFieldValues>,
73
+ sectionIndex: number,
74
+ ): SectionNode => {
75
+ const baseNode: SectionNode = {
76
+ id: section.id ?? `section-${sectionIndex}`,
77
+ title: section.title,
78
+ subtitle: section.description,
79
+ variant: section.variant ?? 'plain',
80
+ className: section.className,
81
+ layout: section.layout ?? (section.tabs && section.tabs.length > 0 ? 'tabs' : 'grid'),
82
+ grid: section.grid ?? { cols: 1, mdCols: 2, gap: 'gap-4' },
83
+ flex: section.flex,
84
+ hidden: section.hidden,
85
+ }
86
+
87
+ if (baseNode.layout === 'tabs' && section.tabs && section.tabs.length > 0) {
88
+ baseNode.defaultTabId = section.defaultTabId ?? section.tabs[0]?.id
89
+ baseNode.tabsListClassName = section.tabsListClassName
90
+ baseNode.tabsContentClassName = section.tabsContentClassName
91
+ baseNode.tabs = section.tabs.map((tab, _tabIdx) => {
92
+ const nestedNodes = tab.sections.map((subSection, subIdx) => buildSectionNode(subSection, subIdx))
93
+ const containerNode: SectionNode = {
94
+ id: `${baseNode.id}-tab-${tab.id}`,
95
+ variant: 'plain',
96
+ layout: 'grid',
97
+ grid: section.grid ?? { cols: 1, mdCols: 2, gap: 'gap-4' },
98
+ children: nestedNodes,
99
+ }
100
+ return {
101
+ id: tab.id,
102
+ label: tab.label,
103
+ className: tab.className,
104
+ contentClassName: tab.contentClassName,
105
+ node: containerNode,
106
+ }
107
+ })
108
+ return baseNode
109
+ }
110
+
111
+ baseNode.children = buildLeavesFromFields(section.fields)
112
+ return baseNode
113
+ }
114
+
115
+ return sections.map((section, sectionIndex) => buildSectionNode(section, sectionIndex))
116
+ }
@@ -1,2 +1,3 @@
1
1
  export * from './components';
2
2
  export * from './utils';
3
+ export * from './types';
@@ -0,0 +1,200 @@
1
+ import type React from 'react'
2
+ import type {
3
+ Control,
4
+ DeepPartial,
5
+ FieldValues,
6
+ Path,
7
+ UseFormGetValues,
8
+ UseFormReturn,
9
+ UseFormSetValue,
10
+ DefaultValues,
11
+ } from 'react-hook-form'
12
+ import type { z } from 'zod'
13
+ import type { Accept } from 'react-dropzone'
14
+ import type { SectionFlexOptions, SectionGridOptions, SectionLayout } from '../section/types'
15
+ import type { AutocompleteFetcher, AutocompleteOption } from '../../components/autocomplete/types'
16
+ import type { FileRecord, FileUploaderLayout } from '../../components/fileuploader/types'
17
+
18
+ export type FieldType =
19
+ | 'text'
20
+ | 'email'
21
+ | 'password'
22
+ | 'number'
23
+ | 'textarea'
24
+ | 'select'
25
+ | 'autocomplete'
26
+ | 'checkbox'
27
+ | 'switch'
28
+ | 'radio'
29
+ | 'date'
30
+ | 'date_picker'
31
+ | 'date_range'
32
+ | 'month'
33
+ | 'month_range'
34
+ | 'time'
35
+ | 'time_range'
36
+ | 'date_time'
37
+ | 'date_time_range'
38
+ | 'file'
39
+ | 'object'
40
+ | 'array'
41
+
42
+ export interface Dependency<TFieldValues extends FieldValues> {
43
+ field: Path<TFieldValues>
44
+ condition: (value: unknown) => boolean
45
+ action: 'show' | 'hide' | 'enable' | 'disable' | 'setValue'
46
+ value?: unknown
47
+ }
48
+
49
+ export interface FormBuilderFieldConfig<
50
+ TFieldValues extends FieldValues = FieldValues,
51
+ TName extends Path<TFieldValues> | string = Path<TFieldValues>
52
+ > {
53
+ id?: string
54
+ name: TName
55
+ label: string
56
+ type: FieldType
57
+ placeholder?: string
58
+ description?: string
59
+ required?: boolean
60
+ disabled?: boolean
61
+ options?: { label: string; value: string | number | null }[]
62
+ autocompleteMode?: 'client' | 'server'
63
+ fetcher?: AutocompleteFetcher
64
+ pageSize?: number
65
+ searchPlaceholder?: string
66
+ renderOption?: (option: AutocompleteOption, selected: boolean) => React.ReactNode
67
+ multiple?: boolean
68
+ allowCustomValue?: boolean
69
+ chipVariant?: 'default' | 'secondary' | 'destructive' | 'outline'
70
+ chipClassName?: string
71
+ clearable?: boolean
72
+ initialSelectedOptions?: AutocompleteOption | AutocompleteOption[] | null
73
+ loadSelected?: (values: Array<string | number>) => Promise<AutocompleteOption[]>
74
+ validation?:
75
+ | z.ZodType<unknown>
76
+ | {
77
+ pattern?: { value: RegExp; message: string }
78
+ min?: { value: number; message: string }
79
+ max?: { value: number; message: string }
80
+ minLength?: { value: number; message: string }
81
+ maxLength?: { value: number; message: string }
82
+ minItems?: { value: number; message: string }
83
+ maxItems?: { value: number; message: string }
84
+ }
85
+ // Accept any for defaultValue to support relative string names for nested fields
86
+ defaultValue?: unknown
87
+ // For nested object/array fields, use relative names (eg. 'uomName')
88
+ fields?: Array<FormBuilderFieldConfig<TFieldValues, string>>
89
+ dependencies?: Array<Dependency<TFieldValues>>
90
+ onChange?: (
91
+ value: unknown,
92
+ extras: unknown,
93
+ setValue: UseFormSetValue<TFieldValues>,
94
+ getValues: UseFormGetValues<TFieldValues>
95
+ ) => void
96
+ className?: string
97
+ gridCols?: number
98
+ rows?: number
99
+ itemType?: string
100
+ arrayLayout?: 'card' | 'table' | 'custom'
101
+ arrayRender?: (params: {
102
+ field: FormBuilderFieldConfig<TFieldValues>
103
+ control: Control<TFieldValues>
104
+ fieldPath: string
105
+ value: unknown
106
+ onChange: (value: unknown) => void
107
+ addItem: () => void
108
+ removeItem: (index: number) => void
109
+ disabled?: boolean
110
+ rows?: { id: string }[]
111
+ }) => React.ReactNode
112
+ arrayColors?: {
113
+ headerBgClass?: string
114
+ headerTextClass?: string
115
+ rowAltBgClass?: string
116
+ }
117
+ conditional?: {
118
+ field: Path<TFieldValues>
119
+ value: unknown
120
+ }
121
+ hidden?: boolean
122
+ labelPlacement?: 'stacked' | 'inline' | 'hidden'
123
+ wrapperClassName?: string
124
+ minDate?: Date
125
+ maxDate?: Date
126
+ disabledDates?: Array<Date | { from: Date; to: Date }>
127
+ numberOfMonths?: number
128
+ popoverSide?: 'top' | 'right' | 'bottom' | 'left'
129
+ showFooter?: boolean
130
+ cancelLabel?: string
131
+ applyLabel?: string
132
+ timePrecision?: 'hour' | 'minute' | 'second'
133
+ hourCycle?: 12 | 24
134
+ minuteStep?: number
135
+ secondStep?: number
136
+ fileMultiple?: boolean
137
+ fileMaxFiles?: number
138
+ fileAccept?: Accept
139
+ fileLayout?: FileUploaderLayout
140
+ fileWithDownload?: boolean
141
+ fileUploader?: (file: File, onProgress: (pct: number) => void) => Promise<Partial<FileRecord>>
142
+ fileOnUploadSuccess?: (file: FileRecord) => void
143
+ fileOnUploadError?: (file: FileRecord, error: unknown) => void
144
+ fileOnRemove?: (file: FileRecord) => void | Promise<void>
145
+ fileOnRetry?: (file: FileRecord) => void
146
+ fileOnRetryAll?: (files: FileRecord[]) => void
147
+ }
148
+
149
+ export interface FormBuilderSectionConfig<TFieldValues extends FieldValues = FieldValues> {
150
+ id?: string
151
+ title?: string
152
+ description?: string
153
+ fields?: Array<FormBuilderFieldConfig<TFieldValues>>
154
+ variant?: 'card' | 'separator' | 'plain'
155
+ className?: string
156
+ collapsible?: boolean
157
+ defaultCollapsed?: boolean
158
+ layout?: SectionLayout
159
+ grid?: SectionGridOptions
160
+ flex?: SectionFlexOptions
161
+ hidden?: boolean
162
+ tabs?: Array<{
163
+ id: string
164
+ label: React.ReactNode
165
+ sections: Array<FormBuilderSectionConfig<TFieldValues>>
166
+ className?: string
167
+ contentClassName?: string
168
+ }>
169
+ defaultTabId?: string
170
+ tabsListClassName?: string
171
+ tabsContentClassName?: string
172
+ }
173
+
174
+ export interface FormBuilderProps<TFieldValues extends FieldValues = FieldValues> {
175
+ sections: Array<FormBuilderSectionConfig<TFieldValues>>
176
+ schema?: z.ZodType<TFieldValues>
177
+ defaultValues?: DeepPartial<TFieldValues> | DefaultValues<TFieldValues> | null
178
+ onSubmit: (data: TFieldValues) => void | Promise<void>
179
+ onCancel?: () => void
180
+ onReset?: () => void
181
+ onFieldChange?: (
182
+ name: Path<TFieldValues> | string,
183
+ value: unknown,
184
+ allValues: TFieldValues
185
+ ) => void
186
+ submitLabel?: string
187
+ cancelLabel?: string
188
+ resetLabel?: string
189
+ isSubmitting?: boolean
190
+ className?: string
191
+ formClassName?: string
192
+ actionsClassName?: string
193
+ showActions?: boolean
194
+ customActions?: React.ReactNode
195
+ showActionsSeparator?: boolean
196
+ form?: UseFormReturn<TFieldValues>
197
+ }
198
+
199
+ // Re-export for external consumers that build custom section nodes
200
+ export type { SectionNode } from '../section/types'
@@ -1,4 +1,4 @@
1
- import { FormBuilderSectionConfig } from '../components/FormBuilder';
1
+ import type { FormBuilderSectionConfig } from '../types';
2
2
  import { createField } from './field-factories';
3
3
  import { createSection } from './section-factories';
4
4
  import { commonValidations } from './validations';
@@ -1,4 +1,4 @@
1
- import { FormBuilderFieldConfig } from '../components/FormBuilder';
1
+ import type { FormBuilderFieldConfig } from '../types';
2
2
  import { commonValidations } from './validations';
3
3
 
4
4
  // Field factory functions
@@ -7,7 +7,7 @@ export const createField = {
7
7
  name: string,
8
8
  label: string,
9
9
  options: Partial<FormBuilderFieldConfig> = {},
10
- ): FormBuilderFieldConfig => ({
10
+ ): FormBuilderFieldConfig<any> => ({
11
11
  name,
12
12
  label,
13
13
  type: 'text',
@@ -147,9 +147,9 @@ export const createField = {
147
147
  object: (
148
148
  name: string,
149
149
  label: string,
150
- fields: FormBuilderFieldConfig[],
151
- options: Partial<FormBuilderFieldConfig> = {},
152
- ): FormBuilderFieldConfig => ({
150
+ fields: FormBuilderFieldConfig<any>[],
151
+ options: Partial<FormBuilderFieldConfig<any>> = {},
152
+ ): FormBuilderFieldConfig<any> => ({
153
153
  name,
154
154
  label,
155
155
  type: 'object',
@@ -1,12 +1,12 @@
1
- import { FormBuilderFieldConfig, FormBuilderSectionConfig } from '../components/FormBuilder';
1
+ import type { FormBuilderFieldConfig, FormBuilderSectionConfig } from '../types';
2
2
 
3
3
  // Section factory functions
4
4
  export const createSection = {
5
5
  card: (
6
6
  title: string,
7
- fields: FormBuilderFieldConfig[],
8
- options: Partial<FormBuilderSectionConfig> = {},
9
- ): FormBuilderSectionConfig => ({
7
+ fields: FormBuilderFieldConfig<any>[],
8
+ options: Partial<FormBuilderSectionConfig<any>> = {},
9
+ ): FormBuilderSectionConfig<any> => ({
10
10
  title,
11
11
  fields,
12
12
  variant: 'card',
@@ -15,9 +15,9 @@ export const createSection = {
15
15
 
16
16
  separator: (
17
17
  title: string,
18
- fields: FormBuilderFieldConfig[],
19
- options: Partial<FormBuilderSectionConfig> = {},
20
- ): FormBuilderSectionConfig => ({
18
+ fields: FormBuilderFieldConfig<any>[],
19
+ options: Partial<FormBuilderSectionConfig<any>> = {},
20
+ ): FormBuilderSectionConfig<any> => ({
21
21
  title,
22
22
  fields,
23
23
  variant: 'separator',
@@ -25,9 +25,9 @@ export const createSection = {
25
25
  }),
26
26
 
27
27
  plain: (
28
- fields: FormBuilderFieldConfig[],
29
- options: Partial<FormBuilderSectionConfig> = {},
30
- ): FormBuilderSectionConfig => ({
28
+ fields: FormBuilderFieldConfig<any>[],
29
+ options: Partial<FormBuilderSectionConfig<any>> = {},
30
+ ): FormBuilderSectionConfig<any> => ({
31
31
  fields,
32
32
  variant: 'plain',
33
33
  ...options,