@k3-universe/react-kit 0.0.3 → 0.0.5

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 (54) hide show
  1. package/dist/kit/builder/form/components/FormBuilder.d.ts +20 -0
  2. package/dist/kit/builder/form/components/FormBuilder.d.ts.map +1 -1
  3. package/dist/kit/builder/form/components/FormBuilderField.d.ts +5 -5
  4. package/dist/kit/builder/form/components/FormBuilderField.d.ts.map +1 -1
  5. package/dist/kit/builder/form/components/fields/ArrayField.d.ts +3 -0
  6. package/dist/kit/builder/form/components/fields/ArrayField.d.ts.map +1 -0
  7. package/dist/kit/builder/form/components/fields/AutocompleteField.d.ts +3 -0
  8. package/dist/kit/builder/form/components/fields/AutocompleteField.d.ts.map +1 -0
  9. package/dist/kit/builder/form/components/fields/CheckboxField.d.ts +3 -0
  10. package/dist/kit/builder/form/components/fields/CheckboxField.d.ts.map +1 -0
  11. package/dist/kit/builder/form/components/fields/DateField.d.ts +3 -0
  12. package/dist/kit/builder/form/components/fields/DateField.d.ts.map +1 -0
  13. package/dist/kit/builder/form/components/fields/FileField.d.ts +3 -0
  14. package/dist/kit/builder/form/components/fields/FileField.d.ts.map +1 -0
  15. package/dist/kit/builder/form/components/fields/NumberField.d.ts +3 -0
  16. package/dist/kit/builder/form/components/fields/NumberField.d.ts.map +1 -0
  17. package/dist/kit/builder/form/components/fields/ObjectField.d.ts +3 -0
  18. package/dist/kit/builder/form/components/fields/ObjectField.d.ts.map +1 -0
  19. package/dist/kit/builder/form/components/fields/RadioField.d.ts +3 -0
  20. package/dist/kit/builder/form/components/fields/RadioField.d.ts.map +1 -0
  21. package/dist/kit/builder/form/components/fields/SelectField.d.ts +3 -0
  22. package/dist/kit/builder/form/components/fields/SelectField.d.ts.map +1 -0
  23. package/dist/kit/builder/form/components/fields/SwitchField.d.ts +3 -0
  24. package/dist/kit/builder/form/components/fields/SwitchField.d.ts.map +1 -0
  25. package/dist/kit/builder/form/components/fields/TextField.d.ts +3 -0
  26. package/dist/kit/builder/form/components/fields/TextField.d.ts.map +1 -0
  27. package/dist/kit/builder/form/components/fields/TextareaField.d.ts +3 -0
  28. package/dist/kit/builder/form/components/fields/TextareaField.d.ts.map +1 -0
  29. package/dist/kit/builder/form/components/fields/index.d.ts +14 -0
  30. package/dist/kit/builder/form/components/fields/index.d.ts.map +1 -0
  31. package/dist/kit/builder/form/components/fields/types.d.ts +14 -0
  32. package/dist/kit/builder/form/components/fields/types.d.ts.map +1 -0
  33. package/dist/kit/themes/clean-slate.css +1 -1
  34. package/dist/kit/themes/default.css +1 -1
  35. package/dist/kit/themes/minimal-modern.css +1 -1
  36. package/dist/kit/themes/spotify.css +1 -1
  37. package/package.json +1 -1
  38. package/src/kit/builder/form/components/FormBuilder.tsx +66 -16
  39. package/src/kit/builder/form/components/FormBuilderField.tsx +139 -387
  40. package/src/kit/builder/form/components/fields/ArrayField.tsx +222 -0
  41. package/src/kit/builder/form/components/fields/AutocompleteField.tsx +25 -0
  42. package/src/kit/builder/form/components/fields/CheckboxField.tsx +56 -0
  43. package/src/kit/builder/form/components/fields/DateField.tsx +15 -0
  44. package/src/kit/builder/form/components/fields/FileField.tsx +14 -0
  45. package/src/kit/builder/form/components/fields/NumberField.tsx +15 -0
  46. package/src/kit/builder/form/components/fields/ObjectField.tsx +30 -0
  47. package/src/kit/builder/form/components/fields/RadioField.tsx +29 -0
  48. package/src/kit/builder/form/components/fields/SelectField.tsx +31 -0
  49. package/src/kit/builder/form/components/fields/SwitchField.tsx +56 -0
  50. package/src/kit/builder/form/components/fields/TextField.tsx +18 -0
  51. package/src/kit/builder/form/components/fields/TextareaField.tsx +15 -0
  52. package/src/kit/builder/form/components/fields/index.ts +13 -0
  53. package/src/kit/builder/form/components/fields/types.ts +14 -0
  54. package/src/stories/kit/builder/Form.ArrayLayouts.stories.tsx +153 -0
