@rilaykit/forms 2.0.1 → 4.0.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.
package/dist/index.d.mts CHANGED
@@ -5,118 +5,504 @@ export { createZodValidator, ril } from '@rilaykit/core';
5
5
  import * as React$1 from 'react';
6
6
  import React__default from 'react';
7
7
 
8
- interface FieldConfig {
9
- id: string;
10
- type: string;
11
- props?: Record<string, any>;
8
+ /**
9
+ * Configuration for a form field with type safety
10
+ *
11
+ * @template C - The component configuration map
12
+ * @template T - The specific component type key
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const fieldConfig: FieldConfig<MyComponents, 'text'> = {
17
+ * type: 'text',
18
+ * props: { placeholder: 'Enter your name' },
19
+ * validation: { required: true }
20
+ * };
21
+ * ```
22
+ */
23
+ type FieldConfig<C extends Record<string, any>, T extends keyof C> = {
24
+ /** Unique identifier for the field. Auto-generated if not provided */
25
+ id?: string;
26
+ /** Component type from the registered components */
27
+ type: T;
28
+ /** Component-specific properties */
29
+ props?: Partial<C[T]>;
30
+ /** Validation rules for the field */
12
31
  validation?: ValidationConfig;
32
+ /** Conditional display logic */
13
33
  conditional?: ConditionalConfig;
14
- }
34
+ };
35
+ /**
36
+ * Options for configuring row layout and appearance
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const rowOptions: RowOptions = {
41
+ * spacing: 'loose',
42
+ * alignment: 'center'
43
+ * };
44
+ * ```
45
+ */
15
46
  interface RowOptions {
47
+ /** Spacing between fields in the row */
16
48
  spacing?: 'tight' | 'normal' | 'loose';
49
+ /** Vertical alignment of fields in the row */
17
50
  alignment?: 'start' | 'center' | 'end' | 'stretch';
18
51
  }
19
52
  /**
20
- * Form builder class for creating form configurations
21
- * Simplified API with matrix support and auto-build capability
53
+ * Form builder class for creating type-safe form configurations
54
+ *
55
+ * This class provides a fluent API for building forms with automatic validation,
56
+ * type safety, and flexible layout options. It manages field registration,
57
+ * row organization, and form configuration generation.
58
+ *
59
+ * @template C - Component configuration map defining available component types
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * // Create a form builder with typed components
64
+ * const formBuilder = form.create(rilConfig, 'user-registration')
65
+ * .add({ type: 'text', props: { label: 'Name' } })
66
+ * .add(
67
+ * { type: 'email', props: { label: 'Email' } },
68
+ * { type: 'password', props: { label: 'Password' } }
69
+ * )
70
+ * .build();
71
+ * ```
72
+ *
73
+ * @remarks
74
+ * - Supports up to 3 fields per row for optimal layout
75
+ * - Automatically generates unique IDs for fields and rows
76
+ * - Provides comprehensive validation before building
77
+ * - Maintains type safety throughout the building process
22
78
  */
