@rilaykit/forms 0.1.1 → 0.1.3
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 +239 -0
- package/dist/index.d.mts +220 -10
- package/dist/index.d.ts +220 -10
- package/dist/index.js +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +12 -9
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# @rilaykit/forms
|
|
2
|
+
|
|
3
|
+
The form builder and React rendering layer for [RilayKit](https://rilay.dev) — build type-safe, headless forms from declarative schemas.
|
|
4
|
+
|
|
5
|
+
`@rilaykit/forms` provides a fluent builder API to define form configurations and headless React components to render them. State management is powered by Zustand with granular selectors for optimal re-render performance.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# pnpm (recommended)
|
|
11
|
+
pnpm add @rilaykit/core @rilaykit/forms
|
|
12
|
+
|
|
13
|
+
# npm
|
|
14
|
+
npm install @rilaykit/core @rilaykit/forms
|
|
15
|
+
|
|
16
|
+
# yarn
|
|
17
|
+
yarn add @rilaykit/core @rilaykit/forms
|
|
18
|
+
|
|
19
|
+
# bun
|
|
20
|
+
bun add @rilaykit/core @rilaykit/forms
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> `@rilaykit/core` is a required peer dependency.
|
|
24
|
+
|
|
25
|
+
### Requirements
|
|
26
|
+
|
|
27
|
+
- React >= 18
|
|
28
|
+
- React DOM >= 18
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### 1. Create Your Registry
|
|
33
|
+
|
|
34
|
+
```tsx
|
|
35
|
+
import { ril } from '@rilaykit/core';
|
|
36
|
+
import { Input } from './components/Input';
|
|
37
|
+
|
|
38
|
+
const rilay = ril.create()
|
|
39
|
+
.addComponent('input', { renderer: Input });
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2. Build a Form
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { required, email } from '@rilaykit/core';
|
|
46
|
+
|
|
47
|
+
const loginForm = rilay
|
|
48
|
+
.form('login')
|
|
49
|
+
.add({
|
|
50
|
+
id: 'email',
|
|
51
|
+
type: 'input',
|
|
52
|
+
props: { label: 'Email', type: 'email' },
|
|
53
|
+
validation: { validate: [required(), email()] },
|
|
54
|
+
})
|
|
55
|
+
.add({
|
|
56
|
+
id: 'password',
|
|
57
|
+
type: 'input',
|
|
58
|
+
props: { label: 'Password', type: 'password' },
|
|
59
|
+
validation: { validate: [required()] },
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 3. Render It
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { Form, FormField } from '@rilaykit/forms';
|
|
67
|
+
|
|
68
|
+
function LoginForm() {
|
|
69
|
+
const handleSubmit = (data: { email: string; password: string }) => {
|
|
70
|
+
console.log('Login:', data);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<Form formConfig={loginForm} onSubmit={handleSubmit}>
|
|
75
|
+
<FormField fieldId="email" />
|
|
76
|
+
<FormField fieldId="password" />
|
|
77
|
+
<button type="submit">Sign In</button>
|
|
78
|
+
</Form>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Features
|
|
84
|
+
|
|
85
|
+
### Fluent Form Builder
|
|
86
|
+
|
|
87
|
+
Construct forms with a chainable, type-safe API. Each field type and its props are validated at compile time.
|
|
88
|
+
|
|
89
|
+
```tsx
|
|
90
|
+
const contactForm = rilay
|
|
91
|
+
.form('contact')
|
|
92
|
+
.add(
|
|
93
|
+
{ id: 'firstName', type: 'input', props: { label: 'First Name' } },
|
|
94
|
+
{ id: 'lastName', type: 'input', props: { label: 'Last Name' } },
|
|
95
|
+
)
|
|
96
|
+
.add({
|
|
97
|
+
id: 'message',
|
|
98
|
+
type: 'textarea',
|
|
99
|
+
props: { label: 'Message', rows: 5 },
|
|
100
|
+
validation: { validate: [required()] },
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// Serialize, clone, inspect
|
|
104
|
+
const json = contactForm.toJSON();
|
|
105
|
+
const variant = contactForm.clone('contact-v2');
|
|
106
|
+
const stats = contactForm.getStats();
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Headless React Components
|
|
110
|
+
|
|
111
|
+
Zero HTML, zero CSS. You provide the renderers, RilayKit handles state, validation, and orchestration.
|
|
112
|
+
|
|
113
|
+
| Component | Description |
|
|
114
|
+
|-----------|-------------|
|
|
115
|
+
| `<Form>` | Main wrapper — manages context, state, and submission |
|
|
116
|
+
| `<FormProvider>` | Context provider (used separately from Form when needed) |
|
|
117
|
+
| `<FormBody>` | Renders the full form body from configuration |
|
|
118
|
+
| `<FormField>` | Renders a single field by ID |
|
|
119
|
+
| `<FormRow>` | Renders a row of fields |
|
|
120
|
+
| `<FormSubmitButton>` | Submit button with loading/disabled state |
|
|
121
|
+
|
|
122
|
+
### Zustand-Powered Store
|
|
123
|
+
|
|
124
|
+
Each form instance gets its own Zustand store with granular selectors — only the fields that change trigger re-renders.
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
import {
|
|
128
|
+
useFieldValue,
|
|
129
|
+
useFieldErrors,
|
|
130
|
+
useFieldTouched,
|
|
131
|
+
useFieldState,
|
|
132
|
+
useFormValues,
|
|
133
|
+
useFormValid,
|
|
134
|
+
useFormDirty,
|
|
135
|
+
useFormSubmitting,
|
|
136
|
+
useFieldActions,
|
|
137
|
+
useFormActions,
|
|
138
|
+
} from '@rilaykit/forms';
|
|
139
|
+
|
|
140
|
+
function CustomField({ fieldId }: { fieldId: string }) {
|
|
141
|
+
const value = useFieldValue(fieldId);
|
|
142
|
+
const errors = useFieldErrors(fieldId);
|
|
143
|
+
const { setValue, setTouched } = useFieldActions(fieldId);
|
|
144
|
+
// ...
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### Conditional Fields
|
|
149
|
+
|
|
150
|
+
Combined with `@rilaykit/core`'s condition system, fields show/hide reactively based on other field values.
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { when } from '@rilaykit/core';
|
|
154
|
+
|
|
155
|
+
rilay.form('account')
|
|
156
|
+
.add({
|
|
157
|
+
id: 'accountType',
|
|
158
|
+
type: 'select',
|
|
159
|
+
props: { options: [{ value: 'business', label: 'Business' }] },
|
|
160
|
+
})
|
|
161
|
+
.add({
|
|
162
|
+
id: 'companyName',
|
|
163
|
+
type: 'input',
|
|
164
|
+
props: { label: 'Company Name' },
|
|
165
|
+
conditions: { visible: when('accountType').equals('business') },
|
|
166
|
+
});
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Validation
|
|
170
|
+
|
|
171
|
+
Supports built-in validators, Standard Schema libraries (Zod, Valibot, Yup...), and custom validators — all in the same field.
|
|
172
|
+
|
|
173
|
+
```tsx
|
|
174
|
+
import { z } from 'zod';
|
|
175
|
+
import { required } from '@rilaykit/core';
|
|
176
|
+
|
|
177
|
+
validation: {
|
|
178
|
+
validate: [required(), z.string().email()],
|
|
179
|
+
validateOnBlur: true,
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## API Overview
|
|
184
|
+
|
|
185
|
+
### Builder
|
|
186
|
+
|
|
187
|
+
| Method | Description |
|
|
188
|
+
|--------|-------------|
|
|
189
|
+
| `form.create(ril, id?)` | Create a new form builder |
|
|
190
|
+
| `.add(...fields)` | Add fields (1-3 per row) |
|
|
191
|
+
| `.addSeparateRows(fields)` | Each field on its own row |
|
|
192
|
+
| `.updateField(id, updates)` | Update a field definition |
|
|
193
|
+
| `.removeField(id)` | Remove a field |
|
|
194
|
+
| `.setValidation(config)` | Set form-level validation |
|
|
195
|
+
| `.addFieldConditions(id, conditions)` | Add conditional logic |
|
|
196
|
+
| `.build()` | Produce the final `FormConfiguration` |
|
|
197
|
+
| `.toJSON()` / `.fromJSON(json)` | Serialize / deserialize |
|
|
198
|
+
| `.clone(newId?)` | Clone the form configuration |
|
|
199
|
+
|
|
200
|
+
### Hooks
|
|
201
|
+
|
|
202
|
+
| Hook | Description |
|
|
203
|
+
|------|-------------|
|
|
204
|
+
| `useFieldValue(id)` | Current field value |
|
|
205
|
+
| `useFieldErrors(id)` | Field validation errors |
|
|
206
|
+
| `useFieldTouched(id)` | Whether field has been touched |
|
|
207
|
+
| `useFieldState(id)` | Combined field state |
|
|
208
|
+
| `useFieldActions(id)` | `setValue`, `setTouched`, etc. |
|
|
209
|
+
| `useFieldConditions(id)` | Evaluated condition results |
|
|
210
|
+
| `useFormValues()` | All form values |
|
|
211
|
+
| `useFormValid()` | Whether form is valid |
|
|
212
|
+
| `useFormDirty()` | Whether form has unsaved changes |
|
|
213
|
+
| `useFormSubmitting()` | Whether form is submitting |
|
|
214
|
+
| `useFormActions()` | `submit`, `reset`, `validate`, etc. |
|
|
215
|
+
|
|
216
|
+
## Architecture
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
@rilaykit/core (registry, types, validation, conditions)
|
|
220
|
+
↑
|
|
221
|
+
@rilaykit/forms ← you are here
|
|
222
|
+
↑
|
|
223
|
+
@rilaykit/workflow (multi-step workflows)
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
## Documentation
|
|
227
|
+
|
|
228
|
+
Full documentation at [rilay.dev](https://rilay.dev):
|
|
229
|
+
|
|
230
|
+
- [Building Forms](https://rilay.dev/forms/building-forms)
|
|
231
|
+
- [Rendering Forms](https://rilay.dev/forms/rendering-forms)
|
|
232
|
+
- [Form Validation](https://rilay.dev/forms/validation)
|
|
233
|
+
- [Advanced Forms](https://rilay.dev/forms/advanced-forms)
|
|
234
|
+
- [Form Hooks](https://rilay.dev/forms/hooks)
|
|
235
|
+
- [API Reference](https://rilay.dev/api)
|
|
236
|
+
|
|
237
|
+
## License
|
|
238
|
+
|
|
239
|
+
MIT — see [LICENSE](./LICENSE) for details.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,66 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ril, FieldValidationConfig, ConditionalBehavior, FormFieldConfig,
|
|
2
|
+
import { ril, FieldValidationConfig, RepeatableFieldConfig, ConditionalBehavior, FormFieldConfig, FormRowEntry, FormValidationConfig, SubmitOptions, FormConfiguration, ComponentRendererBaseProps, FormBodyRendererProps, FieldConditions, FormState, ValidationError, ValidationState, FieldState, ValidationResult, RepeatableFieldItem, MonitoringConfig, FormPerformanceMetrics, FormRowRendererProps, FormFieldRow, FormSubmitButtonRendererProps } from '@rilaykit/core';
|
|
3
3
|
import * as React$1 from 'react';
|
|
4
4
|
import React__default from 'react';
|
|
5
5
|
import * as zustand from 'zustand';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Fluent builder for configuring repeatable field groups
|
|
9
|
+
*
|
|
10
|
+
* Used via callback in `form.addRepeatable()`:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* form.create(ril, "order")
|
|
13
|
+
* .addRepeatable("items", r => r
|
|
14
|
+
* .add(
|
|
15
|
+
* { id: "name", type: "text", props: { label: "Item" } },
|
|
16
|
+
* { id: "qty", type: "number", props: { label: "Qty" } }
|
|
17
|
+
* )
|
|
18
|
+
* .min(1)
|
|
19
|
+
* .max(10)
|
|
20
|
+
* .defaultValue({ name: "", qty: 1 })
|
|
21
|
+
* )
|
|
22
|
+
* .build()
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare class RepeatableBuilder<C extends Record<string, any>> {
|
|
26
|
+
private innerForm;
|
|
27
|
+
private _min?;
|
|
28
|
+
private _max?;
|
|
29
|
+
private _defaultValue?;
|
|
30
|
+
private _validation?;
|
|
31
|
+
constructor(config: ril<C>);
|
|
32
|
+
/**
|
|
33
|
+
* Add fields to the repeatable template
|
|
34
|
+
* Same API as form.add() — variadic ≤3 puts them on the same row
|
|
35
|
+
*/
|
|
36
|
+
add<T extends keyof C & string>(...fields: FieldConfig<C, T>[]): this;
|
|
37
|
+
add<T extends keyof C & string>(fields: FieldConfig<C, T>[]): this;
|
|
38
|
+
/**
|
|
39
|
+
* Add fields each on their own row
|
|
40
|
+
*/
|
|
41
|
+
addSeparateRows<T extends keyof C & string>(fields: FieldConfig<C, T>[]): this;
|
|
42
|
+
/**
|
|
43
|
+
* Set minimum number of items (defaults to 0)
|
|
44
|
+
*/
|
|
45
|
+
min(value: number): this;
|
|
46
|
+
/**
|
|
47
|
+
* Set maximum number of items (unlimited if not set)
|
|
48
|
+
*/
|
|
49
|
+
max(value: number): this;
|
|
50
|
+
/**
|
|
51
|
+
* Set default values for new items
|
|
52
|
+
*/
|
|
53
|
+
defaultValue(value: Record<string, unknown>): this;
|
|
54
|
+
/**
|
|
55
|
+
* Set group-level validation (applied to the entire array)
|
|
56
|
+
*/
|
|
57
|
+
validation(config: FieldValidationConfig): this;
|
|
58
|
+
/** @internal — called by form.addRepeatable */
|
|
59
|
+
_build(id: string): RepeatableFieldConfig;
|
|
60
|
+
/** @internal — check if the inner builder has repeatables (nesting forbidden) */
|
|
61
|
+
_hasRepeatables(): boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
7
64
|
/**
|
|
8
65
|
* Configuration for a form field with type safety
|
|
9
66
|
*
|
|
@@ -88,6 +145,8 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
88
145
|
private idGenerator;
|
|
89
146
|
/** Form-level validation configuration */
|
|
90
147
|
private formValidation?;
|
|
148
|
+
/** Default submit options for this form */
|
|
149
|
+
private _submitOptions?;
|
|
91
150
|
/**
|
|
92
151
|
* Creates a new form builder instance
|
|
93
152
|
*
|
|
@@ -206,6 +265,30 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
206
265
|
* ```
|
|
207
266
|
*/
|
|
208
267
|
addSeparateRows<T extends keyof C & string>(fieldConfigs: FieldConfig<C, T>[]): this;
|
|
268
|
+
/**
|
|
269
|
+
* Adds a repeatable field group to the form
|
|
270
|
+
*
|
|
271
|
+
* Repeatable fields allow users to add/remove instances of a group of fields
|
|
272
|
+
* at runtime (e.g., "Add another item", "Add another contact").
|
|
273
|
+
*
|
|
274
|
+
* @param id - Unique identifier for the repeatable group (cannot contain [ or ])
|
|
275
|
+
* @param configure - Callback receiving a RepeatableBuilder for fluent configuration
|
|
276
|
+
* @returns The form builder instance for method chaining
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* builder.addRepeatable("items", r => r
|
|
281
|
+
* .add(
|
|
282
|
+
* { id: "name", type: "text", props: { label: "Item" } },
|
|
283
|
+
* { id: "qty", type: "number", props: { label: "Qty" } }
|
|
284
|
+
* )
|
|
285
|
+
* .min(1)
|
|
286
|
+
* .max(10)
|
|
287
|
+
* .defaultValue({ name: "", qty: 1 })
|
|
288
|
+
* );
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
addRepeatable(id: string, configure: (builder: RepeatableBuilder<C>) => RepeatableBuilder<C>): this;
|
|
209
292
|
/**
|
|
210
293
|
* Sets the form identifier
|
|
211
294
|
*
|
|
@@ -308,7 +391,7 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
308
391
|
* console.log(`Form has ${rows.length} rows`);
|
|
309
392
|
* ```
|
|
310
393
|
*/
|
|
311
|
-
getRows():
|
|
394
|
+
getRows(): FormRowEntry[];
|
|
312
395
|
/**
|
|
313
396
|
* Clears all fields and rows from the form
|
|
314
397
|
*
|
|
@@ -349,6 +432,24 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
349
432
|
* ```
|
|
350
433
|
*/
|
|
351
434
|
setValidation(validationConfig: FormValidationConfig): this;
|
|
435
|
+
/**
|
|
436
|
+
* Sets default submit options for this form
|
|
437
|
+
*
|
|
438
|
+
* These options can be overridden at submit-time by passing options to `submit()`.
|
|
439
|
+
*
|
|
440
|
+
* @param options - Submit options to use as defaults
|
|
441
|
+
* @returns The form builder instance for method chaining
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* // Always skip invalid fields on submit
|
|
446
|
+
* builder.setSubmitOptions({ skipInvalid: true });
|
|
447
|
+
*
|
|
448
|
+
* // Force submit by default (bypass validation)
|
|
449
|
+
* builder.setSubmitOptions({ force: true });
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
setSubmitOptions(options: SubmitOptions): this;
|
|
352
453
|
/**
|
|
353
454
|
* Adds validators to the form-level validation
|
|
354
455
|
*
|
|
@@ -516,16 +617,20 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
516
617
|
* - Useful for form complexity analysis
|
|
517
618
|
*/
|
|
518
619
|
getStats(): {
|
|
519
|
-
/** Total number of fields across all rows */
|
|
620
|
+
/** Total number of static fields across all rows */
|
|
520
621
|
totalFields: number;
|
|
521
622
|
/** Total number of rows in the form */
|
|
522
623
|
totalRows: number;
|
|
523
|
-
/** Average number of fields per row */
|
|
624
|
+
/** Average number of fields per row (field rows only) */
|
|
524
625
|
averageFieldsPerRow: number;
|
|
525
626
|
/** Maximum number of fields in any single row */
|
|
526
627
|
maxFieldsInRow: number;
|
|
527
628
|
/** Minimum number of fields in any single row */
|
|
528
629
|
minFieldsInRow: number;
|
|
630
|
+
/** Total number of repeatable groups */
|
|
631
|
+
totalRepeatables: number;
|
|
632
|
+
/** Total number of fields across all repeatable templates */
|
|
633
|
+
totalRepeatableFields: number;
|
|
529
634
|
};
|
|
530
635
|
}
|
|
531
636
|
|
|
@@ -537,12 +642,14 @@ interface FormProps {
|
|
|
537
642
|
className?: string;
|
|
538
643
|
children: React.ReactNode;
|
|
539
644
|
}
|
|
540
|
-
declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, children }: FormProps): react_jsx_runtime.JSX.Element;
|
|
645
|
+
declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, className, children, }: FormProps): react_jsx_runtime.JSX.Element;
|
|
541
646
|
|
|
542
647
|
declare const FormBody: React__default.NamedExoticComponent<ComponentRendererBaseProps<FormBodyRendererProps>>;
|
|
543
648
|
|
|
544
649
|
interface FormFieldProps {
|
|
545
650
|
fieldId: string;
|
|
651
|
+
/** Pre-resolved field config (used by RepeatableItem to skip allFields lookup) */
|
|
652
|
+
fieldConfig?: FormFieldConfig;
|
|
546
653
|
disabled?: boolean;
|
|
547
654
|
customProps?: Record<string, unknown>;
|
|
548
655
|
className?: string;
|
|
@@ -626,6 +733,8 @@ declare function useFieldConditionsWithRefresh(fieldId: string, conditions?: Con
|
|
|
626
733
|
interface UseFormConditionsProps {
|
|
627
734
|
formConfig: FormConfiguration;
|
|
628
735
|
formValues: Record<string, any>;
|
|
736
|
+
/** Active repeatable item keys, keyed by repeatable ID */
|
|
737
|
+
repeatableOrder?: Record<string, string[]>;
|
|
629
738
|
}
|
|
630
739
|
interface UseFormConditionsReturn {
|
|
631
740
|
fieldConditions: Record<string, ConditionEvaluationResult>;
|
|
@@ -662,11 +771,14 @@ interface UseFormConditionsReturn {
|
|
|
662
771
|
* }
|
|
663
772
|
* ```
|
|
664
773
|
*/
|
|
665
|
-
declare function useFormConditions({ formConfig, formValues, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
774
|
+
declare function useFormConditions({ formConfig, formValues, repeatableOrder, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
666
775
|
|
|
667
776
|
interface FormStoreState extends FormState {
|
|
668
777
|
_defaultValues: Record<string, unknown>;
|
|
669
778
|
_fieldConditions: Record<string, FieldConditions>;
|
|
779
|
+
_repeatableConfigs: Record<string, RepeatableFieldConfig>;
|
|
780
|
+
_repeatableOrder: Record<string, string[]>;
|
|
781
|
+
_repeatableNextKey: Record<string, number>;
|
|
670
782
|
_setValue: (fieldId: string, value: unknown) => void;
|
|
671
783
|
_setTouched: (fieldId: string) => void;
|
|
672
784
|
_setErrors: (fieldId: string, errors: ValidationError[]) => void;
|
|
@@ -676,6 +788,11 @@ interface FormStoreState extends FormState {
|
|
|
676
788
|
_reset: (values?: Record<string, unknown>) => void;
|
|
677
789
|
_setFieldConditions: (fieldId: string, conditions: FieldConditions) => void;
|
|
678
790
|
_updateIsValid: () => void;
|
|
791
|
+
_setRepeatableConfig: (id: string, config: RepeatableFieldConfig) => void;
|
|
792
|
+
_appendRepeatableItem: (repeatableId: string, defaultValue?: Record<string, unknown>) => string | null;
|
|
793
|
+
_removeRepeatableItem: (repeatableId: string, key: string) => boolean;
|
|
794
|
+
_moveRepeatableItem: (repeatableId: string, fromIndex: number, toIndex: number) => void;
|
|
795
|
+
_insertRepeatableItem: (repeatableId: string, index: number, defaultValue?: Record<string, unknown>) => string | null;
|
|
679
796
|
}
|
|
680
797
|
type FormStore = ReturnType<typeof createFormStore>;
|
|
681
798
|
declare function createFormStore(initialValues?: Record<string, unknown>): Omit<zustand.StoreApi<FormStoreState>, "subscribe"> & {
|
|
@@ -749,6 +866,10 @@ declare function useFormSubmitState(): {
|
|
|
749
866
|
isValid: boolean;
|
|
750
867
|
isDirty: boolean;
|
|
751
868
|
};
|
|
869
|
+
/**
|
|
870
|
+
* Select ordered keys for a repeatable field — re-renders when the order changes
|
|
871
|
+
*/
|
|
872
|
+
declare function useRepeatableKeys(repeatableId: string): string[];
|
|
752
873
|
interface UseFieldActionsResult {
|
|
753
874
|
setValue: (value: unknown) => void;
|
|
754
875
|
setTouched: () => void;
|
|
@@ -783,9 +904,10 @@ interface UseFormSubmissionWithStoreProps {
|
|
|
783
904
|
store: FormStore;
|
|
784
905
|
onSubmit?: (data: Record<string, unknown>) => void | Promise<void>;
|
|
785
906
|
validateForm: () => Promise<ValidationResult>;
|
|
907
|
+
defaultSubmitOptions?: SubmitOptions;
|
|
786
908
|
}
|
|
787
|
-
declare function useFormSubmissionWithStore({ store, onSubmit, validateForm, }: UseFormSubmissionWithStoreProps): {
|
|
788
|
-
submit: (
|
|
909
|
+
declare function useFormSubmissionWithStore({ store, onSubmit, validateForm, defaultSubmitOptions, }: UseFormSubmissionWithStoreProps): {
|
|
910
|
+
submit: (eventOrOptions?: React__default.FormEvent | SubmitOptions) => Promise<boolean>;
|
|
789
911
|
};
|
|
790
912
|
|
|
791
913
|
interface UseFormValidationWithStoreProps {
|
|
@@ -798,6 +920,45 @@ declare function useFormValidationWithStore({ formConfig, store, conditionsHelpe
|
|
|
798
920
|
validateForm: () => Promise<ValidationResult>;
|
|
799
921
|
};
|
|
800
922
|
|
|
923
|
+
interface UseRepeatableFieldReturn {
|
|
924
|
+
items: RepeatableFieldItem[];
|
|
925
|
+
append: (defaultValue?: Record<string, unknown>) => void;
|
|
926
|
+
remove: (key: string) => void;
|
|
927
|
+
move: (fromIndex: number, toIndex: number) => void;
|
|
928
|
+
canAdd: boolean;
|
|
929
|
+
canRemove: boolean;
|
|
930
|
+
count: number;
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Hook to manage a repeatable field group
|
|
934
|
+
*
|
|
935
|
+
* Provides the list of items and actions to add, remove, and reorder them.
|
|
936
|
+
* Each item contains scoped field configs ready for rendering.
|
|
937
|
+
*
|
|
938
|
+
* @param repeatableId - The ID of the repeatable group (as defined in addRepeatable)
|
|
939
|
+
* @returns Items, actions, and constraints
|
|
940
|
+
*
|
|
941
|
+
* @example
|
|
942
|
+
* ```tsx
|
|
943
|
+
* const { items, append, remove, canAdd, canRemove } = useRepeatableField("items");
|
|
944
|
+
*
|
|
945
|
+
* return (
|
|
946
|
+
* <div>
|
|
947
|
+
* {items.map(item => (
|
|
948
|
+
* <div key={item.key}>
|
|
949
|
+
* {item.allFields.map(field => (
|
|
950
|
+
* <FormField key={field.id} fieldId={field.id} fieldConfig={field} />
|
|
951
|
+
* ))}
|
|
952
|
+
* {canRemove && <button onClick={() => remove(item.key)}>Remove</button>}
|
|
953
|
+
* </div>
|
|
954
|
+
* ))}
|
|
955
|
+
* {canAdd && <button onClick={append}>Add</button>}
|
|
956
|
+
* </div>
|
|
957
|
+
* );
|
|
958
|
+
* ```
|
|
959
|
+
*/
|
|
960
|
+
declare function useRepeatableField(repeatableId: string): UseRepeatableFieldReturn;
|
|
961
|
+
|
|
801
962
|
interface UseFormMonitoringProps {
|
|
802
963
|
formConfig: FormConfiguration;
|
|
803
964
|
monitoring?: MonitoringConfig;
|
|
@@ -818,7 +979,7 @@ interface FormConfigContextValue {
|
|
|
818
979
|
conditionsHelpers: Omit<UseFormConditionsReturn, 'fieldConditions'>;
|
|
819
980
|
validateField: (fieldId: string, value?: unknown) => Promise<ValidationResult>;
|
|
820
981
|
validateForm: () => Promise<ValidationResult>;
|
|
821
|
-
submit: (
|
|
982
|
+
submit: (eventOrOptions?: React__default.FormEvent | SubmitOptions) => Promise<boolean>;
|
|
822
983
|
}
|
|
823
984
|
/**
|
|
824
985
|
* Access form configuration and validation methods
|
|
@@ -848,4 +1009,53 @@ interface FormSubmitButtonProps extends ComponentRendererBaseProps<FormSubmitBut
|
|
|
848
1009
|
}
|
|
849
1010
|
declare const FormSubmitButton: React__default.NamedExoticComponent<FormSubmitButtonProps>;
|
|
850
1011
|
|
|
851
|
-
|
|
1012
|
+
interface RepeatableFieldProps {
|
|
1013
|
+
repeatableId: string;
|
|
1014
|
+
repeatableConfig: RepeatableFieldConfig;
|
|
1015
|
+
className?: string;
|
|
1016
|
+
}
|
|
1017
|
+
declare const RepeatableField: React__default.NamedExoticComponent<RepeatableFieldProps>;
|
|
1018
|
+
|
|
1019
|
+
interface RepeatableItemProps {
|
|
1020
|
+
item: RepeatableFieldItem;
|
|
1021
|
+
index: number;
|
|
1022
|
+
total: number;
|
|
1023
|
+
canRemove: boolean;
|
|
1024
|
+
canMoveUp: boolean;
|
|
1025
|
+
canMoveDown: boolean;
|
|
1026
|
+
onRemove: () => void;
|
|
1027
|
+
onMoveUp: () => void;
|
|
1028
|
+
onMoveDown: () => void;
|
|
1029
|
+
}
|
|
1030
|
+
declare const RepeatableItem: React__default.NamedExoticComponent<RepeatableItemProps>;
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Converts flat store values with composite keys into structured nested data.
|
|
1034
|
+
*
|
|
1035
|
+
* Input (store values):
|
|
1036
|
+
* { customerName: "John", "items[k0].name": "Widget", "items[k0].qty": 2, "items[k1].name": "Gadget", "items[k1].qty": 1 }
|
|
1037
|
+
*
|
|
1038
|
+
* Output (structured):
|
|
1039
|
+
* { customerName: "John", items: [{ name: "Widget", qty: 2 }, { name: "Gadget", qty: 1 }] }
|
|
1040
|
+
*/
|
|
1041
|
+
declare function structureFormValues(values: Record<string, unknown>, repeatableConfigs: Record<string, RepeatableFieldConfig>, repeatableOrder: Record<string, string[]>): Record<string, unknown>;
|
|
1042
|
+
/**
|
|
1043
|
+
* Converts structured nested data into flat store values with composite keys.
|
|
1044
|
+
*
|
|
1045
|
+
* Input (structured):
|
|
1046
|
+
* { customerName: "John", items: [{ name: "Widget", qty: 2 }, { name: "Gadget", qty: 1 }] }
|
|
1047
|
+
*
|
|
1048
|
+
* Output:
|
|
1049
|
+
* {
|
|
1050
|
+
* values: { customerName: "John", "items[k0].name": "Widget", "items[k0].qty": 2, "items[k1].name": "Gadget", "items[k1].qty": 1 },
|
|
1051
|
+
* order: { items: ["k0", "k1"] },
|
|
1052
|
+
* nextKeys: { items: 2 }
|
|
1053
|
+
* }
|
|
1054
|
+
*/
|
|
1055
|
+
declare function flattenRepeatableValues(data: Record<string, unknown>, repeatableConfigs: Record<string, RepeatableFieldConfig>): {
|
|
1056
|
+
values: Record<string, unknown>;
|
|
1057
|
+
order: Record<string, string[]>;
|
|
1058
|
+
nextKeys: Record<string, number>;
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
export { type ConditionEvaluationResult, type FieldConfig, Form, FormBody, form as FormBuilder, type FormConfigContextValue, FormField, FormProvider, type FormProviderProps, FormRow, type FormStore, FormStoreContext, type FormStoreState, FormSubmitButton, RepeatableBuilder, RepeatableField, RepeatableItem, type UseFieldActionsResult, type UseFieldConditionsLazyOptions, type UseFormActionsResult, type UseFormConditionsProps, type UseFormConditionsReturn, type UseFormMonitoringProps, type UseFormMonitoringReturn, type UseFormSubmissionWithStoreProps, type UseFormValidationWithStoreProps, type UseRepeatableFieldReturn, createFormStore, flattenRepeatableValues, form, structureFormValues, useConditionEvaluation, useConditionEvaluator, useFieldActions, useFieldConditions, useFieldConditionsLazy, useFieldConditionsWithRefresh, useFieldErrors, useFieldState, useFieldTouched, useFieldValidationState, useFieldValue, useFormActions, useFormConditions, useFormConfigContext, useFormDirty, useFormMonitoring, useFormStore, useFormStoreApi, useFormSubmissionWithStore, useFormSubmitState, useFormSubmitting, useFormValid, useFormValidationWithStore, useFormValues, useMultipleConditionEvaluation, useRepeatableField, useRepeatableKeys };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,66 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
-
import { ril, FieldValidationConfig, ConditionalBehavior, FormFieldConfig,
|
|
2
|
+
import { ril, FieldValidationConfig, RepeatableFieldConfig, ConditionalBehavior, FormFieldConfig, FormRowEntry, FormValidationConfig, SubmitOptions, FormConfiguration, ComponentRendererBaseProps, FormBodyRendererProps, FieldConditions, FormState, ValidationError, ValidationState, FieldState, ValidationResult, RepeatableFieldItem, MonitoringConfig, FormPerformanceMetrics, FormRowRendererProps, FormFieldRow, FormSubmitButtonRendererProps } from '@rilaykit/core';
|
|
3
3
|
import * as React$1 from 'react';
|
|
4
4
|
import React__default from 'react';
|
|
5
5
|
import * as zustand from 'zustand';
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Fluent builder for configuring repeatable field groups
|
|
9
|
+
*
|
|
10
|
+
* Used via callback in `form.addRepeatable()`:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* form.create(ril, "order")
|
|
13
|
+
* .addRepeatable("items", r => r
|
|
14
|
+
* .add(
|
|
15
|
+
* { id: "name", type: "text", props: { label: "Item" } },
|
|
16
|
+
* { id: "qty", type: "number", props: { label: "Qty" } }
|
|
17
|
+
* )
|
|
18
|
+
* .min(1)
|
|
19
|
+
* .max(10)
|
|
20
|
+
* .defaultValue({ name: "", qty: 1 })
|
|
21
|
+
* )
|
|
22
|
+
* .build()
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
declare class RepeatableBuilder<C extends Record<string, any>> {
|
|
26
|
+
private innerForm;
|
|
27
|
+
private _min?;
|
|
28
|
+
private _max?;
|
|
29
|
+
private _defaultValue?;
|
|
30
|
+
private _validation?;
|
|
31
|
+
constructor(config: ril<C>);
|
|
32
|
+
/**
|
|
33
|
+
* Add fields to the repeatable template
|
|
34
|
+
* Same API as form.add() — variadic ≤3 puts them on the same row
|
|
35
|
+
*/
|
|
36
|
+
add<T extends keyof C & string>(...fields: FieldConfig<C, T>[]): this;
|
|
37
|
+
add<T extends keyof C & string>(fields: FieldConfig<C, T>[]): this;
|
|
38
|
+
/**
|
|
39
|
+
* Add fields each on their own row
|
|
40
|
+
*/
|
|
41
|
+
addSeparateRows<T extends keyof C & string>(fields: FieldConfig<C, T>[]): this;
|
|
42
|
+
/**
|
|
43
|
+
* Set minimum number of items (defaults to 0)
|
|
44
|
+
*/
|
|
45
|
+
min(value: number): this;
|
|
46
|
+
/**
|
|
47
|
+
* Set maximum number of items (unlimited if not set)
|
|
48
|
+
*/
|
|
49
|
+
max(value: number): this;
|
|
50
|
+
/**
|
|
51
|
+
* Set default values for new items
|
|
52
|
+
*/
|
|
53
|
+
defaultValue(value: Record<string, unknown>): this;
|
|
54
|
+
/**
|
|
55
|
+
* Set group-level validation (applied to the entire array)
|
|
56
|
+
*/
|
|
57
|
+
validation(config: FieldValidationConfig): this;
|
|
58
|
+
/** @internal — called by form.addRepeatable */
|
|
59
|
+
_build(id: string): RepeatableFieldConfig;
|
|
60
|
+
/** @internal — check if the inner builder has repeatables (nesting forbidden) */
|
|
61
|
+
_hasRepeatables(): boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
7
64
|
/**
|
|
8
65
|
* Configuration for a form field with type safety
|
|
9
66
|
*
|
|
@@ -88,6 +145,8 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
88
145
|
private idGenerator;
|
|
89
146
|
/** Form-level validation configuration */
|
|
90
147
|
private formValidation?;
|
|
148
|
+
/** Default submit options for this form */
|
|
149
|
+
private _submitOptions?;
|
|
91
150
|
/**
|
|
92
151
|
* Creates a new form builder instance
|
|
93
152
|
*
|
|
@@ -206,6 +265,30 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
206
265
|
* ```
|
|
207
266
|
*/
|
|
208
267
|
addSeparateRows<T extends keyof C & string>(fieldConfigs: FieldConfig<C, T>[]): this;
|
|
268
|
+
/**
|
|
269
|
+
* Adds a repeatable field group to the form
|
|
270
|
+
*
|
|
271
|
+
* Repeatable fields allow users to add/remove instances of a group of fields
|
|
272
|
+
* at runtime (e.g., "Add another item", "Add another contact").
|
|
273
|
+
*
|
|
274
|
+
* @param id - Unique identifier for the repeatable group (cannot contain [ or ])
|
|
275
|
+
* @param configure - Callback receiving a RepeatableBuilder for fluent configuration
|
|
276
|
+
* @returns The form builder instance for method chaining
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* builder.addRepeatable("items", r => r
|
|
281
|
+
* .add(
|
|
282
|
+
* { id: "name", type: "text", props: { label: "Item" } },
|
|
283
|
+
* { id: "qty", type: "number", props: { label: "Qty" } }
|
|
284
|
+
* )
|
|
285
|
+
* .min(1)
|
|
286
|
+
* .max(10)
|
|
287
|
+
* .defaultValue({ name: "", qty: 1 })
|
|
288
|
+
* );
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
addRepeatable(id: string, configure: (builder: RepeatableBuilder<C>) => RepeatableBuilder<C>): this;
|
|
209
292
|
/**
|
|
210
293
|
* Sets the form identifier
|
|
211
294
|
*
|
|
@@ -308,7 +391,7 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
308
391
|
* console.log(`Form has ${rows.length} rows`);
|
|
309
392
|
* ```
|
|
310
393
|
*/
|
|
311
|
-
getRows():
|
|
394
|
+
getRows(): FormRowEntry[];
|
|
312
395
|
/**
|
|
313
396
|
* Clears all fields and rows from the form
|
|
314
397
|
*
|
|
@@ -349,6 +432,24 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
349
432
|
* ```
|
|
350
433
|
*/
|
|
351
434
|
setValidation(validationConfig: FormValidationConfig): this;
|
|
435
|
+
/**
|
|
436
|
+
* Sets default submit options for this form
|
|
437
|
+
*
|
|
438
|
+
* These options can be overridden at submit-time by passing options to `submit()`.
|
|
439
|
+
*
|
|
440
|
+
* @param options - Submit options to use as defaults
|
|
441
|
+
* @returns The form builder instance for method chaining
|
|
442
|
+
*
|
|
443
|
+
* @example
|
|
444
|
+
* ```typescript
|
|
445
|
+
* // Always skip invalid fields on submit
|
|
446
|
+
* builder.setSubmitOptions({ skipInvalid: true });
|
|
447
|
+
*
|
|
448
|
+
* // Force submit by default (bypass validation)
|
|
449
|
+
* builder.setSubmitOptions({ force: true });
|
|
450
|
+
* ```
|
|
451
|
+
*/
|
|
452
|
+
setSubmitOptions(options: SubmitOptions): this;
|
|
352
453
|
/**
|
|
353
454
|
* Adds validators to the form-level validation
|
|
354
455
|
*
|
|
@@ -516,16 +617,20 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
516
617
|
* - Useful for form complexity analysis
|
|
517
618
|
*/
|
|
518
619
|
getStats(): {
|
|
519
|
-
/** Total number of fields across all rows */
|
|
620
|
+
/** Total number of static fields across all rows */
|
|
520
621
|
totalFields: number;
|
|
521
622
|
/** Total number of rows in the form */
|
|
522
623
|
totalRows: number;
|
|
523
|
-
/** Average number of fields per row */
|
|
624
|
+
/** Average number of fields per row (field rows only) */
|
|
524
625
|
averageFieldsPerRow: number;
|
|
525
626
|
/** Maximum number of fields in any single row */
|
|
526
627
|
maxFieldsInRow: number;
|
|
527
628
|
/** Minimum number of fields in any single row */
|
|
528
629
|
minFieldsInRow: number;
|
|
630
|
+
/** Total number of repeatable groups */
|
|
631
|
+
totalRepeatables: number;
|
|
632
|
+
/** Total number of fields across all repeatable templates */
|
|
633
|
+
totalRepeatableFields: number;
|
|
529
634
|
};
|
|
530
635
|
}
|
|
531
636
|
|
|
@@ -537,12 +642,14 @@ interface FormProps {
|
|
|
537
642
|
className?: string;
|
|
538
643
|
children: React.ReactNode;
|
|
539
644
|
}
|
|
540
|
-
declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, children }: FormProps): react_jsx_runtime.JSX.Element;
|
|
645
|
+
declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, className, children, }: FormProps): react_jsx_runtime.JSX.Element;
|
|
541
646
|
|
|
542
647
|
declare const FormBody: React__default.NamedExoticComponent<ComponentRendererBaseProps<FormBodyRendererProps>>;
|
|
543
648
|
|
|
544
649
|
interface FormFieldProps {
|
|
545
650
|
fieldId: string;
|
|
651
|
+
/** Pre-resolved field config (used by RepeatableItem to skip allFields lookup) */
|
|
652
|
+
fieldConfig?: FormFieldConfig;
|
|
546
653
|
disabled?: boolean;
|
|
547
654
|
customProps?: Record<string, unknown>;
|
|
548
655
|
className?: string;
|
|
@@ -626,6 +733,8 @@ declare function useFieldConditionsWithRefresh(fieldId: string, conditions?: Con
|
|
|
626
733
|
interface UseFormConditionsProps {
|
|
627
734
|
formConfig: FormConfiguration;
|
|
628
735
|
formValues: Record<string, any>;
|
|
736
|
+
/** Active repeatable item keys, keyed by repeatable ID */
|
|
737
|
+
repeatableOrder?: Record<string, string[]>;
|
|
629
738
|
}
|
|
630
739
|
interface UseFormConditionsReturn {
|
|
631
740
|
fieldConditions: Record<string, ConditionEvaluationResult>;
|
|
@@ -662,11 +771,14 @@ interface UseFormConditionsReturn {
|
|
|
662
771
|
* }
|
|
663
772
|
* ```
|
|
664
773
|
*/
|
|
665
|
-
declare function useFormConditions({ formConfig, formValues, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
774
|
+
declare function useFormConditions({ formConfig, formValues, repeatableOrder, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
666
775
|
|
|
667
776
|
interface FormStoreState extends FormState {
|
|
668
777
|
_defaultValues: Record<string, unknown>;
|
|
669
778
|
_fieldConditions: Record<string, FieldConditions>;
|
|
779
|
+
_repeatableConfigs: Record<string, RepeatableFieldConfig>;
|
|
780
|
+
_repeatableOrder: Record<string, string[]>;
|
|
781
|
+
_repeatableNextKey: Record<string, number>;
|
|
670
782
|
_setValue: (fieldId: string, value: unknown) => void;
|
|
671
783
|
_setTouched: (fieldId: string) => void;
|
|
672
784
|
_setErrors: (fieldId: string, errors: ValidationError[]) => void;
|
|
@@ -676,6 +788,11 @@ interface FormStoreState extends FormState {
|
|
|
676
788
|
_reset: (values?: Record<string, unknown>) => void;
|
|
677
789
|
_setFieldConditions: (fieldId: string, conditions: FieldConditions) => void;
|
|
678
790
|
_updateIsValid: () => void;
|
|
791
|
+
_setRepeatableConfig: (id: string, config: RepeatableFieldConfig) => void;
|
|
792
|
+
_appendRepeatableItem: (repeatableId: string, defaultValue?: Record<string, unknown>) => string | null;
|
|
793
|
+
_removeRepeatableItem: (repeatableId: string, key: string) => boolean;
|
|
794
|
+
_moveRepeatableItem: (repeatableId: string, fromIndex: number, toIndex: number) => void;
|
|
795
|
+
_insertRepeatableItem: (repeatableId: string, index: number, defaultValue?: Record<string, unknown>) => string | null;
|
|
679
796
|
}
|
|
680
797
|
type FormStore = ReturnType<typeof createFormStore>;
|
|
681
798
|
declare function createFormStore(initialValues?: Record<string, unknown>): Omit<zustand.StoreApi<FormStoreState>, "subscribe"> & {
|
|
@@ -749,6 +866,10 @@ declare function useFormSubmitState(): {
|
|
|
749
866
|
isValid: boolean;
|
|
750
867
|
isDirty: boolean;
|
|
751
868
|
};
|
|
869
|
+
/**
|
|
870
|
+
* Select ordered keys for a repeatable field — re-renders when the order changes
|
|
871
|
+
*/
|
|
872
|
+
declare function useRepeatableKeys(repeatableId: string): string[];
|
|
752
873
|
interface UseFieldActionsResult {
|
|
753
874
|
setValue: (value: unknown) => void;
|
|
754
875
|
setTouched: () => void;
|
|
@@ -783,9 +904,10 @@ interface UseFormSubmissionWithStoreProps {
|
|
|
783
904
|
store: FormStore;
|
|
784
905
|
onSubmit?: (data: Record<string, unknown>) => void | Promise<void>;
|
|
785
906
|
validateForm: () => Promise<ValidationResult>;
|
|
907
|
+
defaultSubmitOptions?: SubmitOptions;
|
|
786
908
|
}
|
|
787
|
-
declare function useFormSubmissionWithStore({ store, onSubmit, validateForm, }: UseFormSubmissionWithStoreProps): {
|
|
788
|
-
submit: (
|
|
909
|
+
declare function useFormSubmissionWithStore({ store, onSubmit, validateForm, defaultSubmitOptions, }: UseFormSubmissionWithStoreProps): {
|
|
910
|
+
submit: (eventOrOptions?: React__default.FormEvent | SubmitOptions) => Promise<boolean>;
|
|
789
911
|
};
|
|
790
912
|
|
|
791
913
|
interface UseFormValidationWithStoreProps {
|
|
@@ -798,6 +920,45 @@ declare function useFormValidationWithStore({ formConfig, store, conditionsHelpe
|
|
|
798
920
|
validateForm: () => Promise<ValidationResult>;
|
|
799
921
|
};
|
|
800
922
|
|
|
923
|
+
interface UseRepeatableFieldReturn {
|
|
924
|
+
items: RepeatableFieldItem[];
|
|
925
|
+
append: (defaultValue?: Record<string, unknown>) => void;
|
|
926
|
+
remove: (key: string) => void;
|
|
927
|
+
move: (fromIndex: number, toIndex: number) => void;
|
|
928
|
+
canAdd: boolean;
|
|
929
|
+
canRemove: boolean;
|
|
930
|
+
count: number;
|
|
931
|
+
}
|
|
932
|
+
/**
|
|
933
|
+
* Hook to manage a repeatable field group
|
|
934
|
+
*
|
|
935
|
+
* Provides the list of items and actions to add, remove, and reorder them.
|
|
936
|
+
* Each item contains scoped field configs ready for rendering.
|
|
937
|
+
*
|
|
938
|
+
* @param repeatableId - The ID of the repeatable group (as defined in addRepeatable)
|
|
939
|
+
* @returns Items, actions, and constraints
|
|
940
|
+
*
|
|
941
|
+
* @example
|
|
942
|
+
* ```tsx
|
|
943
|
+
* const { items, append, remove, canAdd, canRemove } = useRepeatableField("items");
|
|
944
|
+
*
|
|
945
|
+
* return (
|
|
946
|
+
* <div>
|
|
947
|
+
* {items.map(item => (
|
|
948
|
+
* <div key={item.key}>
|
|
949
|
+
* {item.allFields.map(field => (
|
|
950
|
+
* <FormField key={field.id} fieldId={field.id} fieldConfig={field} />
|
|
951
|
+
* ))}
|
|
952
|
+
* {canRemove && <button onClick={() => remove(item.key)}>Remove</button>}
|
|
953
|
+
* </div>
|
|
954
|
+
* ))}
|
|
955
|
+
* {canAdd && <button onClick={append}>Add</button>}
|
|
956
|
+
* </div>
|
|
957
|
+
* );
|
|
958
|
+
* ```
|
|
959
|
+
*/
|
|
960
|
+
declare function useRepeatableField(repeatableId: string): UseRepeatableFieldReturn;
|
|
961
|
+
|
|
801
962
|
interface UseFormMonitoringProps {
|
|
802
963
|
formConfig: FormConfiguration;
|
|
803
964
|
monitoring?: MonitoringConfig;
|
|
@@ -818,7 +979,7 @@ interface FormConfigContextValue {
|
|
|
818
979
|
conditionsHelpers: Omit<UseFormConditionsReturn, 'fieldConditions'>;
|
|
819
980
|
validateField: (fieldId: string, value?: unknown) => Promise<ValidationResult>;
|
|
820
981
|
validateForm: () => Promise<ValidationResult>;
|
|
821
|
-
submit: (
|
|
982
|
+
submit: (eventOrOptions?: React__default.FormEvent | SubmitOptions) => Promise<boolean>;
|
|
822
983
|
}
|
|
823
984
|
/**
|
|
824
985
|
* Access form configuration and validation methods
|
|
@@ -848,4 +1009,53 @@ interface FormSubmitButtonProps extends ComponentRendererBaseProps<FormSubmitBut
|
|
|
848
1009
|
}
|
|
849
1010
|
declare const FormSubmitButton: React__default.NamedExoticComponent<FormSubmitButtonProps>;
|
|
850
1011
|
|
|
851
|
-
|
|
1012
|
+
interface RepeatableFieldProps {
|
|
1013
|
+
repeatableId: string;
|
|
1014
|
+
repeatableConfig: RepeatableFieldConfig;
|
|
1015
|
+
className?: string;
|
|
1016
|
+
}
|
|
1017
|
+
declare const RepeatableField: React__default.NamedExoticComponent<RepeatableFieldProps>;
|
|
1018
|
+
|
|
1019
|
+
interface RepeatableItemProps {
|
|
1020
|
+
item: RepeatableFieldItem;
|
|
1021
|
+
index: number;
|
|
1022
|
+
total: number;
|
|
1023
|
+
canRemove: boolean;
|
|
1024
|
+
canMoveUp: boolean;
|
|
1025
|
+
canMoveDown: boolean;
|
|
1026
|
+
onRemove: () => void;
|
|
1027
|
+
onMoveUp: () => void;
|
|
1028
|
+
onMoveDown: () => void;
|
|
1029
|
+
}
|
|
1030
|
+
declare const RepeatableItem: React__default.NamedExoticComponent<RepeatableItemProps>;
|
|
1031
|
+
|
|
1032
|
+
/**
|
|
1033
|
+
* Converts flat store values with composite keys into structured nested data.
|
|
1034
|
+
*
|
|
1035
|
+
* Input (store values):
|
|
1036
|
+
* { customerName: "John", "items[k0].name": "Widget", "items[k0].qty": 2, "items[k1].name": "Gadget", "items[k1].qty": 1 }
|
|
1037
|
+
*
|
|
1038
|
+
* Output (structured):
|
|
1039
|
+
* { customerName: "John", items: [{ name: "Widget", qty: 2 }, { name: "Gadget", qty: 1 }] }
|
|
1040
|
+
*/
|
|
1041
|
+
declare function structureFormValues(values: Record<string, unknown>, repeatableConfigs: Record<string, RepeatableFieldConfig>, repeatableOrder: Record<string, string[]>): Record<string, unknown>;
|
|
1042
|
+
/**
|
|
1043
|
+
* Converts structured nested data into flat store values with composite keys.
|
|
1044
|
+
*
|
|
1045
|
+
* Input (structured):
|
|
1046
|
+
* { customerName: "John", items: [{ name: "Widget", qty: 2 }, { name: "Gadget", qty: 1 }] }
|
|
1047
|
+
*
|
|
1048
|
+
* Output:
|
|
1049
|
+
* {
|
|
1050
|
+
* values: { customerName: "John", "items[k0].name": "Widget", "items[k0].qty": 2, "items[k1].name": "Gadget", "items[k1].qty": 1 },
|
|
1051
|
+
* order: { items: ["k0", "k1"] },
|
|
1052
|
+
* nextKeys: { items: 2 }
|
|
1053
|
+
* }
|
|
1054
|
+
*/
|
|
1055
|
+
declare function flattenRepeatableValues(data: Record<string, unknown>, repeatableConfigs: Record<string, RepeatableFieldConfig>): {
|
|
1056
|
+
values: Record<string, unknown>;
|
|
1057
|
+
order: Record<string, string[]>;
|
|
1058
|
+
nextKeys: Record<string, number>;
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
export { type ConditionEvaluationResult, type FieldConfig, Form, FormBody, form as FormBuilder, type FormConfigContextValue, FormField, FormProvider, type FormProviderProps, FormRow, type FormStore, FormStoreContext, type FormStoreState, FormSubmitButton, RepeatableBuilder, RepeatableField, RepeatableItem, type UseFieldActionsResult, type UseFieldConditionsLazyOptions, type UseFormActionsResult, type UseFormConditionsProps, type UseFormConditionsReturn, type UseFormMonitoringProps, type UseFormMonitoringReturn, type UseFormSubmissionWithStoreProps, type UseFormValidationWithStoreProps, type UseRepeatableFieldReturn, createFormStore, flattenRepeatableValues, form, structureFormValues, useConditionEvaluation, useConditionEvaluator, useFieldActions, useFieldConditions, useFieldConditionsLazy, useFieldConditionsWithRefresh, useFieldErrors, useFieldState, useFieldTouched, useFieldValidationState, useFieldValue, useFormActions, useFormConditions, useFormConfigContext, useFormDirty, useFormMonitoring, useFormStore, useFormStoreApi, useFormSubmissionWithStore, useFormSubmitState, useFormSubmitting, useFormValid, useFormValidationWithStore, useFormValues, useMultipleConditionEvaluation, useRepeatableField, useRepeatableKeys };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
'use strict';var Xe=require('react'),core=require('@rilaykit/core'),zustand=require('zustand'),middleware=require('zustand/middleware'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Xe__default=/*#__PURE__*/_interopDefault(Xe);var x=class o{constructor(e,t){this.rows=[];this.idGenerator=new core.IdGenerator;this.config=e,this.formId=t||`form-${Math.random().toString(36).substring(2,15)}`;}static create(e,t){return new o(e,t)}createFormField(e){let t=this.config.getComponent(e.type);if(!t)throw new Error(`No component found with type "${e.type}"`);let i;return (t.validation||e.validation)&&(i={validateOnChange:e.validation?.validateOnChange??t.validation?.validateOnChange,validateOnBlur:e.validation?.validateOnBlur??t.validation?.validateOnBlur,debounceMs:e.validation?.debounceMs??t.validation?.debounceMs,validate:(()=>{let r=t.validation?.validate,n=e.validation?.validate;if(!r)return n;if(!n)return r;let a=Array.isArray(r)?r:[r],s=Array.isArray(n)?n:[n];return [...a,...s]})()}),{id:e.id||this.idGenerator.next("field"),componentId:t.id,props:{...t.defaultProps,...e.props},validation:i,conditions:e.conditions}}createRow(e){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 t=e.map(i=>this.createFormField(i));return {id:this.idGenerator.next("row"),fields:t,maxColumns:e.length}}add(...e){let t,i=false;if(e.length===1&&Array.isArray(e[0])?(t=e[0],i=true):t=e,t.length===0)throw new Error("At least one field is required");if(i&&t.length>3)throw new Error("Maximum 3 fields per row");if(t.length===1){let r=this.createRow(t);return this.rows.push(r),this}if(t.length<=3){let r=this.createRow(t);return this.rows.push(r),this}for(let r of t){let n=this.createRow([r]);this.rows.push(n);}return this}addSeparateRows(e){for(let t of e)this.add(t);return this}setId(e){return this.formId=e,this}updateField(e,t){let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);return Object.assign(i,{...t,props:{...i.props,...t.props}}),this}findField(e){for(let t of this.rows){let i=t.fields.find(r=>r.id===e);if(i)return i}return null}removeField(e){return this.rows=this.rows.map(t=>({...t,fields:t.fields.filter(i=>i.id!==e)})).filter(t=>t.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}setValidation(e){return this.formValidation=e,this}addFieldValidation(e,t){console.warn("addFieldValidation is deprecated. Use updateField with validation.validate property instead.");let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let r={...i.validation,...t};return this.updateField(e,{validation:r})}addFieldConditions(e,t){let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let r={...i.conditions,...t};return this.updateField(e,{conditions:r})}clone(e){let t=new o(this.config,e||`${this.formId}-clone`);return t.rows=core.deepClone(this.rows),t}validate(){let e=[],t=this.getFields(),i=t.map(r=>r.id);try{core.ensureUnique(i,"field");}catch(r){e.push(r instanceof Error?r.message:String(r));}for(let r of t)this.config.hasComponent(r.componentId)||e.push(`Component "${r.componentId}" not found for field "${r.id}"`);for(let r of this.rows)r.fields.length>3&&e.push(`Row "${r.id}" has ${r.fields.length} fields, maximum is 3`),r.fields.length===0&&e.push(`Row "${r.id}" is empty`);return e}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(),validation:this.formValidation}}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(),t=this.rows.map(i=>i.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?e.length/this.rows.length:0,maxFieldsInRow:t.length>0?Math.max(...t):0,minFieldsInRow:t.length>0?Math.min(...t):0}}};function Ft(o,e={},t={}){return Xe.useMemo(()=>{if(!o)return {visible:t.visible??true,disabled:t.disabled??false,required:t.required??false,readonly:t.readonly??false};let i=r=>{try{return r&&typeof r=="object"&&"build"in r?core.evaluateCondition(r.build(),e):core.evaluateCondition(r,e)}catch(n){return console.warn("Error evaluating condition:",n),false}};return {visible:o.visible?i(o.visible):t.visible??true,disabled:o.disabled?i(o.disabled):t.disabled??false,required:o.required?i(o.required):t.required??false,readonly:o.readonly?i(o.readonly):t.readonly??false}},[o,e,t])}function oe(o,e={}){return Xe.useMemo(()=>{let t={};for(let[i,r]of Object.entries(o))if(t[i]={visible:true,disabled:false,required:false,readonly:false},r){let n=a=>{try{return a&&typeof a=="object"&&"build"in a?core.evaluateCondition(a.build(),e):core.evaluateCondition(a,e)}catch(s){return console.warn(`Error evaluating condition for field ${i}:`,s),false}};t[i]={visible:r.visible?n(r.visible):true,disabled:r.disabled?n(r.disabled):false,required:r.required?n(r.required):false,readonly:r.readonly?n(r.readonly):false};}return t},[o,e])}function $(o={}){return zustand.createStore()(middleware.subscribeWithSelector((e,t)=>({values:{...o},errors:{},validationStates:{},touched:{},isDirty:false,isSubmitting:false,isValid:true,_defaultValues:{...o},_fieldConditions:{},_setValue:(i,r)=>{e(n=>({values:{...n.values,[i]:r},isDirty:true}));},_setTouched:i=>{e(r=>({touched:{...r.touched,[i]:true}}));},_setErrors:(i,r)=>{e(n=>{let a={...n.errors,[i]:r},s=r.length>0?"invalid":"valid";return {errors:a,validationStates:{...n.validationStates,[i]:s}}}),t()._updateIsValid();},_clearErrors:i=>{e(r=>{let n={...r.errors};return delete n[i],{errors:n,validationStates:{...r.validationStates,[i]:"idle"}}}),t()._updateIsValid();},_setValidationState:(i,r)=>{e(n=>({validationStates:{...n.validationStates,[i]:r}}));},_setSubmitting:i=>{e({isSubmitting:i});},_reset:i=>{let r=i??t()._defaultValues;e({values:{...r},errors:{},validationStates:{},touched:{},isDirty:false,isSubmitting:false,isValid:true});},_setFieldConditions:(i,r)=>{e(n=>({_fieldConditions:{...n._fieldConditions,[i]:r}}));},_updateIsValid:()=>{let i=t(),r=Object.values(i.errors).some(a=>a&&a.length>0),n=Object.values(i.validationStates).some(a=>a==="invalid");e({isValid:!r&&!n});}})))}var O=Xe.createContext(null);function v(){let o=Xe.useContext(O);if(!o)throw new Error("useFormStore must be used within a FormProvider");return o}var re=[];function N(o){let e=v();return zustand.useStore(e,t=>t.values[o])}function Me(o){let e=v();return zustand.useStore(e,t=>t.errors[o]??re)}function Be(o){let e=v();return zustand.useStore(e,t=>t.touched[o]??false)}function Te(o){let e=v();return zustand.useStore(e,t=>t.validationStates[o]??"idle")}var De={visible:true,disabled:false,required:false,readonly:false};function P(o){let e=v();return zustand.useStore(e,t=>t._fieldConditions[o]??De)}function W(o){let e=v(),t=zustand.useStore(e,s=>s.values[o]),i=zustand.useStore(e,s=>s.errors[o]??re),r=zustand.useStore(e,s=>s.validationStates[o]??"idle"),n=zustand.useStore(e,s=>s.touched[o]??false),a=zustand.useStore(e,s=>s._defaultValues[o]);return {value:t,errors:i,validationState:r,touched:n,dirty:t!==a}}function qe(){let o=v();return zustand.useStore(o,e=>e.isSubmitting)}function Oe(){let o=v();return zustand.useStore(o,e=>e.isValid)}function Ue(){let o=v();return zustand.useStore(o,e=>e.isDirty)}function Ae(){let o=v();return zustand.useStore(o,e=>e.values)}function H(){let o=v(),e=zustand.useStore(o,r=>r.isSubmitting),t=zustand.useStore(o,r=>r.isValid),i=zustand.useStore(o,r=>r.isDirty);return {isSubmitting:e,isValid:t,isDirty:i}}function L(o){let e=v();return {setValue:t=>e.getState()._setValue(o,t),setTouched:()=>e.getState()._setTouched(o),setErrors:t=>e.getState()._setErrors(o,t),clearErrors:()=>e.getState()._clearErrors(o),setValidationState:t=>e.getState()._setValidationState(o,t)}}function Ie(){let o=v();return {setValue:(e,t)=>o.getState()._setValue(e,t),setTouched:e=>o.getState()._setTouched(e),setErrors:(e,t)=>o.getState()._setErrors(e,t),setSubmitting:e=>o.getState()._setSubmitting(e),reset:e=>o.getState()._reset(e),setFieldConditions:(e,t)=>o.getState()._setFieldConditions(e,t)}}function B(){return v()}var se={visible:true,disabled:false,required:false,readonly:false};function ae(o){try{return JSON.stringify(o)}catch{return String(Date.now())}}function U(o,e){if(o)try{return typeof o=="object"&&"build"in o?core.evaluateCondition(o.build(),e):core.evaluateCondition(o,e)}catch(t){console.warn("Error evaluating condition:",t);return}}function j(o,e){if(!o)return se;let t=U(o.visible,e),i=U(o.disabled,e),r=U(o.required,e),n=U(o.readonly,e);return {visible:t??true,disabled:i??false,required:r??false,readonly:n??false}}function Vt(o,e={}){let{conditions:t,skip:i=false}=e,r=B(),n=P(o),a=Xe.useRef(null);if(i||!t)return n;let s=r.getState().values,F=ae(s);if(a.current?.valuesHash===F)return a.current.result;let m=j(t,s);return a.current={result:m,valuesHash:F},m}function xt(){let o=B(),e=Xe.useRef(new Map),t=Xe.useRef("");return Xe.useMemo(()=>function(r,n){if(!n)return se;let a=o.getState().values,s=ae(a);t.current!==s&&(e.current.clear(),t.current=s);let F=e.current.get(r);if(F)return F.result;let m=j(n,a);return e.current.set(r,{result:m,valuesHash:s}),m},[o])}function Pt(o,e){let t=B(),i=P(o),r=Xe.useMemo(()=>()=>{if(!e)return i;let n=t.getState().values;return j(e,n)},[t,e,i]);return {conditions:i,refresh:r}}function de({formConfig:o,formValues:e}){let t=Xe.useMemo(()=>{let l={};for(let u of o.allFields)u.conditions&&(l[u.id]=u.conditions);return l},[o.allFields]),i=Xe.useMemo(()=>Object.keys(t).length>0,[t]),r=oe(i?t:{},i?e:{}),n=Xe.useCallback(l=>r[l],[r]),a=Xe.useCallback(l=>{let u=r[l];return u?u.visible:true},[r]),s=Xe.useCallback(l=>{let u=r[l];return u?u.disabled:false},[r]),F=Xe.useCallback(l=>{let u=r[l];return u?u.required:false},[r]),m=Xe.useCallback(l=>{let u=r[l];return u?u.readonly:false},[r]);return Xe.useMemo(()=>({fieldConditions:r,hasConditionalFields:i,getFieldCondition:n,isFieldVisible:a,isFieldDisabled:s,isFieldRequired:F,isFieldReadonly:m}),[r,i,n,a,s,F,m])}function le({store:o,onSubmit:e,validateForm:t}){let i=Xe.useRef(e);return i.current=e,{submit:Xe.useCallback(async n=>{n?.preventDefault();let a=o.getState();if(a.isSubmitting)return false;a._setSubmitting(true);try{if(!(await t()).isValid)return a._setSubmitting(!1),!1;let F=o.getState().values;return i.current&&await i.current(F),a._setSubmitting(!1),!0}catch(s){return a._setSubmitting(false),console.error("Form submission error:",s),false}},[o,t])}}function A(){return {isValid:true,errors:[]}}function fe({formConfig:o,store:e,conditionsHelpers:t}){let i=Xe.useRef(o),r=Xe.useRef(t);i.current=o,r.current=t;let n=Xe.useCallback(async(s,F)=>{let m=i.current.allFields.find(d=>d.id===s),l=e.getState();if(!m)return A();if(!r.current.isFieldVisible(s))return l._setErrors(s,[]),l._setValidationState(s,"valid"),A();if(!m.validation||!core.hasUnifiedValidation(m.validation))return l._setErrors(s,[]),l._setValidationState(s,"valid"),A();let u=F!==void 0?F:l.values[s],c=core.createValidationContext({fieldId:s,formId:i.current.id,allFormData:{...l.values,[s]:u}});l._setValidationState(s,"validating");try{let d=await core.validateWithUnifiedConfig(m.validation,u,c),f=r.current.isFieldRequired(s),p=u==null||u==="";if(f&&p&&!d.errors.some(g=>g.code==="REQUIRED"||g.message.toLowerCase().includes("required"))){let g={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...d.errors]};return l._setErrors(s,g.errors),l._setValidationState(s,"invalid"),g}return l._setErrors(s,d.errors),l._setValidationState(s,d.isValid?"valid":"invalid"),d}catch(d){let f={isValid:false,errors:[{message:d instanceof Error?d.message:"Validation failed",code:"VALIDATION_ERROR"}]};return l._setErrors(s,f.errors),l._setValidationState(s,"invalid"),f}},[e]),a=Xe.useCallback(async()=>{let s=e.getState(),F=i.current.allFields.filter(d=>{let f=r.current.isFieldVisible(d.id),p=d.validation&&core.hasUnifiedValidation(d.validation);return f&&p}),m=i.current.allFields.filter(d=>!r.current.isFieldVisible(d.id));for(let d of m)s._setErrors(d.id,[]),s._setValidationState(d.id,"valid");let l=await Promise.all(F.map(d=>n(d.id))),u=l.some(d=>!d.isValid),c=A();if(i.current.validation&&core.hasUnifiedValidation(i.current.validation)){let d=Object.keys(s.values).reduce((p,b)=>(r.current.isFieldVisible(b)&&(p[b]=s.values[b]),p),{}),f=core.createValidationContext({formId:i.current.id,allFormData:d});try{c=await core.validateFormWithUnifiedConfig(i.current.validation,d,f);}catch(p){c={isValid:false,errors:[{message:p instanceof Error?p.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!u&&c.isValid,errors:[...l.flatMap(d=>d.errors),...c.errors]}},[e,n]);return {validateField:n,validateForm:a}}function je({formConfig:o,enabled:e=true}){let t=core.getGlobalMonitor(),i=Xe.useRef(null),r=Xe.useRef(0),n=Xe.useRef(0);Xe.useEffect(()=>{t&&e&&(i.current=t.getProfiler());},[t,e]);let a=Xe.useCallback(c=>{if(!t||!e)return;r.current++;let d={formId:o.id,fieldCount:o.allFields.length,timestamp:Date.now(),duration:0,renderDuration:0,validationDuration:0,validationErrors:0,renderCount:c||r.current};t.track("component_render",`form_${o.id}`,{formId:o.id,fieldCount:o.allFields.length,renderCount:r.current},d,"low");},[t,e,o.id,o.allFields.length]),s=Xe.useCallback((c,d)=>{if(!t||!e)return;let f=i.current?.getMetrics(`form_validation_${o.id}`),p={formId:o.id,fieldCount:d||o.allFields.length,timestamp:Date.now(),duration:f?.duration||0,renderDuration:0,validationDuration:f?.duration||0,validationErrors:c,renderCount:r.current};t.track("form_validation",`form_${o.id}`,{formId:o.id,validationErrors:c,fieldCount:d||o.allFields.length},p,c>0?"medium":"low");},[t,e,o.id,o.allFields.length]),F=Xe.useCallback((c,d)=>{if(!t||!e)return;let f=i.current?.getMetrics(`form_submission_${o.id}`),p={formId:o.id,fieldCount:d||o.allFields.length,timestamp:Date.now(),duration:f?.duration||0,renderDuration:0,validationDuration:0,validationErrors:c?0:1,renderCount:r.current};t.track("form_submission",`form_${o.id}`,{formId:o.id,success:c,fieldCount:d||o.allFields.length,fieldChanges:n.current},p,c?"low":"high");},[t,e,o.id,o.allFields.length]),m=Xe.useCallback((c,d)=>{!t||!e||(n.current++,t.track("component_update",`field_${c}`,{formId:o.id,fieldId:c,componentType:d,changeCount:n.current},void 0,"low"));},[t,e,o.id]),l=Xe.useCallback(c=>{!i.current||!e||i.current.start(c,{formId:o.id,renderCount:r.current});},[e,o.id]),u=Xe.useCallback(c=>{if(!i.current||!e)return null;let d=i.current.end(c);return d?{...d,formId:o.id,fieldCount:o.allFields.length,renderDuration:d.duration,validationDuration:0,validationErrors:0}:null},[e,o.id,o.allFields.length]);return {trackFormRender:a,trackFormValidation:s,trackFormSubmission:F,trackFieldChange:m,startPerformanceTracking:l,endPerformanceTracking:u}}var Ce=Xe.createContext(null);function y(){let o=Xe.useContext(Ce);if(!o)throw new Error("useFormConfigContext must be used within a FormProvider");return o}function K({children:o,formConfig:e,defaultValues:t={},onSubmit:i,onFieldChange:r,className:n}){let[a]=Xe.useState(()=>$(t)),s=Xe.useRef(e.id),F=Xe.useRef(r);F.current=r,Xe.useEffect(()=>F.current?a.subscribe(C=>C.values,(C,M)=>{for(let R of Object.keys(C))C[R]!==M[R]&&F.current?.(R,C[R],C);}):void 0,[a]),Xe.useEffect(()=>{s.current!==e.id&&(s.current=e.id,a.getState()._reset(t));},[e.id,a,t]);let[m,l]=Xe.useState(()=>a.getState().values);Xe.useEffect(()=>a.subscribe(C=>C.values,C=>l(C)),[a]);let{fieldConditions:u,hasConditionalFields:c,getFieldCondition:d,isFieldVisible:f,isFieldDisabled:p,isFieldRequired:b,isFieldReadonly:g}=de({formConfig:e,formValues:m});Xe.useEffect(()=>{for(let[V,C]of Object.entries(u)){let M={visible:C.visible,disabled:C.disabled,required:C.required,readonly:C.readonly};a.getState()._setFieldConditions(V,M);}},[u,a]);let w=Xe.useMemo(()=>({hasConditionalFields:c,getFieldCondition:d,isFieldVisible:f,isFieldDisabled:p,isFieldRequired:b,isFieldReadonly:g}),[c,d,f,p,b,g]),{validateField:_,validateForm:S}=fe({formConfig:e,store:a,conditionsHelpers:w}),{submit:k}=le({store:a,onSubmit:i,validateForm:S}),D=Xe.useMemo(()=>({formConfig:e,conditionsHelpers:w,validateField:_,validateForm:S,submit:k}),[e,w,_,S,k]);return jsxRuntime.jsx(O.Provider,{value:a,children:jsxRuntime.jsx(Ce.Provider,{value:D,children:jsxRuntime.jsx("form",{onSubmit:k,className:n,noValidate:true,children:o})})})}function Ye({formConfig:o,defaultValues:e,onSubmit:t,onFieldChange:i,children:r}){let n=Xe.useMemo(()=>o instanceof x?o.build():o,[o]);return jsxRuntime.jsx(K,{formConfig:n,defaultValues:e,onSubmit:t,onFieldChange:i,children:r})}var Z=Xe__default.default.memo(function({fieldId:e,disabled:t=false,customProps:i={},className:r,forceVisible:n=false}){let{formConfig:a,validateField:s,conditionsHelpers:F}=y(),m=N(e),l=W(e),u=P(e),{setValue:c,setTouched:d}=L(e),f=a.allFields.find(R=>R.id===e);if(!f)throw new Error(`Field with ID "${e}" not found`);let p=a.config.getComponent(f.componentId);if(!p)throw new Error(`Component with ID "${f.componentId}" not found`);let b=l.validationState==="validating",g=Xe.useMemo(()=>({isVisible:n||u.visible,isFieldDisabled:t||u.disabled,isFieldRequired:u.required||F.isFieldRequired(e),isFieldReadonly:u.readonly}),[n,t,u,F,e]),w=Xe.useCallback(async R=>{c(R),(f.validation?.validateOnChange||l.touched)&&await s(e,R);},[e,c,s,f.validation?.validateOnChange,l.touched]),_=Xe.useCallback(async()=>{l.touched||d(),f.validation?.validateOnBlur!==false&&await s(e);},[e,l.touched,d,s,f.validation?.validateOnBlur]),S=Xe.useMemo(()=>({...p.defaultProps??{},...f.props,...i,disabled:g.isFieldDisabled,required:g.isFieldRequired,readOnly:g.isFieldReadonly}),[p.defaultProps,f.props,i,g.isFieldDisabled,g.isFieldRequired,g.isFieldReadonly]),k=Xe.useMemo(()=>({id:e,props:S,value:m,onChange:w,onBlur:_,disabled:g.isFieldDisabled,error:l.errors,isValidating:b,touched:l.touched}),[e,S,m,w,_,g.isFieldDisabled,l.errors,b,l.touched]);if(!g.isVisible)return null;let D=p.renderer(k),V=a.renderConfig?.fieldRenderer,C=p.useFieldRenderer!==false,M=V&&C?V({children:D,id:e,...S,error:l.errors,isValidating:b,touched:l.touched}):D;return jsxRuntime.jsx("div",{className:r,"data-field-id":e,"data-field-type":p.type,"data-field-visible":g.isVisible,"data-field-disabled":g.isFieldDisabled,"data-field-required":g.isFieldRequired,"data-field-readonly":g.isFieldReadonly,children:M})});var be=Xe__default.default.memo(function({row:e,className:t,...i}){let{formConfig:r,conditionsHelpers:n}=y(),a=Xe.useMemo(()=>e.fields.filter(m=>n.isFieldVisible(m.id)),[e.fields,n]),s=Xe.useMemo(()=>a.map(m=>jsxRuntime.jsx(Z,{fieldId:m.id},m.id)),[a]),F=Xe.useMemo(()=>({row:e,children:s,className:t}),[e,s,t]);return a.length===0?null:jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormRow",renderer:r.renderConfig?.rowRenderer,props:F,...i,children:s})}),Re=be;var it=Xe__default.default.memo(function({className:e,...t}){let{formConfig:i}=y(),r=Xe.useMemo(()=>i.rows.map(a=>jsxRuntime.jsx(Re,{row:a},a.id)),[i.rows]),n=Xe.useMemo(()=>({formConfig:i,children:r,className:e}),[i,r,e]);return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormBody",renderer:i.renderConfig?.bodyRenderer,props:n,...t,children:r})});var dt=Xe__default.default.memo(function({className:e,isSubmitting:t,...i}){let{formConfig:r,submit:n}=y(),{isSubmitting:a}=H(),s=Xe.useMemo(()=>({isSubmitting:t??a,onSubmit:n,className:e}),[t,a,n,e]);return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormSubmitButton",renderer:r.renderConfig?.submitButtonRenderer,props:s,...i})});exports.Form=Ye;exports.FormBody=it;exports.FormBuilder=x;exports.FormField=Z;exports.FormProvider=K;exports.FormRow=be;exports.FormStoreContext=O;exports.FormSubmitButton=dt;exports.createFormStore=$;exports.form=x;exports.useConditionEvaluation=Ft;exports.useConditionEvaluator=xt;exports.useFieldActions=L;exports.useFieldConditions=P;exports.useFieldConditionsLazy=Vt;exports.useFieldConditionsWithRefresh=Pt;exports.useFieldErrors=Me;exports.useFieldState=W;exports.useFieldTouched=Be;exports.useFieldValidationState=Te;exports.useFieldValue=N;exports.useFormActions=Ie;exports.useFormConditions=de;exports.useFormConfigContext=y;exports.useFormDirty=Ue;exports.useFormMonitoring=je;exports.useFormStore=v;exports.useFormStoreApi=B;exports.useFormSubmissionWithStore=le;exports.useFormSubmitState=H;exports.useFormSubmitting=qe;exports.useFormValid=Oe;exports.useFormValidationWithStore=fe;exports.useFormValues=Ae;exports.useMultipleConditionEvaluation=oe;
|
|
1
|
+
'use strict';var Mt=require('react'),core=require('@rilaykit/core'),zustand=require('zustand'),middleware=require('zustand/middleware'),jsxRuntime=require('react/jsx-runtime');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var Mt__default=/*#__PURE__*/_interopDefault(Mt);var W=class{constructor(e){this.innerForm=new T(e,"__repeatable_template__");}add(...e){return this.innerForm.add(...e),this}addSeparateRows(e){return this.innerForm.addSeparateRows(e),this}min(e){return this._min=e,this}max(e){return this._max=e,this}defaultValue(e){return this._defaultValue=e,this}validation(e){return this._validation=e,this}_build(e){let t=this.innerForm.getRows(),i=this.innerForm.getFields();if(t.length===0)throw new Error(`Repeatable "${e}" must have at least one field`);for(let o of i)if(o.id.includes("[")||o.id.includes("]"))throw new Error(`Repeatable template field ID "${o.id}" cannot contain "[" or "]" (reserved for composite keys)`);if(this._min!==void 0&&this._max!==void 0&&this._min>this._max)throw new Error(`Repeatable "${e}": min (${this._min}) cannot be greater than max (${this._max})`);return {id:e,rows:t.map(o=>({...o,kind:"fields"})),allFields:i,min:this._min,max:this._max,defaultValue:this._defaultValue,validation:this._validation}}_hasRepeatables(){return this.innerForm.getRows().some(e=>"kind"in e&&e.kind==="repeatable")}};var T=class r{constructor(e,t){this.rows=[];this.idGenerator=new core.IdGenerator;this.config=e,this.formId=t||`form-${Math.random().toString(36).substring(2,15)}`;}static create(e,t){return new r(e,t)}createFormField(e){let t=this.config.getComponent(e.type);if(!t)throw new Error(`No component found with type "${e.type}"`);let i;return (t.validation||e.validation)&&(i={validateOnChange:e.validation?.validateOnChange??t.validation?.validateOnChange,validateOnBlur:e.validation?.validateOnBlur??t.validation?.validateOnBlur,debounceMs:e.validation?.debounceMs??t.validation?.debounceMs,validate:(()=>{let o=t.validation?.validate,s=e.validation?.validate;if(!o)return s;if(!s)return o;let a=Array.isArray(o)?o:[o],n=Array.isArray(s)?s:[s];return [...a,...n]})()}),{id:e.id||this.idGenerator.next("field"),componentId:t.id,props:{...t.defaultProps,...e.props},validation:i,conditions:e.conditions}}createRow(e){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 t=e.map(i=>this.createFormField(i));return {kind:"fields",id:this.idGenerator.next("row"),fields:t,maxColumns:e.length}}add(...e){let t,i=false;if(e.length===1&&Array.isArray(e[0])?(t=e[0],i=true):t=e,t.length===0)throw new Error("At least one field is required");if(i&&t.length>3)throw new Error("Maximum 3 fields per row");if(t.length===1){let o=this.createRow(t);return this.rows.push(o),this}if(t.length<=3){let o=this.createRow(t);return this.rows.push(o),this}for(let o of t){let s=this.createRow([o]);this.rows.push(s);}return this}addSeparateRows(e){for(let t of e)this.add(t);return this}addRepeatable(e,t){if(e.includes("[")||e.includes("]"))throw new Error(`Repeatable ID "${e}" cannot contain "[" or "]" (reserved for composite keys)`);let i=new W(this.config),o=t(i);if(o._hasRepeatables())throw new Error(`Nested repeatables are not supported (in repeatable "${e}")`);let s=o._build(e),a={kind:"repeatable",id:this.idGenerator.next("repeatable"),repeatable:s};return this.rows.push(a),this}setId(e){return this.formId=e,this}updateField(e,t){let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);return Object.assign(i,{...t,props:{...i.props,...t.props}}),this}findField(e){for(let t of this.rows)if(t.kind==="fields"){let i=t.fields.find(o=>o.id===e);if(i)return i}else {let i=t.repeatable.allFields.find(o=>o.id===e);if(i)return i}return null}removeField(e){return this.rows=this.rows.map(t=>t.kind==="repeatable"?t:{...t,fields:t.fields.filter(i=>i.id!==e)}).filter(t=>t.kind==="repeatable"||t.fields.length>0),this}getField(e){return this.findField(e)||void 0}getFields(){return this.rows.filter(e=>e.kind==="fields").flatMap(e=>e.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.idGenerator.reset(),this}setValidation(e){return this.formValidation=e,this}setSubmitOptions(e){return this._submitOptions=e,this}addFieldValidation(e,t){console.warn("addFieldValidation is deprecated. Use updateField with validation.validate property instead.");let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let o={...i.validation,...t};return this.updateField(e,{validation:o})}addFieldConditions(e,t){let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let o={...i.conditions,...t};return this.updateField(e,{conditions:o})}clone(e){let t=new r(this.config,e||`${this.formId}-clone`);return t.rows=core.deepClone(this.rows),t}validate(){let e=[],t=this.getFields(),i=this.rows.filter(n=>n.kind==="repeatable"),o=i.flatMap(n=>n.repeatable.allFields),s=[...t.map(n=>n.id),...o.map(n=>n.id)];try{core.ensureUnique(s,"field");}catch(n){e.push(n instanceof Error?n.message:String(n));}let a=i.map(n=>n.repeatable.id);try{core.ensureUnique(a,"repeatable");}catch(n){e.push(n instanceof Error?n.message:String(n));}for(let n of t)this.config.hasComponent(n.componentId)||e.push(`Component "${n.componentId}" not found for field "${n.id}"`);for(let n of o)this.config.hasComponent(n.componentId)||e.push(`Component "${n.componentId}" not found for repeatable template field "${n.id}"`);for(let n of this.rows)n.kind==="fields"&&(n.fields.length>3&&e.push(`Row "${n.id}" has ${n.fields.length} fields, maximum is 3`),n.fields.length===0&&e.push(`Row "${n.id}" is empty`));for(let n of t)(n.id.includes("[")||n.id.includes("]"))&&e.push(`Field ID "${n.id}" cannot contain "[" or "]" (reserved for repeatable composite keys)`);for(let n of a)(n.includes("[")||n.includes("]"))&&e.push(`Repeatable ID "${n}" cannot contain "[" or "]" (reserved for composite keys)`);return e}build(){let e=this.validate();if(e.length>0)throw new Error(`Form validation failed: ${e.join(", ")}`);let t=this.rows.filter(o=>o.kind==="repeatable"),i=t.length>0?Object.fromEntries(t.map(o=>[o.repeatable.id,o.repeatable])):void 0;return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),repeatableFields:i,config:this.config,renderConfig:this.config.getFormRenderConfig(),validation:this.formValidation,submitOptions:this._submitOptions}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(e){return e.id&&(this.formId=e.id),e.rows&&(this.rows=e.rows.map(t=>t.kind?t:{...t,kind:"fields"})),this}getStats(){let e=this.getFields(),t=this.rows.filter(s=>s.kind==="fields"),i=this.rows.filter(s=>s.kind==="repeatable"),o=t.map(s=>s.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:t.length>0?e.length/t.length:0,maxFieldsInRow:o.length>0?Math.max(...o):0,minFieldsInRow:o.length>0?Math.min(...o):0,totalRepeatables:i.length,totalRepeatableFields:i.reduce((s,a)=>s+a.repeatable.allFields.length,0)}}};function tr(r,e={},t={}){return Mt.useMemo(()=>{if(!r)return {visible:t.visible??true,disabled:t.disabled??false,required:t.required??false,readonly:t.readonly??false};let i=o=>{try{return o&&typeof o=="object"&&"build"in o?core.evaluateCondition(o.build(),e):core.evaluateCondition(o,e)}catch(s){return console.warn("Error evaluating condition:",s),false}};return {visible:r.visible?i(r.visible):t.visible??true,disabled:r.disabled?i(r.disabled):t.disabled??false,required:r.required?i(r.required):t.required??false,readonly:r.readonly?i(r.readonly):t.readonly??false}},[r,e,t])}function Te(r,e={}){return Mt.useMemo(()=>{let t={};for(let[i,o]of Object.entries(r))if(t[i]={visible:true,disabled:false,required:false,readonly:false},o){let s=a=>{try{return a&&typeof a=="object"&&"build"in a?core.evaluateCondition(a.build(),e):core.evaluateCondition(a,e)}catch(n){return console.warn(`Error evaluating condition for field ${i}:`,n),false}};t[i]={visible:o.visible?s(o.visible):true,disabled:o.disabled?s(o.disabled):false,required:o.required?s(o.required):false,readonly:o.readonly?s(o.readonly):false};}return t},[r,e])}var nt=/^([^[\]]+)\[([^\]]+)\]\.(.+)$/;function V(r,e,t){return `${r}[${e}].${t}`}function L(r){let e=nt.exec(r);return e?{repeatableId:e[1],itemKey:e[2],fieldId:e[3]}:null}function re(r,e,t){let i={},o=new Set;for(let[s,a]of Object.entries(t)){if(!e[s])continue;let n=e[s],f=[];for(let p of a){let m={};for(let c of n.allFields){let l=V(s,p,c.id);l in r&&(m[c.id]=r[l],o.add(l));}f.push(m);}i[s]=f;}for(let[s,a]of Object.entries(r))!o.has(s)&&!L(s)&&(i[s]=a);return i}function oe(r,e){let t={},i={},o={};for(let[s,a]of Object.entries(r))if(e[s]&&Array.isArray(a)){let n=[],f=0;for(let p of a){let m=`k${f}`;n.push(m);for(let[c,l]of Object.entries(p))t[V(s,m,c)]=l;f++;}i[s]=n,o[s]=f;}else t[s]=a;return {values:t,order:i,nextKeys:o}}function ue(r={}){return zustand.createStore()(middleware.subscribeWithSelector((e,t)=>({values:{...r},errors:{},validationStates:{},touched:{},isDirty:false,isSubmitting:false,isValid:true,_defaultValues:{...r},_fieldConditions:{},_repeatableConfigs:{},_repeatableOrder:{},_repeatableNextKey:{},_setValue:(i,o)=>{e(s=>({values:{...s.values,[i]:o},isDirty:true}));},_setTouched:i=>{e(o=>({touched:{...o.touched,[i]:true}}));},_setErrors:(i,o)=>{e(s=>{let a={...s.errors,[i]:o},n=o.length>0?"invalid":"valid";return {errors:a,validationStates:{...s.validationStates,[i]:n}}}),t()._updateIsValid();},_clearErrors:i=>{e(o=>{let s={...o.errors};return delete s[i],{errors:s,validationStates:{...o.validationStates,[i]:"idle"}}}),t()._updateIsValid();},_setValidationState:(i,o)=>{e(s=>({validationStates:{...s.validationStates,[i]:o}}));},_setSubmitting:i=>{e({isSubmitting:i});},_reset:i=>{let o=i??t()._defaultValues;e({values:{...o},errors:{},validationStates:{},touched:{},isDirty:false,isSubmitting:false,isValid:true,_repeatableOrder:{},_repeatableNextKey:{}});},_setFieldConditions:(i,o)=>{e(s=>({_fieldConditions:{...s._fieldConditions,[i]:o}}));},_updateIsValid:()=>{let i=t(),o=Object.values(i.errors).some(a=>a&&a.length>0),s=Object.values(i.validationStates).some(a=>a==="invalid");e({isValid:!o&&!s});},_setRepeatableConfig:(i,o)=>{e(s=>({_repeatableConfigs:{...s._repeatableConfigs,[i]:o}}));},_appendRepeatableItem:(i,o)=>{let s=t(),a=s._repeatableConfigs[i];if(!a)return null;let n=s._repeatableOrder[i]??[];if(a.max!==void 0&&n.length>=a.max)return null;let f=s._repeatableNextKey[i]??0,p=`k${f}`,m=o??a.defaultValue??{},c={...s.values};for(let l of a.allFields){let d=V(i,p,l.id);c[d]=m[l.id]??void 0;}return e({values:c,isDirty:true,_repeatableOrder:{...s._repeatableOrder,[i]:[...n,p]},_repeatableNextKey:{...s._repeatableNextKey,[i]:f+1}}),p},_removeRepeatableItem:(i,o)=>{let s=t(),a=s._repeatableConfigs[i];if(!a)return false;let n=s._repeatableOrder[i]??[];if(a.min!==void 0&&n.length<=a.min||!n.includes(o))return false;let f=n.filter(F=>F!==o),p={...s.values},m={...s.errors},c={...s.validationStates},l={...s.touched},d={...s._fieldConditions};for(let F of a.allFields){let g=V(i,o,F.id);delete p[g],delete m[g],delete c[g],delete l[g],delete d[g];}return e({values:p,errors:m,validationStates:c,touched:l,isDirty:true,_fieldConditions:d,_repeatableOrder:{...s._repeatableOrder,[i]:f}}),t()._updateIsValid(),true},_moveRepeatableItem:(i,o,s)=>{let a=t(),n=a._repeatableOrder[i];if(!n||o<0||o>=n.length||s<0||s>=n.length||o===s)return;let f=[...n],[p]=f.splice(o,1);f.splice(s,0,p),e({_repeatableOrder:{...a._repeatableOrder,[i]:f}});},_insertRepeatableItem:(i,o,s)=>{let a=t(),n=a._repeatableConfigs[i];if(!n)return null;let f=a._repeatableOrder[i]??[];if(n.max!==void 0&&f.length>=n.max)return null;let p=a._repeatableNextKey[i]??0,m=`k${p}`,c=s??n.defaultValue??{},l={...a.values};for(let g of n.allFields){let u=V(i,m,g.id);l[u]=c[g.id]??void 0;}let d=[...f],F=Math.max(0,Math.min(o,d.length));return d.splice(F,0,m),e({values:l,isDirty:true,_repeatableOrder:{...a._repeatableOrder,[i]:d},_repeatableNextKey:{...a._repeatableNextKey,[i]:p+1}}),m}})))}var ie=Mt.createContext(null);function S(){let r=Mt.useContext(ie);if(!r)throw new Error("useFormStore must be used within a FormProvider");return r}var De=[];function ce(r){let e=S();return zustand.useStore(e,t=>t.values[r])}function ut(r){let e=S();return zustand.useStore(e,t=>t.errors[r]??De)}function ct(r){let e=S();return zustand.useStore(e,t=>t.touched[r]??false)}function mt(r){let e=S();return zustand.useStore(e,t=>t.validationStates[r]??"idle")}var ft={visible:true,disabled:false,required:false,readonly:false};function U(r){let e=S();return zustand.useStore(e,t=>t._fieldConditions[r]??ft)}function me(r){let e=S(),t=zustand.useStore(e,n=>n.values[r]),i=zustand.useStore(e,n=>n.errors[r]??De),o=zustand.useStore(e,n=>n.validationStates[r]??"idle"),s=zustand.useStore(e,n=>n.touched[r]??false),a=zustand.useStore(e,n=>n._defaultValues[r]);return {value:t,errors:i,validationState:o,touched:s,dirty:t!==a}}function pt(){let r=S();return zustand.useStore(r,e=>e.isSubmitting)}function Ft(){let r=S();return zustand.useStore(r,e=>e.isValid)}function gt(){let r=S();return zustand.useStore(r,e=>e.isDirty)}function bt(){let r=S();return zustand.useStore(r,e=>e.values)}function fe(){let r=S(),e=zustand.useStore(r,o=>o.isSubmitting),t=zustand.useStore(r,o=>o.isValid),i=zustand.useStore(r,o=>o.isDirty);return {isSubmitting:e,isValid:t,isDirty:i}}var Ct=[];function pe(r){let e=S();return zustand.useStore(e,t=>t._repeatableOrder[r]??Ct)}function Fe(r){let e=S();return {setValue:t=>e.getState()._setValue(r,t),setTouched:()=>e.getState()._setTouched(r),setErrors:t=>e.getState()._setErrors(r,t),clearErrors:()=>e.getState()._clearErrors(r),setValidationState:t=>e.getState()._setValidationState(r,t)}}function Rt(){let r=S();return {setValue:(e,t)=>r.getState()._setValue(e,t),setTouched:e=>r.getState()._setTouched(e),setErrors:(e,t)=>r.getState()._setErrors(e,t),setSubmitting:e=>r.getState()._setSubmitting(e),reset:e=>r.getState()._reset(e),setFieldConditions:(e,t)=>r.getState()._setFieldConditions(e,t)}}function H(){return S()}var Ae={visible:true,disabled:false,required:false,readonly:false};function Ke(r){try{return JSON.stringify(r)}catch{return String(Date.now())}}function ne(r,e){if(r)try{return typeof r=="object"&&"build"in r?core.evaluateCondition(r.build(),e):core.evaluateCondition(r,e)}catch(t){console.warn("Error evaluating condition:",t);return}}function be(r,e){if(!r)return Ae;let t=ne(r.visible,e),i=ne(r.disabled,e),o=ne(r.required,e),s=ne(r.readonly,e);return {visible:t??true,disabled:i??false,required:o??false,readonly:s??false}}function pr(r,e={}){let{conditions:t,skip:i=false}=e,o=H(),s=U(r),a=Mt.useRef(null);if(i||!t)return s;let n=o.getState().values,f=Ke(n);if(a.current?.valuesHash===f)return a.current.result;let p=be(t,n);return a.current={result:p,valuesHash:f},p}function Fr(){let r=H(),e=Mt.useRef(new Map),t=Mt.useRef("");return Mt.useMemo(()=>function(o,s){if(!s)return Ae;let a=r.getState().values,n=Ke(a);t.current!==n&&(e.current.clear(),t.current=n);let f=e.current.get(o);if(f)return f.result;let p=be(s,a);return e.current.set(o,{result:p,valuesHash:n}),p},[r])}function gr(r,e){let t=H(),i=U(r),o=Mt.useMemo(()=>()=>{if(!e)return i;let s=t.getState().values;return be(e,s)},[t,e,i]);return {conditions:i,refresh:o}}function Y(r,e,t,i){let o={};return r.visible&&(o.visible=G(r.visible,e,t,i)),r.disabled&&(o.disabled=G(r.disabled,e,t,i)),r.required&&(o.required=G(r.required,e,t,i)),r.readonly&&(o.readonly=G(r.readonly,e,t,i)),o}function G(r,e,t,i){let o=r.field&&i.has(r.field)?`${e}[${t}].${r.field}`:r.field,s=r.conditions?.map(a=>G(a,e,t,i));return {...r,field:o,conditions:s}}function Ie({formConfig:r,formValues:e,repeatableOrder:t}){let i=Mt.useMemo(()=>{let c={};for(let l of r.allFields)l.conditions&&(c[l.id]=l.conditions);if(t&&r.repeatableFields)for(let[l,d]of Object.entries(r.repeatableFields)){let F=t[l]??[];if(F.length===0)continue;let g=new Set(d.allFields.map(u=>u.id));for(let u of F)for(let C of d.allFields){if(!C.conditions)continue;let b=V(l,u,C.id);c[b]=Y(C.conditions,l,u,g);}}return c},[r.allFields,r.repeatableFields,t]),o=Mt.useMemo(()=>Object.keys(i).length>0,[i]),s=Te(o?i:{},o?e:{}),a=Mt.useCallback(c=>s[c],[s]),n=Mt.useCallback(c=>{let l=s[c];return l?l.visible:true},[s]),f=Mt.useCallback(c=>{let l=s[c];return l?l.disabled:false},[s]),p=Mt.useCallback(c=>{let l=s[c];return l?l.required:false},[s]),m=Mt.useCallback(c=>{let l=s[c];return l?l.readonly:false},[s]);return Mt.useMemo(()=>({fieldConditions:s,hasConditionalFields:o,getFieldCondition:a,isFieldVisible:n,isFieldDisabled:f,isFieldRequired:p,isFieldReadonly:m}),[s,o,a,n,f,p,m])}function vt(r){return typeof r=="object"&&r!==null&&"preventDefault"in r}function $e({store:r,onSubmit:e,validateForm:t,defaultSubmitOptions:i}){let o=Mt.useRef(e);o.current=e;let s=Mt.useRef(i);return s.current=i,{submit:Mt.useCallback(async n=>{let f={};vt(n)?n.preventDefault():n&&(f=n);let p={...s.current,...f},m=r.getState();if(m.isSubmitting)return false;m._setSubmitting(true);try{if(p.force){let u=r.getState(),b=Object.keys(u._repeatableConfigs).length>0?re(u.values,u._repeatableConfigs,u._repeatableOrder):u.values;return o.current&&await o.current(b),m._setSubmitting(!1),!0}let c=await t();if(!c.isValid&&!p.skipInvalid)return m._setSubmitting(!1),!1;let l=r.getState(),d=l.values;if(p.skipInvalid&&!c.isValid){let u=new Set(Object.entries(l.errors).filter(([,C])=>C.length>0).map(([C])=>C));d=Object.fromEntries(Object.entries(d).filter(([C])=>!u.has(C)));}let g=Object.keys(l._repeatableConfigs).length>0?re(d,l._repeatableConfigs,l._repeatableOrder):d;return o.current&&await o.current(g),m._setSubmitting(!1),!0}catch(c){return m._setSubmitting(false),console.error("Form submission error:",c),false}},[r,t])}}function se(){return {isValid:true,errors:[]}}function He({formConfig:r,store:e,conditionsHelpers:t}){let i=Mt.useRef(r),o=Mt.useRef(t);i.current=r,o.current=t;let s=Mt.useCallback(async(n,f)=>{let p=i.current.allFields.find(d=>d.id===n);if(!p){let d=L(n);if(d&&i.current.repeatableFields){let F=i.current.repeatableFields[d.repeatableId];if(F){let g=F.allFields.find(u=>u.id===d.fieldId);g&&(p={...g,id:n});}}}let m=e.getState();if(!p)return se();if(!o.current.isFieldVisible(n))return m._setErrors(n,[]),m._setValidationState(n,"valid"),se();if(!p.validation||!core.hasUnifiedValidation(p.validation)){let d=o.current.isFieldRequired(n),F=f!==void 0?f:m.values[n];if(d&&core.isEmptyValue(F)){let g={isValid:false,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"}]};return m._setErrors(n,g.errors),m._setValidationState(n,"invalid"),g}return m._setErrors(n,[]),m._setValidationState(n,"valid"),se()}let c=f!==void 0?f:m.values[n],l=core.createValidationContext({fieldId:n,formId:i.current.id,allFormData:{...m.values,[n]:c}});m._setValidationState(n,"validating");try{let d=await core.validateWithUnifiedConfig(p.validation,c,l);if(o.current.isFieldRequired(n)&&core.isEmptyValue(c)&&!d.errors.some(u=>u.code==="REQUIRED"||u.message.toLowerCase().includes("required"))){let u={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...d.errors]};return m._setErrors(n,u.errors),m._setValidationState(n,"invalid"),u}return m._setErrors(n,d.errors),m._setValidationState(n,d.isValid?"valid":"invalid"),d}catch(d){let F={isValid:false,errors:[{message:d instanceof Error?d.message:"Validation failed",code:"VALIDATION_ERROR"}]};return m._setErrors(n,F.errors),m._setValidationState(n,"invalid"),F}},[e]),a=Mt.useCallback(async()=>{let n=e.getState(),f=i.current.allFields.filter(u=>{if(!o.current.isFieldVisible(u.id))return false;let b=u.validation&&core.hasUnifiedValidation(u.validation),k=o.current.isFieldRequired(u.id);return b||k}),p=i.current.allFields.filter(u=>!o.current.isFieldVisible(u.id));for(let u of p)n._setErrors(u.id,[]),n._setValidationState(u.id,"valid");let m=await Promise.all(f.map(u=>s(u.id))),c=m.some(u=>!u.isValid),l=i.current.repeatableFields??{},d=[];for(let[u,C]of Object.entries(l)){let b=n._repeatableOrder[u]??[];for(let k of b)for(let P of C.allFields){let O=V(u,k,P.id);if(!o.current.isFieldVisible(O)){n._setErrors(O,[]),n._setValidationState(O,"valid");continue}let D=await s(O);d.push(D);}C.min!==void 0&&b.length<C.min&&d.push({isValid:false,errors:[{message:`At least ${C.min} item(s) required`,code:"REPEATABLE_MIN_COUNT",path:u}]});}let F=d.some(u=>!u.isValid);c=c||F;let g=se();if(i.current.validation&&core.hasUnifiedValidation(i.current.validation)){let u=Object.keys(n.values).reduce((b,k)=>(o.current.isFieldVisible(k)&&(b[k]=n.values[k]),b),{}),C=core.createValidationContext({formId:i.current.id,allFormData:u});try{g=await core.validateFormWithUnifiedConfig(i.current.validation,u,C);}catch(b){g={isValid:false,errors:[{message:b instanceof Error?b.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!c&&g.isValid,errors:[...m.flatMap(u=>u.errors),...d.flatMap(u=>u.errors),...g.errors]}},[e,s]);return {validateField:s,validateForm:a}}function ve(r){let e=S(),{formConfig:t}=E(),i=pe(r),o=t.repeatableFields?.[r],s=Mt.useMemo(()=>o?new Set(o.allFields.map(l=>l.id)):new Set,[o]),a=Mt.useMemo(()=>o?i.map((l,d)=>{let F=o.allFields.map(u=>{let C=V(r,l,u.id),b=u.conditions?Y(u.conditions,r,l,s):void 0;return {...u,id:C,conditions:b}}),g=o.rows.map(u=>({...u,fields:u.fields.map(C=>{let b=V(r,l,C.id),k=C.conditions?Y(C.conditions,r,l,s):void 0;return {...C,id:b,conditions:k}})}));return {key:l,index:d,rows:g,allFields:F}}):[],[r,i,o,s]),n=Mt.useMemo(()=>o?o.max===void 0?true:i.length<o.max:false,[o,i.length]),f=Mt.useMemo(()=>{if(!o)return false;let l=o.min??0;return i.length>l},[o,i.length]),p=Mt.useCallback(l=>{e.getState()._appendRepeatableItem(r,l);},[e,r]),m=Mt.useCallback(l=>{e.getState()._removeRepeatableItem(r,l);},[e,r]),c=Mt.useCallback((l,d)=>{e.getState()._moveRepeatableItem(r,l,d);},[e,r]);return {items:a,append:p,remove:m,move:c,canAdd:n,canRemove:f,count:i.length}}function xt({formConfig:r,enabled:e=true}){let t=core.getGlobalMonitor(),i=Mt.useRef(null),o=Mt.useRef(0),s=Mt.useRef(0);Mt.useEffect(()=>{t&&e&&(i.current=t.getProfiler());},[t,e]);let a=Mt.useCallback(l=>{if(!t||!e)return;o.current++;let d={formId:r.id,fieldCount:r.allFields.length,timestamp:Date.now(),duration:0,renderDuration:0,validationDuration:0,validationErrors:0,renderCount:l||o.current};t.track("component_render",`form_${r.id}`,{formId:r.id,fieldCount:r.allFields.length,renderCount:o.current},d,"low");},[t,e,r.id,r.allFields.length]),n=Mt.useCallback((l,d)=>{if(!t||!e)return;let F=i.current?.getMetrics(`form_validation_${r.id}`),g={formId:r.id,fieldCount:d||r.allFields.length,timestamp:Date.now(),duration:F?.duration||0,renderDuration:0,validationDuration:F?.duration||0,validationErrors:l,renderCount:o.current};t.track("form_validation",`form_${r.id}`,{formId:r.id,validationErrors:l,fieldCount:d||r.allFields.length},g,l>0?"medium":"low");},[t,e,r.id,r.allFields.length]),f=Mt.useCallback((l,d)=>{if(!t||!e)return;let F=i.current?.getMetrics(`form_submission_${r.id}`),g={formId:r.id,fieldCount:d||r.allFields.length,timestamp:Date.now(),duration:F?.duration||0,renderDuration:0,validationDuration:0,validationErrors:l?0:1,renderCount:o.current};t.track("form_submission",`form_${r.id}`,{formId:r.id,success:l,fieldCount:d||r.allFields.length,fieldChanges:s.current},g,l?"low":"high");},[t,e,r.id,r.allFields.length]),p=Mt.useCallback((l,d)=>{!t||!e||(s.current++,t.track("component_update",`field_${l}`,{formId:r.id,fieldId:l,componentType:d,changeCount:s.current},void 0,"low"));},[t,e,r.id]),m=Mt.useCallback(l=>{!i.current||!e||i.current.start(l,{formId:r.id,renderCount:o.current});},[e,r.id]),c=Mt.useCallback(l=>{if(!i.current||!e)return null;let d=i.current.end(l);return d?{...d,formId:r.id,fieldCount:r.allFields.length,renderDuration:d.duration,validationDuration:0,validationErrors:0}:null},[e,r.id,r.allFields.length]);return {trackFormRender:a,trackFormValidation:n,trackFormSubmission:f,trackFieldChange:p,startPerformanceTracking:m,endPerformanceTracking:c}}var ze=Mt.createContext(null);function E(){let r=Mt.useContext(ze);if(!r)throw new Error("useFormConfigContext must be used within a FormProvider");return r}function _e({children:r,formConfig:e,defaultValues:t={},onSubmit:i,onFieldChange:o,className:s}){let[a]=Mt.useState(()=>{let x=e.repeatableFields??{},R={...t},h={},y={};if(Object.keys(x).some(v=>Array.isArray(t[v]))){let v=oe(t,x);R=v.values,h=v.order,y=v.nextKeys;}for(let[v,B]of Object.entries(x))if(!h[v]){let Z=B.min??0,K=[],I=y[v]??0;for(let N=0;N<Z;N++){let j=`k${I}`;K.push(j);for(let ee of B.allFields){let rt=V(v,j,ee.id);R[rt]=B.defaultValue?.[ee.id]??void 0;}I++;}h[v]=K,y[v]=I;}let M=ue(R),_=M.getState();for(let[v,B]of Object.entries(x))_._setRepeatableConfig(v,B);return M.setState({_repeatableOrder:h,_repeatableNextKey:y}),M}),n=Mt.useRef(e.id),f=Mt.useRef(o);f.current=o,Mt.useEffect(()=>f.current?a.subscribe(R=>R.values,(R,h)=>{for(let y of Object.keys(R))R[y]!==h[y]&&f.current?.(y,R[y],R);}):void 0,[a]),Mt.useEffect(()=>{if(n.current!==e.id){n.current=e.id;let x=e.repeatableFields??{},R={...t},h={},y={};if(Object.keys(x).some(_=>Array.isArray(t[_]))){let _=oe(t,x);R=_.values,h=_.order,y=_.nextKeys;}for(let[_,v]of Object.entries(x))if(!h[_]){let B=v.min??0,Z=[],K=y[_]??0;for(let I=0;I<B;I++){let N=`k${K}`;Z.push(N);for(let j of v.allFields){let ee=V(_,N,j.id);R[ee]=v.defaultValue?.[j.id]??void 0;}K++;}h[_]=Z,y[_]=K;}a.getState()._reset(R);let M=a.getState();for(let[_,v]of Object.entries(x))M._setRepeatableConfig(_,v);a.setState({_repeatableOrder:h,_repeatableNextKey:y});}},[e.id,e.repeatableFields,a,t]);let[p,m]=Mt.useState(()=>a.getState().values);Mt.useEffect(()=>a.subscribe(R=>R.values,R=>m(R)),[a]);let[c,l]=Mt.useState(()=>a.getState()._repeatableOrder);Mt.useEffect(()=>a.subscribe(R=>R._repeatableOrder,R=>l(R)),[a]);let{fieldConditions:d,hasConditionalFields:F,getFieldCondition:g,isFieldVisible:u,isFieldDisabled:C,isFieldRequired:b,isFieldReadonly:k}=Ie({formConfig:e,formValues:p,repeatableOrder:c});Mt.useEffect(()=>{for(let[x,R]of Object.entries(d)){let h={visible:R.visible,disabled:R.disabled,required:R.required,readonly:R.readonly};a.getState()._setFieldConditions(x,h);}},[d,a]);let P=Mt.useMemo(()=>({hasConditionalFields:F,getFieldCondition:g,isFieldVisible:u,isFieldDisabled:C,isFieldRequired:b,isFieldReadonly:k}),[F,g,u,C,b,k]),{validateField:O,validateForm:D}=He({formConfig:e,store:a,conditionsHelpers:P}),{submit:q}=$e({store:a,onSubmit:i,validateForm:D,defaultSubmitOptions:e.submitOptions}),X=Mt.useMemo(()=>({formConfig:e,conditionsHelpers:P,validateField:O,validateForm:D,submit:q}),[e,P,O,D,q]);return jsxRuntime.jsx(ie.Provider,{value:a,children:jsxRuntime.jsx(ze.Provider,{value:X,children:jsxRuntime.jsx("form",{onSubmit:q,className:s,noValidate:true,children:r})})})}function Ot({formConfig:r,defaultValues:e,onSubmit:t,onFieldChange:i,className:o,children:s}){let a=Mt.useMemo(()=>r instanceof T?r.build():r,[r]);return jsxRuntime.jsx(_e,{formConfig:a,defaultValues:e,onSubmit:t,onFieldChange:i,className:o,children:s})}var Q=Mt__default.default.memo(function({fieldId:e,fieldConfig:t,disabled:i=false,customProps:o={},className:s,forceVisible:a=false}){let{formConfig:n,validateField:f,conditionsHelpers:p}=E(),m=ce(e),c=me(e),l=U(e),{setValue:d,setTouched:F}=Fe(e),g=Mt.useMemo(()=>{if(t)return t;let h=n.allFields.find(A=>A.id===e);if(h)return h;let y=L(e);if(y&&n.repeatableFields){let A=n.repeatableFields[y.repeatableId];if(A){let M=A.allFields.find(_=>_.id===y.fieldId);if(M)return {...M,id:e}}}},[t,n.allFields,n.repeatableFields,e]);if(!g)throw new Error(`Field with ID "${e}" not found`);let u=n.config.getComponent(g.componentId);if(!u)throw new Error(`Component with ID "${g.componentId}" not found`);let C=c.validationState==="validating",b=Mt.useMemo(()=>({isVisible:a||l.visible,isFieldDisabled:i||l.disabled,isFieldRequired:l.required||p.isFieldRequired(e),isFieldReadonly:l.readonly}),[a,i,l,p,e]),k=Mt.useCallback(async h=>{d(h),(g.validation?.validateOnChange||c.touched)&&await f(e,h);},[e,d,f,g.validation?.validateOnChange,c.touched]),P=Mt.useCallback(async()=>{c.touched||F(),g.validation?.validateOnBlur!==false&&await f(e);},[e,c.touched,F,f,g.validation?.validateOnBlur]),O=Mt.useMemo(()=>({...u.defaultProps??{},...g.props,...o,disabled:b.isFieldDisabled,required:b.isFieldRequired,readOnly:b.isFieldReadonly}),[u.defaultProps,g.props,o,b.isFieldDisabled,b.isFieldRequired,b.isFieldReadonly]),D=Mt.useMemo(()=>({id:e,props:O,value:m,onChange:k,onBlur:P,disabled:b.isFieldDisabled,error:c.errors,isValidating:C,touched:c.touched}),[e,O,m,k,P,b.isFieldDisabled,c.errors,C,c.touched]);if(!b.isVisible)return null;let q=u.renderer(D),X=n.renderConfig?.fieldRenderer,x=u.useFieldRenderer!==false,R=X&&x?X({children:q,id:e,...O,error:c.errors,isValidating:C,touched:c.touched}):q;return jsxRuntime.jsx("div",{className:s,"data-field-id":e,"data-field-type":u.type,"data-field-visible":b.isVisible,"data-field-disabled":b.isFieldDisabled,"data-field-required":b.isFieldRequired,"data-field-readonly":b.isFieldReadonly,children:R})});var Xe=Mt__default.default.memo(function({row:e,className:t,...i}){let{formConfig:o,conditionsHelpers:s}=E(),a=Mt.useMemo(()=>e.fields.filter(p=>s.isFieldVisible(p.id)),[e.fields,s]),n=Mt.useMemo(()=>a.map(p=>jsxRuntime.jsx(Q,{fieldId:p.id},p.id)),[a]),f=Mt.useMemo(()=>({row:e,children:n,className:t}),[e,n,t]);return a.length===0?null:jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormRow",renderer:o.renderConfig?.rowRenderer,props:f,...i,children:n})}),de=Xe;var ke=Mt__default.default.memo(function({item:e,index:t,total:i,canRemove:o,canMoveUp:s,canMoveDown:a,onRemove:n,onMoveUp:f,onMoveDown:p}){let{formConfig:m}=E(),c=Mt.useMemo(()=>new Map(e.allFields.map(F=>[F.id,F])),[e.allFields]),l=Mt.useMemo(()=>e.rows.map(F=>jsxRuntime.jsx(de,{row:F,children:F.fields.map(g=>jsxRuntime.jsx(Q,{fieldId:g.id,fieldConfig:c.get(g.id)},g.id))},F.id)),[e.rows,c]),d=m.renderConfig?.repeatableItemRenderer;return d?d({item:e,index:t,total:i,canRemove:o,canMoveUp:s,canMoveDown:a,onRemove:n,onMoveUp:f,onMoveDown:p,children:l}):jsxRuntime.jsx("div",{"data-repeatable-item":e.key,"data-repeatable-index":t,children:l})});var Ee=Mt__default.default.memo(function({repeatableId:e,repeatableConfig:t,className:i}){let{formConfig:o}=E(),{items:s,append:a,remove:n,move:f,canAdd:p,canRemove:m}=ve(e),c=Mt.useMemo(()=>s.map((d,F)=>jsxRuntime.jsx(ke,{item:d,index:F,total:s.length,canRemove:m,canMoveUp:F>0,canMoveDown:F<s.length-1,onRemove:()=>n(d.key),onMoveUp:()=>f(F,F-1),onMoveDown:()=>f(F,F+1)},d.key)),[s,m,n,f]),l=o.renderConfig?.repeatableRenderer;return l?l({repeatableId:e,items:s,canAdd:p,canRemove:m,onAdd:()=>a(),min:t.min,max:t.max,children:c}):jsxRuntime.jsxs("div",{className:i,"data-repeatable-id":e,children:[c,p&&jsxRuntime.jsx("button",{type:"button",onClick:()=>a(),"data-repeatable-add":e,children:"Add"})]})});var Nt=Mt__default.default.memo(function({className:e,...t}){let{formConfig:i}=E(),o=Mt.useMemo(()=>i.rows.map(a=>a.kind==="repeatable"?jsxRuntime.jsx(Ee,{repeatableId:a.repeatable.id,repeatableConfig:a.repeatable},a.id):jsxRuntime.jsx(de,{row:a},a.id)),[i.rows]),s=Mt.useMemo(()=>({formConfig:i,children:o,className:e}),[i,o,e]);return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormBody",renderer:i.renderConfig?.bodyRenderer,props:s,...t,children:o})});var Ht=Mt__default.default.memo(function({className:e,isSubmitting:t,...i}){let{formConfig:o,submit:s}=E(),{isSubmitting:a}=fe(),n=Mt.useMemo(()=>({isSubmitting:t??a,onSubmit:s,className:e}),[t,a,s,e]);return jsxRuntime.jsx(core.ComponentRendererWrapper,{name:"FormSubmitButton",renderer:o.renderConfig?.submitButtonRenderer,props:n,...i})});exports.Form=Ot;exports.FormBody=Nt;exports.FormBuilder=T;exports.FormField=Q;exports.FormProvider=_e;exports.FormRow=Xe;exports.FormStoreContext=ie;exports.FormSubmitButton=Ht;exports.RepeatableBuilder=W;exports.RepeatableField=Ee;exports.RepeatableItem=ke;exports.createFormStore=ue;exports.flattenRepeatableValues=oe;exports.form=T;exports.structureFormValues=re;exports.useConditionEvaluation=tr;exports.useConditionEvaluator=Fr;exports.useFieldActions=Fe;exports.useFieldConditions=U;exports.useFieldConditionsLazy=pr;exports.useFieldConditionsWithRefresh=gr;exports.useFieldErrors=ut;exports.useFieldState=me;exports.useFieldTouched=ct;exports.useFieldValidationState=mt;exports.useFieldValue=ce;exports.useFormActions=Rt;exports.useFormConditions=Ie;exports.useFormConfigContext=E;exports.useFormDirty=gt;exports.useFormMonitoring=xt;exports.useFormStore=S;exports.useFormStoreApi=H;exports.useFormSubmissionWithStore=$e;exports.useFormSubmitState=fe;exports.useFormSubmitting=pt;exports.useFormValid=Ft;exports.useFormValidationWithStore=He;exports.useFormValues=bt;exports.useMultipleConditionEvaluation=Te;exports.useRepeatableField=ve;exports.useRepeatableKeys=pe;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import Xe,{createContext,useMemo,useCallback,useContext,useRef,useEffect,useState}from'react';import {ComponentRendererWrapper,IdGenerator,deepClone,ensureUnique,hasUnifiedValidation,createValidationContext,validateWithUnifiedConfig,validateFormWithUnifiedConfig,getGlobalMonitor,evaluateCondition}from'@rilaykit/core';import {useStore,createStore}from'zustand';import {subscribeWithSelector}from'zustand/middleware';import {jsx}from'react/jsx-runtime';var x=class o{constructor(e,t){this.rows=[];this.idGenerator=new IdGenerator;this.config=e,this.formId=t||`form-${Math.random().toString(36).substring(2,15)}`;}static create(e,t){return new o(e,t)}createFormField(e){let t=this.config.getComponent(e.type);if(!t)throw new Error(`No component found with type "${e.type}"`);let i;return (t.validation||e.validation)&&(i={validateOnChange:e.validation?.validateOnChange??t.validation?.validateOnChange,validateOnBlur:e.validation?.validateOnBlur??t.validation?.validateOnBlur,debounceMs:e.validation?.debounceMs??t.validation?.debounceMs,validate:(()=>{let r=t.validation?.validate,n=e.validation?.validate;if(!r)return n;if(!n)return r;let a=Array.isArray(r)?r:[r],s=Array.isArray(n)?n:[n];return [...a,...s]})()}),{id:e.id||this.idGenerator.next("field"),componentId:t.id,props:{...t.defaultProps,...e.props},validation:i,conditions:e.conditions}}createRow(e){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 t=e.map(i=>this.createFormField(i));return {id:this.idGenerator.next("row"),fields:t,maxColumns:e.length}}add(...e){let t,i=false;if(e.length===1&&Array.isArray(e[0])?(t=e[0],i=true):t=e,t.length===0)throw new Error("At least one field is required");if(i&&t.length>3)throw new Error("Maximum 3 fields per row");if(t.length===1){let r=this.createRow(t);return this.rows.push(r),this}if(t.length<=3){let r=this.createRow(t);return this.rows.push(r),this}for(let r of t){let n=this.createRow([r]);this.rows.push(n);}return this}addSeparateRows(e){for(let t of e)this.add(t);return this}setId(e){return this.formId=e,this}updateField(e,t){let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);return Object.assign(i,{...t,props:{...i.props,...t.props}}),this}findField(e){for(let t of this.rows){let i=t.fields.find(r=>r.id===e);if(i)return i}return null}removeField(e){return this.rows=this.rows.map(t=>({...t,fields:t.fields.filter(i=>i.id!==e)})).filter(t=>t.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}setValidation(e){return this.formValidation=e,this}addFieldValidation(e,t){console.warn("addFieldValidation is deprecated. Use updateField with validation.validate property instead.");let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let r={...i.validation,...t};return this.updateField(e,{validation:r})}addFieldConditions(e,t){let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let r={...i.conditions,...t};return this.updateField(e,{conditions:r})}clone(e){let t=new o(this.config,e||`${this.formId}-clone`);return t.rows=deepClone(this.rows),t}validate(){let e=[],t=this.getFields(),i=t.map(r=>r.id);try{ensureUnique(i,"field");}catch(r){e.push(r instanceof Error?r.message:String(r));}for(let r of t)this.config.hasComponent(r.componentId)||e.push(`Component "${r.componentId}" not found for field "${r.id}"`);for(let r of this.rows)r.fields.length>3&&e.push(`Row "${r.id}" has ${r.fields.length} fields, maximum is 3`),r.fields.length===0&&e.push(`Row "${r.id}" is empty`);return e}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(),validation:this.formValidation}}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(),t=this.rows.map(i=>i.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:this.rows.length>0?e.length/this.rows.length:0,maxFieldsInRow:t.length>0?Math.max(...t):0,minFieldsInRow:t.length>0?Math.min(...t):0}}};function Ft(o,e={},t={}){return useMemo(()=>{if(!o)return {visible:t.visible??true,disabled:t.disabled??false,required:t.required??false,readonly:t.readonly??false};let i=r=>{try{return r&&typeof r=="object"&&"build"in r?evaluateCondition(r.build(),e):evaluateCondition(r,e)}catch(n){return console.warn("Error evaluating condition:",n),false}};return {visible:o.visible?i(o.visible):t.visible??true,disabled:o.disabled?i(o.disabled):t.disabled??false,required:o.required?i(o.required):t.required??false,readonly:o.readonly?i(o.readonly):t.readonly??false}},[o,e,t])}function oe(o,e={}){return useMemo(()=>{let t={};for(let[i,r]of Object.entries(o))if(t[i]={visible:true,disabled:false,required:false,readonly:false},r){let n=a=>{try{return a&&typeof a=="object"&&"build"in a?evaluateCondition(a.build(),e):evaluateCondition(a,e)}catch(s){return console.warn(`Error evaluating condition for field ${i}:`,s),false}};t[i]={visible:r.visible?n(r.visible):true,disabled:r.disabled?n(r.disabled):false,required:r.required?n(r.required):false,readonly:r.readonly?n(r.readonly):false};}return t},[o,e])}function $(o={}){return createStore()(subscribeWithSelector((e,t)=>({values:{...o},errors:{},validationStates:{},touched:{},isDirty:false,isSubmitting:false,isValid:true,_defaultValues:{...o},_fieldConditions:{},_setValue:(i,r)=>{e(n=>({values:{...n.values,[i]:r},isDirty:true}));},_setTouched:i=>{e(r=>({touched:{...r.touched,[i]:true}}));},_setErrors:(i,r)=>{e(n=>{let a={...n.errors,[i]:r},s=r.length>0?"invalid":"valid";return {errors:a,validationStates:{...n.validationStates,[i]:s}}}),t()._updateIsValid();},_clearErrors:i=>{e(r=>{let n={...r.errors};return delete n[i],{errors:n,validationStates:{...r.validationStates,[i]:"idle"}}}),t()._updateIsValid();},_setValidationState:(i,r)=>{e(n=>({validationStates:{...n.validationStates,[i]:r}}));},_setSubmitting:i=>{e({isSubmitting:i});},_reset:i=>{let r=i??t()._defaultValues;e({values:{...r},errors:{},validationStates:{},touched:{},isDirty:false,isSubmitting:false,isValid:true});},_setFieldConditions:(i,r)=>{e(n=>({_fieldConditions:{...n._fieldConditions,[i]:r}}));},_updateIsValid:()=>{let i=t(),r=Object.values(i.errors).some(a=>a&&a.length>0),n=Object.values(i.validationStates).some(a=>a==="invalid");e({isValid:!r&&!n});}})))}var O=createContext(null);function v(){let o=useContext(O);if(!o)throw new Error("useFormStore must be used within a FormProvider");return o}var re=[];function N(o){let e=v();return useStore(e,t=>t.values[o])}function Me(o){let e=v();return useStore(e,t=>t.errors[o]??re)}function Be(o){let e=v();return useStore(e,t=>t.touched[o]??false)}function Te(o){let e=v();return useStore(e,t=>t.validationStates[o]??"idle")}var De={visible:true,disabled:false,required:false,readonly:false};function P(o){let e=v();return useStore(e,t=>t._fieldConditions[o]??De)}function W(o){let e=v(),t=useStore(e,s=>s.values[o]),i=useStore(e,s=>s.errors[o]??re),r=useStore(e,s=>s.validationStates[o]??"idle"),n=useStore(e,s=>s.touched[o]??false),a=useStore(e,s=>s._defaultValues[o]);return {value:t,errors:i,validationState:r,touched:n,dirty:t!==a}}function qe(){let o=v();return useStore(o,e=>e.isSubmitting)}function Oe(){let o=v();return useStore(o,e=>e.isValid)}function Ue(){let o=v();return useStore(o,e=>e.isDirty)}function Ae(){let o=v();return useStore(o,e=>e.values)}function H(){let o=v(),e=useStore(o,r=>r.isSubmitting),t=useStore(o,r=>r.isValid),i=useStore(o,r=>r.isDirty);return {isSubmitting:e,isValid:t,isDirty:i}}function L(o){let e=v();return {setValue:t=>e.getState()._setValue(o,t),setTouched:()=>e.getState()._setTouched(o),setErrors:t=>e.getState()._setErrors(o,t),clearErrors:()=>e.getState()._clearErrors(o),setValidationState:t=>e.getState()._setValidationState(o,t)}}function Ie(){let o=v();return {setValue:(e,t)=>o.getState()._setValue(e,t),setTouched:e=>o.getState()._setTouched(e),setErrors:(e,t)=>o.getState()._setErrors(e,t),setSubmitting:e=>o.getState()._setSubmitting(e),reset:e=>o.getState()._reset(e),setFieldConditions:(e,t)=>o.getState()._setFieldConditions(e,t)}}function B(){return v()}var se={visible:true,disabled:false,required:false,readonly:false};function ae(o){try{return JSON.stringify(o)}catch{return String(Date.now())}}function U(o,e){if(o)try{return typeof o=="object"&&"build"in o?evaluateCondition(o.build(),e):evaluateCondition(o,e)}catch(t){console.warn("Error evaluating condition:",t);return}}function j(o,e){if(!o)return se;let t=U(o.visible,e),i=U(o.disabled,e),r=U(o.required,e),n=U(o.readonly,e);return {visible:t??true,disabled:i??false,required:r??false,readonly:n??false}}function Vt(o,e={}){let{conditions:t,skip:i=false}=e,r=B(),n=P(o),a=useRef(null);if(i||!t)return n;let s=r.getState().values,F=ae(s);if(a.current?.valuesHash===F)return a.current.result;let m=j(t,s);return a.current={result:m,valuesHash:F},m}function xt(){let o=B(),e=useRef(new Map),t=useRef("");return useMemo(()=>function(r,n){if(!n)return se;let a=o.getState().values,s=ae(a);t.current!==s&&(e.current.clear(),t.current=s);let F=e.current.get(r);if(F)return F.result;let m=j(n,a);return e.current.set(r,{result:m,valuesHash:s}),m},[o])}function Pt(o,e){let t=B(),i=P(o),r=useMemo(()=>()=>{if(!e)return i;let n=t.getState().values;return j(e,n)},[t,e,i]);return {conditions:i,refresh:r}}function de({formConfig:o,formValues:e}){let t=useMemo(()=>{let l={};for(let u of o.allFields)u.conditions&&(l[u.id]=u.conditions);return l},[o.allFields]),i=useMemo(()=>Object.keys(t).length>0,[t]),r=oe(i?t:{},i?e:{}),n=useCallback(l=>r[l],[r]),a=useCallback(l=>{let u=r[l];return u?u.visible:true},[r]),s=useCallback(l=>{let u=r[l];return u?u.disabled:false},[r]),F=useCallback(l=>{let u=r[l];return u?u.required:false},[r]),m=useCallback(l=>{let u=r[l];return u?u.readonly:false},[r]);return useMemo(()=>({fieldConditions:r,hasConditionalFields:i,getFieldCondition:n,isFieldVisible:a,isFieldDisabled:s,isFieldRequired:F,isFieldReadonly:m}),[r,i,n,a,s,F,m])}function le({store:o,onSubmit:e,validateForm:t}){let i=useRef(e);return i.current=e,{submit:useCallback(async n=>{n?.preventDefault();let a=o.getState();if(a.isSubmitting)return false;a._setSubmitting(true);try{if(!(await t()).isValid)return a._setSubmitting(!1),!1;let F=o.getState().values;return i.current&&await i.current(F),a._setSubmitting(!1),!0}catch(s){return a._setSubmitting(false),console.error("Form submission error:",s),false}},[o,t])}}function A(){return {isValid:true,errors:[]}}function fe({formConfig:o,store:e,conditionsHelpers:t}){let i=useRef(o),r=useRef(t);i.current=o,r.current=t;let n=useCallback(async(s,F)=>{let m=i.current.allFields.find(d=>d.id===s),l=e.getState();if(!m)return A();if(!r.current.isFieldVisible(s))return l._setErrors(s,[]),l._setValidationState(s,"valid"),A();if(!m.validation||!hasUnifiedValidation(m.validation))return l._setErrors(s,[]),l._setValidationState(s,"valid"),A();let u=F!==void 0?F:l.values[s],c=createValidationContext({fieldId:s,formId:i.current.id,allFormData:{...l.values,[s]:u}});l._setValidationState(s,"validating");try{let d=await validateWithUnifiedConfig(m.validation,u,c),f=r.current.isFieldRequired(s),p=u==null||u==="";if(f&&p&&!d.errors.some(g=>g.code==="REQUIRED"||g.message.toLowerCase().includes("required"))){let g={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...d.errors]};return l._setErrors(s,g.errors),l._setValidationState(s,"invalid"),g}return l._setErrors(s,d.errors),l._setValidationState(s,d.isValid?"valid":"invalid"),d}catch(d){let f={isValid:false,errors:[{message:d instanceof Error?d.message:"Validation failed",code:"VALIDATION_ERROR"}]};return l._setErrors(s,f.errors),l._setValidationState(s,"invalid"),f}},[e]),a=useCallback(async()=>{let s=e.getState(),F=i.current.allFields.filter(d=>{let f=r.current.isFieldVisible(d.id),p=d.validation&&hasUnifiedValidation(d.validation);return f&&p}),m=i.current.allFields.filter(d=>!r.current.isFieldVisible(d.id));for(let d of m)s._setErrors(d.id,[]),s._setValidationState(d.id,"valid");let l=await Promise.all(F.map(d=>n(d.id))),u=l.some(d=>!d.isValid),c=A();if(i.current.validation&&hasUnifiedValidation(i.current.validation)){let d=Object.keys(s.values).reduce((p,b)=>(r.current.isFieldVisible(b)&&(p[b]=s.values[b]),p),{}),f=createValidationContext({formId:i.current.id,allFormData:d});try{c=await validateFormWithUnifiedConfig(i.current.validation,d,f);}catch(p){c={isValid:false,errors:[{message:p instanceof Error?p.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!u&&c.isValid,errors:[...l.flatMap(d=>d.errors),...c.errors]}},[e,n]);return {validateField:n,validateForm:a}}function je({formConfig:o,enabled:e=true}){let t=getGlobalMonitor(),i=useRef(null),r=useRef(0),n=useRef(0);useEffect(()=>{t&&e&&(i.current=t.getProfiler());},[t,e]);let a=useCallback(c=>{if(!t||!e)return;r.current++;let d={formId:o.id,fieldCount:o.allFields.length,timestamp:Date.now(),duration:0,renderDuration:0,validationDuration:0,validationErrors:0,renderCount:c||r.current};t.track("component_render",`form_${o.id}`,{formId:o.id,fieldCount:o.allFields.length,renderCount:r.current},d,"low");},[t,e,o.id,o.allFields.length]),s=useCallback((c,d)=>{if(!t||!e)return;let f=i.current?.getMetrics(`form_validation_${o.id}`),p={formId:o.id,fieldCount:d||o.allFields.length,timestamp:Date.now(),duration:f?.duration||0,renderDuration:0,validationDuration:f?.duration||0,validationErrors:c,renderCount:r.current};t.track("form_validation",`form_${o.id}`,{formId:o.id,validationErrors:c,fieldCount:d||o.allFields.length},p,c>0?"medium":"low");},[t,e,o.id,o.allFields.length]),F=useCallback((c,d)=>{if(!t||!e)return;let f=i.current?.getMetrics(`form_submission_${o.id}`),p={formId:o.id,fieldCount:d||o.allFields.length,timestamp:Date.now(),duration:f?.duration||0,renderDuration:0,validationDuration:0,validationErrors:c?0:1,renderCount:r.current};t.track("form_submission",`form_${o.id}`,{formId:o.id,success:c,fieldCount:d||o.allFields.length,fieldChanges:n.current},p,c?"low":"high");},[t,e,o.id,o.allFields.length]),m=useCallback((c,d)=>{!t||!e||(n.current++,t.track("component_update",`field_${c}`,{formId:o.id,fieldId:c,componentType:d,changeCount:n.current},void 0,"low"));},[t,e,o.id]),l=useCallback(c=>{!i.current||!e||i.current.start(c,{formId:o.id,renderCount:r.current});},[e,o.id]),u=useCallback(c=>{if(!i.current||!e)return null;let d=i.current.end(c);return d?{...d,formId:o.id,fieldCount:o.allFields.length,renderDuration:d.duration,validationDuration:0,validationErrors:0}:null},[e,o.id,o.allFields.length]);return {trackFormRender:a,trackFormValidation:s,trackFormSubmission:F,trackFieldChange:m,startPerformanceTracking:l,endPerformanceTracking:u}}var Ce=createContext(null);function y(){let o=useContext(Ce);if(!o)throw new Error("useFormConfigContext must be used within a FormProvider");return o}function K({children:o,formConfig:e,defaultValues:t={},onSubmit:i,onFieldChange:r,className:n}){let[a]=useState(()=>$(t)),s=useRef(e.id),F=useRef(r);F.current=r,useEffect(()=>F.current?a.subscribe(C=>C.values,(C,M)=>{for(let R of Object.keys(C))C[R]!==M[R]&&F.current?.(R,C[R],C);}):void 0,[a]),useEffect(()=>{s.current!==e.id&&(s.current=e.id,a.getState()._reset(t));},[e.id,a,t]);let[m,l]=useState(()=>a.getState().values);useEffect(()=>a.subscribe(C=>C.values,C=>l(C)),[a]);let{fieldConditions:u,hasConditionalFields:c,getFieldCondition:d,isFieldVisible:f,isFieldDisabled:p,isFieldRequired:b,isFieldReadonly:g}=de({formConfig:e,formValues:m});useEffect(()=>{for(let[V,C]of Object.entries(u)){let M={visible:C.visible,disabled:C.disabled,required:C.required,readonly:C.readonly};a.getState()._setFieldConditions(V,M);}},[u,a]);let w=useMemo(()=>({hasConditionalFields:c,getFieldCondition:d,isFieldVisible:f,isFieldDisabled:p,isFieldRequired:b,isFieldReadonly:g}),[c,d,f,p,b,g]),{validateField:_,validateForm:S}=fe({formConfig:e,store:a,conditionsHelpers:w}),{submit:k}=le({store:a,onSubmit:i,validateForm:S}),D=useMemo(()=>({formConfig:e,conditionsHelpers:w,validateField:_,validateForm:S,submit:k}),[e,w,_,S,k]);return jsx(O.Provider,{value:a,children:jsx(Ce.Provider,{value:D,children:jsx("form",{onSubmit:k,className:n,noValidate:true,children:o})})})}function Ye({formConfig:o,defaultValues:e,onSubmit:t,onFieldChange:i,children:r}){let n=useMemo(()=>o instanceof x?o.build():o,[o]);return jsx(K,{formConfig:n,defaultValues:e,onSubmit:t,onFieldChange:i,children:r})}var Z=Xe.memo(function({fieldId:e,disabled:t=false,customProps:i={},className:r,forceVisible:n=false}){let{formConfig:a,validateField:s,conditionsHelpers:F}=y(),m=N(e),l=W(e),u=P(e),{setValue:c,setTouched:d}=L(e),f=a.allFields.find(R=>R.id===e);if(!f)throw new Error(`Field with ID "${e}" not found`);let p=a.config.getComponent(f.componentId);if(!p)throw new Error(`Component with ID "${f.componentId}" not found`);let b=l.validationState==="validating",g=useMemo(()=>({isVisible:n||u.visible,isFieldDisabled:t||u.disabled,isFieldRequired:u.required||F.isFieldRequired(e),isFieldReadonly:u.readonly}),[n,t,u,F,e]),w=useCallback(async R=>{c(R),(f.validation?.validateOnChange||l.touched)&&await s(e,R);},[e,c,s,f.validation?.validateOnChange,l.touched]),_=useCallback(async()=>{l.touched||d(),f.validation?.validateOnBlur!==false&&await s(e);},[e,l.touched,d,s,f.validation?.validateOnBlur]),S=useMemo(()=>({...p.defaultProps??{},...f.props,...i,disabled:g.isFieldDisabled,required:g.isFieldRequired,readOnly:g.isFieldReadonly}),[p.defaultProps,f.props,i,g.isFieldDisabled,g.isFieldRequired,g.isFieldReadonly]),k=useMemo(()=>({id:e,props:S,value:m,onChange:w,onBlur:_,disabled:g.isFieldDisabled,error:l.errors,isValidating:b,touched:l.touched}),[e,S,m,w,_,g.isFieldDisabled,l.errors,b,l.touched]);if(!g.isVisible)return null;let D=p.renderer(k),V=a.renderConfig?.fieldRenderer,C=p.useFieldRenderer!==false,M=V&&C?V({children:D,id:e,...S,error:l.errors,isValidating:b,touched:l.touched}):D;return jsx("div",{className:r,"data-field-id":e,"data-field-type":p.type,"data-field-visible":g.isVisible,"data-field-disabled":g.isFieldDisabled,"data-field-required":g.isFieldRequired,"data-field-readonly":g.isFieldReadonly,children:M})});var be=Xe.memo(function({row:e,className:t,...i}){let{formConfig:r,conditionsHelpers:n}=y(),a=useMemo(()=>e.fields.filter(m=>n.isFieldVisible(m.id)),[e.fields,n]),s=useMemo(()=>a.map(m=>jsx(Z,{fieldId:m.id},m.id)),[a]),F=useMemo(()=>({row:e,children:s,className:t}),[e,s,t]);return a.length===0?null:jsx(ComponentRendererWrapper,{name:"FormRow",renderer:r.renderConfig?.rowRenderer,props:F,...i,children:s})}),Re=be;var it=Xe.memo(function({className:e,...t}){let{formConfig:i}=y(),r=useMemo(()=>i.rows.map(a=>jsx(Re,{row:a},a.id)),[i.rows]),n=useMemo(()=>({formConfig:i,children:r,className:e}),[i,r,e]);return jsx(ComponentRendererWrapper,{name:"FormBody",renderer:i.renderConfig?.bodyRenderer,props:n,...t,children:r})});var dt=Xe.memo(function({className:e,isSubmitting:t,...i}){let{formConfig:r,submit:n}=y(),{isSubmitting:a}=H(),s=useMemo(()=>({isSubmitting:t??a,onSubmit:n,className:e}),[t,a,n,e]);return jsx(ComponentRendererWrapper,{name:"FormSubmitButton",renderer:r.renderConfig?.submitButtonRenderer,props:s,...i})});export{Ye as Form,it as FormBody,x as FormBuilder,Z as FormField,K as FormProvider,be as FormRow,O as FormStoreContext,dt as FormSubmitButton,$ as createFormStore,x as form,Ft as useConditionEvaluation,xt as useConditionEvaluator,L as useFieldActions,P as useFieldConditions,Vt as useFieldConditionsLazy,Pt as useFieldConditionsWithRefresh,Me as useFieldErrors,W as useFieldState,Be as useFieldTouched,Te as useFieldValidationState,N as useFieldValue,Ie as useFormActions,de as useFormConditions,y as useFormConfigContext,Ue as useFormDirty,je as useFormMonitoring,v as useFormStore,B as useFormStoreApi,le as useFormSubmissionWithStore,H as useFormSubmitState,qe as useFormSubmitting,Oe as useFormValid,fe as useFormValidationWithStore,Ae as useFormValues,oe as useMultipleConditionEvaluation};
|
|
1
|
+
import Mt,{createContext,useMemo,useCallback,useContext,useRef,useEffect,useState}from'react';import {ComponentRendererWrapper,IdGenerator,deepClone,ensureUnique,hasUnifiedValidation,isEmptyValue,createValidationContext,validateWithUnifiedConfig,validateFormWithUnifiedConfig,getGlobalMonitor,evaluateCondition}from'@rilaykit/core';import {useStore,createStore}from'zustand';import {subscribeWithSelector}from'zustand/middleware';import {jsx,jsxs}from'react/jsx-runtime';var W=class{constructor(e){this.innerForm=new T(e,"__repeatable_template__");}add(...e){return this.innerForm.add(...e),this}addSeparateRows(e){return this.innerForm.addSeparateRows(e),this}min(e){return this._min=e,this}max(e){return this._max=e,this}defaultValue(e){return this._defaultValue=e,this}validation(e){return this._validation=e,this}_build(e){let t=this.innerForm.getRows(),i=this.innerForm.getFields();if(t.length===0)throw new Error(`Repeatable "${e}" must have at least one field`);for(let o of i)if(o.id.includes("[")||o.id.includes("]"))throw new Error(`Repeatable template field ID "${o.id}" cannot contain "[" or "]" (reserved for composite keys)`);if(this._min!==void 0&&this._max!==void 0&&this._min>this._max)throw new Error(`Repeatable "${e}": min (${this._min}) cannot be greater than max (${this._max})`);return {id:e,rows:t.map(o=>({...o,kind:"fields"})),allFields:i,min:this._min,max:this._max,defaultValue:this._defaultValue,validation:this._validation}}_hasRepeatables(){return this.innerForm.getRows().some(e=>"kind"in e&&e.kind==="repeatable")}};var T=class r{constructor(e,t){this.rows=[];this.idGenerator=new IdGenerator;this.config=e,this.formId=t||`form-${Math.random().toString(36).substring(2,15)}`;}static create(e,t){return new r(e,t)}createFormField(e){let t=this.config.getComponent(e.type);if(!t)throw new Error(`No component found with type "${e.type}"`);let i;return (t.validation||e.validation)&&(i={validateOnChange:e.validation?.validateOnChange??t.validation?.validateOnChange,validateOnBlur:e.validation?.validateOnBlur??t.validation?.validateOnBlur,debounceMs:e.validation?.debounceMs??t.validation?.debounceMs,validate:(()=>{let o=t.validation?.validate,s=e.validation?.validate;if(!o)return s;if(!s)return o;let a=Array.isArray(o)?o:[o],n=Array.isArray(s)?s:[s];return [...a,...n]})()}),{id:e.id||this.idGenerator.next("field"),componentId:t.id,props:{...t.defaultProps,...e.props},validation:i,conditions:e.conditions}}createRow(e){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 t=e.map(i=>this.createFormField(i));return {kind:"fields",id:this.idGenerator.next("row"),fields:t,maxColumns:e.length}}add(...e){let t,i=false;if(e.length===1&&Array.isArray(e[0])?(t=e[0],i=true):t=e,t.length===0)throw new Error("At least one field is required");if(i&&t.length>3)throw new Error("Maximum 3 fields per row");if(t.length===1){let o=this.createRow(t);return this.rows.push(o),this}if(t.length<=3){let o=this.createRow(t);return this.rows.push(o),this}for(let o of t){let s=this.createRow([o]);this.rows.push(s);}return this}addSeparateRows(e){for(let t of e)this.add(t);return this}addRepeatable(e,t){if(e.includes("[")||e.includes("]"))throw new Error(`Repeatable ID "${e}" cannot contain "[" or "]" (reserved for composite keys)`);let i=new W(this.config),o=t(i);if(o._hasRepeatables())throw new Error(`Nested repeatables are not supported (in repeatable "${e}")`);let s=o._build(e),a={kind:"repeatable",id:this.idGenerator.next("repeatable"),repeatable:s};return this.rows.push(a),this}setId(e){return this.formId=e,this}updateField(e,t){let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);return Object.assign(i,{...t,props:{...i.props,...t.props}}),this}findField(e){for(let t of this.rows)if(t.kind==="fields"){let i=t.fields.find(o=>o.id===e);if(i)return i}else {let i=t.repeatable.allFields.find(o=>o.id===e);if(i)return i}return null}removeField(e){return this.rows=this.rows.map(t=>t.kind==="repeatable"?t:{...t,fields:t.fields.filter(i=>i.id!==e)}).filter(t=>t.kind==="repeatable"||t.fields.length>0),this}getField(e){return this.findField(e)||void 0}getFields(){return this.rows.filter(e=>e.kind==="fields").flatMap(e=>e.fields)}getRows(){return [...this.rows]}clear(){return this.rows=[],this.idGenerator.reset(),this}setValidation(e){return this.formValidation=e,this}setSubmitOptions(e){return this._submitOptions=e,this}addFieldValidation(e,t){console.warn("addFieldValidation is deprecated. Use updateField with validation.validate property instead.");let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let o={...i.validation,...t};return this.updateField(e,{validation:o})}addFieldConditions(e,t){let i=this.findField(e);if(!i)throw new Error(`Field with ID "${e}" not found`);let o={...i.conditions,...t};return this.updateField(e,{conditions:o})}clone(e){let t=new r(this.config,e||`${this.formId}-clone`);return t.rows=deepClone(this.rows),t}validate(){let e=[],t=this.getFields(),i=this.rows.filter(n=>n.kind==="repeatable"),o=i.flatMap(n=>n.repeatable.allFields),s=[...t.map(n=>n.id),...o.map(n=>n.id)];try{ensureUnique(s,"field");}catch(n){e.push(n instanceof Error?n.message:String(n));}let a=i.map(n=>n.repeatable.id);try{ensureUnique(a,"repeatable");}catch(n){e.push(n instanceof Error?n.message:String(n));}for(let n of t)this.config.hasComponent(n.componentId)||e.push(`Component "${n.componentId}" not found for field "${n.id}"`);for(let n of o)this.config.hasComponent(n.componentId)||e.push(`Component "${n.componentId}" not found for repeatable template field "${n.id}"`);for(let n of this.rows)n.kind==="fields"&&(n.fields.length>3&&e.push(`Row "${n.id}" has ${n.fields.length} fields, maximum is 3`),n.fields.length===0&&e.push(`Row "${n.id}" is empty`));for(let n of t)(n.id.includes("[")||n.id.includes("]"))&&e.push(`Field ID "${n.id}" cannot contain "[" or "]" (reserved for repeatable composite keys)`);for(let n of a)(n.includes("[")||n.includes("]"))&&e.push(`Repeatable ID "${n}" cannot contain "[" or "]" (reserved for composite keys)`);return e}build(){let e=this.validate();if(e.length>0)throw new Error(`Form validation failed: ${e.join(", ")}`);let t=this.rows.filter(o=>o.kind==="repeatable"),i=t.length>0?Object.fromEntries(t.map(o=>[o.repeatable.id,o.repeatable])):void 0;return {id:this.formId,rows:[...this.rows],allFields:this.getFields(),repeatableFields:i,config:this.config,renderConfig:this.config.getFormRenderConfig(),validation:this.formValidation,submitOptions:this._submitOptions}}toJSON(){return {id:this.formId,rows:this.rows}}fromJSON(e){return e.id&&(this.formId=e.id),e.rows&&(this.rows=e.rows.map(t=>t.kind?t:{...t,kind:"fields"})),this}getStats(){let e=this.getFields(),t=this.rows.filter(s=>s.kind==="fields"),i=this.rows.filter(s=>s.kind==="repeatable"),o=t.map(s=>s.fields.length);return {totalFields:e.length,totalRows:this.rows.length,averageFieldsPerRow:t.length>0?e.length/t.length:0,maxFieldsInRow:o.length>0?Math.max(...o):0,minFieldsInRow:o.length>0?Math.min(...o):0,totalRepeatables:i.length,totalRepeatableFields:i.reduce((s,a)=>s+a.repeatable.allFields.length,0)}}};function tr(r,e={},t={}){return useMemo(()=>{if(!r)return {visible:t.visible??true,disabled:t.disabled??false,required:t.required??false,readonly:t.readonly??false};let i=o=>{try{return o&&typeof o=="object"&&"build"in o?evaluateCondition(o.build(),e):evaluateCondition(o,e)}catch(s){return console.warn("Error evaluating condition:",s),false}};return {visible:r.visible?i(r.visible):t.visible??true,disabled:r.disabled?i(r.disabled):t.disabled??false,required:r.required?i(r.required):t.required??false,readonly:r.readonly?i(r.readonly):t.readonly??false}},[r,e,t])}function Te(r,e={}){return useMemo(()=>{let t={};for(let[i,o]of Object.entries(r))if(t[i]={visible:true,disabled:false,required:false,readonly:false},o){let s=a=>{try{return a&&typeof a=="object"&&"build"in a?evaluateCondition(a.build(),e):evaluateCondition(a,e)}catch(n){return console.warn(`Error evaluating condition for field ${i}:`,n),false}};t[i]={visible:o.visible?s(o.visible):true,disabled:o.disabled?s(o.disabled):false,required:o.required?s(o.required):false,readonly:o.readonly?s(o.readonly):false};}return t},[r,e])}var nt=/^([^[\]]+)\[([^\]]+)\]\.(.+)$/;function V(r,e,t){return `${r}[${e}].${t}`}function L(r){let e=nt.exec(r);return e?{repeatableId:e[1],itemKey:e[2],fieldId:e[3]}:null}function re(r,e,t){let i={},o=new Set;for(let[s,a]of Object.entries(t)){if(!e[s])continue;let n=e[s],f=[];for(let p of a){let m={};for(let c of n.allFields){let l=V(s,p,c.id);l in r&&(m[c.id]=r[l],o.add(l));}f.push(m);}i[s]=f;}for(let[s,a]of Object.entries(r))!o.has(s)&&!L(s)&&(i[s]=a);return i}function oe(r,e){let t={},i={},o={};for(let[s,a]of Object.entries(r))if(e[s]&&Array.isArray(a)){let n=[],f=0;for(let p of a){let m=`k${f}`;n.push(m);for(let[c,l]of Object.entries(p))t[V(s,m,c)]=l;f++;}i[s]=n,o[s]=f;}else t[s]=a;return {values:t,order:i,nextKeys:o}}function ue(r={}){return createStore()(subscribeWithSelector((e,t)=>({values:{...r},errors:{},validationStates:{},touched:{},isDirty:false,isSubmitting:false,isValid:true,_defaultValues:{...r},_fieldConditions:{},_repeatableConfigs:{},_repeatableOrder:{},_repeatableNextKey:{},_setValue:(i,o)=>{e(s=>({values:{...s.values,[i]:o},isDirty:true}));},_setTouched:i=>{e(o=>({touched:{...o.touched,[i]:true}}));},_setErrors:(i,o)=>{e(s=>{let a={...s.errors,[i]:o},n=o.length>0?"invalid":"valid";return {errors:a,validationStates:{...s.validationStates,[i]:n}}}),t()._updateIsValid();},_clearErrors:i=>{e(o=>{let s={...o.errors};return delete s[i],{errors:s,validationStates:{...o.validationStates,[i]:"idle"}}}),t()._updateIsValid();},_setValidationState:(i,o)=>{e(s=>({validationStates:{...s.validationStates,[i]:o}}));},_setSubmitting:i=>{e({isSubmitting:i});},_reset:i=>{let o=i??t()._defaultValues;e({values:{...o},errors:{},validationStates:{},touched:{},isDirty:false,isSubmitting:false,isValid:true,_repeatableOrder:{},_repeatableNextKey:{}});},_setFieldConditions:(i,o)=>{e(s=>({_fieldConditions:{...s._fieldConditions,[i]:o}}));},_updateIsValid:()=>{let i=t(),o=Object.values(i.errors).some(a=>a&&a.length>0),s=Object.values(i.validationStates).some(a=>a==="invalid");e({isValid:!o&&!s});},_setRepeatableConfig:(i,o)=>{e(s=>({_repeatableConfigs:{...s._repeatableConfigs,[i]:o}}));},_appendRepeatableItem:(i,o)=>{let s=t(),a=s._repeatableConfigs[i];if(!a)return null;let n=s._repeatableOrder[i]??[];if(a.max!==void 0&&n.length>=a.max)return null;let f=s._repeatableNextKey[i]??0,p=`k${f}`,m=o??a.defaultValue??{},c={...s.values};for(let l of a.allFields){let d=V(i,p,l.id);c[d]=m[l.id]??void 0;}return e({values:c,isDirty:true,_repeatableOrder:{...s._repeatableOrder,[i]:[...n,p]},_repeatableNextKey:{...s._repeatableNextKey,[i]:f+1}}),p},_removeRepeatableItem:(i,o)=>{let s=t(),a=s._repeatableConfigs[i];if(!a)return false;let n=s._repeatableOrder[i]??[];if(a.min!==void 0&&n.length<=a.min||!n.includes(o))return false;let f=n.filter(F=>F!==o),p={...s.values},m={...s.errors},c={...s.validationStates},l={...s.touched},d={...s._fieldConditions};for(let F of a.allFields){let g=V(i,o,F.id);delete p[g],delete m[g],delete c[g],delete l[g],delete d[g];}return e({values:p,errors:m,validationStates:c,touched:l,isDirty:true,_fieldConditions:d,_repeatableOrder:{...s._repeatableOrder,[i]:f}}),t()._updateIsValid(),true},_moveRepeatableItem:(i,o,s)=>{let a=t(),n=a._repeatableOrder[i];if(!n||o<0||o>=n.length||s<0||s>=n.length||o===s)return;let f=[...n],[p]=f.splice(o,1);f.splice(s,0,p),e({_repeatableOrder:{...a._repeatableOrder,[i]:f}});},_insertRepeatableItem:(i,o,s)=>{let a=t(),n=a._repeatableConfigs[i];if(!n)return null;let f=a._repeatableOrder[i]??[];if(n.max!==void 0&&f.length>=n.max)return null;let p=a._repeatableNextKey[i]??0,m=`k${p}`,c=s??n.defaultValue??{},l={...a.values};for(let g of n.allFields){let u=V(i,m,g.id);l[u]=c[g.id]??void 0;}let d=[...f],F=Math.max(0,Math.min(o,d.length));return d.splice(F,0,m),e({values:l,isDirty:true,_repeatableOrder:{...a._repeatableOrder,[i]:d},_repeatableNextKey:{...a._repeatableNextKey,[i]:p+1}}),m}})))}var ie=createContext(null);function S(){let r=useContext(ie);if(!r)throw new Error("useFormStore must be used within a FormProvider");return r}var De=[];function ce(r){let e=S();return useStore(e,t=>t.values[r])}function ut(r){let e=S();return useStore(e,t=>t.errors[r]??De)}function ct(r){let e=S();return useStore(e,t=>t.touched[r]??false)}function mt(r){let e=S();return useStore(e,t=>t.validationStates[r]??"idle")}var ft={visible:true,disabled:false,required:false,readonly:false};function U(r){let e=S();return useStore(e,t=>t._fieldConditions[r]??ft)}function me(r){let e=S(),t=useStore(e,n=>n.values[r]),i=useStore(e,n=>n.errors[r]??De),o=useStore(e,n=>n.validationStates[r]??"idle"),s=useStore(e,n=>n.touched[r]??false),a=useStore(e,n=>n._defaultValues[r]);return {value:t,errors:i,validationState:o,touched:s,dirty:t!==a}}function pt(){let r=S();return useStore(r,e=>e.isSubmitting)}function Ft(){let r=S();return useStore(r,e=>e.isValid)}function gt(){let r=S();return useStore(r,e=>e.isDirty)}function bt(){let r=S();return useStore(r,e=>e.values)}function fe(){let r=S(),e=useStore(r,o=>o.isSubmitting),t=useStore(r,o=>o.isValid),i=useStore(r,o=>o.isDirty);return {isSubmitting:e,isValid:t,isDirty:i}}var Ct=[];function pe(r){let e=S();return useStore(e,t=>t._repeatableOrder[r]??Ct)}function Fe(r){let e=S();return {setValue:t=>e.getState()._setValue(r,t),setTouched:()=>e.getState()._setTouched(r),setErrors:t=>e.getState()._setErrors(r,t),clearErrors:()=>e.getState()._clearErrors(r),setValidationState:t=>e.getState()._setValidationState(r,t)}}function Rt(){let r=S();return {setValue:(e,t)=>r.getState()._setValue(e,t),setTouched:e=>r.getState()._setTouched(e),setErrors:(e,t)=>r.getState()._setErrors(e,t),setSubmitting:e=>r.getState()._setSubmitting(e),reset:e=>r.getState()._reset(e),setFieldConditions:(e,t)=>r.getState()._setFieldConditions(e,t)}}function H(){return S()}var Ae={visible:true,disabled:false,required:false,readonly:false};function Ke(r){try{return JSON.stringify(r)}catch{return String(Date.now())}}function ne(r,e){if(r)try{return typeof r=="object"&&"build"in r?evaluateCondition(r.build(),e):evaluateCondition(r,e)}catch(t){console.warn("Error evaluating condition:",t);return}}function be(r,e){if(!r)return Ae;let t=ne(r.visible,e),i=ne(r.disabled,e),o=ne(r.required,e),s=ne(r.readonly,e);return {visible:t??true,disabled:i??false,required:o??false,readonly:s??false}}function pr(r,e={}){let{conditions:t,skip:i=false}=e,o=H(),s=U(r),a=useRef(null);if(i||!t)return s;let n=o.getState().values,f=Ke(n);if(a.current?.valuesHash===f)return a.current.result;let p=be(t,n);return a.current={result:p,valuesHash:f},p}function Fr(){let r=H(),e=useRef(new Map),t=useRef("");return useMemo(()=>function(o,s){if(!s)return Ae;let a=r.getState().values,n=Ke(a);t.current!==n&&(e.current.clear(),t.current=n);let f=e.current.get(o);if(f)return f.result;let p=be(s,a);return e.current.set(o,{result:p,valuesHash:n}),p},[r])}function gr(r,e){let t=H(),i=U(r),o=useMemo(()=>()=>{if(!e)return i;let s=t.getState().values;return be(e,s)},[t,e,i]);return {conditions:i,refresh:o}}function Y(r,e,t,i){let o={};return r.visible&&(o.visible=G(r.visible,e,t,i)),r.disabled&&(o.disabled=G(r.disabled,e,t,i)),r.required&&(o.required=G(r.required,e,t,i)),r.readonly&&(o.readonly=G(r.readonly,e,t,i)),o}function G(r,e,t,i){let o=r.field&&i.has(r.field)?`${e}[${t}].${r.field}`:r.field,s=r.conditions?.map(a=>G(a,e,t,i));return {...r,field:o,conditions:s}}function Ie({formConfig:r,formValues:e,repeatableOrder:t}){let i=useMemo(()=>{let c={};for(let l of r.allFields)l.conditions&&(c[l.id]=l.conditions);if(t&&r.repeatableFields)for(let[l,d]of Object.entries(r.repeatableFields)){let F=t[l]??[];if(F.length===0)continue;let g=new Set(d.allFields.map(u=>u.id));for(let u of F)for(let C of d.allFields){if(!C.conditions)continue;let b=V(l,u,C.id);c[b]=Y(C.conditions,l,u,g);}}return c},[r.allFields,r.repeatableFields,t]),o=useMemo(()=>Object.keys(i).length>0,[i]),s=Te(o?i:{},o?e:{}),a=useCallback(c=>s[c],[s]),n=useCallback(c=>{let l=s[c];return l?l.visible:true},[s]),f=useCallback(c=>{let l=s[c];return l?l.disabled:false},[s]),p=useCallback(c=>{let l=s[c];return l?l.required:false},[s]),m=useCallback(c=>{let l=s[c];return l?l.readonly:false},[s]);return useMemo(()=>({fieldConditions:s,hasConditionalFields:o,getFieldCondition:a,isFieldVisible:n,isFieldDisabled:f,isFieldRequired:p,isFieldReadonly:m}),[s,o,a,n,f,p,m])}function vt(r){return typeof r=="object"&&r!==null&&"preventDefault"in r}function $e({store:r,onSubmit:e,validateForm:t,defaultSubmitOptions:i}){let o=useRef(e);o.current=e;let s=useRef(i);return s.current=i,{submit:useCallback(async n=>{let f={};vt(n)?n.preventDefault():n&&(f=n);let p={...s.current,...f},m=r.getState();if(m.isSubmitting)return false;m._setSubmitting(true);try{if(p.force){let u=r.getState(),b=Object.keys(u._repeatableConfigs).length>0?re(u.values,u._repeatableConfigs,u._repeatableOrder):u.values;return o.current&&await o.current(b),m._setSubmitting(!1),!0}let c=await t();if(!c.isValid&&!p.skipInvalid)return m._setSubmitting(!1),!1;let l=r.getState(),d=l.values;if(p.skipInvalid&&!c.isValid){let u=new Set(Object.entries(l.errors).filter(([,C])=>C.length>0).map(([C])=>C));d=Object.fromEntries(Object.entries(d).filter(([C])=>!u.has(C)));}let g=Object.keys(l._repeatableConfigs).length>0?re(d,l._repeatableConfigs,l._repeatableOrder):d;return o.current&&await o.current(g),m._setSubmitting(!1),!0}catch(c){return m._setSubmitting(false),console.error("Form submission error:",c),false}},[r,t])}}function se(){return {isValid:true,errors:[]}}function He({formConfig:r,store:e,conditionsHelpers:t}){let i=useRef(r),o=useRef(t);i.current=r,o.current=t;let s=useCallback(async(n,f)=>{let p=i.current.allFields.find(d=>d.id===n);if(!p){let d=L(n);if(d&&i.current.repeatableFields){let F=i.current.repeatableFields[d.repeatableId];if(F){let g=F.allFields.find(u=>u.id===d.fieldId);g&&(p={...g,id:n});}}}let m=e.getState();if(!p)return se();if(!o.current.isFieldVisible(n))return m._setErrors(n,[]),m._setValidationState(n,"valid"),se();if(!p.validation||!hasUnifiedValidation(p.validation)){let d=o.current.isFieldRequired(n),F=f!==void 0?f:m.values[n];if(d&&isEmptyValue(F)){let g={isValid:false,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"}]};return m._setErrors(n,g.errors),m._setValidationState(n,"invalid"),g}return m._setErrors(n,[]),m._setValidationState(n,"valid"),se()}let c=f!==void 0?f:m.values[n],l=createValidationContext({fieldId:n,formId:i.current.id,allFormData:{...m.values,[n]:c}});m._setValidationState(n,"validating");try{let d=await validateWithUnifiedConfig(p.validation,c,l);if(o.current.isFieldRequired(n)&&isEmptyValue(c)&&!d.errors.some(u=>u.code==="REQUIRED"||u.message.toLowerCase().includes("required"))){let u={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...d.errors]};return m._setErrors(n,u.errors),m._setValidationState(n,"invalid"),u}return m._setErrors(n,d.errors),m._setValidationState(n,d.isValid?"valid":"invalid"),d}catch(d){let F={isValid:false,errors:[{message:d instanceof Error?d.message:"Validation failed",code:"VALIDATION_ERROR"}]};return m._setErrors(n,F.errors),m._setValidationState(n,"invalid"),F}},[e]),a=useCallback(async()=>{let n=e.getState(),f=i.current.allFields.filter(u=>{if(!o.current.isFieldVisible(u.id))return false;let b=u.validation&&hasUnifiedValidation(u.validation),k=o.current.isFieldRequired(u.id);return b||k}),p=i.current.allFields.filter(u=>!o.current.isFieldVisible(u.id));for(let u of p)n._setErrors(u.id,[]),n._setValidationState(u.id,"valid");let m=await Promise.all(f.map(u=>s(u.id))),c=m.some(u=>!u.isValid),l=i.current.repeatableFields??{},d=[];for(let[u,C]of Object.entries(l)){let b=n._repeatableOrder[u]??[];for(let k of b)for(let P of C.allFields){let O=V(u,k,P.id);if(!o.current.isFieldVisible(O)){n._setErrors(O,[]),n._setValidationState(O,"valid");continue}let D=await s(O);d.push(D);}C.min!==void 0&&b.length<C.min&&d.push({isValid:false,errors:[{message:`At least ${C.min} item(s) required`,code:"REPEATABLE_MIN_COUNT",path:u}]});}let F=d.some(u=>!u.isValid);c=c||F;let g=se();if(i.current.validation&&hasUnifiedValidation(i.current.validation)){let u=Object.keys(n.values).reduce((b,k)=>(o.current.isFieldVisible(k)&&(b[k]=n.values[k]),b),{}),C=createValidationContext({formId:i.current.id,allFormData:u});try{g=await validateFormWithUnifiedConfig(i.current.validation,u,C);}catch(b){g={isValid:false,errors:[{message:b instanceof Error?b.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!c&&g.isValid,errors:[...m.flatMap(u=>u.errors),...d.flatMap(u=>u.errors),...g.errors]}},[e,s]);return {validateField:s,validateForm:a}}function ve(r){let e=S(),{formConfig:t}=E(),i=pe(r),o=t.repeatableFields?.[r],s=useMemo(()=>o?new Set(o.allFields.map(l=>l.id)):new Set,[o]),a=useMemo(()=>o?i.map((l,d)=>{let F=o.allFields.map(u=>{let C=V(r,l,u.id),b=u.conditions?Y(u.conditions,r,l,s):void 0;return {...u,id:C,conditions:b}}),g=o.rows.map(u=>({...u,fields:u.fields.map(C=>{let b=V(r,l,C.id),k=C.conditions?Y(C.conditions,r,l,s):void 0;return {...C,id:b,conditions:k}})}));return {key:l,index:d,rows:g,allFields:F}}):[],[r,i,o,s]),n=useMemo(()=>o?o.max===void 0?true:i.length<o.max:false,[o,i.length]),f=useMemo(()=>{if(!o)return false;let l=o.min??0;return i.length>l},[o,i.length]),p=useCallback(l=>{e.getState()._appendRepeatableItem(r,l);},[e,r]),m=useCallback(l=>{e.getState()._removeRepeatableItem(r,l);},[e,r]),c=useCallback((l,d)=>{e.getState()._moveRepeatableItem(r,l,d);},[e,r]);return {items:a,append:p,remove:m,move:c,canAdd:n,canRemove:f,count:i.length}}function xt({formConfig:r,enabled:e=true}){let t=getGlobalMonitor(),i=useRef(null),o=useRef(0),s=useRef(0);useEffect(()=>{t&&e&&(i.current=t.getProfiler());},[t,e]);let a=useCallback(l=>{if(!t||!e)return;o.current++;let d={formId:r.id,fieldCount:r.allFields.length,timestamp:Date.now(),duration:0,renderDuration:0,validationDuration:0,validationErrors:0,renderCount:l||o.current};t.track("component_render",`form_${r.id}`,{formId:r.id,fieldCount:r.allFields.length,renderCount:o.current},d,"low");},[t,e,r.id,r.allFields.length]),n=useCallback((l,d)=>{if(!t||!e)return;let F=i.current?.getMetrics(`form_validation_${r.id}`),g={formId:r.id,fieldCount:d||r.allFields.length,timestamp:Date.now(),duration:F?.duration||0,renderDuration:0,validationDuration:F?.duration||0,validationErrors:l,renderCount:o.current};t.track("form_validation",`form_${r.id}`,{formId:r.id,validationErrors:l,fieldCount:d||r.allFields.length},g,l>0?"medium":"low");},[t,e,r.id,r.allFields.length]),f=useCallback((l,d)=>{if(!t||!e)return;let F=i.current?.getMetrics(`form_submission_${r.id}`),g={formId:r.id,fieldCount:d||r.allFields.length,timestamp:Date.now(),duration:F?.duration||0,renderDuration:0,validationDuration:0,validationErrors:l?0:1,renderCount:o.current};t.track("form_submission",`form_${r.id}`,{formId:r.id,success:l,fieldCount:d||r.allFields.length,fieldChanges:s.current},g,l?"low":"high");},[t,e,r.id,r.allFields.length]),p=useCallback((l,d)=>{!t||!e||(s.current++,t.track("component_update",`field_${l}`,{formId:r.id,fieldId:l,componentType:d,changeCount:s.current},void 0,"low"));},[t,e,r.id]),m=useCallback(l=>{!i.current||!e||i.current.start(l,{formId:r.id,renderCount:o.current});},[e,r.id]),c=useCallback(l=>{if(!i.current||!e)return null;let d=i.current.end(l);return d?{...d,formId:r.id,fieldCount:r.allFields.length,renderDuration:d.duration,validationDuration:0,validationErrors:0}:null},[e,r.id,r.allFields.length]);return {trackFormRender:a,trackFormValidation:n,trackFormSubmission:f,trackFieldChange:p,startPerformanceTracking:m,endPerformanceTracking:c}}var ze=createContext(null);function E(){let r=useContext(ze);if(!r)throw new Error("useFormConfigContext must be used within a FormProvider");return r}function _e({children:r,formConfig:e,defaultValues:t={},onSubmit:i,onFieldChange:o,className:s}){let[a]=useState(()=>{let x=e.repeatableFields??{},R={...t},h={},y={};if(Object.keys(x).some(v=>Array.isArray(t[v]))){let v=oe(t,x);R=v.values,h=v.order,y=v.nextKeys;}for(let[v,B]of Object.entries(x))if(!h[v]){let Z=B.min??0,K=[],I=y[v]??0;for(let N=0;N<Z;N++){let j=`k${I}`;K.push(j);for(let ee of B.allFields){let rt=V(v,j,ee.id);R[rt]=B.defaultValue?.[ee.id]??void 0;}I++;}h[v]=K,y[v]=I;}let M=ue(R),_=M.getState();for(let[v,B]of Object.entries(x))_._setRepeatableConfig(v,B);return M.setState({_repeatableOrder:h,_repeatableNextKey:y}),M}),n=useRef(e.id),f=useRef(o);f.current=o,useEffect(()=>f.current?a.subscribe(R=>R.values,(R,h)=>{for(let y of Object.keys(R))R[y]!==h[y]&&f.current?.(y,R[y],R);}):void 0,[a]),useEffect(()=>{if(n.current!==e.id){n.current=e.id;let x=e.repeatableFields??{},R={...t},h={},y={};if(Object.keys(x).some(_=>Array.isArray(t[_]))){let _=oe(t,x);R=_.values,h=_.order,y=_.nextKeys;}for(let[_,v]of Object.entries(x))if(!h[_]){let B=v.min??0,Z=[],K=y[_]??0;for(let I=0;I<B;I++){let N=`k${K}`;Z.push(N);for(let j of v.allFields){let ee=V(_,N,j.id);R[ee]=v.defaultValue?.[j.id]??void 0;}K++;}h[_]=Z,y[_]=K;}a.getState()._reset(R);let M=a.getState();for(let[_,v]of Object.entries(x))M._setRepeatableConfig(_,v);a.setState({_repeatableOrder:h,_repeatableNextKey:y});}},[e.id,e.repeatableFields,a,t]);let[p,m]=useState(()=>a.getState().values);useEffect(()=>a.subscribe(R=>R.values,R=>m(R)),[a]);let[c,l]=useState(()=>a.getState()._repeatableOrder);useEffect(()=>a.subscribe(R=>R._repeatableOrder,R=>l(R)),[a]);let{fieldConditions:d,hasConditionalFields:F,getFieldCondition:g,isFieldVisible:u,isFieldDisabled:C,isFieldRequired:b,isFieldReadonly:k}=Ie({formConfig:e,formValues:p,repeatableOrder:c});useEffect(()=>{for(let[x,R]of Object.entries(d)){let h={visible:R.visible,disabled:R.disabled,required:R.required,readonly:R.readonly};a.getState()._setFieldConditions(x,h);}},[d,a]);let P=useMemo(()=>({hasConditionalFields:F,getFieldCondition:g,isFieldVisible:u,isFieldDisabled:C,isFieldRequired:b,isFieldReadonly:k}),[F,g,u,C,b,k]),{validateField:O,validateForm:D}=He({formConfig:e,store:a,conditionsHelpers:P}),{submit:q}=$e({store:a,onSubmit:i,validateForm:D,defaultSubmitOptions:e.submitOptions}),X=useMemo(()=>({formConfig:e,conditionsHelpers:P,validateField:O,validateForm:D,submit:q}),[e,P,O,D,q]);return jsx(ie.Provider,{value:a,children:jsx(ze.Provider,{value:X,children:jsx("form",{onSubmit:q,className:s,noValidate:true,children:r})})})}function Ot({formConfig:r,defaultValues:e,onSubmit:t,onFieldChange:i,className:o,children:s}){let a=useMemo(()=>r instanceof T?r.build():r,[r]);return jsx(_e,{formConfig:a,defaultValues:e,onSubmit:t,onFieldChange:i,className:o,children:s})}var Q=Mt.memo(function({fieldId:e,fieldConfig:t,disabled:i=false,customProps:o={},className:s,forceVisible:a=false}){let{formConfig:n,validateField:f,conditionsHelpers:p}=E(),m=ce(e),c=me(e),l=U(e),{setValue:d,setTouched:F}=Fe(e),g=useMemo(()=>{if(t)return t;let h=n.allFields.find(A=>A.id===e);if(h)return h;let y=L(e);if(y&&n.repeatableFields){let A=n.repeatableFields[y.repeatableId];if(A){let M=A.allFields.find(_=>_.id===y.fieldId);if(M)return {...M,id:e}}}},[t,n.allFields,n.repeatableFields,e]);if(!g)throw new Error(`Field with ID "${e}" not found`);let u=n.config.getComponent(g.componentId);if(!u)throw new Error(`Component with ID "${g.componentId}" not found`);let C=c.validationState==="validating",b=useMemo(()=>({isVisible:a||l.visible,isFieldDisabled:i||l.disabled,isFieldRequired:l.required||p.isFieldRequired(e),isFieldReadonly:l.readonly}),[a,i,l,p,e]),k=useCallback(async h=>{d(h),(g.validation?.validateOnChange||c.touched)&&await f(e,h);},[e,d,f,g.validation?.validateOnChange,c.touched]),P=useCallback(async()=>{c.touched||F(),g.validation?.validateOnBlur!==false&&await f(e);},[e,c.touched,F,f,g.validation?.validateOnBlur]),O=useMemo(()=>({...u.defaultProps??{},...g.props,...o,disabled:b.isFieldDisabled,required:b.isFieldRequired,readOnly:b.isFieldReadonly}),[u.defaultProps,g.props,o,b.isFieldDisabled,b.isFieldRequired,b.isFieldReadonly]),D=useMemo(()=>({id:e,props:O,value:m,onChange:k,onBlur:P,disabled:b.isFieldDisabled,error:c.errors,isValidating:C,touched:c.touched}),[e,O,m,k,P,b.isFieldDisabled,c.errors,C,c.touched]);if(!b.isVisible)return null;let q=u.renderer(D),X=n.renderConfig?.fieldRenderer,x=u.useFieldRenderer!==false,R=X&&x?X({children:q,id:e,...O,error:c.errors,isValidating:C,touched:c.touched}):q;return jsx("div",{className:s,"data-field-id":e,"data-field-type":u.type,"data-field-visible":b.isVisible,"data-field-disabled":b.isFieldDisabled,"data-field-required":b.isFieldRequired,"data-field-readonly":b.isFieldReadonly,children:R})});var Xe=Mt.memo(function({row:e,className:t,...i}){let{formConfig:o,conditionsHelpers:s}=E(),a=useMemo(()=>e.fields.filter(p=>s.isFieldVisible(p.id)),[e.fields,s]),n=useMemo(()=>a.map(p=>jsx(Q,{fieldId:p.id},p.id)),[a]),f=useMemo(()=>({row:e,children:n,className:t}),[e,n,t]);return a.length===0?null:jsx(ComponentRendererWrapper,{name:"FormRow",renderer:o.renderConfig?.rowRenderer,props:f,...i,children:n})}),de=Xe;var ke=Mt.memo(function({item:e,index:t,total:i,canRemove:o,canMoveUp:s,canMoveDown:a,onRemove:n,onMoveUp:f,onMoveDown:p}){let{formConfig:m}=E(),c=useMemo(()=>new Map(e.allFields.map(F=>[F.id,F])),[e.allFields]),l=useMemo(()=>e.rows.map(F=>jsx(de,{row:F,children:F.fields.map(g=>jsx(Q,{fieldId:g.id,fieldConfig:c.get(g.id)},g.id))},F.id)),[e.rows,c]),d=m.renderConfig?.repeatableItemRenderer;return d?d({item:e,index:t,total:i,canRemove:o,canMoveUp:s,canMoveDown:a,onRemove:n,onMoveUp:f,onMoveDown:p,children:l}):jsx("div",{"data-repeatable-item":e.key,"data-repeatable-index":t,children:l})});var Ee=Mt.memo(function({repeatableId:e,repeatableConfig:t,className:i}){let{formConfig:o}=E(),{items:s,append:a,remove:n,move:f,canAdd:p,canRemove:m}=ve(e),c=useMemo(()=>s.map((d,F)=>jsx(ke,{item:d,index:F,total:s.length,canRemove:m,canMoveUp:F>0,canMoveDown:F<s.length-1,onRemove:()=>n(d.key),onMoveUp:()=>f(F,F-1),onMoveDown:()=>f(F,F+1)},d.key)),[s,m,n,f]),l=o.renderConfig?.repeatableRenderer;return l?l({repeatableId:e,items:s,canAdd:p,canRemove:m,onAdd:()=>a(),min:t.min,max:t.max,children:c}):jsxs("div",{className:i,"data-repeatable-id":e,children:[c,p&&jsx("button",{type:"button",onClick:()=>a(),"data-repeatable-add":e,children:"Add"})]})});var Nt=Mt.memo(function({className:e,...t}){let{formConfig:i}=E(),o=useMemo(()=>i.rows.map(a=>a.kind==="repeatable"?jsx(Ee,{repeatableId:a.repeatable.id,repeatableConfig:a.repeatable},a.id):jsx(de,{row:a},a.id)),[i.rows]),s=useMemo(()=>({formConfig:i,children:o,className:e}),[i,o,e]);return jsx(ComponentRendererWrapper,{name:"FormBody",renderer:i.renderConfig?.bodyRenderer,props:s,...t,children:o})});var Ht=Mt.memo(function({className:e,isSubmitting:t,...i}){let{formConfig:o,submit:s}=E(),{isSubmitting:a}=fe(),n=useMemo(()=>({isSubmitting:t??a,onSubmit:s,className:e}),[t,a,s,e]);return jsx(ComponentRendererWrapper,{name:"FormSubmitButton",renderer:o.renderConfig?.submitButtonRenderer,props:n,...i})});export{Ot as Form,Nt as FormBody,T as FormBuilder,Q as FormField,_e as FormProvider,Xe as FormRow,ie as FormStoreContext,Ht as FormSubmitButton,W as RepeatableBuilder,Ee as RepeatableField,ke as RepeatableItem,ue as createFormStore,oe as flattenRepeatableValues,T as form,re as structureFormValues,tr as useConditionEvaluation,Fr as useConditionEvaluator,Fe as useFieldActions,U as useFieldConditions,pr as useFieldConditionsLazy,gr as useFieldConditionsWithRefresh,ut as useFieldErrors,me as useFieldState,ct as useFieldTouched,mt as useFieldValidationState,ce as useFieldValue,Rt as useFormActions,Ie as useFormConditions,E as useFormConfigContext,gt as useFormDirty,xt as useFormMonitoring,S as useFormStore,H as useFormStoreApi,$e as useFormSubmissionWithStore,fe as useFormSubmitState,pt as useFormSubmitting,Ft as useFormValid,He as useFormValidationWithStore,bt as useFormValues,Te as useMultipleConditionEvaluation,ve as useRepeatableField,pe as useRepeatableKeys};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rilaykit/forms",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Form building utilities and components for RilayKit",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"provenance": true
|
|
21
|
+
},
|
|
19
22
|
"keywords": [
|
|
20
23
|
"react",
|
|
21
24
|
"forms",
|
|
@@ -24,17 +27,17 @@
|
|
|
24
27
|
],
|
|
25
28
|
"author": "AND YOU CREATE <contact@andyoucreate.com>",
|
|
26
29
|
"license": "MIT",
|
|
27
|
-
"homepage": "https://rilay.
|
|
30
|
+
"homepage": "https://rilay.dev",
|
|
28
31
|
"repository": {
|
|
29
32
|
"type": "git",
|
|
30
|
-
"url": "https://github.com/andyoucreate/
|
|
33
|
+
"url": "https://github.com/andyoucreate/rilaykit.git"
|
|
31
34
|
},
|
|
32
35
|
"bugs": {
|
|
33
|
-
"url": "https://github.com/andyoucreate/
|
|
36
|
+
"url": "https://github.com/andyoucreate/rilaykit/issues"
|
|
34
37
|
},
|
|
35
38
|
"dependencies": {
|
|
36
39
|
"zustand": "^5.0.5",
|
|
37
|
-
"@rilaykit/core": "0.1.
|
|
40
|
+
"@rilaykit/core": "0.1.3"
|
|
38
41
|
},
|
|
39
42
|
"peerDependencies": {
|
|
40
43
|
"react": ">=18.0.0",
|
|
@@ -44,10 +47,10 @@
|
|
|
44
47
|
"@testing-library/jest-dom": "^6.6.3",
|
|
45
48
|
"@testing-library/react": "^16.3.0",
|
|
46
49
|
"@testing-library/user-event": "^14.6.1",
|
|
47
|
-
"@types/react": "^
|
|
48
|
-
"@types/react-dom": "^
|
|
49
|
-
"react": "^
|
|
50
|
-
"react-dom": "^
|
|
50
|
+
"@types/react": "^19.0.0",
|
|
51
|
+
"@types/react-dom": "^19.0.0",
|
|
52
|
+
"react": "^19.0.0",
|
|
53
|
+
"react-dom": "^19.0.0",
|
|
51
54
|
"typescript": "^5.8.3",
|
|
52
55
|
"vitest": "^3.2.4"
|
|
53
56
|
},
|