@@ -1,31 +1,33 @@
1
1
  import { useCallback } from 'react';
2
- import { Control, useController, useFieldArray } from 'react-hook-form';
2
+ import { Control, FieldValues, useController } from 'react-hook-form';
3
3
  import { cn } from '../../../../shadcn/lib/utils';
4
- import { Button } from '../../../../shadcn/ui/button';
5
- import { Input } from '../../../../shadcn/ui/input';
6
- import { Textarea } from '../../../../shadcn/ui/textarea';
7
- import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../../../shadcn/ui/select';
8
- import { Checkbox } from '../../../../shadcn/ui/checkbox';
9
- import { Switch } from '../../../../shadcn/ui/switch';
10
- import { RadioGroup, RadioGroupItem } from '../../../../shadcn/ui/radio-group';
11
4
  import { Label } from '../../../../shadcn/ui/label';
12
- import { Card, CardContent, CardHeader, CardTitle } from '../../../../shadcn/ui/card';
13
- import { Plus, Trash2, GripVertical } from 'lucide-react';
14
5
  import { FormBuilderFieldConfig } from './FormBuilder';
15
- import { Autocomplete } from '../../../components/autocomplete/Autocomplete';
16
- import type { AutocompleteOption } from '../../../components/autocomplete/types';
6
+ import {
7
+ AutocompleteField,
8
+ TextField,
9
+ NumberField,
10
+ TextareaField,
11
+ SelectField,
12
+ CheckboxField,
13
+ SwitchField,
14
+ RadioField,
15
+ DateField,
16
+ FileField,
17
+ ObjectField,
18
+ ArrayField,
19
+ } from './fields';
17
20
 
18
21
  export interface FormBuilderFieldProps {
19
22
  field: FormBuilderFieldConfig;
20
- control: Control<any>;
21
- onChange?: (value: any) => void;
22
- onFieldChange?: (name: string, value: any, allValues: Record<string, any>) => void;
23
+ control: Control<FieldValues>;
24
+ onChange?: (value: unknown) => void;
25
+ onFieldChange?: (name: string, value: unknown, allValues: Record<string, unknown>) => void;
23
26
  parentPath?: string;
24
27
  }
25
28
 