23
- declare class form {
79
+ declare class form<C extends Record<string, any> = Record<string, never>> {
80
+ /** The ril configuration instance containing component definitions */
24
81
  private config;
82
+ /** Array of form rows containing field configurations */
25
83
  private rows;
84
+ /** Unique identifier for this form */
26
85
  private formId;
27
- private rowCounter;
28
- constructor(config: ril, formId?: string);
29
- static create(config: ril, formId?: string): form;
86
+ /** Generator for creating unique IDs */
87
+ private idGenerator;
30
88
  /**
31
- * Helper method to create a FormFieldConfig from a FieldConfig
89
+ * Creates a new form builder instance
90
+ *
91
+ * @param config - The ril configuration containing component definitions
92
+ * @param formId - Optional unique identifier for the form. Auto-generated if not provided
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const builder = new form(rilConfig, 'my-form');
97
+ * ```
32
98
  */
33
- private createFormField;
99
+ constructor(config: ril<C>, formId?: string);
34
100
  /**
35
- * Helper method to create a row with validation
101
+ * Static factory method to create a new form builder
102
+ *
103
+ * @template Cm - Component configuration map
104
+ * @param config - The ril configuration instance
105
+ * @param formId - Optional form identifier
106
+ * @returns A new form builder instance
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const builder = form.create(rilConfig, 'registration-form');
111
+ * ```
36
112
  */
37
- private createRow;
113
+ static create<Cm extends Record<string, any> = Record<string, never>>(config: ril<Cm>, formId?: string): form<Cm>;
38
114
  /**
39
- * Add a single field using simplified FieldConfig object
115
+ * Converts a FieldConfig to a FormFieldConfig with proper validation
116
+ *
117
+ * This internal method handles the transformation from the builder's field
118
+ * configuration format to the final form field configuration, including
119
+ * component lookup, prop merging, and ID generation.
120
+ *
121
+ * @template T - The component type
122
+ * @param fieldConfig - The field configuration to convert
123
+ * @returns A complete FormFieldConfig ready for use
124
+ * @throws Error if the specified component type is not registered
125
+ *
126
+ * @internal
40
127
  */
41
- addField(fieldConfig: FieldConfig): this;
128
+ private createFormField;
129
+ /**
130
+ * Creates a form row with the specified fields and options
131
+ *
132
+ * This internal method handles row creation with validation of field limits,
133
+ * proper spacing, and alignment configuration.
134
+ *
135
+ * @template T - The component type
136
+ * @param fieldConfigs - Array of field configurations for the row
137
+ * @param rowOptions - Optional row layout configuration
138
+ * @returns A complete FormFieldRow configuration
139
+ * @throws Error if no fields provided or more than 3 fields specified
140
+ *
141
+ * @internal
142
+ */
143
+ private createRow;
42
144
  /**
43
- * Add multiple fields on the same row (max 3 fields)
145
+ * Universal method for adding fields to the form
146
+ *
147
+ * This is the primary method for adding fields to your form. It supports multiple
148
+ * usage patterns for maximum flexibility:
149
+ *
150
+ * - Single field: Creates a new row with one field
151
+ * - Multiple fields (≤3): Creates one row with all fields
152
+ * - Multiple fields (>3): Creates separate rows for each field
153
+ * - Array with options: Explicit control over row configuration
154
+ *
155
+ * @template T - The component type
156
+ * @param fields - Field configurations (variadic or array)
157
+ * @returns The form builder instance for method chaining
158
+ * @throws Error if no fields provided or invalid configuration
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * // Single field on its own row
163
+ * builder.add({ type: 'text', props: { label: 'Name' } });
164
+ *
165
+ * // Multiple fields on same row (max 3)
166
+ * builder.add(
167
+ * { type: 'text', props: { label: 'First Name' } },
168
+ * { type: 'text', props: { label: 'Last Name' } }
169
+ * );
170
+ *
171
+ * // Array syntax with row options
172
+ * builder.add([
173
+ * { type: 'email', props: { label: 'Email' } },
174
+ * { type: 'phone', props: { label: 'Phone' } }
175
+ * ], { spacing: 'loose', alignment: 'center' });
176
+ * ```
44
177
  */
45
- addRowFields(fieldConfigs: FieldConfig[], rowOptions?: RowOptions): this;
178
+ add<T extends keyof C & string>(...fields: FieldConfig<C, T>[]): this;
179
+ add<T extends keyof C & string>(fields: FieldConfig<C, T>[], rowOptions?: RowOptions): this;
46
180
  /**
47
- * Add multiple fields, each on its own row
181
+ * Adds multiple fields on separate rows
182
+ *
183
+ * This method is useful when you want to ensure each field gets its own row,
184
+ * regardless of the number of fields. It's an alternative to the add() method
185
+ * when you need explicit control over row separation.
186
+ *
187
+ * @template T - The component type
188
+ * @param fieldConfigs - Array of field configurations
189
+ * @param rowOptions - Optional row layout configuration applied to all rows
190
+ * @returns The form builder instance for method chaining
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * // Each field will be on its own row
195
+ * builder.addSeparateRows([
196
+ * { type: 'text', props: { label: 'Field 1' } },
197
+ * { type: 'text', props: { label: 'Field 2' } },
198
+ * { type: 'text', props: { label: 'Field 3' } }
199
+ * ], { spacing: 'loose' });
200
+ * ```
48
201
  */
49
- addFields(fieldConfigs: FieldConfig[]): this;
202
+ addSeparateRows<T extends keyof C & string>(fieldConfigs: FieldConfig<C, T>[], rowOptions?: RowOptions): this;
50
203
  /**
51
- * Set form ID
204
+ * Sets the form identifier
205
+ *
206
+ * @param id - The new form identifier
207
+ * @returns The form builder instance for method chaining
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * builder.setId('user-profile-form');
212
+ * ```
52
213
  */
53
214
  setId(id: string): this;
54
215
  /**
55
- * Update field configuration
216
+ * Updates an existing field's configuration
217
+ *
218
+ * This method allows you to modify field properties, validation rules,
219
+ * or conditional logic after the field has been added to the form.
220
+ *
221
+ * @param fieldId - The unique identifier of the field to update
222
+ * @param updates - Partial field configuration with updates to apply
223
+ * @returns The form builder instance for method chaining
224
+ * @throws Error if the field with the specified ID is not found
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * builder.updateField('email-field', {
229
+ * props: { placeholder: 'Enter your email address' },
230
+ * validation: { required: true, email: true }
231
+ * });
232
+ * ```
56
233
  */
57
234
  updateField(fieldId: string, updates: Partial<Omit<FormFieldConfig, 'id'>>): this;
58
235
  /**
59
- * Helper method to find a field by ID
236
+ * Finds a field by its unique identifier
237
+ *
238
+ * This internal method searches through all rows to locate a field
239
+ * with the specified ID.
240
+ *
241
+ * @param fieldId - The field identifier to search for
242
+ * @returns The field configuration if found, null otherwise
243
+ *
244
+ * @internal
60
245
  */
61
246
  private findField;
62
247
  /**
63
- * Remove a field from the form
248
+ * Removes a field from the form
249
+ *
250
+ * This method removes the specified field and cleans up any empty rows
251
+ * that result from the removal. The form structure is automatically
252
+ * reorganized to maintain consistency.
253
+ *
254
+ * @param fieldId - The unique identifier of the field to remove
255
+ * @returns The form builder instance for method chaining
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * builder.removeField('unwanted-field-id');
260
+ * ```
64
261
  */
65
262
  removeField(fieldId: string): this;
66
263
  /**
67
- * Get field configuration by ID
264
+ * Retrieves a field configuration by its ID
265
+ *
266
+ * @param fieldId - The unique identifier of the field
267
+ * @returns The field configuration if found, undefined otherwise
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * const emailField = builder.getField('email-field');
272
+ * if (emailField) {
273
+ * console.log('Email field props:', emailField.props);
274
+ * }
275
+ * ```
68
276
  */
69
277
  getField(fieldId: string): FormFieldConfig | undefined;
70
278
  /**
71
- * Get all fields as a flat array
279
+ * Gets all fields as a flat array
280
+ *
281
+ * This method flattens the row structure to provide a simple array
282
+ * of all field configurations in the form, maintaining their order.
283
+ *
284
+ * @returns Array of all field configurations in the form
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * const allFields = builder.getFields();
289
+ * console.log(`Form has ${allFields.length} fields`);
290
+ * ```
72
291
  */
73
292
  getFields(): FormFieldConfig[];
74
293
  /**
75
- * Get all rows
294
+ * Gets all rows in the form
295
+ *
296
+ * Returns a copy of the internal rows array to prevent external
297
+ * modification while allowing inspection of the form structure.
298
+ *
299
+ * @returns Array of all form rows
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * const rows = builder.getRows();
304
+ * console.log(`Form has ${rows.length} rows`);
305
+ * ```
76
306
  */
77
307
  getRows(): FormFieldRow[];
78
308
  /**
79
- * Clear all fields and rows
309
+ * Clears all fields and rows from the form
310
+ *
311
+ * This method resets the form to an empty state and resets the ID generator
312
+ * to ensure clean ID generation for subsequent fields.
313
+ *
314
+ * @returns The form builder instance for method chaining
315
+ *
316
+ * @example
317
+ * ```typescript
318
+ * builder.clear().add({ type: 'text', props: { label: 'New start' } });
319
+ * ```
80
320
  */
81
321
  clear(): this;
82
322
  /**
83
- * Clone the current form builder
323
+ * Creates a deep copy of the current form builder
324
+ *
325
+ * This method creates a completely independent copy of the form builder,
326
+ * including all field configurations and internal state. The cloned
327
+ * builder can be modified without affecting the original.
328
+ *
329
+ * @param newFormId - Optional new form ID for the clone
330
+ * @returns A new form builder instance with copied configuration
331
+ *
332
+ * @example
333
+ * ```typescript
334
+ * const originalForm = builder.clone();
335
+ * const modifiedForm = builder.clone('modified-form')
336
+ * .add({ type: 'text', props: { label: 'Additional field' } });
337
+ * ```
84
338
  */
85
- clone(newFormId?: string): form;
339
+ clone(newFormId?: string): form<C>;
86
340
  /**
87
- * Validate the form configuration
341
+ * Validates the current form configuration
342
+ *
343
+ * This method performs comprehensive validation of the form structure,
344
+ * checking for common issues like duplicate IDs, missing components,
345
+ * and invalid row configurations.
346
+ *
347
+ * @returns Array of validation error messages (empty if valid)
348
+ *
349
+ * @example
350
+ * ```typescript
351
+ * const errors = builder.validate();
352
+ * if (errors.length > 0) {
353
+ * console.error('Form validation failed:', errors);
354
+ * }
355
+ * ```
356
+ *
357
+ * @remarks
358
+ * Validation checks include:
359
+ * - Duplicate field IDs across the form
360
+ * - Missing component definitions for referenced types
361
+ * - Row constraints (max 3 fields, no empty rows)
88
362
  */
89
363
  validate(): string[];
90
364
  /**
91
- * Build the final form configuration with matrix support
365
+ * Builds the final form configuration
366
+ *
367
+ * This method performs final validation and creates the complete form
368
+ * configuration object ready for rendering. It includes all field
369
+ * configurations, render settings, and metadata.
370
+ *
371
+ * @returns Complete form configuration ready for use
372
+ * @throws Error if validation fails
373
+ *
374
+ * @example
375
+ * ```typescript
376
+ * try {
377
+ * const formConfig = builder.build();
378
+ * // Use formConfig with your form renderer
379
+ * } catch (error) {
380
+ * console.error('Failed to build form:', error.message);
381
+ * }
382
+ * ```
383
+ *
384
+ * @remarks
385
+ * The returned configuration includes:
386
+ * - Form ID and metadata
387
+ * - All rows with their field configurations
388
+ * - Flattened array of all fields for easy access
389
+ * - Component configuration reference
390
+ * - Render configuration for customization
92
391
  */
93
392
  build(): FormConfiguration;
94
393
  /**
95
- * Export form configuration as JSON
394
+ * Exports the form configuration as JSON
395
+ *
396
+ * This method serializes the form configuration to a plain JavaScript
397
+ * object suitable for storage, transmission, or debugging.
398
+ *
399
+ * @returns Plain object representation of the form
400
+ *
401
+ * @example
402
+ * ```typescript
403
+ * const formJson = builder.toJSON();
404
+ * localStorage.setItem('savedForm', JSON.stringify(formJson));
405
+ * ```
96
406
  */
97
407
  toJSON(): any;
98
408
  /**
99
- * Import form configuration from JSON
409
+ * Imports form configuration from JSON
410
+ *
411
+ * This method restores form state from a previously exported JSON
412
+ * configuration. It's useful for loading saved forms or restoring
413
+ * form state from external sources.
414
+ *
415
+ * @param json - The JSON object containing form configuration
416
+ * @returns The form builder instance for method chaining
417
+ *
418
+ * @example
419
+ * ```typescript
420
+ * const savedForm = JSON.parse(localStorage.getItem('savedForm'));
421
+ * builder.fromJSON(savedForm);
422
+ * ```
423
+ *
424
+ * @remarks
425
+ * - Only imports basic form structure (ID and rows)
426
+ * - Does not validate imported configuration
427
+ * - Existing form content is replaced
100
428
  */
101
429
  fromJSON(json: any): this;
102
430
  /**
103
- * Get form statistics
431
+ * Gets comprehensive statistics about the form
432
+ *
433
+ * This method provides useful metrics about the form structure,
434
+ * helpful for analytics, debugging, or UI display purposes.
435
+ *
436
+ * @returns Object containing form statistics
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * const stats = builder.getStats();
441
+ * console.log(`Form has ${stats.totalFields} fields in ${stats.totalRows} rows`);
442
+ * console.log(`Average fields per row: ${stats.averageFieldsPerRow.toFixed(1)}`);
443
+ * ```
444
+ *
445
+ * @remarks
446
+ * Statistics include:
447
+ * - Total number of fields and rows
448
+ * - Average fields per row
449
+ * - Maximum and minimum fields in any row
450
+ * - Useful for form complexity analysis
104
451
  */
105
452
  getStats(): {
453
+ /** Total number of fields across all rows */
106
454
  totalFields: number;
455
+ /** Total number of rows in the form */
107
456
  totalRows: number;
457
+ /** Average number of fields per row */
108
458
  averageFieldsPerRow: number;
459
+ /** Maximum number of fields in any single row */
109
460
  maxFieldsInRow: number;
461
+ /** Minimum number of fields in any single row */
110
462
  minFieldsInRow: number;
111
463
  };
112
464
  }
113
465
  /**
114
466
  * Factory function to create a form builder directly
467
+ *
468
+ * This is a convenience function that provides an alternative to using
469
+ * the class constructor or static create method. It's particularly useful
470
+ * for functional programming styles or when you prefer function calls
471
+ * over class instantiation.
472
+ *
473
+ * @template C - Component configuration map
474
+ * @param config - The ril configuration instance
475
+ * @param formId - Optional form identifier
476
+ * @returns A new form builder instance
477
+ *
478
+ * @example
479
+ * ```typescript
480
+ * const builder = createForm(rilConfig, 'contact-form')
481
+ * .add({ type: 'text', props: { label: 'Name' } })
482
+ * .add({ type: 'email', props: { label: 'Email' } });
483
+ * ```
484
+ */
485
+ declare function createForm<C extends Record<string, any>>(config: ril<C>, formId?: string): form<C>;
486
+ /**
487
+ * Module augmentation to add createForm method to ril instances
488
+ *
489
+ * This declaration extends the ril interface to include the createForm
490
+ * method, allowing for a more integrated API experience.
115
491
  */
116
- declare function createForm(config: ril, formId?: string): form;
117
492
  declare module '@rilaykit/core' {
118
- interface ril {
119
- createForm(formId?: string): form;
493
+ interface ril<C extends Record<string, any> = Record<string, never>> {
494
+ /**
495
+ * Creates a new form builder using this ril configuration
496
+ *
497
+ * @param formId - Optional form identifier
498
+ * @returns A new form builder instance
499
+ *
500
+ * @example
501
+ * ```typescript
502
+ * const builder = rilConfig.createForm('my-form');
503
+ * ```
504
+ */
505
+ form(formId?: string): form<C>;
120
506
  }
121
507
  }
122
508
 
@@ -135,7 +521,7 @@ interface FormBodyProps {
135
521
  children?: React.ReactNode | RendererChildrenFunction<FormBodyRendererProps>;
136
522
  renderAs?: 'default' | 'children' | boolean;
137
523
  }
138
- declare function FormBody({ className, children, renderAs }: FormBodyProps): React$1.ReactNode;
524
+ declare function FormBody({ className, children, renderAs }: FormBodyProps): string | number | boolean | React$1.ReactElement<any, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined;
139
525
 
140
526
  interface FormFieldProps {
141
527
  fieldId: string;
@@ -143,7 +529,7 @@ interface FormFieldProps {
143
529
  customProps?: Record<string, any>;
144
530
  className?: string;
145
531
  }
146
- declare function FormField({ fieldId, disabled, customProps, className, }: FormFieldProps): react_jsx_runtime.JSX.Element | null;
532
+ declare function FormField({ fieldId, disabled, customProps, className, }: FormFieldProps): react_jsx_runtime.JSX.Element;
147
533
 
148
534
  interface FormState {
149
535
  values: Record<string, any>;
@@ -184,13 +570,13 @@ interface FormRowProps {
184
570
  children?: React.ReactNode | RendererChildrenFunction<FormRowRendererProps>;
185
571
  renderAs?: 'default' | 'children' | boolean;
186
572
  }
187
- declare function FormRow({ row, className, children, renderAs }: FormRowProps): React$1.ReactNode;
573
+ declare function FormRow({ row, className, children, renderAs }: FormRowProps): string | number | boolean | React$1.ReactElement<any, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined;
188
574
 
189
575
  interface FormSubmitButtonProps {
190
576
  className?: string;
191
577
  children?: React__default.ReactNode | RendererChildrenFunction<FormSubmitButtonRendererProps>;
192
578
  renderAs?: 'default' | 'children' | boolean;
193
579
  }
194
- declare function FormSubmitButton({ className, children, renderAs }: FormSubmitButtonProps): React__default.ReactNode;
580
+ declare function FormSubmitButton({ className, children, renderAs }: FormSubmitButtonProps): string | number | boolean | React__default.ReactElement<any, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined;
195
581
 
196
582
  export { type FieldConfig, Form, FormBody, type FormBodyProps, form as FormBuilder, type FormContextValue, FormField, type FormFieldProps, type FormProps, FormProvider, type FormProviderProps, FormRow, type FormRowProps, type FormState, FormSubmitButton, type FormSubmitButtonProps, createForm, form, useFormContext };
package/dist/index.d.ts CHANGED
@@ -5,118 +5,504 @@ export { createZodValidator, ril } from '@rilaykit/core';
5
5
  import * as React$1 from 'react';
6
6
  import React__default from 'react';
7
7
 
8
- interface FieldConfig {
9
- id: string;
10
- type: string;
11
- props?: Record<string, any>;
8
+ /**
9
+ * Configuration for a form field with type safety
10
+ *
11
+ * @template C - The component configuration map
12
+ * @template T - The specific component type key
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * const fieldConfig: FieldConfig<MyComponents, 'text'> = {
17
+ * type: 'text',
18
+ * props: { placeholder: 'Enter your name' },
19
+ * validation: { required: true }
20
+ * };
21
+ * ```
22
+ */
23
+ type FieldConfig<C extends Record<string, any>, T extends keyof C> = {
24
+ /** Unique identifier for the field. Auto-generated if not provided */
25
+ id?: string;
26
+ /** Component type from the registered components */
27
+ type: T;
28
+ /** Component-specific properties */
29
+ props?: Partial<C[T]>;
30
+ /** Validation rules for the field */
12
31
  validation?: ValidationConfig;
32
+ /** Conditional display logic */
13
33
  conditional?: ConditionalConfig;
14
- }
34
+ };
35
+ /**
36
+ * Options for configuring row layout and appearance
37
+ *
38
+ * @example
39
+ * ```typescript
40
+ * const rowOptions: RowOptions = {
41
+ * spacing: 'loose',
42
+ * alignment: 'center'
43
+ * };
44
+ * ```
45
+ */
15
46
  interface RowOptions {
47
+ /** Spacing between fields in the row */
16
48
  spacing?: 'tight' | 'normal' | 'loose';
49
+ /** Vertical alignment of fields in the row */
17
50
  alignment?: 'start' | 'center' | 'end' | 'stretch';
18
51
  }
19
52
  /**
20
- * Form builder class for creating form configurations
21
- * Simplified API with matrix support and auto-build capability
53
+ * Form builder class for creating type-safe form configurations
54
+ *
55
+ * This class provides a fluent API for building forms with automatic validation,
56
+ * type safety, and flexible layout options. It manages field registration,
57
+ * row organization, and form configuration generation.
58
+ *
59
+ * @template C - Component configuration map defining available component types
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * // Create a form builder with typed components
64
+ * const formBuilder = form.create(rilConfig, 'user-registration')
65
+ * .add({ type: 'text', props: { label: 'Name' } })
66
+ * .add(
67
+ * { type: 'email', props: { label: 'Email' } },
68
+ * { type: 'password', props: { label: 'Password' } }
69
+ * )
70
+ * .build();
71
+ * ```
72
+ *
73
+ * @remarks
74
+ * - Supports up to 3 fields per row for optimal layout
75
+ * - Automatically generates unique IDs for fields and rows
76
+ * - Provides comprehensive validation before building
77
+ * - Maintains type safety throughout the building process
22
78
  */
23
- declare class form {
79
+ declare class form<C extends Record<string, any> = Record<string, never>> {
80
+ /** The ril configuration instance containing component definitions */
24
81
  private config;
82
+ /** Array of form rows containing field configurations */
25
83
  private rows;
84
+ /** Unique identifier for this form */
26
85
  private formId;
27
- private rowCounter;
28
- constructor(config: ril, formId?: string);
29
- static create(config: ril, formId?: string): form;
86
+ /** Generator for creating unique IDs */
87
+ private idGenerator;
30
88
  /**
31
- * Helper method to create a FormFieldConfig from a FieldConfig
89
+ * Creates a new form builder instance
90
+ *
91
+ * @param config - The ril configuration containing component definitions
92
+ * @param formId - Optional unique identifier for the form. Auto-generated if not provided
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const builder = new form(rilConfig, 'my-form');
97
+ * ```
32
98
  */
33
- private createFormField;
99
+ constructor(config: ril<C>, formId?: string);
34
100
  /**
35
- * Helper method to create a row with validation
101
+ * Static factory method to create a new form builder
102
+ *
103
+ * @template Cm - Component configuration map
104
+ * @param config - The ril configuration instance
105
+ * @param formId - Optional form identifier
106
+ * @returns A new form builder instance
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const builder = form.create(rilConfig, 'registration-form');
111
+ * ```
36
112
  */
37
- private createRow;
113
+ static create<Cm extends Record<string, any> = Record<string, never>>(config: ril<Cm>, formId?: string): form<Cm>;
38
114
  /**
39
- * Add a single field using simplified FieldConfig object
115
+ * Converts a FieldConfig to a FormFieldConfig with proper validation
116
+ *
117
+ * This internal method handles the transformation from the builder's field
118
+ * configuration format to the final form field configuration, including
119
+ * component lookup, prop merging, and ID generation.
120
+ *
121
+ * @template T - The component type
122
+ * @param fieldConfig - The field configuration to convert
123
+ * @returns A complete FormFieldConfig ready for use
124
+ * @throws Error if the specified component type is not registered
125
+ *
126
+ * @internal
40
127
  */
41
- addField(fieldConfig: FieldConfig): this;
128
+ private createFormField;
129
+ /**
130
+ * Creates a form row with the specified fields and options
131
+ *
132
+ * This internal method handles row creation with validation of field limits,
133
+ * proper spacing, and alignment configuration.
134
+ *
135
+ * @template T - The component type
136
+ * @param fieldConfigs - Array of field configurations for the row
137
+ * @param rowOptions - Optional row layout configuration
138
+ * @returns A complete FormFieldRow configuration
139
+ * @throws Error if no fields provided or more than 3 fields specified
140
+ *
141
+ * @internal
142
+ */
143
+ private createRow;
42
144
  /**
43
- * Add multiple fields on the same row (max 3 fields)
145
+ * Universal method for adding fields to the form
146
+ *
147
+ * This is the primary method for adding fields to your form. It supports multiple
148
+ * usage patterns for maximum flexibility:
149
+ *
150
+ * - Single field: Creates a new row with one field
151
+ * - Multiple fields (≤3): Creates one row with all fields
152
+ * - Multiple fields (>3): Creates separate rows for each field
153
+ * - Array with options: Explicit control over row configuration
154
+ *
155
+ * @template T - The component type
156
+ * @param fields - Field configurations (variadic or array)
157
+ * @returns The form builder instance for method chaining
158
+ * @throws Error if no fields provided or invalid configuration
159
+ *
160
+ * @example
161
+ * ```typescript
162
+ * // Single field on its own row
163
+ * builder.add({ type: 'text', props: { label: 'Name' } });
164
+ *
165
+ * // Multiple fields on same row (max 3)
166
+ * builder.add(
167
+ * { type: 'text', props: { label: 'First Name' } },
168
+ * { type: 'text', props: { label: 'Last Name' } }
169
+ * );
170
+ *
171
+ * // Array syntax with row options
172
+ * builder.add([
173
+ * { type: 'email', props: { label: 'Email' } },
174
+ * { type: 'phone', props: { label: 'Phone' } }
175
+ * ], { spacing: 'loose', alignment: 'center' });
176
+ * ```
44
177
  */
45
- addRowFields(fieldConfigs: FieldConfig[], rowOptions?: RowOptions): this;
178
+ add<T extends keyof C & string>(...fields: FieldConfig<C, T>[]): this;
179
+ add<T extends keyof C & string>(fields: FieldConfig<C, T>[], rowOptions?: RowOptions): this;
46
180
  /**
47
- * Add multiple fields, each on its own row
181
+ * Adds multiple fields on separate rows
182
+ *
183
+ * This method is useful when you want to ensure each field gets its own row,
184
+ * regardless of the number of fields. It's an alternative to the add() method
185
+ * when you need explicit control over row separation.
186
+ *
187
+ * @template T - The component type
188
+ * @param fieldConfigs - Array of field configurations
189
+ * @param rowOptions - Optional row layout configuration applied to all rows
190
+ * @returns The form builder instance for method chaining
191
+ *
192
+ * @example
193
+ * ```typescript
194
+ * // Each field will be on its own row
195
+ * builder.addSeparateRows([
196
+ * { type: 'text', props: { label: 'Field 1' } },
197
+ * { type: 'text', props: { label: 'Field 2' } },
198
+ * { type: 'text', props: { label: 'Field 3' } }
199
+ * ], { spacing: 'loose' });
200
+ * ```
48
201
  */
49
- addFields(fieldConfigs: FieldConfig[]): this;
202
+ addSeparateRows<T extends keyof C & string>(fieldConfigs: FieldConfig<C, T>[], rowOptions?: RowOptions): this;
50
203
  /**
51
- * Set form ID
204
+ * Sets the form identifier
205
+ *
206
+ * @param id - The new form identifier
207
+ * @returns The form builder instance for method chaining
208
+ *
209
+ * @example
210
+ * ```typescript
211
+ * builder.setId('user-profile-form');
212
+ * ```
52
213
  */
53
214
  setId(id: string): this;
54
215
  /**
55
- * Update field configuration
216
+ * Updates an existing field's configuration
217
+ *
218
+ * This method allows you to modify field properties, validation rules,
219
+ * or conditional logic after the field has been added to the form.
220
+ *
221
+ * @param fieldId - The unique identifier of the field to update
222
+ * @param updates - Partial field configuration with updates to apply
223
+ * @returns The form builder instance for method chaining
224
+ * @throws Error if the field with the specified ID is not found
225
+ *
226
+ * @example
227
+ * ```typescript
228
+ * builder.updateField('email-field', {
229
+ * props: { placeholder: 'Enter your email address' },
230
+ * validation: { required: true, email: true }
231
+ * });
232
+ * ```
56
233
  */
57
234
  updateField(fieldId: string, updates: Partial<Omit<FormFieldConfig, 'id'>>): this;
58
235
  /**
59
- * Helper method to find a field by ID
236
+ * Finds a field by its unique identifier
237
+ *
238
+ * This internal method searches through all rows to locate a field
239
+ * with the specified ID.
240
+ *
241
+ * @param fieldId - The field identifier to search for
242
+ * @returns The field configuration if found, null otherwise
243
+ *
244
+ * @internal
60
245
  */
61
246
  private findField;
62
247
  /**
63
- * Remove a field from the form
248
+ * Removes a field from the form
249
+ *
250
+ * This method removes the specified field and cleans up any empty rows
251
+ * that result from the removal. The form structure is automatically
252
+ * reorganized to maintain consistency.
253
+ *
254
+ * @param fieldId - The unique identifier of the field to remove
255
+ * @returns The form builder instance for method chaining
256
+ *
257
+ * @example
258
+ * ```typescript
259
+ * builder.removeField('unwanted-field-id');
260
+ * ```
64
261
  */
65
262
  removeField(fieldId: string): this;
66
263
  /**
67
- * Get field configuration by ID
264
+ * Retrieves a field configuration by its ID
265
+ *
266
+ * @param fieldId - The unique identifier of the field
267
+ * @returns The field configuration if found, undefined otherwise
268
+ *
269
+ * @example
270
+ * ```typescript
271
+ * const emailField = builder.getField('email-field');
272
+ * if (emailField) {
273
+ * console.log('Email field props:', emailField.props);
274
+ * }
275
+ * ```
68
276
  */
69
277
  getField(fieldId: string): FormFieldConfig | undefined;
70
278
  /**
71
- * Get all fields as a flat array
279
+ * Gets all fields as a flat array
280
+ *
281
+ * This method flattens the row structure to provide a simple array
282
+ * of all field configurations in the form, maintaining their order.
283
+ *
284
+ * @returns Array of all field configurations in the form
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * const allFields = builder.getFields();
289
+ * console.log(`Form has ${allFields.length} fields`);
290
+ * ```
72
291
  */
73
292
  getFields(): FormFieldConfig[];
74
293
  /**
75
- * Get all rows
294
+ * Gets all rows in the form
295
+ *
296
+ * Returns a copy of the internal rows array to prevent external
297
+ * modification while allowing inspection of the form structure.
298
+ *
299
+ * @returns Array of all form rows
300
+ *
301
+ * @example
302
+ * ```typescript
303
+ * const rows = builder.getRows();
304
+ * console.log(`Form has ${rows.length} rows`);
305
+ * ```
76
306
  */
77
307
  getRows(): FormFieldRow[];
78
308
  /**
79
- * Clear all fields and rows
309
+ * Clears all fields and rows from the form
310
+ *
311
+ * This method resets the form to an empty state and resets the ID generator
312
+ * to ensure clean ID generation for subsequent fields.
313
+ *
314
+ * @returns The form builder instance for method chaining
315
+ *
316
+ * @example
317
+ * ```typescript
318
+ * builder.clear().add({ type: 'text', props: { label: 'New start' } });
319
+ * ```
80
320
  */
81
321
  clear(): this;
82
322
  /**
83
- * Clone the current form builder
323
+ * Creates a deep copy of the current form builder
324
+ *
325
+ * This method creates a completely independent copy of the form builder,
326
+ * including all field configurations and internal state. The cloned
327
+ * builder can be modified without affecting the original.
328
+ *
329
+ * @param newFormId - Optional new form ID for the clone
330
+ * @returns A new form builder instance with copied configuration
331
+ *
332
+ * @example
333
+ * ```typescript
334
+ * const originalForm = builder.clone();
335
+ * const modifiedForm = builder.clone('modified-form')
336
+ * .add({ type: 'text', props: { label: 'Additional field' } });
337
+ * ```
84
338
  */
85
- clone(newFormId?: string): form;
339
+ clone(newFormId?: string): form<C>;
86
340
  /**
87
- * Validate the form configuration
341
+ * Validates the current form configuration
342
+ *
343
+ * This method performs comprehensive validation of the form structure,
344
+ * checking for common issues like duplicate IDs, missing components,
345
+ * and invalid row configurations.
346
+ *
347
+ * @returns Array of validation error messages (empty if valid)
348
+ *
349
+ * @example
350
+ * ```typescript
351
+ * const errors = builder.validate();
352
+ * if (errors.length > 0) {
353
+ * console.error('Form validation failed:', errors);
354
+ * }
355
+ * ```
356
+ *
357
+ * @remarks
358
+ * Validation checks include:
359
+ * - Duplicate field IDs across the form
360
+ * - Missing component definitions for referenced types
361
+ * - Row constraints (max 3 fields, no empty rows)
88
362
  */
89
363
  validate(): string[];
90
364
  /**
91
- * Build the final form configuration with matrix support
365
+ * Builds the final form configuration
366
+ *
367
+ * This method performs final validation and creates the complete form
368
+ * configuration object ready for rendering. It includes all field
369
+ * configurations, render settings, and metadata.
370
+ *
371
+ * @returns Complete form configuration ready for use
372
+ * @throws Error if validation fails
373
+ *
374
+ * @example
375
+ * ```typescript
376
+ * try {
377
+ * const formConfig = builder.build();
378
+ * // Use formConfig with your form renderer
379
+ * } catch (error) {
380
+ * console.error('Failed to build form:', error.message);
381
+ * }
382
+ * ```
383
+ *
384
+ * @remarks
385
+ * The returned configuration includes:
386
+ * - Form ID and metadata
387
+ * - All rows with their field configurations
388
+ * - Flattened array of all fields for easy access
389
+ * - Component configuration reference
390
+ * - Render configuration for customization
92
391
  */
93
392
  build(): FormConfiguration;
94
393
  /**
95
- * Export form configuration as JSON
394
+ * Exports the form configuration as JSON
395
+ *
396
+ * This method serializes the form configuration to a plain JavaScript
397
+ * object suitable for storage, transmission, or debugging.
398
+ *
399
+ * @returns Plain object representation of the form
400
+ *
401
+ * @example
402
+ * ```typescript
403
+ * const formJson = builder.toJSON();
404
+ * localStorage.setItem('savedForm', JSON.stringify(formJson));
405
+ * ```
96
406
  */
97
407
  toJSON(): any;
98
408
  /**
99
- * Import form configuration from JSON
409
+ * Imports form configuration from JSON
410
+ *
411
+ * This method restores form state from a previously exported JSON
412
+ * configuration. It's useful for loading saved forms or restoring
413
+ * form state from external sources.
414
+ *
415
+ * @param json - The JSON object containing form configuration
416
+ * @returns The form builder instance for method chaining
417
+ *
418
+ * @example
419
+ * ```typescript
420
+ * const savedForm = JSON.parse(localStorage.getItem('savedForm'));
421
+ * builder.fromJSON(savedForm);
422
+ * ```
423
+ *
424
+ * @remarks
425
+ * - Only imports basic form structure (ID and rows)
426
+ * - Does not validate imported configuration
427
+ * - Existing form content is replaced
100
428
  */
101
429
  fromJSON(json: any): this;
102
430
  /**
103
- * Get form statistics
431
+ * Gets comprehensive statistics about the form
432
+ *
433
+ * This method provides useful metrics about the form structure,
434
+ * helpful for analytics, debugging, or UI display purposes.
435
+ *
436
+ * @returns Object containing form statistics
437
+ *
438
+ * @example
439
+ * ```typescript
440
+ * const stats = builder.getStats();
441
+ * console.log(`Form has ${stats.totalFields} fields in ${stats.totalRows} rows`);
442
+ * console.log(`Average fields per row: ${stats.averageFieldsPerRow.toFixed(1)}`);
443
+ * ```
444
+ *
445
+ * @remarks
446
+ * Statistics include:
447
+ * - Total number of fields and rows
448
+ * - Average fields per row
449
+ * - Maximum and minimum fields in any row
450
+ * - Useful for form complexity analysis
104
451
  */
105
452
  getStats(): {
453
+ /** Total number of fields across all rows */
106
454
  totalFields: number;
455
+ /** Total number of rows in the form */
107
456
  totalRows: number;
457
+ /** Average number of fields per row */
108
458
  averageFieldsPerRow: number;
459
+ /** Maximum number of fields in any single row */
109
460
  maxFieldsInRow: number;
461
+ /** Minimum number of fields in any single row */
110
462
  minFieldsInRow: number;
111
463
  };
112
464
  }
113
465
  /**
114
466
  * Factory function to create a form builder directly
467
+ *
468
+ * This is a convenience function that provides an alternative to using
469
+ * the class constructor or static create method. It's particularly useful
470
+ * for functional programming styles or when you prefer function calls
471
+ * over class instantiation.
472
+ *
473
+ * @template C - Component configuration map
474
+ * @param config - The ril configuration instance
475
+ * @param formId - Optional form identifier
476
+ * @returns A new form builder instance
477
+ *
478
+ * @example
479
+ * ```typescript
480
+ * const builder = createForm(rilConfig, 'contact-form')
481
+ * .add({ type: 'text', props: { label: 'Name' } })
482
+ * .add({ type: 'email', props: { label: 'Email' } });
483
+ * ```
484
+ */
485
+ declare function createForm<C extends Record<string, any>>(config: ril<C>, formId?: string): form<C>;
486
+ /**
487
+ * Module augmentation to add createForm method to ril instances
488
+ *
489
+ * This declaration extends the ril interface to include the createForm
490
+ * method, allowing for a more integrated API experience.
115
491
  */
116
- declare function createForm(config: ril, formId?: string): form;
117
492
  declare module '@rilaykit/core' {
118
- interface ril {
119
- createForm(formId?: string): form;
493
+ interface ril<C extends Record<string, any> = Record<string, never>> {
494
+ /**
495
+ * Creates a new form builder using this ril configuration
496
+ *
497
+ * @param formId - Optional form identifier
498
+ * @returns A new form builder instance
499
+ *
500
+ * @example
501
+ * ```typescript
502
+ * const builder = rilConfig.createForm('my-form');
503
+ * ```
504
+ */
505
+ form(formId?: string): form<C>;
120
506
  }
121
507
  }
122
508
 
@@ -135,7 +521,7 @@ interface FormBodyProps {
135
521
  children?: React.ReactNode | RendererChildrenFunction<FormBodyRendererProps>;
136
522
  renderAs?: 'default' | 'children' | boolean;
137
523
  }
138
- declare function FormBody({ className, children, renderAs }: FormBodyProps): React$1.ReactNode;
524
+ declare function FormBody({ className, children, renderAs }: FormBodyProps): string | number | boolean | React$1.ReactElement<any, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined;
139
525
 
140
526
  interface FormFieldProps {
141
527
  fieldId: string;
@@ -143,7 +529,7 @@ interface FormFieldProps {
143
529
  customProps?: Record<string, any>;
144
530
  className?: string;
145
531
  }
146
- declare function FormField({ fieldId, disabled, customProps, className, }: FormFieldProps): react_jsx_runtime.JSX.Element | null;
532
+ declare function FormField({ fieldId, disabled, customProps, className, }: FormFieldProps): react_jsx_runtime.JSX.Element;
147
533
 
148
534
  interface FormState {
149
535
  values: Record<string, any>;
@@ -184,13 +570,13 @@ interface FormRowProps {
184
570
  children?: React.ReactNode | RendererChildrenFunction<FormRowRendererProps>;
185
571
  renderAs?: 'default' | 'children' | boolean;
186
572
  }
187
- declare function FormRow({ row, className, children, renderAs }: FormRowProps): React$1.ReactNode;
573
+ declare function FormRow({ row, className, children, renderAs }: FormRowProps): string | number | boolean | React$1.ReactElement<any, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined;
188
574
 
189
575
  interface FormSubmitButtonProps {
190
576
  className?: string;
191
577
  children?: React__default.ReactNode | RendererChildrenFunction<FormSubmitButtonRendererProps>;
192
578
  renderAs?: 'default' | 'children' | boolean;
193
579
  }
194
- declare function FormSubmitButton({ className, children, renderAs }: FormSubmitButtonProps): React__default.ReactNode;
580
+ declare function FormSubmitButton({ className, children, renderAs }: FormSubmitButtonProps): string | number | boolean | React__default.ReactElement<any, string | React__default.JSXElementConstructor<any>> | Iterable<React__default.ReactNode> | null | undefined;
195
581
 
196
582
  export { type FieldConfig, Form, FormBody, type FormBodyProps, form as FormBuilder, type FormContextValue, FormField, type FormFieldProps, type FormProps, FormProvider, type FormProviderProps, FormRow, type FormRowProps, type FormState, FormSubmitButton, type FormSubmitButtonProps, createForm, form, useFormContext };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- 'use strict';var core=require('@rilaykit/core'),or=require('react'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var or__default=/*#__PURE__*/_interopDefault(or);var v=class o{constructor(r,e){this.rows=[];this.rowCounter=0;this.config=r,this.formId=e||`form-${Date.now()}`;}static create(r,e){return new o(r,e)}createFormField(r){let e=this.config.getComponent(r.type);if(!e)throw new Error(`No component found with type "${r.type}"`);return {id:r.id,componentId:e.id,props:{...e.defaultProps,...r.props},validation:r.validation,conditional:r.conditional}}createRow(r,e){if(r.length===0)throw new Error("At least one field is required");if(r.length>3)throw new Error("Maximum 3 fields per row");let i=r.map(s=>this.createFormField(s));return {id:`row-${++this.rowCounter}`,fields:i,maxColumns:r.length,spacing:e?.spacing||"normal",alignment:e?.alignment||"stretch"}}addField(r){return this.addRowFields([r])}addRowFields(r,e){let i=this.createRow(r,e);return this.rows.push(i),this}addFields(r){for(let e of r)this.addField(e);return this}setId(r){return this.formId=r,this}updateField(r,e){let i=this.findField(r);if(!i)throw new Error(`Field with ID "${r}" not found`);return Object.assign(i,{...e,props:{...i.props,...e.props}}),this}findField(r){for(let e of this.rows){let i=e.fields.find(s=>s.id===r);if(i)return i}return null}removeField(r){return this.rows=this.rows.map(e=>({...e,fields:e.fields.filter(i=>i.id!==r)})).filter(e=>e.fields.length>0),this}getField(r){return this.findField(r)||void 0}getFields(){return this.rows.flatMap(r=>r.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.rowCounter=0,this}clone(r){let e=new o(this.config,r||`${this.formId}-clone`);return e.rows=this.rows.map(i=>({...i,fields:i.fields.map(s=>({...s}))})),e.rowCounter=this.rowCounter,e}validate(){let r=[],e=this.getFields(),i=e.map(d=>d.id),s=i.filter((d,u)=>i.indexOf(d)!==u);s.length>0&&r.push(`Duplicate field IDs: ${s.join(", ")}`);for(let d of e)this.config.hasComponent(d.componentId)||r.push(`Component "${d.componentId}" not found for field "${d.id}"`);for(let d of this.rows)d.fields.length>3&&r.push(`Row "${d.id}" has ${d.fields.length} fields, maximum is 3`),d.fields.length===0&&r.push(`Row "${d.id}" is empty`);return r}build(){let r=this.validate();if(r.length>0)throw new Error(`Form validation failed: ${r.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig()}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(r){return r.id&&(this.formId=r.id),r.rows&&(this.rows=r.rows,this.rowCounter=this.rows.length),this}getStats(){let r=this.getFields(),e=this.rows.map(i=>i.fields.length);return {totalFields:r.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?r.length/this.rows.length:0,maxFieldsInRow:e.length>0?Math.max(...e):0,minFieldsInRow:e.length>0?Math.min(...e):0}}};function Z(o,r){return v.create(o,r)}core.ril.prototype.createForm=function(o){return v.create(this,o)};function j(o,r){switch(r.type){case "SET_VALUE":{let e={...o.values,[r.fieldId]:r.value};return {...o,values:e,isDirty:true}}case "SET_ERROR":return {...o,errors:{...o.errors,[r.fieldId]:r.errors},isValid:false};case "CLEAR_ERROR":{let e={...o.errors};return delete e[r.fieldId],{...o,errors:e}}case "MARK_TOUCHED":return {...o,touched:new Set([...o.touched,r.fieldId])};case "SET_VALIDATING":{let e=new Set(o.isValidating);return r.isValidating?e.add(r.fieldId):e.delete(r.fieldId),{...o,isValidating:e}}case "SET_SUBMITTING":return {...o,isSubmitting:r.isSubmitting};case "RESET":return {values:r.values||{},errors:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false};case "UPDATE_VALIDATION_STATE":{let e=Object.keys(o.errors).some(i=>o.errors[i].length>0);return {...o,isValid:!e}}default:return o}}var k=or.createContext(null);function B({children:o,formConfig:r,defaultValues:e={},onSubmit:i,onFieldChange:s,className:d}){let u={values:e,errors:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false},[a,l]=or.useReducer(j,u),t=or.useRef(new Map),c=or.useRef(i),m=or.useRef(s);c.current=i,m.current=s;let R=or.useCallback((n,f)=>{l({type:"SET_ERROR",fieldId:n,errors:f}),l({type:"UPDATE_VALIDATION_STATE"});},[]),b=or.useCallback(n=>{l({type:"CLEAR_ERROR",fieldId:n}),l({type:"UPDATE_VALIDATION_STATE"});},[]),V=or.useCallback(n=>{l({type:"MARK_TOUCHED",fieldId:n});},[]),p=or.useCallback((n,f)=>{l({type:"SET_VALIDATING",fieldId:n,isValidating:f});},[]),x=or.useCallback((n,f)=>{if(l({type:"SET_VALUE",fieldId:n,value:f}),a.errors[n]&&a.errors[n].length>0&&(l({type:"CLEAR_ERROR",fieldId:n}),l({type:"UPDATE_VALIDATION_STATE"})),m.current){let g={...a.values,[n]:f};m.current(n,f,g);}},[a.errors,a.values]),w=or.useMemo(()=>r,[r]),E=or.useCallback(async(n,f)=>{let g=w.allFields.find(I=>I.id===n);if(!g?.validation?.validator)return {isValid:true,errors:[]};let K=f!==void 0?f:a.values[n],N=t.current.get(n);N&&clearTimeout(N);let W={fieldId:n,formData:a.values,fieldProps:g.props,touched:a.touched.has(n),dirty:a.isDirty},_=g.validation.debounceMs||0;return new Promise(I=>{let O=async()=>{p(n,true);try{let y=await g.validation.validator(K,W,g.props);y.errors.length>0?R(n,y.errors):b(n),I(y);}catch(y){let U={isValid:false,errors:[{code:"validation_error",message:y instanceof Error?y.message:"Validation error"}]};R(n,U.errors),I(U);}finally{p(n,false);}};if(_>0){let y=setTimeout(O,_);t.current.set(n,y);}else O();})},[w,a.values,a.touched,a.isDirty,R,b,p]),P=or.useCallback(async()=>{let n=w.allFields.map(g=>E(g.id));return (await Promise.all(n)).every(g=>g.isValid)},[w,E]),A=or.useCallback(n=>{l({type:"RESET",values:n});},[]),T=or.useCallback(async n=>{if(n?.preventDefault(),!c.current)return true;l({type:"SET_SUBMITTING",isSubmitting:true});try{return await P()?(await c.current(a.values),!0):!1}catch(f){return console.error("Error during form submission:",f),false}finally{l({type:"SET_SUBMITTING",isSubmitting:false});}},[a.values,P]),h=or.useMemo(()=>({formState:a,formConfig:w,setValue:x,setError:R,clearError:b,markFieldTouched:V,setFieldValidating:p,validateField:E,validateAllFields:P,reset:A,submit:T}),[a,w,x,R,b,V,p,E,P,A,T]);return jsxRuntime.jsx(k.Provider,{value:h,children:jsxRuntime.jsx("form",{onSubmit:T,className:d,noValidate:true,children:o})})}function C(){let o=or.useContext(k);if(!o)throw new Error("useFormContext must be used within a FormProvider");return o}function rr({formConfig:o,defaultValues:r,onSubmit:e,onFieldChange:i,children:s}){let d=o instanceof v?o.build():o;return jsxRuntime.jsx(B,{formConfig:d,defaultValues:r,onSubmit:e,onFieldChange:i,children:s})}function G({fieldId:o,disabled:r=false,customProps:e={},className:i}){let{formState:s,formConfig:d,setValue:u,markFieldTouched:a,validateField:l}=C(),t=or.useMemo(()=>d.allFields.find(h=>h.id===o),[d.allFields,o]);if(!t)throw new Error(`Field with ID "${o}" not found`);let c=or.useMemo(()=>d.config.getComponent(t.componentId),[d.config,t.componentId]);if(!c)throw new Error(`Component with ID "${t.componentId}" not found`);let m=or.useMemo(()=>({value:s.values[t.id],errors:s.errors[t.id]||[],touched:s.touched.has(t.id),validating:s.isValidating.has(t.id)}),[s.values,s.errors,s.touched,s.isValidating,t.id]),R=or.useCallback(h=>{let n=m.errors.length>0;u(t.id,h),(t.validation?.validateOnChange||n&&t.validation?.validator||m.touched&&t.validation?.validator)&&l(t.id,h);},[t.id,t.validation,u,l,m.errors.length,m.touched]),b=or.useCallback(()=>{a(t.id),(t.validation?.validateOnBlur||t.validation?.validator)&&l(t.id);},[t.id,t.validation,a,l]),V=or.useMemo(()=>{if(!t.conditional)return true;try{return t.conditional.condition(s.values)}catch(h){return console.warn(`Conditional evaluation failed for field "${t.id}":`,h),true}},[t.conditional,s.values,t.id]),p=or.useMemo(()=>{if(!t.conditional||!V)return {};switch(t.conditional.action){case "disable":return {disabled:true};case "require":return {required:true};default:return {}}},[t.conditional,V]);if(!V&&t.conditional?.action==="hide")return null;let x=or.useMemo(()=>({...c.defaultProps,...t.props,...e,...p}),[c.defaultProps,t.props,e,p]),w=or.useMemo(()=>({id:t.id,props:x,value:m.value,onChange:R,onBlur:b,error:m.errors,touched:m.touched,disabled:r||p.disabled,isValidating:m.validating}),[t.id,x,m.value,R,b,m.errors,m.touched,r,p.disabled,m.validating]),E=d.renderConfig?.fieldRenderer,P=c.renderer(w),A=c.useFieldRenderer!==false,T=E&&A?E({children:P,id:t.id,error:m.errors,touched:m.touched,disabled:r||p.disabled,isValidating:m.validating,...x}):P;return jsxRuntime.jsx("div",{className:i,"data-field-id":t.id,"data-field-type":c.type,children:T})}var q=or__default.default.memo(G);function H({row:o,className:r,children:e,renderAs:i}){let{formConfig:s}=C(),d=o.fields.map(m=>jsxRuntime.jsx(q,{fieldId:m.id},m.id));if(i==="children"||i===true){if(typeof e!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');let m={row:o,children:d,className:r,spacing:o.spacing,alignment:o.alignment};return e(m)}let a=s.renderConfig?.rowRenderer;if(!a)throw new Error(`No rowRenderer configured for form "${s.id}". Please configure a rowRenderer using config.setRowRenderer() or config.setFormRenderConfig().`);let l={row:o,children:d,className:r,spacing:o.spacing,alignment:o.alignment},t=core.resolveRendererChildren(e,l),c={...l,children:t||d};return a(c)}var J=H;function ar({className:o,children:r,renderAs:e}){let{formConfig:i}=C(),s=or.useMemo(()=>i.rows.map(c=>jsxRuntime.jsx(J,{row:c},c.id)),[i.rows]);if(e==="children"||e===true){if(typeof r!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');return r({formConfig:i,children:s,className:o})}let u=i.renderConfig?.bodyRenderer;if(!u)throw new Error(`No bodyRenderer configured for form "${i.id}". Please configure a bodyRenderer using config.setBodyRenderer() or config.setFormRenderConfig().`);let a={formConfig:i,children:s,className:o},l=core.resolveRendererChildren(r,a),t={...a,children:l||s};return u(t)}function cr({className:o,children:r,renderAs:e}){let{formState:i,submit:s,formConfig:d}=C(),u={isSubmitting:i.isSubmitting,isValid:i.isValid,isDirty:i.isDirty,onSubmit:s,className:o};if(e==="children"||e===true){if(typeof r!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');return r(u)}let l=d.renderConfig?.submitButtonRenderer;if(!l)throw new Error(`No submitButtonRenderer configured for form "${d.id}". Please configure a submitButtonRenderer using config.setSubmitButtonRenderer() or config.setFormRenderConfig().`);let t=core.resolveRendererChildren(r,u),c={...u,children:t};return l(c)}Object.defineProperty(exports,"createZodValidator",{enumerable:true,get:function(){return core.createZodValidator}});Object.defineProperty(exports,"ril",{enumerable:true,get:function(){return core.ril}});exports.Form=rr;exports.FormBody=ar;exports.FormBuilder=v;exports.FormField=G;exports.FormProvider=B;exports.FormRow=H;exports.FormSubmitButton=cr;exports.createForm=Z;exports.form=v;exports.useFormContext=C;
1
+ 'use strict';var core=require('@rilaykit/core'),se=require('react'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var se__default=/*#__PURE__*/_interopDefault(se);var w=class t{constructor(e,r){this.rows=[];this.idGenerator=new core.IdGenerator;this.config=e,this.formId=r||`form-${Date.now()}`;}static create(e,r){return new t(e,r)}createFormField(e){let r=this.config.getComponent(e.type);if(!r)throw new Error(`No component found with type "${e.type}"`);return {id:e.id||this.idGenerator.next("field"),componentId:r.id,props:{...r.defaultProps,...e.props},validation:e.validation,conditional:e.conditional}}createRow(e,r){if(e.length===0)throw new Error("At least one field is required");if(e.length>3)throw new Error("Maximum 3 fields per row");let n=e.map(o=>this.createFormField(o));return {id:this.idGenerator.next("row"),fields:n,maxColumns:e.length,spacing:r?.spacing||"normal",alignment:r?.alignment||"stretch"}}add(...e){let r,n,o=false;if(e.length===1&&Array.isArray(e[0])?(r=e[0],o=true):e.length===2&&Array.isArray(e[0])?(r=e[0],n=e[1],o=true):r=e,r.length===0)throw new Error("At least one field is required");if(o&&r.length>3)throw new Error("Maximum 3 fields per row");if(r.length===1){let c=this.createRow(r,n);return this.rows.push(c),this}if(r.length<=3){let c=this.createRow(r,n);return this.rows.push(c),this}for(let c of r){let f=this.createRow([c],n);this.rows.push(f);}return this}addSeparateRows(e,r){for(let n of e)this.add([n],r);return this}setId(e){return this.formId=e,this}updateField(e,r){let n=this.findField(e);if(!n)throw new Error(`Field with ID "${e}" not found`);return Object.assign(n,{...r,props:{...n.props,...r.props}}),this}findField(e){for(let r of this.rows){let n=r.fields.find(o=>o.id===e);if(n)return n}return null}removeField(e){return this.rows=this.rows.map(r=>({...r,fields:r.fields.filter(n=>n.id!==e)})).filter(r=>r.fields.length>0),this}getField(e){return this.findField(e)||void 0}getFields(){return this.rows.flatMap(e=>e.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.idGenerator.reset(),this}clone(e){let r=new t(this.config,e||`${this.formId}-clone`);return r.rows=core.deepClone(this.rows),r}validate(){let e=new core.ValidationErrorBuilder,r=this.getFields(),n=r.map(o=>o.id);try{core.ensureUnique(n,"field");}catch(o){e.add("DUPLICATE_FIELD_IDS",o instanceof Error?o.message:String(o));}for(let o of r)e.addIf(!this.config.hasComponent(o.componentId),"MISSING_COMPONENT",`Component "${o.componentId}" not found for field "${o.id}"`);for(let o of this.rows)e.addIf(o.fields.length>3,"TOO_MANY_FIELDS_IN_ROW",`Row "${o.id}" has ${o.fields.length} fields, maximum is 3`),e.addIf(o.fields.length===0,"EMPTY_ROW",`Row "${o.id}" is empty`);return e.build().map(o=>o.message)}build(){let e=this.validate();if(e.length>0)throw new Error(`Form validation failed: ${e.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig()}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(e){return e.id&&(this.formId=e.id),e.rows&&(this.rows=e.rows),this}getStats(){let e=this.getFields(),r=this.rows.map(n=>n.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?e.length/this.rows.length:0,maxFieldsInRow:r.length>0?Math.max(...r):0,minFieldsInRow:r.length>0?Math.min(...r):0}}};function j(t,e){return w.create(t,e)}core.ril.prototype.form=function(t){return w.create(this,t)};function te(t,e){switch(e.type){case "SET_VALUE":{let r={...t.values,[e.fieldId]:e.value};return {...t,values:r,isDirty:true}}case "SET_ERROR":return {...t,errors:{...t.errors,[e.fieldId]:e.errors},isValid:false};case "CLEAR_ERROR":{let r={...t.errors};return delete r[e.fieldId],{...t,errors:r}}case "MARK_TOUCHED":return {...t,touched:new Set([...t.touched,e.fieldId])};case "SET_VALIDATING":{let r=new Set(t.isValidating);return e.isValidating?r.add(e.fieldId):r.delete(e.fieldId),{...t,isValidating:r}}case "SET_SUBMITTING":return {...t,isSubmitting:e.isSubmitting};case "RESET":return {values:e.values||{},errors:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false};case "UPDATE_VALIDATION_STATE":{let r=Object.keys(t.errors).some(n=>t.errors[n].length>0);return {...t,isValid:!r}}default:return t}}var L=se.createContext(null);function D({children:t,formConfig:e,defaultValues:r={},onSubmit:n,onFieldChange:o,className:c}){let f={values:r,errors:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false},[d,a]=se.useReducer(te,f),i=se.useRef(new Map),m=se.useRef(n),l=se.useRef(o);m.current=n,l.current=o;let C=se.useCallback((s,u)=>{a({type:"SET_ERROR",fieldId:s,errors:u}),a({type:"UPDATE_VALIDATION_STATE"});},[]),v=se.useCallback(s=>{a({type:"CLEAR_ERROR",fieldId:s}),a({type:"UPDATE_VALIDATION_STATE"});},[]),S=se.useCallback(s=>{a({type:"MARK_TOUCHED",fieldId:s});},[]),p=se.useCallback((s,u)=>{a({type:"SET_VALIDATING",fieldId:s,isValidating:u});},[]),V=se.useCallback((s,u)=>{if(a({type:"SET_VALUE",fieldId:s,value:u}),d.errors[s]&&d.errors[s].length>0&&(a({type:"CLEAR_ERROR",fieldId:s}),a({type:"UPDATE_VALIDATION_STATE"})),l.current){let g={...d.values,[s]:u};l.current(s,u,g);}},[d.errors,d.values]),h=se.useMemo(()=>e,[e]),T=se.useCallback(async(s,u)=>{let g=h.allFields.find(I=>I.id===s);if(!g?.validation?.validator)return {isValid:true,errors:[]};let J=u!==void 0?u:d.values[s],O=i.current.get(s);O&&clearTimeout(O);let K={fieldId:s,formData:d.values,fieldProps:g.props,touched:d.touched.has(s),dirty:d.isDirty},N=g.validation.debounceMs||0;return new Promise(I=>{let B=async()=>{p(s,true);try{let y=await g.validation.validator(J,K,g.props);y.errors.length>0?C(s,y.errors):v(s),I(y);}catch(y){let k={isValid:false,errors:[{code:"validation_error",message:y instanceof Error?y.message:"Validation error"}]};C(s,k.errors),I(k);}finally{p(s,false);}};if(N>0){let y=setTimeout(B,N);i.current.set(s,y);}else B();})},[h,d.values,d.touched,d.isDirty,C,v,p]),E=se.useCallback(async()=>{let s=h.allFields.map(g=>T(g.id));return (await Promise.all(s)).every(g=>g.isValid)},[h,T]),b=se.useCallback(s=>{a({type:"RESET",values:s});},[]),x=se.useCallback(async s=>{if(s?.preventDefault(),!m.current)return true;a({type:"SET_SUBMITTING",isSubmitting:true});try{return await E()?(await m.current(d.values),!0):!1}catch(u){return console.error("Error during form submission:",u),false}finally{a({type:"SET_SUBMITTING",isSubmitting:false});}},[d.values,E]),A=se.useMemo(()=>({formState:d,formConfig:h,setValue:V,setError:C,clearError:v,markFieldTouched:S,setFieldValidating:p,validateField:T,validateAllFields:E,reset:b,submit:x}),[d,h,V,C,v,S,p,T,E,b,x]);return jsxRuntime.jsx(L.Provider,{value:A,children:jsxRuntime.jsx("form",{onSubmit:x,className:c,noValidate:true,children:t})})}function R(){let t=se.useContext(L);if(!t)throw new Error("useFormContext must be used within a FormProvider");return t}function ie({formConfig:t,defaultValues:e,onSubmit:r,onFieldChange:n,children:o}){let c=t instanceof w?t.build():t;return jsxRuntime.jsx(D,{formConfig:c,defaultValues:e,onSubmit:r,onFieldChange:n,children:o})}function G({fieldId:t,disabled:e=false,customProps:r={},className:n}){let{formState:o,formConfig:c,setValue:f,markFieldTouched:d,validateField:a}=R(),i=se.useMemo(()=>c.allFields.find(s=>s.id===t),[c.allFields,t]);if(!i)throw new Error(`Field with ID "${t}" not found`);let m=se.useMemo(()=>c.config.getComponent(i.componentId),[c.config,i.componentId]);if(!m)throw new Error(`Component with ID "${i.componentId}" not found`);let l=se.useMemo(()=>({value:o.values[i.id],errors:o.errors[i.id]||[],touched:o.touched.has(i.id),validating:o.isValidating.has(i.id)}),[o.values,o.errors,o.touched,o.isValidating,i.id]),C=se.useCallback(s=>{let u=l.errors.length>0;f(i.id,s),(i.validation?.validateOnChange||u&&i.validation?.validator||l.touched&&i.validation?.validator)&&a(i.id,s);},[i.id,i.validation,f,a,l.errors.length,l.touched]),v=se.useCallback(()=>{d(i.id),(i.validation?.validateOnBlur||i.validation?.validator)&&a(i.id);},[i.id,i.validation,d,a]),S=se.useMemo(()=>{if(!i.conditional)return true;try{return i.conditional.condition(o.values)}catch(s){return console.warn(`Conditional evaluation failed for field "${i.id}":`,s),true}},[i.conditional,o.values,i.id]),p=se.useMemo(()=>{if(!i.conditional||!S)return {};switch(i.conditional.action){case "disable":return {disabled:true};case "require":return {required:true};default:return {}}},[i.conditional,S]),V=!S&&i.conditional?.action==="hide",h=se.useMemo(()=>({...m.defaultProps??{},...i.props,...r,...p}),[m.defaultProps,i.props,r,p]),T=se.useMemo(()=>({id:i.id,props:h,value:l.value,onChange:C,onBlur:v,error:l.errors,touched:l.touched,disabled:e||p.disabled,isValidating:l.validating}),[i.id,h,l.value,C,v,l.errors,l.touched,e,p.disabled,l.validating]),E=c.renderConfig?.fieldRenderer,b=m.renderer(T),x=m.useFieldRenderer!==false,A=E&&x?E({children:b,id:i.id,error:l.errors,touched:l.touched,disabled:e||p.disabled,isValidating:l.validating,...h}):b;return jsxRuntime.jsx("div",{className:n,"data-field-id":i.id,"data-field-type":m.type,style:V?{display:"none !important"}:void 0,children:A})}var q=se__default.default.memo(G);function W({row:t,className:e,children:r,renderAs:n}){let{formConfig:o}=R(),c=t.fields.map(l=>jsxRuntime.jsx(q,{fieldId:l.id},l.id));if(n==="children"||n===true){if(typeof r!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');let l={row:t,children:c,className:e,spacing:t.spacing,alignment:t.alignment};return r(l)}let d=o.renderConfig?.rowRenderer;if(!d)throw new Error(`No rowRenderer configured for form "${o.id}". Please configure a rowRenderer using config.setRowRenderer() or config.setFormRenderConfig().`);let a={row:t,children:c,className:e,spacing:t.spacing,alignment:t.alignment},i=core.resolveRendererChildren(r,a),m={...a,children:i||c};return d(m)}var H=W;function ue({className:t,children:e,renderAs:r}){let{formConfig:n}=R(),o=se.useMemo(()=>n.rows.map(m=>jsxRuntime.jsx(H,{row:m},m.id)),[n.rows]);if(r==="children"||r===true){if(typeof e!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');return e({formConfig:n,children:o,className:t})}let f=n.renderConfig?.bodyRenderer;if(!f)throw new Error(`No bodyRenderer configured for form "${n.id}". Please configure a bodyRenderer using config.setBodyRenderer() or config.setFormRenderConfig().`);let d={formConfig:n,children:o,className:t},a=core.resolveRendererChildren(e,d),i={...d,children:a||o};return f(i)}function ge({className:t,children:e,renderAs:r}){let{formState:n,submit:o,formConfig:c}=R(),f={isSubmitting:n.isSubmitting,isValid:n.isValid,isDirty:n.isDirty,onSubmit:o,className:t};if(r==="children"||r===true){if(typeof e!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');return e(f)}let a=c.renderConfig?.submitButtonRenderer;if(!a)throw new Error(`No submitButtonRenderer configured for form "${c.id}". Please configure a submitButtonRenderer using config.setSubmitButtonRenderer() or config.setFormRenderConfig().`);let i=core.resolveRendererChildren(e,f),m={...f,children:i};return a(m)}Object.defineProperty(exports,"createZodValidator",{enumerable:true,get:function(){return core.createZodValidator}});Object.defineProperty(exports,"ril",{enumerable:true,get:function(){return core.ril}});exports.Form=ie;exports.FormBody=ue;exports.FormBuilder=w;exports.FormField=G;exports.FormProvider=D;exports.FormRow=W;exports.FormSubmitButton=ge;exports.createForm=j;exports.form=w;exports.useFormContext=R;
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- import {ril,resolveRendererChildren}from'@rilaykit/core';export{createZodValidator,ril}from'@rilaykit/core';import or,{createContext,useMemo,useCallback,useContext,useReducer,useRef}from'react';import {jsx}from'react/jsx-runtime';var v=class o{constructor(r,e){this.rows=[];this.rowCounter=0;this.config=r,this.formId=e||`form-${Date.now()}`;}static create(r,e){return new o(r,e)}createFormField(r){let e=this.config.getComponent(r.type);if(!e)throw new Error(`No component found with type "${r.type}"`);return {id:r.id,componentId:e.id,props:{...e.defaultProps,...r.props},validation:r.validation,conditional:r.conditional}}createRow(r,e){if(r.length===0)throw new Error("At least one field is required");if(r.length>3)throw new Error("Maximum 3 fields per row");let i=r.map(s=>this.createFormField(s));return {id:`row-${++this.rowCounter}`,fields:i,maxColumns:r.length,spacing:e?.spacing||"normal",alignment:e?.alignment||"stretch"}}addField(r){return this.addRowFields([r])}addRowFields(r,e){let i=this.createRow(r,e);return this.rows.push(i),this}addFields(r){for(let e of r)this.addField(e);return this}setId(r){return this.formId=r,this}updateField(r,e){let i=this.findField(r);if(!i)throw new Error(`Field with ID "${r}" not found`);return Object.assign(i,{...e,props:{...i.props,...e.props}}),this}findField(r){for(let e of this.rows){let i=e.fields.find(s=>s.id===r);if(i)return i}return null}removeField(r){return this.rows=this.rows.map(e=>({...e,fields:e.fields.filter(i=>i.id!==r)})).filter(e=>e.fields.length>0),this}getField(r){return this.findField(r)||void 0}getFields(){return this.rows.flatMap(r=>r.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.rowCounter=0,this}clone(r){let e=new o(this.config,r||`${this.formId}-clone`);return e.rows=this.rows.map(i=>({...i,fields:i.fields.map(s=>({...s}))})),e.rowCounter=this.rowCounter,e}validate(){let r=[],e=this.getFields(),i=e.map(d=>d.id),s=i.filter((d,u)=>i.indexOf(d)!==u);s.length>0&&r.push(`Duplicate field IDs: ${s.join(", ")}`);for(let d of e)this.config.hasComponent(d.componentId)||r.push(`Component "${d.componentId}" not found for field "${d.id}"`);for(let d of this.rows)d.fields.length>3&&r.push(`Row "${d.id}" has ${d.fields.length} fields, maximum is 3`),d.fields.length===0&&r.push(`Row "${d.id}" is empty`);return r}build(){let r=this.validate();if(r.length>0)throw new Error(`Form validation failed: ${r.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig()}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(r){return r.id&&(this.formId=r.id),r.rows&&(this.rows=r.rows,this.rowCounter=this.rows.length),this}getStats(){let r=this.getFields(),e=this.rows.map(i=>i.fields.length);return {totalFields:r.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?r.length/this.rows.length:0,maxFieldsInRow:e.length>0?Math.max(...e):0,minFieldsInRow:e.length>0?Math.min(...e):0}}};function Z(o,r){return v.create(o,r)}ril.prototype.createForm=function(o){return v.create(this,o)};function j(o,r){switch(r.type){case "SET_VALUE":{let e={...o.values,[r.fieldId]:r.value};return {...o,values:e,isDirty:true}}case "SET_ERROR":return {...o,errors:{...o.errors,[r.fieldId]:r.errors},isValid:false};case "CLEAR_ERROR":{let e={...o.errors};return delete e[r.fieldId],{...o,errors:e}}case "MARK_TOUCHED":return {...o,touched:new Set([...o.touched,r.fieldId])};case "SET_VALIDATING":{let e=new Set(o.isValidating);return r.isValidating?e.add(r.fieldId):e.delete(r.fieldId),{...o,isValidating:e}}case "SET_SUBMITTING":return {...o,isSubmitting:r.isSubmitting};case "RESET":return {values:r.values||{},errors:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false};case "UPDATE_VALIDATION_STATE":{let e=Object.keys(o.errors).some(i=>o.errors[i].length>0);return {...o,isValid:!e}}default:return o}}var k=createContext(null);function B({children:o,formConfig:r,defaultValues:e={},onSubmit:i,onFieldChange:s,className:d}){let u={values:e,errors:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false},[a,l]=useReducer(j,u),t=useRef(new Map),c=useRef(i),m=useRef(s);c.current=i,m.current=s;let R=useCallback((n,f)=>{l({type:"SET_ERROR",fieldId:n,errors:f}),l({type:"UPDATE_VALIDATION_STATE"});},[]),b=useCallback(n=>{l({type:"CLEAR_ERROR",fieldId:n}),l({type:"UPDATE_VALIDATION_STATE"});},[]),V=useCallback(n=>{l({type:"MARK_TOUCHED",fieldId:n});},[]),p=useCallback((n,f)=>{l({type:"SET_VALIDATING",fieldId:n,isValidating:f});},[]),x=useCallback((n,f)=>{if(l({type:"SET_VALUE",fieldId:n,value:f}),a.errors[n]&&a.errors[n].length>0&&(l({type:"CLEAR_ERROR",fieldId:n}),l({type:"UPDATE_VALIDATION_STATE"})),m.current){let g={...a.values,[n]:f};m.current(n,f,g);}},[a.errors,a.values]),w=useMemo(()=>r,[r]),E=useCallback(async(n,f)=>{let g=w.allFields.find(I=>I.id===n);if(!g?.validation?.validator)return {isValid:true,errors:[]};let K=f!==void 0?f:a.values[n],N=t.current.get(n);N&&clearTimeout(N);let W={fieldId:n,formData:a.values,fieldProps:g.props,touched:a.touched.has(n),dirty:a.isDirty},_=g.validation.debounceMs||0;return new Promise(I=>{let O=async()=>{p(n,true);try{let y=await g.validation.validator(K,W,g.props);y.errors.length>0?R(n,y.errors):b(n),I(y);}catch(y){let U={isValid:false,errors:[{code:"validation_error",message:y instanceof Error?y.message:"Validation error"}]};R(n,U.errors),I(U);}finally{p(n,false);}};if(_>0){let y=setTimeout(O,_);t.current.set(n,y);}else O();})},[w,a.values,a.touched,a.isDirty,R,b,p]),P=useCallback(async()=>{let n=w.allFields.map(g=>E(g.id));return (await Promise.all(n)).every(g=>g.isValid)},[w,E]),A=useCallback(n=>{l({type:"RESET",values:n});},[]),T=useCallback(async n=>{if(n?.preventDefault(),!c.current)return true;l({type:"SET_SUBMITTING",isSubmitting:true});try{return await P()?(await c.current(a.values),!0):!1}catch(f){return console.error("Error during form submission:",f),false}finally{l({type:"SET_SUBMITTING",isSubmitting:false});}},[a.values,P]),h=useMemo(()=>({formState:a,formConfig:w,setValue:x,setError:R,clearError:b,markFieldTouched:V,setFieldValidating:p,validateField:E,validateAllFields:P,reset:A,submit:T}),[a,w,x,R,b,V,p,E,P,A,T]);return jsx(k.Provider,{value:h,children:jsx("form",{onSubmit:T,className:d,noValidate:true,children:o})})}function C(){let o=useContext(k);if(!o)throw new Error("useFormContext must be used within a FormProvider");return o}function rr({formConfig:o,defaultValues:r,onSubmit:e,onFieldChange:i,children:s}){let d=o instanceof v?o.build():o;return jsx(B,{formConfig:d,defaultValues:r,onSubmit:e,onFieldChange:i,children:s})}function G({fieldId:o,disabled:r=false,customProps:e={},className:i}){let{formState:s,formConfig:d,setValue:u,markFieldTouched:a,validateField:l}=C(),t=useMemo(()=>d.allFields.find(h=>h.id===o),[d.allFields,o]);if(!t)throw new Error(`Field with ID "${o}" not found`);let c=useMemo(()=>d.config.getComponent(t.componentId),[d.config,t.componentId]);if(!c)throw new Error(`Component with ID "${t.componentId}" not found`);let m=useMemo(()=>({value:s.values[t.id],errors:s.errors[t.id]||[],touched:s.touched.has(t.id),validating:s.isValidating.has(t.id)}),[s.values,s.errors,s.touched,s.isValidating,t.id]),R=useCallback(h=>{let n=m.errors.length>0;u(t.id,h),(t.validation?.validateOnChange||n&&t.validation?.validator||m.touched&&t.validation?.validator)&&l(t.id,h);},[t.id,t.validation,u,l,m.errors.length,m.touched]),b=useCallback(()=>{a(t.id),(t.validation?.validateOnBlur||t.validation?.validator)&&l(t.id);},[t.id,t.validation,a,l]),V=useMemo(()=>{if(!t.conditional)return true;try{return t.conditional.condition(s.values)}catch(h){return console.warn(`Conditional evaluation failed for field "${t.id}":`,h),true}},[t.conditional,s.values,t.id]),p=useMemo(()=>{if(!t.conditional||!V)return {};switch(t.conditional.action){case "disable":return {disabled:true};case "require":return {required:true};default:return {}}},[t.conditional,V]);if(!V&&t.conditional?.action==="hide")return null;let x=useMemo(()=>({...c.defaultProps,...t.props,...e,...p}),[c.defaultProps,t.props,e,p]),w=useMemo(()=>({id:t.id,props:x,value:m.value,onChange:R,onBlur:b,error:m.errors,touched:m.touched,disabled:r||p.disabled,isValidating:m.validating}),[t.id,x,m.value,R,b,m.errors,m.touched,r,p.disabled,m.validating]),E=d.renderConfig?.fieldRenderer,P=c.renderer(w),A=c.useFieldRenderer!==false,T=E&&A?E({children:P,id:t.id,error:m.errors,touched:m.touched,disabled:r||p.disabled,isValidating:m.validating,...x}):P;return jsx("div",{className:i,"data-field-id":t.id,"data-field-type":c.type,children:T})}var q=or.memo(G);function H({row:o,className:r,children:e,renderAs:i}){let{formConfig:s}=C(),d=o.fields.map(m=>jsx(q,{fieldId:m.id},m.id));if(i==="children"||i===true){if(typeof e!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');let m={row:o,children:d,className:r,spacing:o.spacing,alignment:o.alignment};return e(m)}let a=s.renderConfig?.rowRenderer;if(!a)throw new Error(`No rowRenderer configured for form "${s.id}". Please configure a rowRenderer using config.setRowRenderer() or config.setFormRenderConfig().`);let l={row:o,children:d,className:r,spacing:o.spacing,alignment:o.alignment},t=resolveRendererChildren(e,l),c={...l,children:t||d};return a(c)}var J=H;function ar({className:o,children:r,renderAs:e}){let{formConfig:i}=C(),s=useMemo(()=>i.rows.map(c=>jsx(J,{row:c},c.id)),[i.rows]);if(e==="children"||e===true){if(typeof r!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');return r({formConfig:i,children:s,className:o})}let u=i.renderConfig?.bodyRenderer;if(!u)throw new Error(`No bodyRenderer configured for form "${i.id}". Please configure a bodyRenderer using config.setBodyRenderer() or config.setFormRenderConfig().`);let a={formConfig:i,children:s,className:o},l=resolveRendererChildren(r,a),t={...a,children:l||s};return u(t)}function cr({className:o,children:r,renderAs:e}){let{formState:i,submit:s,formConfig:d}=C(),u={isSubmitting:i.isSubmitting,isValid:i.isValid,isDirty:i.isDirty,onSubmit:s,className:o};if(e==="children"||e===true){if(typeof r!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');return r(u)}let l=d.renderConfig?.submitButtonRenderer;if(!l)throw new Error(`No submitButtonRenderer configured for form "${d.id}". Please configure a submitButtonRenderer using config.setSubmitButtonRenderer() or config.setFormRenderConfig().`);let t=resolveRendererChildren(r,u),c={...u,children:t};return l(c)}export{rr as Form,ar as FormBody,v as FormBuilder,G as FormField,B as FormProvider,H as FormRow,cr as FormSubmitButton,Z as createForm,v as form,C as useFormContext};
1
+ import {ril,IdGenerator,deepClone,ValidationErrorBuilder,ensureUnique,resolveRendererChildren}from'@rilaykit/core';export{createZodValidator,ril}from'@rilaykit/core';import se,{createContext,useMemo,useCallback,useContext,useReducer,useRef}from'react';import {jsx}from'react/jsx-runtime';var w=class t{constructor(e,r){this.rows=[];this.idGenerator=new IdGenerator;this.config=e,this.formId=r||`form-${Date.now()}`;}static create(e,r){return new t(e,r)}createFormField(e){let r=this.config.getComponent(e.type);if(!r)throw new Error(`No component found with type "${e.type}"`);return {id:e.id||this.idGenerator.next("field"),componentId:r.id,props:{...r.defaultProps,...e.props},validation:e.validation,conditional:e.conditional}}createRow(e,r){if(e.length===0)throw new Error("At least one field is required");if(e.length>3)throw new Error("Maximum 3 fields per row");let n=e.map(o=>this.createFormField(o));return {id:this.idGenerator.next("row"),fields:n,maxColumns:e.length,spacing:r?.spacing||"normal",alignment:r?.alignment||"stretch"}}add(...e){let r,n,o=false;if(e.length===1&&Array.isArray(e[0])?(r=e[0],o=true):e.length===2&&Array.isArray(e[0])?(r=e[0],n=e[1],o=true):r=e,r.length===0)throw new Error("At least one field is required");if(o&&r.length>3)throw new Error("Maximum 3 fields per row");if(r.length===1){let c=this.createRow(r,n);return this.rows.push(c),this}if(r.length<=3){let c=this.createRow(r,n);return this.rows.push(c),this}for(let c of r){let f=this.createRow([c],n);this.rows.push(f);}return this}addSeparateRows(e,r){for(let n of e)this.add([n],r);return this}setId(e){return this.formId=e,this}updateField(e,r){let n=this.findField(e);if(!n)throw new Error(`Field with ID "${e}" not found`);return Object.assign(n,{...r,props:{...n.props,...r.props}}),this}findField(e){for(let r of this.rows){let n=r.fields.find(o=>o.id===e);if(n)return n}return null}removeField(e){return this.rows=this.rows.map(r=>({...r,fields:r.fields.filter(n=>n.id!==e)})).filter(r=>r.fields.length>0),this}getField(e){return this.findField(e)||void 0}getFields(){return this.rows.flatMap(e=>e.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.idGenerator.reset(),this}clone(e){let r=new t(this.config,e||`${this.formId}-clone`);return r.rows=deepClone(this.rows),r}validate(){let e=new ValidationErrorBuilder,r=this.getFields(),n=r.map(o=>o.id);try{ensureUnique(n,"field");}catch(o){e.add("DUPLICATE_FIELD_IDS",o instanceof Error?o.message:String(o));}for(let o of r)e.addIf(!this.config.hasComponent(o.componentId),"MISSING_COMPONENT",`Component "${o.componentId}" not found for field "${o.id}"`);for(let o of this.rows)e.addIf(o.fields.length>3,"TOO_MANY_FIELDS_IN_ROW",`Row "${o.id}" has ${o.fields.length} fields, maximum is 3`),e.addIf(o.fields.length===0,"EMPTY_ROW",`Row "${o.id}" is empty`);return e.build().map(o=>o.message)}build(){let e=this.validate();if(e.length>0)throw new Error(`Form validation failed: ${e.join(", ")}`);return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),config:this.config,renderConfig:this.config.getFormRenderConfig()}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(e){return e.id&&(this.formId=e.id),e.rows&&(this.rows=e.rows),this}getStats(){let e=this.getFields(),r=this.rows.map(n=>n.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?e.length/this.rows.length:0,maxFieldsInRow:r.length>0?Math.max(...r):0,minFieldsInRow:r.length>0?Math.min(...r):0}}};function j(t,e){return w.create(t,e)}ril.prototype.form=function(t){return w.create(this,t)};function te(t,e){switch(e.type){case "SET_VALUE":{let r={...t.values,[e.fieldId]:e.value};return {...t,values:r,isDirty:true}}case "SET_ERROR":return {...t,errors:{...t.errors,[e.fieldId]:e.errors},isValid:false};case "CLEAR_ERROR":{let r={...t.errors};return delete r[e.fieldId],{...t,errors:r}}case "MARK_TOUCHED":return {...t,touched:new Set([...t.touched,e.fieldId])};case "SET_VALIDATING":{let r=new Set(t.isValidating);return e.isValidating?r.add(e.fieldId):r.delete(e.fieldId),{...t,isValidating:r}}case "SET_SUBMITTING":return {...t,isSubmitting:e.isSubmitting};case "RESET":return {values:e.values||{},errors:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false};case "UPDATE_VALIDATION_STATE":{let r=Object.keys(t.errors).some(n=>t.errors[n].length>0);return {...t,isValid:!r}}default:return t}}var L=createContext(null);function D({children:t,formConfig:e,defaultValues:r={},onSubmit:n,onFieldChange:o,className:c}){let f={values:r,errors:{},touched:new Set,isValidating:new Set,isDirty:false,isValid:true,isSubmitting:false},[d,a]=useReducer(te,f),i=useRef(new Map),m=useRef(n),l=useRef(o);m.current=n,l.current=o;let C=useCallback((s,u)=>{a({type:"SET_ERROR",fieldId:s,errors:u}),a({type:"UPDATE_VALIDATION_STATE"});},[]),v=useCallback(s=>{a({type:"CLEAR_ERROR",fieldId:s}),a({type:"UPDATE_VALIDATION_STATE"});},[]),S=useCallback(s=>{a({type:"MARK_TOUCHED",fieldId:s});},[]),p=useCallback((s,u)=>{a({type:"SET_VALIDATING",fieldId:s,isValidating:u});},[]),V=useCallback((s,u)=>{if(a({type:"SET_VALUE",fieldId:s,value:u}),d.errors[s]&&d.errors[s].length>0&&(a({type:"CLEAR_ERROR",fieldId:s}),a({type:"UPDATE_VALIDATION_STATE"})),l.current){let g={...d.values,[s]:u};l.current(s,u,g);}},[d.errors,d.values]),h=useMemo(()=>e,[e]),T=useCallback(async(s,u)=>{let g=h.allFields.find(I=>I.id===s);if(!g?.validation?.validator)return {isValid:true,errors:[]};let J=u!==void 0?u:d.values[s],O=i.current.get(s);O&&clearTimeout(O);let K={fieldId:s,formData:d.values,fieldProps:g.props,touched:d.touched.has(s),dirty:d.isDirty},N=g.validation.debounceMs||0;return new Promise(I=>{let B=async()=>{p(s,true);try{let y=await g.validation.validator(J,K,g.props);y.errors.length>0?C(s,y.errors):v(s),I(y);}catch(y){let k={isValid:false,errors:[{code:"validation_error",message:y instanceof Error?y.message:"Validation error"}]};C(s,k.errors),I(k);}finally{p(s,false);}};if(N>0){let y=setTimeout(B,N);i.current.set(s,y);}else B();})},[h,d.values,d.touched,d.isDirty,C,v,p]),E=useCallback(async()=>{let s=h.allFields.map(g=>T(g.id));return (await Promise.all(s)).every(g=>g.isValid)},[h,T]),b=useCallback(s=>{a({type:"RESET",values:s});},[]),x=useCallback(async s=>{if(s?.preventDefault(),!m.current)return true;a({type:"SET_SUBMITTING",isSubmitting:true});try{return await E()?(await m.current(d.values),!0):!1}catch(u){return console.error("Error during form submission:",u),false}finally{a({type:"SET_SUBMITTING",isSubmitting:false});}},[d.values,E]),A=useMemo(()=>({formState:d,formConfig:h,setValue:V,setError:C,clearError:v,markFieldTouched:S,setFieldValidating:p,validateField:T,validateAllFields:E,reset:b,submit:x}),[d,h,V,C,v,S,p,T,E,b,x]);return jsx(L.Provider,{value:A,children:jsx("form",{onSubmit:x,className:c,noValidate:true,children:t})})}function R(){let t=useContext(L);if(!t)throw new Error("useFormContext must be used within a FormProvider");return t}function ie({formConfig:t,defaultValues:e,onSubmit:r,onFieldChange:n,children:o}){let c=t instanceof w?t.build():t;return jsx(D,{formConfig:c,defaultValues:e,onSubmit:r,onFieldChange:n,children:o})}function G({fieldId:t,disabled:e=false,customProps:r={},className:n}){let{formState:o,formConfig:c,setValue:f,markFieldTouched:d,validateField:a}=R(),i=useMemo(()=>c.allFields.find(s=>s.id===t),[c.allFields,t]);if(!i)throw new Error(`Field with ID "${t}" not found`);let m=useMemo(()=>c.config.getComponent(i.componentId),[c.config,i.componentId]);if(!m)throw new Error(`Component with ID "${i.componentId}" not found`);let l=useMemo(()=>({value:o.values[i.id],errors:o.errors[i.id]||[],touched:o.touched.has(i.id),validating:o.isValidating.has(i.id)}),[o.values,o.errors,o.touched,o.isValidating,i.id]),C=useCallback(s=>{let u=l.errors.length>0;f(i.id,s),(i.validation?.validateOnChange||u&&i.validation?.validator||l.touched&&i.validation?.validator)&&a(i.id,s);},[i.id,i.validation,f,a,l.errors.length,l.touched]),v=useCallback(()=>{d(i.id),(i.validation?.validateOnBlur||i.validation?.validator)&&a(i.id);},[i.id,i.validation,d,a]),S=useMemo(()=>{if(!i.conditional)return true;try{return i.conditional.condition(o.values)}catch(s){return console.warn(`Conditional evaluation failed for field "${i.id}":`,s),true}},[i.conditional,o.values,i.id]),p=useMemo(()=>{if(!i.conditional||!S)return {};switch(i.conditional.action){case "disable":return {disabled:true};case "require":return {required:true};default:return {}}},[i.conditional,S]),V=!S&&i.conditional?.action==="hide",h=useMemo(()=>({...m.defaultProps??{},...i.props,...r,...p}),[m.defaultProps,i.props,r,p]),T=useMemo(()=>({id:i.id,props:h,value:l.value,onChange:C,onBlur:v,error:l.errors,touched:l.touched,disabled:e||p.disabled,isValidating:l.validating}),[i.id,h,l.value,C,v,l.errors,l.touched,e,p.disabled,l.validating]),E=c.renderConfig?.fieldRenderer,b=m.renderer(T),x=m.useFieldRenderer!==false,A=E&&x?E({children:b,id:i.id,error:l.errors,touched:l.touched,disabled:e||p.disabled,isValidating:l.validating,...h}):b;return jsx("div",{className:n,"data-field-id":i.id,"data-field-type":m.type,style:V?{display:"none !important"}:void 0,children:A})}var q=se.memo(G);function W({row:t,className:e,children:r,renderAs:n}){let{formConfig:o}=R(),c=t.fields.map(l=>jsx(q,{fieldId:l.id},l.id));if(n==="children"||n===true){if(typeof r!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');let l={row:t,children:c,className:e,spacing:t.spacing,alignment:t.alignment};return r(l)}let d=o.renderConfig?.rowRenderer;if(!d)throw new Error(`No rowRenderer configured for form "${o.id}". Please configure a rowRenderer using config.setRowRenderer() or config.setFormRenderConfig().`);let a={row:t,children:c,className:e,spacing:t.spacing,alignment:t.alignment},i=resolveRendererChildren(r,a),m={...a,children:i||c};return d(m)}var H=W;function ue({className:t,children:e,renderAs:r}){let{formConfig:n}=R(),o=useMemo(()=>n.rows.map(m=>jsx(H,{row:m},m.id)),[n.rows]);if(r==="children"||r===true){if(typeof e!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');return e({formConfig:n,children:o,className:t})}let f=n.renderConfig?.bodyRenderer;if(!f)throw new Error(`No bodyRenderer configured for form "${n.id}". Please configure a bodyRenderer using config.setBodyRenderer() or config.setFormRenderConfig().`);let d={formConfig:n,children:o,className:t},a=resolveRendererChildren(e,d),i={...d,children:a||o};return f(i)}function ge({className:t,children:e,renderAs:r}){let{formState:n,submit:o,formConfig:c}=R(),f={isSubmitting:n.isSubmitting,isValid:n.isValid,isDirty:n.isDirty,onSubmit:o,className:t};if(r==="children"||r===true){if(typeof e!="function")throw new Error('When renderAs="children" is used, children must be a function that returns React elements');return e(f)}let a=c.renderConfig?.submitButtonRenderer;if(!a)throw new Error(`No submitButtonRenderer configured for form "${c.id}". Please configure a submitButtonRenderer using config.setSubmitButtonRenderer() or config.setFormRenderConfig().`);let i=resolveRendererChildren(e,f),m={...f,children:i};return a(m)}export{ie as Form,ue as FormBody,w as FormBuilder,G as FormField,D as FormProvider,W as FormRow,ge as FormSubmitButton,j as createForm,w as form,R as useFormContext};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rilaykit/forms",
3
- "version": "2.0.1",
3
+ "version": "4.0.0",
4
4
  "description": "Form building utilities and components for RilayKit",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -24,7 +24,7 @@
24
24
  "url": "https://github.com/andyoucreate/rilay/issues"
25
25
  },
26
26
  "dependencies": {
27
- "@rilaykit/core": "2.0.1"
27
+ "@rilaykit/core": "4.0.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "react": ">=18.0.0",