@connect-soft/form-generator 1.1.0-alpha5 → 1.1.0-alpha6

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.
package/README.md CHANGED
@@ -16,7 +16,8 @@
16
16
  - **Imperative API**: Control form via ref (`setValues`, `reset`, `submit`, etc.)
17
17
  - **Flexible**: Register custom field components with a simple API
18
18
  - **Validation**: Built-in Zod validation support
19
- - **Layouts**: Support for sections and multi-column layouts
19
+ - **Array Fields**: Repeatable field groups with `useFieldArray` integration
20
+ - **Custom Layouts**: Full control over form layout via render props
20
21
  - **Lightweight**: No UI dependencies, minimal footprint
21
22
  - **HTML Fallbacks**: Works out of the box with native HTML inputs
22
23
 
@@ -69,54 +70,56 @@ function MyForm() {
69
70
 
70
71
  ## Registering Custom Components
71
72
 
72
- Register your own UI components to replace the HTML fallbacks:
73
+ Register your own UI components to replace the HTML fallbacks. Field components are fully responsible for rendering their own label, description, and error messages:
73
74
 
74
75
  ```typescript
75
- import { registerFields, registerFormComponents } from '@connect-soft/form-generator';
76
+ import { registerFields, registerFormComponent } from '@connect-soft/form-generator';
76
77
  import { Input } from './ui/input';
77
78
  import { Label } from './ui/label';
78
79
  import { Checkbox } from './ui/checkbox';
79
80
  import { Button } from './ui/button';
80
81
 
81
- // Register field components
82
+ // Register field components - they handle their own rendering
82
83
  registerFields({
83
- text: ({ field, formField }) => (
84
- <Input
85
- {...formField}
86
- type={field.fieldType || 'text'}
87
- placeholder={field.placeholder}
88
- disabled={field.disabled}
89
- />
84
+ text: ({ field, formField, fieldState }) => (
85
+ <div className="form-field">
86
+ {field.label && <Label>{field.label}{field.required && ' *'}</Label>}
87
+ <Input
88
+ {...formField}
89
+ type={field.fieldType || 'text'}
90
+ placeholder={field.placeholder}
91
+ disabled={field.disabled}
92
+ />
93
+ {field.description && <p className="text-sm text-muted">{field.description}</p>}
94
+ {fieldState.error && <span className="text-red-500 text-sm">{fieldState.error.message}</span>}
95
+ </div>
90
96
  ),
91
- number: ({ field, formField }) => (
92
- <Input
93
- {...formField}
94
- type="number"
95
- min={field.min}
96
- max={field.max}
97
- />
97
+ number: ({ field, formField, fieldState }) => (
98
+ <div className="form-field">
99
+ {field.label && <Label>{field.label}</Label>}
100
+ <Input
101
+ {...formField}
102
+ type="number"
103
+ min={field.min}
104
+ max={field.max}
105
+ />
106
+ {fieldState.error && <span className="text-red-500 text-sm">{fieldState.error.message}</span>}
107
+ </div>
98
108
  ),
99
- checkbox: {
100
- component: ({ field, formField }) => (
109
+ checkbox: ({ field, formField }) => (
110
+ <div className="flex items-center gap-2">
101
111
  <Checkbox
102
112
  checked={formField.value}
103
113
  onCheckedChange={formField.onChange}
104
114
  disabled={field.disabled}
105
115
  />
106
- ),
107
- options: {
108
- className: 'flex items-center gap-2',
109
- }
110
- },
116
+ {field.label && <Label>{field.label}</Label>}
117
+ </div>
118
+ ),
111
119
  });
112
120
 
113
- // Register form wrapper components
114
- registerFormComponents({
115
- FormItem: ({ children, className }) => <div className={className}>{children}</div>,
116
- FormLabel: Label,
117
- FormMessage: ({ children }) => <span className="text-red-500 text-sm">{children}</span>,
118
- SubmitButton: Button,
119
- });
121
+ // Register the submit button component
122
+ registerFormComponent('SubmitButton', Button);
120
123
  ```
121
124
 
122
125
  ---
@@ -230,40 +233,96 @@ const schema = z.object({
230
233
 
231
234
  ---
232
235
 
233
- ## Layouts
236
+ ## Array Fields
234
237
 
235
- Organize fields with sections and columns:
238
+ Create repeatable field groups with `useFieldArray` integration:
236
239
 
237
240
  ```typescript
238
241
  const fields = [
242
+ { type: 'text', name: 'name', label: 'Name' },
239
243
  {
240
- type: 'section',
241
- title: 'Personal Information',
242
- children: [
243
- { type: 'text', name: 'firstName', label: 'First Name' },
244
- { type: 'text', name: 'lastName', label: 'Last Name' },
245
- ],
246
- },
247
- {
248
- type: 'columns',
249
- columns: [
250
- {
251
- width: '1',
252
- children: [
253
- { type: 'text', name: 'city', label: 'City' },
254
- ],
255
- },
256
- {
257
- width: '1',
258
- children: [
259
- { type: 'text', name: 'zip', label: 'ZIP Code' },
260
- ],
261
- },
244
+ type: 'array',
245
+ name: 'contacts',
246
+ label: 'Contacts',
247
+ fields: [
248
+ { type: 'text', name: 'email', label: 'Email' },
249
+ { type: 'text', name: 'phone', label: 'Phone' },
262
250
  ],
251
+ minItems: 1,
252
+ maxItems: 5,
263
253
  },
264
- ];
254
+ ] as const;
255
+ ```
256
+
257
+ ### Default Array Rendering
258
+
259
+ Array fields render automatically with add/remove functionality:
260
+
261
+ ```typescript
262
+ <FormGenerator
263
+ fields={fields}
264
+ onSubmit={(values) => {
265
+ console.log(values.contacts); // Array<{ email: string, phone: string }>
266
+ }}
267
+ />
268
+ ```
269
+
270
+ ### Custom Array Rendering with useArrayField
271
+
272
+ For full control, use the `useArrayField` hook in a custom layout:
273
+
274
+ ```typescript
275
+ import { FormGenerator, useArrayField } from '@connect-soft/form-generator';
276
+
277
+ <FormGenerator fields={fields} onSubmit={handleSubmit}>
278
+ {({ fields, arrays, buttons }) => {
279
+ const contacts = useArrayField(arrays.contacts.field);
280
+
281
+ return (
282
+ <div>
283
+ {fields.name}
284
+
285
+ <h3>Contacts</h3>
286
+ {contacts.items.map(({ id, index, remove, fields: itemFields }) => (
287
+ <div key={id} className="contact-row">
288
+ {itemFields.email}
289
+ {itemFields.phone}
290
+ {contacts.canRemove && (
291
+ <button type="button" onClick={remove}>Remove</button>
292
+ )}
293
+ </div>
294
+ ))}
295
+
296
+ {contacts.canAppend && (
297
+ <button type="button" onClick={contacts.append}>
298
+ Add Contact
299
+ </button>
300
+ )}
301
+
302
+ {buttons.submit}
303
+ </div>
304
+ );
305
+ }}
306
+ </FormGenerator>
265
307
  ```
266
308
 
309
+ ### useArrayField Return Values
310
+
311
+ | Property | Type | Description |
312
+ |----------|------|-------------|
313
+ | `items` | `Array<{ id, index }>` | Array items with unique ids |
314
+ | `append` | `() => void` | Add new empty item |
315
+ | `appendWith` | `(values) => void` | Add item with values |
316
+ | `prepend` | `() => void` | Add item at beginning |
317
+ | `remove` | `(index) => void` | Remove item at index |
318
+ | `move` | `(from, to) => void` | Move item |
319
+ | `swap` | `(a, b) => void` | Swap two items |
320
+ | `insert` | `(index, values?) => void` | Insert at index |
321
+ | `canAppend` | `boolean` | Can add more items (respects maxItems) |
322
+ | `canRemove` | `boolean` | Can remove items (respects minItems) |
323
+ | `renderField` | `(index, name) => ReactElement` | Render single field |
324
+ | `renderItem` | `(index) => Record<string, ReactElement>` | Render all fields for item |
325
+
267
326
  ---
268
327
 
269
328
  ## Custom Layouts
@@ -297,7 +356,7 @@ The render function receives:
297
356
  | Property | Type | Description |
298
357
  |----------|------|-------------|
299
358
  | `fields` | `TemplateFields` | Pre-rendered fields (see below) |
300
- | `layouts` | `TemplateLayouts` | Pre-rendered named layouts |
359
+ | `arrays` | `Record<string, TemplateArrayField>` | Array field definitions (use with `useArrayField`) |
301
360
  | `buttons` | `{ submit, reset? }` | Pre-rendered buttons |
302
361
  | `title` | `string` | Form title prop |
303
362
  | `description` | `string` | Form description prop |
@@ -306,7 +365,6 @@ The render function receives:
306
365
  | `isValid` | `boolean` | Form validity state |
307
366
  | `isDirty` | `boolean` | Form dirty state |
308
367
  | `renderField` | `function` | Manual field renderer |
309
- | `renderLayout` | `function` | Manual layout renderer |
310
368
 
311
369
  ### Fields Object
312
370