26
- export function FormBuilderField({ field, control, onChange, onFieldChange, parentPath }: FormBuilderFieldProps) {
29
+ export function FormBuilderField({ field, control, onChange, parentPath }: FormBuilderFieldProps) {
27
30
  const fieldPath = parentPath ? `${parentPath}.${field.name}` : field.name;
28
- const NULL_SENTINEL = '__NULL__';
29
31
 
30
32
  const {
31
33
  field: controllerField,
@@ -36,420 +38,170 @@ export function FormBuilderField({ field, control, onChange, onFieldChange, pare
36
38
  disabled: field.disabled,
37
39
  });
38
40
 
39
- const handleChange = useCallback((value: any) => {
41
+ const handleChange = useCallback((value: unknown) => {
40
42
  controllerField.onChange(value);
41
43
  onChange?.(value);
42
44
  }, [controllerField.onChange, onChange]);
45
+ const baseClassName = cn(
46
+ error && 'border-destructive focus-visible:ring-destructive',
47
+ field.className,
48
+ );
43
49
 
44
- const renderBasicField = () => {
45
- const baseProps = {
46
- id: fieldPath,
47
- disabled: field.disabled,
48
- placeholder: field.placeholder,
49
- className: cn(
50
- error && 'border-destructive focus-visible:ring-destructive',
51
- field.className,
52
- ),
53
- };
54
-
50
+ const renderField = () => {
55
51
  switch (field.type) {
56
- case 'autocomplete': {
57
- const options: AutocompleteOption[] = (field.options ?? [])
58
- .filter((o): o is { label: string; value: string | number } => o.value !== null && o.value !== undefined)
59
- .map(o => ({ label: o.label, value: o.value }));
52
+ case 'autocomplete':
60
53
  return (
61
- <Autocomplete
62
- mode={field.autocompleteMode ?? 'client'}
63
- options={options}
64
- fetcher={field.fetcher}
65
- pageSize={field.pageSize}
66
- value={(controllerField.value as string | number | null) ?? null}
67
- onChange={(val) => handleChange(val)}
68
- placeholder={field.placeholder}
69
- searchPlaceholder={field.searchPlaceholder}
70
- renderOption={field.renderOption}
71
- disabled={field.disabled}
72
- className={baseProps.className}
54
+ <AutocompleteField
55
+ field={field}
56
+ control={control}
57
+ fieldPath={fieldPath}
58
+ value={controllerField.value}
59
+ onChange={handleChange}
60
+ className={baseClassName}
73
61
  />
74
62
  );
75
- }
76
63
  case 'text':
77
64
  case 'email':
78
65
  case 'password':
79
66
  return (
80
- <Input
81
- {...baseProps}
82
- type={field.type}
83
- value={controllerField.value || ''}
84
- onChange={e => handleChange(e.target.value)}
67
+ <TextField
68
+ field={field}
69
+ control={control}
70
+ fieldPath={fieldPath}
71
+ value={controllerField.value}
72
+ onChange={handleChange}
73
+ className={baseClassName}
85
74
  />
86
75
  );
87
-
88
76
  case 'number':
89
77
  return (
90
- <Input
91
- {...baseProps}
92
- type="number"
93
- value={controllerField.value || ''}
94
- onChange={e => handleChange(Number(e.target.value))}
78
+ <NumberField
79
+ field={field}
80
+ control={control}
81
+ fieldPath={fieldPath}
82
+ value={controllerField.value}
83
+ onChange={handleChange}
84
+ className={baseClassName}
95
85
  />
96
86
  );
97
-
98
87
  case 'textarea':
99
88
  return (
100
- <Textarea
101
- {...baseProps}
102
- value={controllerField.value || ''}
103
- onChange={e => handleChange(e.target.value)}
104
- rows={4}
89
+ <TextareaField
90
+ field={field}
91
+ control={control}
92
+ fieldPath={fieldPath}
93
+ value={controllerField.value}
94
+ onChange={handleChange}
95
+ className={baseClassName}
105
96
  />
106
97
  );
107
-
108
- case 'select': {
109
- const toUiValue = (val: unknown) => (val === null || val === undefined ? NULL_SENTINEL : String(val));
110
- const fromUiValue = (val: string) => {
111
- const match = field.options?.find(opt => toUiValue(opt.value) === val);
112
- return match ? match.value : null;
113
- };
98
+ case 'select':
114
99
  return (
115
- <Select
116
- value={toUiValue(controllerField.value)}
117
- onValueChange={val => handleChange(fromUiValue(val))}
118
- disabled={field.disabled}
119
- >
120
- <SelectTrigger className={baseProps.className}>
121
- <SelectValue placeholder={field.placeholder} />
122
- </SelectTrigger>
123
- <SelectContent>
124
- {field.options?.map(option => (
125
- <SelectItem key={toUiValue(option.value)} value={toUiValue(option.value)}>
126
- {option.label}
127
- </SelectItem>
128
- ))}
129
- </SelectContent>
130
- </Select>
100
+ <SelectField
101
+ field={field}
102
+ control={control}
103
+ fieldPath={fieldPath}
104
+ value={controllerField.value}
105
+ onChange={handleChange}
106
+ className={baseClassName}
107
+ />
131
108
  );
132
- }
133
-
134
- case 'checkbox': {
135
- const placement = field.labelPlacement ?? 'inline';
136
- if (placement === 'stacked') {
137
- const labelId = `${fieldPath}-label`;
138
- return (
139
- <div className="space-y-2">
140
- <Label id={labelId} className="text-sm font-medium">
141
- {field.label}
142
- {field.required && <span className="text-destructive ml-1">*</span>}
143
- </Label>
144
- <Checkbox
145
- aria-labelledby={labelId}
146
- id={fieldPath}
147
- checked={controllerField.value || false}
148
- onCheckedChange={handleChange}
149
- disabled={field.disabled}
150
- className={cn(error && 'border-destructive', field.className)}
151
- />
152
- </div>
153
- );
154
- }
155
- if (placement === 'hidden') {
156
- return (
157
- <Checkbox
158
- id={fieldPath}
159
- checked={controllerField.value || false}
160
- onCheckedChange={handleChange}
161
- disabled={field.disabled}
162
- className={cn(error && 'border-destructive', field.className)}
163
- />
164
- );
165
- }
166
- // inline (default)
109
+ case 'checkbox':
167
110
  return (
168
- <div className="flex items-center space-x-2">
169
- <Checkbox
170
- id={fieldPath}
171
- checked={controllerField.value || false}
172
- onCheckedChange={handleChange}
173
- disabled={field.disabled}
174
- className={cn(error && 'border-destructive', field.className)}
175
- />
176
- <Label htmlFor={fieldPath} className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
177
- {field.label}
178
- {field.required && <span className="text-destructive ml-1">*</span>}
179
- </Label>
180
- </div>
111
+ <CheckboxField
112
+ field={field}
113
+ control={control}
114
+ fieldPath={fieldPath}
115
+ value={controllerField.value}
116
+ onChange={handleChange}
117
+ className={baseClassName}
118
+ />
181
119
  );
182
- }
183
-
184
- case 'switch': {
185
- const placement = field.labelPlacement ?? 'inline';
186
- if (placement === 'stacked') {
187
- const labelId = `${fieldPath}-label`;
188
- return (
189
- <div className="space-y-2">
190
- <Label id={labelId} className="text-sm font-medium">
191
- {field.label}
192
- {field.required && <span className="text-destructive ml-1">*</span>}
193
- </Label>
194
- <Switch
195
- aria-labelledby={labelId}
196
- id={fieldPath}
197
- checked={controllerField.value || false}
198
- onCheckedChange={handleChange}
199
- disabled={field.disabled}
200
- className={cn(error && 'border-destructive', field.className)}
201
- />
202
- </div>
203
- );
204
- }
205
- if (placement === 'hidden') {
206
- return (
207
- <Switch
208
- id={fieldPath}
209
- checked={controllerField.value || false}
210
- onCheckedChange={handleChange}
211
- disabled={field.disabled}
212
- className={cn(error && 'border-destructive', field.className)}
213
- />
214
- );
215
- }
216
- // inline (default)
120
+ case 'switch':
217
121
  return (
218
- <div className="flex items-center space-x-2">
219
- <Switch
220
- id={fieldPath}
221
- checked={controllerField.value || false}
222
- onCheckedChange={handleChange}
223
- disabled={field.disabled}
224
- className={cn(error && 'border-destructive', field.className)}
225
- />
226
- <Label htmlFor={fieldPath} className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70">
227
- {field.label}
228
- {field.required && <span className="text-destructive ml-1">*</span>}
229
- </Label>
230
- </div>
122
+ <SwitchField
123
+ field={field}
124
+ control={control}
125
+ fieldPath={fieldPath}
126
+ value={controllerField.value}
127
+ onChange={handleChange}
128
+ className={baseClassName}
129
+ />
231
130
  );
232
- }
233
-
234
- case 'radio': {
235
- const toUiValue = (val: unknown) => (val === null || val === undefined ? NULL_SENTINEL : String(val));
236
- const fromUiValue = (val: string) => {
237
- const match = field.options?.find(opt => toUiValue(opt.value) === val);
238
- return match ? match.value : null;
239
- };
131
+ case 'radio':
240
132
  return (
241
- <RadioGroup
242
- value={toUiValue(controllerField.value)}
243
- onValueChange={val => handleChange(fromUiValue(val))}
244
- disabled={field.disabled}
245
- className={field.className}
246
- >
247
- {field.options?.map(option => (
248
- <div key={toUiValue(option.value)} className="flex items-center space-x-2">
249
- <RadioGroupItem value={toUiValue(option.value)} id={`${fieldPath}-${toUiValue(option.value)}`} />
250
- <Label htmlFor={`${fieldPath}-${toUiValue(option.value)}`}>{option.label}</Label>
251
- </div>
252
- ))}
253
- </RadioGroup>
133
+ <RadioField
134
+ field={field}
135
+ control={control}
136
+ fieldPath={fieldPath}
137
+ value={controllerField.value}
138
+ onChange={handleChange}
139
+ className={baseClassName}
140
+ />
254
141
  );
255
- }
256
-
257
142
  case 'date':
258
143
  return (
259
- <Input
260
- {...baseProps}
261
- type="date"
262
- value={controllerField.value ? new Date(controllerField.value).toISOString().split('T')[0] : ''}
263
- onChange={e => handleChange(e.target.value ? new Date(e.target.value) : null)}
144
+ <DateField
145
+ field={field}
146
+ control={control}
147
+ fieldPath={fieldPath}
148
+ value={controllerField.value}
149
+ onChange={handleChange}
150
+ className={baseClassName}
264
151
  />
265
152
  );
266
-
267
153
  case 'file':
268
154
  return (
269
- <Input
270
- {...baseProps}
271
- type="file"
272
- onChange={e => handleChange(e.target.files?.[0] || null)}
155
+ <FileField
156
+ field={field}
157
+ control={control}
158
+ fieldPath={fieldPath}
159
+ value={controllerField.value}
160
+ onChange={handleChange}
161
+ className={baseClassName}
162
+ />
163
+ );
164
+ case 'object':
165
+ return (
166
+ <ObjectField
167
+ field={field}
168
+ control={control}
169
+ fieldPath={fieldPath}
170
+ value={controllerField.value}
171
+ onChange={handleChange}
172
+ className={baseClassName}
173
+ />
174
+ );
175
+ case 'array':
176
+ return (
177
+ <ArrayField
178
+ field={field}
179
+ control={control}
180
+ fieldPath={fieldPath}
181
+ value={controllerField.value}
182
+ onChange={handleChange}
183
+ className={baseClassName}
273
184
  />
274
185
  );
275
-
276
186
  default:
277
187
  return (
278
- <Input
279
- {...baseProps}
280
- value={controllerField.value || ''}
281
- onChange={e => handleChange(e.target.value)}
188
+ <TextField
189
+ field={field}
190
+ control={control}
191
+ fieldPath={fieldPath}
192
+ value={controllerField.value}
193
+ onChange={handleChange}
194
+ className={baseClassName}
282
195
  />
283
196
  );
284
197
  }
285
198
  };
286
199
 
287
- const renderObjectField = () => {
288
- if (!field.fields) return null;
289
-
290
- return (
291
- <Card className={field.className}>
292
- <CardHeader className="pb-3">
293
- <CardTitle className="text-base">{field.label}</CardTitle>
294
- {field.description && (
295
- <p className="text-sm text-muted-foreground">{field.description}</p>
296
- )}
297
- </CardHeader>
298
- <CardContent className="space-y-4">
299
- <div className="grid gap-4 md:grid-cols-2">
300
- {field.fields.map(subField => (
301
- <FormBuilderField
302
- key={subField.name}
303
- field={subField}
304
- control={control}
305
- parentPath={fieldPath}
306
- onChange={onChange}
307
- onFieldChange={onFieldChange}
308
- />
309
- ))}
310
- </div>
311
- </CardContent>
312
- </Card>
313
- );
314
- };
315
-
316
- const renderArrayField = () => {
317
- const { fields, append, remove } = useFieldArray({
318
- control,
319
- name: fieldPath,
320
- });
321
-
322
- const addItem = () => {
323
- if (field.fields && field.fields.length === 1) {
324
- // Single field array (e.g., array of strings)
325
- const defaultValue = field.fields[0].defaultValue || '';
326
- append(defaultValue);
327
- }
328
- else if (field.fields) {
329
- // Object array
330
- const defaultObject: Record<string, any> = {};
331
- field.fields.forEach((subField) => {
332
- defaultObject[subField.name] = subField.defaultValue || '';
333
- });
334
- append(defaultObject);
335
- }
336
- else {
337
- append('');
338
- }
339
- };
340
-
341
- return (
342
- <Card className={field.className}>
343
- <CardHeader className="pb-3">
344
- <div className="flex items-center justify-between">
345
- <div>
346
- <CardTitle className="text-base">{field.label}</CardTitle>
347
- {field.description && (
348
- <p className="text-sm text-muted-foreground">{field.description}</p>
349
- )}
350
- </div>
351
- <Button
352
- type="button"
353
- variant="outline"
354
- size="sm"
355
- onClick={addItem}
356
- disabled={field.disabled}
357
- >
358
- <Plus className="h-4 w-4 mr-1" />
359
- Add Item
360
- </Button>
361
- </div>
362
- </CardHeader>
363
- <CardContent className="space-y-4">
364
- {fields.length === 0 ? (
365
- <p className="text-sm text-muted-foreground text-center py-4">
366
- No items added yet. Click "Add Item" to get started.
367
- </p>
368
- ) : (
369
- fields.map((item, index) => (
370
- <Card key={item.id} className="relative">
371
- <CardHeader className="pb-3">
372
- <div className="flex items-center justify-between">
373
- <div className="flex items-center gap-2">
374
- <GripVertical className="h-4 w-4 text-muted-foreground" />
375
- <span className="text-sm font-medium">
376
- Item
377
- {index + 1}
378
- </span>
379
- </div>
380
- <Button
381
- type="button"
382
- variant="ghost"
383
- size="sm"
384
- onClick={() => remove(index)}
385
- disabled={field.disabled}
386
- >
387
- <Trash2 className="h-4 w-4" />
388
- </Button>
389
- </div>
390
- </CardHeader>
391
- <CardContent>
392
- {field.fields && field.fields.length === 1 ? (
393
- // Single field array
394
- <FormBuilderField
395
- field={{
396
- ...field.fields[0],
397
- name: field.fields[0].name,
398
- label: field.fields[0].label || 'Value',
399
- }}
400
- control={control}
401
- parentPath={`${fieldPath}.${index}`}
402
- onChange={onChange}
403
- />
404
- ) : field.fields ? (
405
- // Object array
406
- <div className="grid gap-4 md:grid-cols-2">
407
- {field.fields.map(subField => (
408
- <FormBuilderField
409
- key={subField.name}
410
- field={subField}
411
- control={control}
412
- parentPath={`${fieldPath}.${index}`}
413
- onChange={onChange}
414
- onFieldChange={onFieldChange}
415
- />
416
- ))}
417
- </div>
418
- ) : (
419
- // Fallback for arrays without field definitions
420
- <Input
421
- value={controllerField.value?.[index] || ''}
422
- onChange={(e) => {
423
- const newArray = [...(controllerField.value || [])];
424
- newArray[index] = e.target.value;
425
- handleChange(newArray);
426
- }}
427
- placeholder={`Item ${index + 1}`}
428
- disabled={field.disabled}
429
- />
430
- )}
431
- </CardContent>
432
- </Card>
433
- ))
434
- )}
435
- </CardContent>
436
- </Card>
437
- );
438
- };
439
-
440
- if (field.type === 'object') {
441
- return renderObjectField();
442
- }
443
-
444
- if (field.type === 'array') {
445
- return renderArrayField();
446
- }
447
-
448
- // For checkbox/switch, label may be inline/stacked/hidden handled inside renderBasicField
200
+ // For checkbox/switch, label is handled inside the specific field component
449
201
  if (field.type === 'checkbox' || field.type === 'switch') {
450
202
  return (
451
203
  <div className={cn('space-y-2', field.gridCols && `md:col-span-${field.gridCols}`)}>
452
- {renderBasicField()}
204
+ {renderField()}
453
205
  {field.description && (
454
206
  <p className="text-sm text-muted-foreground">{field.description}</p>
455
207
  )}
@@ -462,10 +214,10 @@ export function FormBuilderField({ field, control, onChange, onFieldChange, pare
462
214
 
463
215
  // Non-checkbox fields: support labelPlacement
464
216
  const placement = field.labelPlacement ?? 'stacked';
465
- if (placement === 'hidden') {
217
+ if (placement === 'hidden' || field.type === 'array') {
466
218
  return (
467
219
  <div className={cn('space-y-2', field.gridCols && `md:col-span-${field.gridCols}`)}>
468
- {renderBasicField()}
220
+ {renderField()}
469
221
  {field.description && (
470
222
  <p className="text-sm text-muted-foreground">{field.description}</p>
471
223
  )}
@@ -484,7 +236,7 @@ export function FormBuilderField({ field, control, onChange, onFieldChange, pare
484
236
  {field.label}
485
237
  {field.required && <span className="text-destructive ml-1">*</span>}
486
238
  </Label>
487
- {renderBasicField()}
239
+ {renderField()}
488
240
  </div>
489
241
  {field.description && (
490
242
  <p className="text-sm text-muted-foreground">{field.description}</p>
@@ -503,7 +255,7 @@ export function FormBuilderField({ field, control, onChange, onFieldChange, pare
503
255
  {field.label}
504
256
  {field.required && <span className="text-destructive ml-1">*</span>}
505
257
  </Label>
506
- {renderBasicField()}
258
+ {renderField()}
507
259
  {field.description && (
508
260
  <p className="text-sm text-muted-foreground">{field.description}</p>
509
261
  )}