@rilaykit/forms 0.1.1 → 0.1.2
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 +196 -7
- package/dist/index.d.ts +196 -7
- 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, 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
|
*
|
|
@@ -206,6 +263,30 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
206
263
|
* ```
|
|
207
264
|
*/
|
|
208
265
|
addSeparateRows<T extends keyof C & string>(fieldConfigs: FieldConfig<C, T>[]): this;
|
|
266
|
+
/**
|
|
267
|
+
* Adds a repeatable field group to the form
|
|
268
|
+
*
|
|
269
|
+
* Repeatable fields allow users to add/remove instances of a group of fields
|
|
270
|
+
* at runtime (e.g., "Add another item", "Add another contact").
|
|
271
|
+
*
|
|
272
|
+
* @param id - Unique identifier for the repeatable group (cannot contain [ or ])
|
|
273
|
+
* @param configure - Callback receiving a RepeatableBuilder for fluent configuration
|
|
274
|
+
* @returns The form builder instance for method chaining
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* builder.addRepeatable("items", r => r
|
|
279
|
+
* .add(
|
|
280
|
+
* { id: "name", type: "text", props: { label: "Item" } },
|
|
281
|
+
* { id: "qty", type: "number", props: { label: "Qty" } }
|
|
282
|
+
* )
|
|
283
|
+
* .min(1)
|
|
284
|
+
* .max(10)
|
|
285
|
+
* .defaultValue({ name: "", qty: 1 })
|
|
286
|
+
* );
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
addRepeatable(id: string, configure: (builder: RepeatableBuilder<C>) => RepeatableBuilder<C>): this;
|
|
209
290
|
/**
|
|
210
291
|
* Sets the form identifier
|
|
211
292
|
*
|
|
@@ -308,7 +389,7 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
308
389
|
* console.log(`Form has ${rows.length} rows`);
|
|
309
390
|
* ```
|
|
310
391
|
*/
|
|
311
|
-
getRows():
|
|
392
|
+
getRows(): FormRowEntry[];
|
|
312
393
|
/**
|
|
313
394
|
* Clears all fields and rows from the form
|
|
314
395
|
*
|
|
@@ -516,16 +597,20 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
516
597
|
* - Useful for form complexity analysis
|
|
517
598
|
*/
|
|
518
599
|
getStats(): {
|
|
519
|
-
/** Total number of fields across all rows */
|
|
600
|
+
/** Total number of static fields across all rows */
|
|
520
601
|
totalFields: number;
|
|
521
602
|
/** Total number of rows in the form */
|
|
522
603
|
totalRows: number;
|
|
523
|
-
/** Average number of fields per row */
|
|
604
|
+
/** Average number of fields per row (field rows only) */
|
|
524
605
|
averageFieldsPerRow: number;
|
|
525
606
|
/** Maximum number of fields in any single row */
|
|
526
607
|
maxFieldsInRow: number;
|
|
527
608
|
/** Minimum number of fields in any single row */
|
|
528
609
|
minFieldsInRow: number;
|
|
610
|
+
/** Total number of repeatable groups */
|
|
611
|
+
totalRepeatables: number;
|
|
612
|
+
/** Total number of fields across all repeatable templates */
|
|
613
|
+
totalRepeatableFields: number;
|
|
529
614
|
};
|
|
530
615
|
}
|
|
531
616
|
|
|
@@ -537,12 +622,14 @@ interface FormProps {
|
|
|
537
622
|
className?: string;
|
|
538
623
|
children: React.ReactNode;
|
|
539
624
|
}
|
|
540
|
-
declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, children }: FormProps): react_jsx_runtime.JSX.Element;
|
|
625
|
+
declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, className, children, }: FormProps): react_jsx_runtime.JSX.Element;
|
|
541
626
|
|
|
542
627
|
declare const FormBody: React__default.NamedExoticComponent<ComponentRendererBaseProps<FormBodyRendererProps>>;
|
|
543
628
|
|
|
544
629
|
interface FormFieldProps {
|
|
545
630
|
fieldId: string;
|
|
631
|
+
/** Pre-resolved field config (used by RepeatableItem to skip allFields lookup) */
|
|
632
|
+
fieldConfig?: FormFieldConfig;
|
|
546
633
|
disabled?: boolean;
|
|
547
634
|
customProps?: Record<string, unknown>;
|
|
548
635
|
className?: string;
|
|
@@ -626,6 +713,8 @@ declare function useFieldConditionsWithRefresh(fieldId: string, conditions?: Con
|
|
|
626
713
|
interface UseFormConditionsProps {
|
|
627
714
|
formConfig: FormConfiguration;
|
|
628
715
|
formValues: Record<string, any>;
|
|
716
|
+
/** Active repeatable item keys, keyed by repeatable ID */
|
|
717
|
+
repeatableOrder?: Record<string, string[]>;
|
|
629
718
|
}
|
|
630
719
|
interface UseFormConditionsReturn {
|
|
631
720
|
fieldConditions: Record<string, ConditionEvaluationResult>;
|
|
@@ -662,11 +751,14 @@ interface UseFormConditionsReturn {
|
|
|
662
751
|
* }
|
|
663
752
|
* ```
|
|
664
753
|
*/
|
|
665
|
-
declare function useFormConditions({ formConfig, formValues, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
754
|
+
declare function useFormConditions({ formConfig, formValues, repeatableOrder, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
666
755
|
|
|
667
756
|
interface FormStoreState extends FormState {
|
|
668
757
|
_defaultValues: Record<string, unknown>;
|
|
669
758
|
_fieldConditions: Record<string, FieldConditions>;
|
|
759
|
+
_repeatableConfigs: Record<string, RepeatableFieldConfig>;
|
|
760
|
+
_repeatableOrder: Record<string, string[]>;
|
|
761
|
+
_repeatableNextKey: Record<string, number>;
|
|
670
762
|
_setValue: (fieldId: string, value: unknown) => void;
|
|
671
763
|
_setTouched: (fieldId: string) => void;
|
|
672
764
|
_setErrors: (fieldId: string, errors: ValidationError[]) => void;
|
|
@@ -676,6 +768,11 @@ interface FormStoreState extends FormState {
|
|
|
676
768
|
_reset: (values?: Record<string, unknown>) => void;
|
|
677
769
|
_setFieldConditions: (fieldId: string, conditions: FieldConditions) => void;
|
|
678
770
|
_updateIsValid: () => void;
|
|
771
|
+
_setRepeatableConfig: (id: string, config: RepeatableFieldConfig) => void;
|
|
772
|
+
_appendRepeatableItem: (repeatableId: string, defaultValue?: Record<string, unknown>) => string | null;
|
|
773
|
+
_removeRepeatableItem: (repeatableId: string, key: string) => boolean;
|
|
774
|
+
_moveRepeatableItem: (repeatableId: string, fromIndex: number, toIndex: number) => void;
|
|
775
|
+
_insertRepeatableItem: (repeatableId: string, index: number, defaultValue?: Record<string, unknown>) => string | null;
|
|
679
776
|
}
|
|
680
777
|
type FormStore = ReturnType<typeof createFormStore>;
|
|
681
778
|
declare function createFormStore(initialValues?: Record<string, unknown>): Omit<zustand.StoreApi<FormStoreState>, "subscribe"> & {
|
|
@@ -749,6 +846,10 @@ declare function useFormSubmitState(): {
|
|
|
749
846
|
isValid: boolean;
|
|
750
847
|
isDirty: boolean;
|
|
751
848
|
};
|
|
849
|
+
/**
|
|
850
|
+
* Select ordered keys for a repeatable field — re-renders when the order changes
|
|
851
|
+
*/
|
|
852
|
+
declare function useRepeatableKeys(repeatableId: string): string[];
|
|
752
853
|
interface UseFieldActionsResult {
|
|
753
854
|
setValue: (value: unknown) => void;
|
|
754
855
|
setTouched: () => void;
|
|
@@ -798,6 +899,45 @@ declare function useFormValidationWithStore({ formConfig, store, conditionsHelpe
|
|
|
798
899
|
validateForm: () => Promise<ValidationResult>;
|
|
799
900
|
};
|
|
800
901
|
|
|
902
|
+
interface UseRepeatableFieldReturn {
|
|
903
|
+
items: RepeatableFieldItem[];
|
|
904
|
+
append: (defaultValue?: Record<string, unknown>) => void;
|
|
905
|
+
remove: (key: string) => void;
|
|
906
|
+
move: (fromIndex: number, toIndex: number) => void;
|
|
907
|
+
canAdd: boolean;
|
|
908
|
+
canRemove: boolean;
|
|
909
|
+
count: number;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Hook to manage a repeatable field group
|
|
913
|
+
*
|
|
914
|
+
* Provides the list of items and actions to add, remove, and reorder them.
|
|
915
|
+
* Each item contains scoped field configs ready for rendering.
|
|
916
|
+
*
|
|
917
|
+
* @param repeatableId - The ID of the repeatable group (as defined in addRepeatable)
|
|
918
|
+
* @returns Items, actions, and constraints
|
|
919
|
+
*
|
|
920
|
+
* @example
|
|
921
|
+
* ```tsx
|
|
922
|
+
* const { items, append, remove, canAdd, canRemove } = useRepeatableField("items");
|
|
923
|
+
*
|
|
924
|
+
* return (
|
|
925
|
+
* <div>
|
|
926
|
+
* {items.map(item => (
|
|
927
|
+
* <div key={item.key}>
|
|
928
|
+
* {item.allFields.map(field => (
|
|
929
|
+
* <FormField key={field.id} fieldId={field.id} fieldConfig={field} />
|
|
930
|
+
* ))}
|
|
931
|
+
* {canRemove && <button onClick={() => remove(item.key)}>Remove</button>}
|
|
932
|
+
* </div>
|
|
933
|
+
* ))}
|
|
934
|
+
* {canAdd && <button onClick={append}>Add</button>}
|
|
935
|
+
* </div>
|
|
936
|
+
* );
|
|
937
|
+
* ```
|
|
938
|
+
*/
|
|
939
|
+
declare function useRepeatableField(repeatableId: string): UseRepeatableFieldReturn;
|
|
940
|
+
|
|
801
941
|
interface UseFormMonitoringProps {
|
|
802
942
|
formConfig: FormConfiguration;
|
|
803
943
|
monitoring?: MonitoringConfig;
|
|
@@ -848,4 +988,53 @@ interface FormSubmitButtonProps extends ComponentRendererBaseProps<FormSubmitBut
|
|
|
848
988
|
}
|
|
849
989
|
declare const FormSubmitButton: React__default.NamedExoticComponent<FormSubmitButtonProps>;
|
|
850
990
|
|
|
851
|
-
|
|
991
|
+
interface RepeatableFieldProps {
|
|
992
|
+
repeatableId: string;
|
|
993
|
+
repeatableConfig: RepeatableFieldConfig;
|
|
994
|
+
className?: string;
|
|
995
|
+
}
|
|
996
|
+
declare const RepeatableField: React__default.NamedExoticComponent<RepeatableFieldProps>;
|
|
997
|
+
|
|
998
|
+
interface RepeatableItemProps {
|
|
999
|
+
item: RepeatableFieldItem;
|
|
1000
|
+
index: number;
|
|
1001
|
+
total: number;
|
|
1002
|
+
canRemove: boolean;
|
|
1003
|
+
canMoveUp: boolean;
|
|
1004
|
+
canMoveDown: boolean;
|
|
1005
|
+
onRemove: () => void;
|
|
1006
|
+
onMoveUp: () => void;
|
|
1007
|
+
onMoveDown: () => void;
|
|
1008
|
+
}
|
|
1009
|
+
declare const RepeatableItem: React__default.NamedExoticComponent<RepeatableItemProps>;
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Converts flat store values with composite keys into structured nested data.
|
|
1013
|
+
*
|
|
1014
|
+
* Input (store values):
|
|
1015
|
+
* { customerName: "John", "items[k0].name": "Widget", "items[k0].qty": 2, "items[k1].name": "Gadget", "items[k1].qty": 1 }
|
|
1016
|
+
*
|
|
1017
|
+
* Output (structured):
|
|
1018
|
+
* { customerName: "John", items: [{ name: "Widget", qty: 2 }, { name: "Gadget", qty: 1 }] }
|
|
1019
|
+
*/
|
|
1020
|
+
declare function structureFormValues(values: Record<string, unknown>, repeatableConfigs: Record<string, RepeatableFieldConfig>, repeatableOrder: Record<string, string[]>): Record<string, unknown>;
|
|
1021
|
+
/**
|
|
1022
|
+
* Converts structured nested data into flat store values with composite keys.
|
|
1023
|
+
*
|
|
1024
|
+
* Input (structured):
|
|
1025
|
+
* { customerName: "John", items: [{ name: "Widget", qty: 2 }, { name: "Gadget", qty: 1 }] }
|
|
1026
|
+
*
|
|
1027
|
+
* Output:
|
|
1028
|
+
* {
|
|
1029
|
+
* values: { customerName: "John", "items[k0].name": "Widget", "items[k0].qty": 2, "items[k1].name": "Gadget", "items[k1].qty": 1 },
|
|
1030
|
+
* order: { items: ["k0", "k1"] },
|
|
1031
|
+
* nextKeys: { items: 2 }
|
|
1032
|
+
* }
|
|
1033
|
+
*/
|
|
1034
|
+
declare function flattenRepeatableValues(data: Record<string, unknown>, repeatableConfigs: Record<string, RepeatableFieldConfig>): {
|
|
1035
|
+
values: Record<string, unknown>;
|
|
1036
|
+
order: Record<string, string[]>;
|
|
1037
|
+
nextKeys: Record<string, number>;
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
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, 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
|
*
|
|
@@ -206,6 +263,30 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
206
263
|
* ```
|
|
207
264
|
*/
|
|
208
265
|
addSeparateRows<T extends keyof C & string>(fieldConfigs: FieldConfig<C, T>[]): this;
|
|
266
|
+
/**
|
|
267
|
+
* Adds a repeatable field group to the form
|
|
268
|
+
*
|
|
269
|
+
* Repeatable fields allow users to add/remove instances of a group of fields
|
|
270
|
+
* at runtime (e.g., "Add another item", "Add another contact").
|
|
271
|
+
*
|
|
272
|
+
* @param id - Unique identifier for the repeatable group (cannot contain [ or ])
|
|
273
|
+
* @param configure - Callback receiving a RepeatableBuilder for fluent configuration
|
|
274
|
+
* @returns The form builder instance for method chaining
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```typescript
|
|
278
|
+
* builder.addRepeatable("items", r => r
|
|
279
|
+
* .add(
|
|
280
|
+
* { id: "name", type: "text", props: { label: "Item" } },
|
|
281
|
+
* { id: "qty", type: "number", props: { label: "Qty" } }
|
|
282
|
+
* )
|
|
283
|
+
* .min(1)
|
|
284
|
+
* .max(10)
|
|
285
|
+
* .defaultValue({ name: "", qty: 1 })
|
|
286
|
+
* );
|
|
287
|
+
* ```
|
|
288
|
+
*/
|
|
289
|
+
addRepeatable(id: string, configure: (builder: RepeatableBuilder<C>) => RepeatableBuilder<C>): this;
|
|
209
290
|
/**
|
|
210
291
|
* Sets the form identifier
|
|
211
292
|
*
|
|
@@ -308,7 +389,7 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
308
389
|
* console.log(`Form has ${rows.length} rows`);
|
|
309
390
|
* ```
|
|
310
391
|
*/
|
|
311
|
-
getRows():
|
|
392
|
+
getRows(): FormRowEntry[];
|
|
312
393
|
/**
|
|
313
394
|
* Clears all fields and rows from the form
|
|
314
395
|
*
|
|
@@ -516,16 +597,20 @@ declare class form<C extends Record<string, any> = Record<string, never>> {
|
|
|
516
597
|
* - Useful for form complexity analysis
|
|
517
598
|
*/
|
|
518
599
|
getStats(): {
|
|
519
|
-
/** Total number of fields across all rows */
|
|
600
|
+
/** Total number of static fields across all rows */
|
|
520
601
|
totalFields: number;
|
|
521
602
|
/** Total number of rows in the form */
|
|
522
603
|
totalRows: number;
|
|
523
|
-
/** Average number of fields per row */
|
|
604
|
+
/** Average number of fields per row (field rows only) */
|
|
524
605
|
averageFieldsPerRow: number;
|
|
525
606
|
/** Maximum number of fields in any single row */
|
|
526
607
|
maxFieldsInRow: number;
|
|
527
608
|
/** Minimum number of fields in any single row */
|
|
528
609
|
minFieldsInRow: number;
|
|
610
|
+
/** Total number of repeatable groups */
|
|
611
|
+
totalRepeatables: number;
|
|
612
|
+
/** Total number of fields across all repeatable templates */
|
|
613
|
+
totalRepeatableFields: number;
|
|
529
614
|
};
|
|
530
615
|
}
|
|
531
616
|
|
|
@@ -537,12 +622,14 @@ interface FormProps {
|
|
|
537
622
|
className?: string;
|
|
538
623
|
children: React.ReactNode;
|
|
539
624
|
}
|
|
540
|
-
declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, children }: FormProps): react_jsx_runtime.JSX.Element;
|
|
625
|
+
declare function Form({ formConfig, defaultValues, onSubmit, onFieldChange, className, children, }: FormProps): react_jsx_runtime.JSX.Element;
|
|
541
626
|
|
|
542
627
|
declare const FormBody: React__default.NamedExoticComponent<ComponentRendererBaseProps<FormBodyRendererProps>>;
|
|
543
628
|
|
|
544
629
|
interface FormFieldProps {
|
|
545
630
|
fieldId: string;
|
|
631
|
+
/** Pre-resolved field config (used by RepeatableItem to skip allFields lookup) */
|
|
632
|
+
fieldConfig?: FormFieldConfig;
|
|
546
633
|
disabled?: boolean;
|
|
547
634
|
customProps?: Record<string, unknown>;
|
|
548
635
|
className?: string;
|
|
@@ -626,6 +713,8 @@ declare function useFieldConditionsWithRefresh(fieldId: string, conditions?: Con
|
|
|
626
713
|
interface UseFormConditionsProps {
|
|
627
714
|
formConfig: FormConfiguration;
|
|
628
715
|
formValues: Record<string, any>;
|
|
716
|
+
/** Active repeatable item keys, keyed by repeatable ID */
|
|
717
|
+
repeatableOrder?: Record<string, string[]>;
|
|
629
718
|
}
|
|
630
719
|
interface UseFormConditionsReturn {
|
|
631
720
|
fieldConditions: Record<string, ConditionEvaluationResult>;
|
|
@@ -662,11 +751,14 @@ interface UseFormConditionsReturn {
|
|
|
662
751
|
* }
|
|
663
752
|
* ```
|
|
664
753
|
*/
|
|
665
|
-
declare function useFormConditions({ formConfig, formValues, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
754
|
+
declare function useFormConditions({ formConfig, formValues, repeatableOrder, }: UseFormConditionsProps): UseFormConditionsReturn;
|
|
666
755
|
|
|
667
756
|
interface FormStoreState extends FormState {
|
|
668
757
|
_defaultValues: Record<string, unknown>;
|
|
669
758
|
_fieldConditions: Record<string, FieldConditions>;
|
|
759
|
+
_repeatableConfigs: Record<string, RepeatableFieldConfig>;
|
|
760
|
+
_repeatableOrder: Record<string, string[]>;
|
|
761
|
+
_repeatableNextKey: Record<string, number>;
|
|
670
762
|
_setValue: (fieldId: string, value: unknown) => void;
|
|
671
763
|
_setTouched: (fieldId: string) => void;
|
|
672
764
|
_setErrors: (fieldId: string, errors: ValidationError[]) => void;
|
|
@@ -676,6 +768,11 @@ interface FormStoreState extends FormState {
|
|
|
676
768
|
_reset: (values?: Record<string, unknown>) => void;
|
|
677
769
|
_setFieldConditions: (fieldId: string, conditions: FieldConditions) => void;
|
|
678
770
|
_updateIsValid: () => void;
|
|
771
|
+
_setRepeatableConfig: (id: string, config: RepeatableFieldConfig) => void;
|
|
772
|
+
_appendRepeatableItem: (repeatableId: string, defaultValue?: Record<string, unknown>) => string | null;
|
|
773
|
+
_removeRepeatableItem: (repeatableId: string, key: string) => boolean;
|
|
774
|
+
_moveRepeatableItem: (repeatableId: string, fromIndex: number, toIndex: number) => void;
|
|
775
|
+
_insertRepeatableItem: (repeatableId: string, index: number, defaultValue?: Record<string, unknown>) => string | null;
|
|
679
776
|
}
|
|
680
777
|
type FormStore = ReturnType<typeof createFormStore>;
|
|
681
778
|
declare function createFormStore(initialValues?: Record<string, unknown>): Omit<zustand.StoreApi<FormStoreState>, "subscribe"> & {
|
|
@@ -749,6 +846,10 @@ declare function useFormSubmitState(): {
|
|
|
749
846
|
isValid: boolean;
|
|
750
847
|
isDirty: boolean;
|
|
751
848
|
};
|
|
849
|
+
/**
|
|
850
|
+
* Select ordered keys for a repeatable field — re-renders when the order changes
|
|
851
|
+
*/
|
|
852
|
+
declare function useRepeatableKeys(repeatableId: string): string[];
|
|
752
853
|
interface UseFieldActionsResult {
|
|
753
854
|
setValue: (value: unknown) => void;
|
|
754
855
|
setTouched: () => void;
|
|
@@ -798,6 +899,45 @@ declare function useFormValidationWithStore({ formConfig, store, conditionsHelpe
|
|
|
798
899
|
validateForm: () => Promise<ValidationResult>;
|
|
799
900
|
};
|
|
800
901
|
|
|
902
|
+
interface UseRepeatableFieldReturn {
|
|
903
|
+
items: RepeatableFieldItem[];
|
|
904
|
+
append: (defaultValue?: Record<string, unknown>) => void;
|
|
905
|
+
remove: (key: string) => void;
|
|
906
|
+
move: (fromIndex: number, toIndex: number) => void;
|
|
907
|
+
canAdd: boolean;
|
|
908
|
+
canRemove: boolean;
|
|
909
|
+
count: number;
|
|
910
|
+
}
|
|
911
|
+
/**
|
|
912
|
+
* Hook to manage a repeatable field group
|
|
913
|
+
*
|
|
914
|
+
* Provides the list of items and actions to add, remove, and reorder them.
|
|
915
|
+
* Each item contains scoped field configs ready for rendering.
|
|
916
|
+
*
|
|
917
|
+
* @param repeatableId - The ID of the repeatable group (as defined in addRepeatable)
|
|
918
|
+
* @returns Items, actions, and constraints
|
|
919
|
+
*
|
|
920
|
+
* @example
|
|
921
|
+
* ```tsx
|
|
922
|
+
* const { items, append, remove, canAdd, canRemove } = useRepeatableField("items");
|
|
923
|
+
*
|
|
924
|
+
* return (
|
|
925
|
+
* <div>
|
|
926
|
+
* {items.map(item => (
|
|
927
|
+
* <div key={item.key}>
|
|
928
|
+
* {item.allFields.map(field => (
|
|
929
|
+
* <FormField key={field.id} fieldId={field.id} fieldConfig={field} />
|
|
930
|
+
* ))}
|
|
931
|
+
* {canRemove && <button onClick={() => remove(item.key)}>Remove</button>}
|
|
932
|
+
* </div>
|
|
933
|
+
* ))}
|
|
934
|
+
* {canAdd && <button onClick={append}>Add</button>}
|
|
935
|
+
* </div>
|
|
936
|
+
* );
|
|
937
|
+
* ```
|
|
938
|
+
*/
|
|
939
|
+
declare function useRepeatableField(repeatableId: string): UseRepeatableFieldReturn;
|
|
940
|
+
|
|
801
941
|
interface UseFormMonitoringProps {
|
|
802
942
|
formConfig: FormConfiguration;
|
|
803
943
|
monitoring?: MonitoringConfig;
|
|
@@ -848,4 +988,53 @@ interface FormSubmitButtonProps extends ComponentRendererBaseProps<FormSubmitBut
|
|
|
848
988
|
}
|
|
849
989
|
declare const FormSubmitButton: React__default.NamedExoticComponent<FormSubmitButtonProps>;
|
|
850
990
|
|
|
851
|
-
|
|
991
|
+
interface RepeatableFieldProps {
|
|
992
|
+
repeatableId: string;
|
|
993
|
+
repeatableConfig: RepeatableFieldConfig;
|
|
994
|
+
className?: string;
|
|
995
|
+
}
|
|
996
|
+
declare const RepeatableField: React__default.NamedExoticComponent<RepeatableFieldProps>;
|
|
997
|
+
|
|
998
|
+
interface RepeatableItemProps {
|
|
999
|
+
item: RepeatableFieldItem;
|
|
1000
|
+
index: number;
|
|
1001
|
+
total: number;
|
|
1002
|
+
canRemove: boolean;
|
|
1003
|
+
canMoveUp: boolean;
|
|
1004
|
+
canMoveDown: boolean;
|
|
1005
|
+
onRemove: () => void;
|
|
1006
|
+
onMoveUp: () => void;
|
|
1007
|
+
onMoveDown: () => void;
|
|
1008
|
+
}
|
|
1009
|
+
declare const RepeatableItem: React__default.NamedExoticComponent<RepeatableItemProps>;
|
|
1010
|
+
|
|
1011
|
+
/**
|
|
1012
|
+
* Converts flat store values with composite keys into structured nested data.
|
|
1013
|
+
*
|
|
1014
|
+
* Input (store values):
|
|
1015
|
+
* { customerName: "John", "items[k0].name": "Widget", "items[k0].qty": 2, "items[k1].name": "Gadget", "items[k1].qty": 1 }
|
|
1016
|
+
*
|
|
1017
|
+
* Output (structured):
|
|
1018
|
+
* { customerName: "John", items: [{ name: "Widget", qty: 2 }, { name: "Gadget", qty: 1 }] }
|
|
1019
|
+
*/
|
|
1020
|
+
declare function structureFormValues(values: Record<string, unknown>, repeatableConfigs: Record<string, RepeatableFieldConfig>, repeatableOrder: Record<string, string[]>): Record<string, unknown>;
|
|
1021
|
+
/**
|
|
1022
|
+
* Converts structured nested data into flat store values with composite keys.
|
|
1023
|
+
*
|
|
1024
|
+
* Input (structured):
|
|
1025
|
+
* { customerName: "John", items: [{ name: "Widget", qty: 2 }, { name: "Gadget", qty: 1 }] }
|
|
1026
|
+
*
|
|
1027
|
+
* Output:
|
|
1028
|
+
* {
|
|
1029
|
+
* values: { customerName: "John", "items[k0].name": "Widget", "items[k0].qty": 2, "items[k1].name": "Gadget", "items[k1].qty": 1 },
|
|
1030
|
+
* order: { items: ["k0", "k1"] },
|
|
1031
|
+
* nextKeys: { items: 2 }
|
|
1032
|
+
* }
|
|
1033
|
+
*/
|
|
1034
|
+
declare function flattenRepeatableValues(data: Record<string, unknown>, repeatableConfigs: Record<string, RepeatableFieldConfig>): {
|
|
1035
|
+
values: Record<string, unknown>;
|
|
1036
|
+
order: Record<string, string[]>;
|
|
1037
|
+
nextKeys: Record<string, number>;
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
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 Ot=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 Ot__default=/*#__PURE__*/_interopDefault(Ot);var j=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 j(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}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}}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 er(r,e={},t={}){return Ot.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 Ot.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 it=/^([^[\]]+)\[([^\]]+)\]\.(.+)$/;function V(r,e,t){return `${r}[${e}].${t}`}function L(r){let e=it.exec(r);return e?{repeatableId:e[1],itemKey:e[2],fieldId:e[3]}:null}function de(r,e,t){let i={},o=new Set;for(let[s,a]of Object.entries(t)){if(!e[s])continue;let n=e[s],u=[];for(let p of a){let f={};for(let m of n.allFields){let l=V(s,p,m.id);l in r&&(f[m.id]=r[l],o.add(l));}u.push(f);}i[s]=u;}for(let[s,a]of Object.entries(r))!o.has(s)&&!L(s)&&(i[s]=a);return i}function re(r,e){let t={},i={},o={};for(let[s,a]of Object.entries(r))if(e[s]&&Array.isArray(a)){let n=[],u=0;for(let p of a){let f=`k${u}`;n.push(f);for(let[m,l]of Object.entries(p))t[V(s,f,m)]=l;u++;}i[s]=n,o[s]=u;}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 u=s._repeatableNextKey[i]??0,p=`k${u}`,f=o??a.defaultValue??{},m={...s.values};for(let l of a.allFields){let d=V(i,p,l.id);m[d]=f[l.id]??void 0;}return e({values:m,isDirty:true,_repeatableOrder:{...s._repeatableOrder,[i]:[...n,p]},_repeatableNextKey:{...s._repeatableNextKey,[i]:u+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 u=n.filter(F=>F!==o),p={...s.values},f={...s.errors},m={...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 f[g],delete m[g],delete l[g],delete d[g];}return e({values:p,errors:f,validationStates:m,touched:l,isDirty:true,_fieldConditions:d,_repeatableOrder:{...s._repeatableOrder,[i]:u}}),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 u=[...n],[p]=u.splice(o,1);u.splice(s,0,p),e({_repeatableOrder:{...a._repeatableOrder,[i]:u}});},_insertRepeatableItem:(i,o,s)=>{let a=t(),n=a._repeatableConfigs[i];if(!n)return null;let u=a._repeatableOrder[i]??[];if(n.max!==void 0&&u.length>=n.max)return null;let p=a._repeatableNextKey[i]??0,f=`k${p}`,m=s??n.defaultValue??{},l={...a.values};for(let g of n.allFields){let c=V(i,f,g.id);l[c]=m[g.id]??void 0;}let d=[...u],F=Math.max(0,Math.min(o,d.length));return d.splice(F,0,f),e({values:l,isDirty:true,_repeatableOrder:{...a._repeatableOrder,[i]:d},_repeatableNextKey:{...a._repeatableNextKey,[i]:p+1}}),f}})))}var oe=Ot.createContext(null);function S(){let r=Ot.useContext(oe);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 dt(r){let e=S();return zustand.useStore(e,t=>t.errors[r]??De)}function ut(r){let e=S();return zustand.useStore(e,t=>t.touched[r]??false)}function ct(r){let e=S();return zustand.useStore(e,t=>t.validationStates[r]??"idle")}var mt={visible:true,disabled:false,required:false,readonly:false};function $(r){let e=S();return zustand.useStore(e,t=>t._fieldConditions[r]??mt)}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 ft(){let r=S();return zustand.useStore(r,e=>e.isSubmitting)}function pt(){let r=S();return zustand.useStore(r,e=>e.isValid)}function Ft(){let r=S();return zustand.useStore(r,e=>e.isDirty)}function gt(){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 bt=[];function pe(r){let e=S();return zustand.useStore(e,t=>t._repeatableOrder[r]??bt)}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 Ct(){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 ie(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=ie(r.visible,e),i=ie(r.disabled,e),o=ie(r.required,e),s=ie(r.readonly,e);return {visible:t??true,disabled:i??false,required:o??false,readonly:s??false}}function fr(r,e={}){let{conditions:t,skip:i=false}=e,o=H(),s=$(r),a=Ot.useRef(null);if(i||!t)return s;let n=o.getState().values,u=Ke(n);if(a.current?.valuesHash===u)return a.current.result;let p=be(t,n);return a.current={result:p,valuesHash:u},p}function pr(){let r=H(),e=Ot.useRef(new Map),t=Ot.useRef("");return Ot.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 u=e.current.get(o);if(u)return u.result;let p=be(s,a);return e.current.set(o,{result:p,valuesHash:n}),p},[r])}function Fr(r,e){let t=H(),i=$(r),o=Ot.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 Ue({formConfig:r,formValues:e,repeatableOrder:t}){let i=Ot.useMemo(()=>{let m={};for(let l of r.allFields)l.conditions&&(m[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(c=>c.id));for(let c of F)for(let R of d.allFields){if(!R.conditions)continue;let b=V(l,c,R.id);m[b]=Y(R.conditions,l,c,g);}}return m},[r.allFields,r.repeatableFields,t]),o=Ot.useMemo(()=>Object.keys(i).length>0,[i]),s=Te(o?i:{},o?e:{}),a=Ot.useCallback(m=>s[m],[s]),n=Ot.useCallback(m=>{let l=s[m];return l?l.visible:true},[s]),u=Ot.useCallback(m=>{let l=s[m];return l?l.disabled:false},[s]),p=Ot.useCallback(m=>{let l=s[m];return l?l.required:false},[s]),f=Ot.useCallback(m=>{let l=s[m];return l?l.readonly:false},[s]);return Ot.useMemo(()=>({fieldConditions:s,hasConditionalFields:o,getFieldCondition:a,isFieldVisible:n,isFieldDisabled:u,isFieldRequired:p,isFieldReadonly:f}),[s,o,a,n,u,p,f])}function $e({store:r,onSubmit:e,validateForm:t}){let i=Ot.useRef(e);return i.current=e,{submit:Ot.useCallback(async s=>{s?.preventDefault();let a=r.getState();if(a.isSubmitting)return false;a._setSubmitting(true);try{if(!(await t()).isValid)return a._setSubmitting(!1),!1;let u=r.getState(),f=Object.keys(u._repeatableConfigs).length>0?de(u.values,u._repeatableConfigs,u._repeatableOrder):u.values;return i.current&&await i.current(f),a._setSubmitting(!1),!0}catch(n){return a._setSubmitting(false),console.error("Form submission error:",n),false}},[r,t])}}function ne(){return {isValid:true,errors:[]}}function Le({formConfig:r,store:e,conditionsHelpers:t}){let i=Ot.useRef(r),o=Ot.useRef(t);i.current=r,o.current=t;let s=Ot.useCallback(async(n,u)=>{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(c=>c.id===d.fieldId);g&&(p={...g,id:n});}}}let f=e.getState();if(!p)return ne();if(!o.current.isFieldVisible(n))return f._setErrors(n,[]),f._setValidationState(n,"valid"),ne();if(!p.validation||!core.hasUnifiedValidation(p.validation)){let d=o.current.isFieldRequired(n),F=u!==void 0?u:f.values[n];if(d&&core.isEmptyValue(F)){let g={isValid:false,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"}]};return f._setErrors(n,g.errors),f._setValidationState(n,"invalid"),g}return f._setErrors(n,[]),f._setValidationState(n,"valid"),ne()}let m=u!==void 0?u:f.values[n],l=core.createValidationContext({fieldId:n,formId:i.current.id,allFormData:{...f.values,[n]:m}});f._setValidationState(n,"validating");try{let d=await core.validateWithUnifiedConfig(p.validation,m,l);if(o.current.isFieldRequired(n)&&core.isEmptyValue(m)&&!d.errors.some(c=>c.code==="REQUIRED"||c.message.toLowerCase().includes("required"))){let c={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...d.errors]};return f._setErrors(n,c.errors),f._setValidationState(n,"invalid"),c}return f._setErrors(n,d.errors),f._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 f._setErrors(n,F.errors),f._setValidationState(n,"invalid"),F}},[e]),a=Ot.useCallback(async()=>{let n=e.getState(),u=i.current.allFields.filter(c=>{if(!o.current.isFieldVisible(c.id))return false;let b=c.validation&&core.hasUnifiedValidation(c.validation),k=o.current.isFieldRequired(c.id);return b||k}),p=i.current.allFields.filter(c=>!o.current.isFieldVisible(c.id));for(let c of p)n._setErrors(c.id,[]),n._setValidationState(c.id,"valid");let f=await Promise.all(u.map(c=>s(c.id))),m=f.some(c=>!c.isValid),l=i.current.repeatableFields??{},d=[];for(let[c,R]of Object.entries(l)){let b=n._repeatableOrder[c]??[];for(let k of b)for(let O of R.allFields){let P=V(c,k,O.id);if(!o.current.isFieldVisible(P)){n._setErrors(P,[]),n._setValidationState(P,"valid");continue}let D=await s(P);d.push(D);}R.min!==void 0&&b.length<R.min&&d.push({isValid:false,errors:[{message:`At least ${R.min} item(s) required`,code:"REPEATABLE_MIN_COUNT",path:c}]});}let F=d.some(c=>!c.isValid);m=m||F;let g=ne();if(i.current.validation&&core.hasUnifiedValidation(i.current.validation)){let c=Object.keys(n.values).reduce((b,k)=>(o.current.isFieldVisible(k)&&(b[k]=n.values[k]),b),{}),R=core.createValidationContext({formId:i.current.id,allFormData:c});try{g=await core.validateFormWithUnifiedConfig(i.current.validation,c,R);}catch(b){g={isValid:false,errors:[{message:b instanceof Error?b.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!m&&g.isValid,errors:[...f.flatMap(c=>c.errors),...d.flatMap(c=>c.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=Ot.useMemo(()=>o?new Set(o.allFields.map(l=>l.id)):new Set,[o]),a=Ot.useMemo(()=>o?i.map((l,d)=>{let F=o.allFields.map(c=>{let R=V(r,l,c.id),b=c.conditions?Y(c.conditions,r,l,s):void 0;return {...c,id:R,conditions:b}}),g=o.rows.map(c=>({...c,fields:c.fields.map(R=>{let b=V(r,l,R.id),k=R.conditions?Y(R.conditions,r,l,s):void 0;return {...R,id:b,conditions:k}})}));return {key:l,index:d,rows:g,allFields:F}}):[],[r,i,o,s]),n=Ot.useMemo(()=>o?o.max===void 0?true:i.length<o.max:false,[o,i.length]),u=Ot.useMemo(()=>{if(!o)return false;let l=o.min??0;return i.length>l},[o,i.length]),p=Ot.useCallback(l=>{e.getState()._appendRepeatableItem(r,l);},[e,r]),f=Ot.useCallback(l=>{e.getState()._removeRepeatableItem(r,l);},[e,r]),m=Ot.useCallback((l,d)=>{e.getState()._moveRepeatableItem(r,l,d);},[e,r]);return {items:a,append:p,remove:f,move:m,canAdd:n,canRemove:u,count:i.length}}function xt({formConfig:r,enabled:e=true}){let t=core.getGlobalMonitor(),i=Ot.useRef(null),o=Ot.useRef(0),s=Ot.useRef(0);Ot.useEffect(()=>{t&&e&&(i.current=t.getProfiler());},[t,e]);let a=Ot.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=Ot.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]),u=Ot.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=Ot.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]),f=Ot.useCallback(l=>{!i.current||!e||i.current.start(l,{formId:r.id,renderCount:o.current});},[e,r.id]),m=Ot.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:u,trackFieldChange:p,startPerformanceTracking:f,endPerformanceTracking:m}}var Ye=Ot.createContext(null);function E(){let r=Ot.useContext(Ye);if(!r)throw new Error("useFormConfigContext must be used within a FormProvider");return r}function xe({children:r,formConfig:e,defaultValues:t={},onSubmit:i,onFieldChange:o,className:s}){let[a]=Ot.useState(()=>{let _=e.repeatableFields??{},C={...t},h={},y={};if(Object.keys(_).some(v=>Array.isArray(t[v]))){let v=re(t,_);C=v.values,h=v.order,y=v.nextKeys;}for(let[v,B]of Object.entries(_))if(!h[v]){let Z=B.min??0,K=[],U=y[v]??0;for(let N=0;N<Z;N++){let W=`k${U}`;K.push(W);for(let ee of B.allFields){let tt=V(v,W,ee.id);C[tt]=B.defaultValue?.[ee.id]??void 0;}U++;}h[v]=K,y[v]=U;}let M=ue(C),x=M.getState();for(let[v,B]of Object.entries(_))x._setRepeatableConfig(v,B);return M.setState({_repeatableOrder:h,_repeatableNextKey:y}),M}),n=Ot.useRef(e.id),u=Ot.useRef(o);u.current=o,Ot.useEffect(()=>u.current?a.subscribe(C=>C.values,(C,h)=>{for(let y of Object.keys(C))C[y]!==h[y]&&u.current?.(y,C[y],C);}):void 0,[a]),Ot.useEffect(()=>{if(n.current!==e.id){n.current=e.id;let _=e.repeatableFields??{},C={...t},h={},y={};if(Object.keys(_).some(x=>Array.isArray(t[x]))){let x=re(t,_);C=x.values,h=x.order,y=x.nextKeys;}for(let[x,v]of Object.entries(_))if(!h[x]){let B=v.min??0,Z=[],K=y[x]??0;for(let U=0;U<B;U++){let N=`k${K}`;Z.push(N);for(let W of v.allFields){let ee=V(x,N,W.id);C[ee]=v.defaultValue?.[W.id]??void 0;}K++;}h[x]=Z,y[x]=K;}a.getState()._reset(C);let M=a.getState();for(let[x,v]of Object.entries(_))M._setRepeatableConfig(x,v);a.setState({_repeatableOrder:h,_repeatableNextKey:y});}},[e.id,e.repeatableFields,a,t]);let[p,f]=Ot.useState(()=>a.getState().values);Ot.useEffect(()=>a.subscribe(C=>C.values,C=>f(C)),[a]);let[m,l]=Ot.useState(()=>a.getState()._repeatableOrder);Ot.useEffect(()=>a.subscribe(C=>C._repeatableOrder,C=>l(C)),[a]);let{fieldConditions:d,hasConditionalFields:F,getFieldCondition:g,isFieldVisible:c,isFieldDisabled:R,isFieldRequired:b,isFieldReadonly:k}=Ue({formConfig:e,formValues:p,repeatableOrder:m});Ot.useEffect(()=>{for(let[_,C]of Object.entries(d)){let h={visible:C.visible,disabled:C.disabled,required:C.required,readonly:C.readonly};a.getState()._setFieldConditions(_,h);}},[d,a]);let O=Ot.useMemo(()=>({hasConditionalFields:F,getFieldCondition:g,isFieldVisible:c,isFieldDisabled:R,isFieldRequired:b,isFieldReadonly:k}),[F,g,c,R,b,k]),{validateField:P,validateForm:D}=Le({formConfig:e,store:a,conditionsHelpers:O}),{submit:q}=$e({store:a,onSubmit:i,validateForm:D}),X=Ot.useMemo(()=>({formConfig:e,conditionsHelpers:O,validateField:P,validateForm:D,submit:q}),[e,O,P,D,q]);return jsxRuntime.jsx(oe.Provider,{value:a,children:jsxRuntime.jsx(Ye.Provider,{value:X,children:jsxRuntime.jsx("form",{onSubmit:q,className:s,noValidate:true,children:r})})})}function Et({formConfig:r,defaultValues:e,onSubmit:t,onFieldChange:i,className:o,children:s}){let a=Ot.useMemo(()=>r instanceof T?r.build():r,[r]);return jsxRuntime.jsx(xe,{formConfig:a,defaultValues:e,onSubmit:t,onFieldChange:i,className:o,children:s})}var Q=Ot__default.default.memo(function({fieldId:e,fieldConfig:t,disabled:i=false,customProps:o={},className:s,forceVisible:a=false}){let{formConfig:n,validateField:u,conditionsHelpers:p}=E(),f=ce(e),m=me(e),l=$(e),{setValue:d,setTouched:F}=Fe(e),g=Ot.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(x=>x.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 c=n.config.getComponent(g.componentId);if(!c)throw new Error(`Component with ID "${g.componentId}" not found`);let R=m.validationState==="validating",b=Ot.useMemo(()=>({isVisible:a||l.visible,isFieldDisabled:i||l.disabled,isFieldRequired:l.required||p.isFieldRequired(e),isFieldReadonly:l.readonly}),[a,i,l,p,e]),k=Ot.useCallback(async h=>{d(h),(g.validation?.validateOnChange||m.touched)&&await u(e,h);},[e,d,u,g.validation?.validateOnChange,m.touched]),O=Ot.useCallback(async()=>{m.touched||F(),g.validation?.validateOnBlur!==false&&await u(e);},[e,m.touched,F,u,g.validation?.validateOnBlur]),P=Ot.useMemo(()=>({...c.defaultProps??{},...g.props,...o,disabled:b.isFieldDisabled,required:b.isFieldRequired,readOnly:b.isFieldReadonly}),[c.defaultProps,g.props,o,b.isFieldDisabled,b.isFieldRequired,b.isFieldReadonly]),D=Ot.useMemo(()=>({id:e,props:P,value:f,onChange:k,onBlur:O,disabled:b.isFieldDisabled,error:m.errors,isValidating:R,touched:m.touched}),[e,P,f,k,O,b.isFieldDisabled,m.errors,R,m.touched]);if(!b.isVisible)return null;let q=c.renderer(D),X=n.renderConfig?.fieldRenderer,_=c.useFieldRenderer!==false,C=X&&_?X({children:q,id:e,...P,error:m.errors,isValidating:R,touched:m.touched}):q;return jsxRuntime.jsx("div",{className:s,"data-field-id":e,"data-field-type":c.type,"data-field-visible":b.isVisible,"data-field-disabled":b.isFieldDisabled,"data-field-required":b.isFieldRequired,"data-field-readonly":b.isFieldReadonly,children:C})});var Qe=Ot__default.default.memo(function({row:e,className:t,...i}){let{formConfig:o,conditionsHelpers:s}=E(),a=Ot.useMemo(()=>e.fields.filter(p=>s.isFieldVisible(p.id)),[e.fields,s]),n=Ot.useMemo(()=>a.map(p=>jsxRuntime.jsx(Q,{fieldId:p.id},p.id)),[a]),u=Ot.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:u,...i,children:n})}),le=Qe;var ke=Ot__default.default.memo(function({item:e,index:t,total:i,canRemove:o,canMoveUp:s,canMoveDown:a,onRemove:n,onMoveUp:u,onMoveDown:p}){let{formConfig:f}=E(),m=Ot.useMemo(()=>new Map(e.allFields.map(F=>[F.id,F])),[e.allFields]),l=Ot.useMemo(()=>e.rows.map(F=>jsxRuntime.jsx(le,{row:F,children:F.fields.map(g=>jsxRuntime.jsx(Q,{fieldId:g.id,fieldConfig:m.get(g.id)},g.id))},F.id)),[e.rows,m]),d=f.renderConfig?.repeatableItemRenderer;return d?d({item:e,index:t,total:i,canRemove:o,canMoveUp:s,canMoveDown:a,onRemove:n,onMoveUp:u,onMoveDown:p,children:l}):jsxRuntime.jsx("div",{"data-repeatable-item":e.key,"data-repeatable-index":t,children:l})});var Ee=Ot__default.default.memo(function({repeatableId:e,repeatableConfig:t,className:i}){let{formConfig:o}=E(),{items:s,append:a,remove:n,move:u,canAdd:p,canRemove:f}=ve(e),m=Ot.useMemo(()=>s.map((d,F)=>jsxRuntime.jsx(ke,{item:d,index:F,total:s.length,canRemove:f,canMoveUp:F>0,canMoveDown:F<s.length-1,onRemove:()=>n(d.key),onMoveUp:()=>u(F,F-1),onMoveDown:()=>u(F,F+1)},d.key)),[s,f,n,u]),l=o.renderConfig?.repeatableRenderer;return l?l({repeatableId:e,items:s,canAdd:p,canRemove:f,onAdd:()=>a(),min:t.min,max:t.max,children:m}):jsxRuntime.jsxs("div",{className:i,"data-repeatable-id":e,children:[m,p&&jsxRuntime.jsx("button",{type:"button",onClick:()=>a(),"data-repeatable-add":e,children:"Add"})]})});var It=Ot__default.default.memo(function({className:e,...t}){let{formConfig:i}=E(),o=Ot.useMemo(()=>i.rows.map(a=>a.kind==="repeatable"?jsxRuntime.jsx(Ee,{repeatableId:a.repeatable.id,repeatableConfig:a.repeatable},a.id):jsxRuntime.jsx(le,{row:a},a.id)),[i.rows]),s=Ot.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 Lt=Ot__default.default.memo(function({className:e,isSubmitting:t,...i}){let{formConfig:o,submit:s}=E(),{isSubmitting:a}=fe(),n=Ot.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=Et;exports.FormBody=It;exports.FormBuilder=T;exports.FormField=Q;exports.FormProvider=xe;exports.FormRow=Qe;exports.FormStoreContext=oe;exports.FormSubmitButton=Lt;exports.RepeatableBuilder=j;exports.RepeatableField=Ee;exports.RepeatableItem=ke;exports.createFormStore=ue;exports.flattenRepeatableValues=re;exports.form=T;exports.structureFormValues=de;exports.useConditionEvaluation=er;exports.useConditionEvaluator=pr;exports.useFieldActions=Fe;exports.useFieldConditions=$;exports.useFieldConditionsLazy=fr;exports.useFieldConditionsWithRefresh=Fr;exports.useFieldErrors=dt;exports.useFieldState=me;exports.useFieldTouched=ut;exports.useFieldValidationState=ct;exports.useFieldValue=ce;exports.useFormActions=Ct;exports.useFormConditions=Ue;exports.useFormConfigContext=E;exports.useFormDirty=Ft;exports.useFormMonitoring=xt;exports.useFormStore=S;exports.useFormStoreApi=H;exports.useFormSubmissionWithStore=$e;exports.useFormSubmitState=fe;exports.useFormSubmitting=ft;exports.useFormValid=pt;exports.useFormValidationWithStore=Le;exports.useFormValues=gt;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 Ot,{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 j=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 j(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}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}}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 er(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 it=/^([^[\]]+)\[([^\]]+)\]\.(.+)$/;function V(r,e,t){return `${r}[${e}].${t}`}function L(r){let e=it.exec(r);return e?{repeatableId:e[1],itemKey:e[2],fieldId:e[3]}:null}function de(r,e,t){let i={},o=new Set;for(let[s,a]of Object.entries(t)){if(!e[s])continue;let n=e[s],u=[];for(let p of a){let f={};for(let m of n.allFields){let l=V(s,p,m.id);l in r&&(f[m.id]=r[l],o.add(l));}u.push(f);}i[s]=u;}for(let[s,a]of Object.entries(r))!o.has(s)&&!L(s)&&(i[s]=a);return i}function re(r,e){let t={},i={},o={};for(let[s,a]of Object.entries(r))if(e[s]&&Array.isArray(a)){let n=[],u=0;for(let p of a){let f=`k${u}`;n.push(f);for(let[m,l]of Object.entries(p))t[V(s,f,m)]=l;u++;}i[s]=n,o[s]=u;}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 u=s._repeatableNextKey[i]??0,p=`k${u}`,f=o??a.defaultValue??{},m={...s.values};for(let l of a.allFields){let d=V(i,p,l.id);m[d]=f[l.id]??void 0;}return e({values:m,isDirty:true,_repeatableOrder:{...s._repeatableOrder,[i]:[...n,p]},_repeatableNextKey:{...s._repeatableNextKey,[i]:u+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 u=n.filter(F=>F!==o),p={...s.values},f={...s.errors},m={...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 f[g],delete m[g],delete l[g],delete d[g];}return e({values:p,errors:f,validationStates:m,touched:l,isDirty:true,_fieldConditions:d,_repeatableOrder:{...s._repeatableOrder,[i]:u}}),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 u=[...n],[p]=u.splice(o,1);u.splice(s,0,p),e({_repeatableOrder:{...a._repeatableOrder,[i]:u}});},_insertRepeatableItem:(i,o,s)=>{let a=t(),n=a._repeatableConfigs[i];if(!n)return null;let u=a._repeatableOrder[i]??[];if(n.max!==void 0&&u.length>=n.max)return null;let p=a._repeatableNextKey[i]??0,f=`k${p}`,m=s??n.defaultValue??{},l={...a.values};for(let g of n.allFields){let c=V(i,f,g.id);l[c]=m[g.id]??void 0;}let d=[...u],F=Math.max(0,Math.min(o,d.length));return d.splice(F,0,f),e({values:l,isDirty:true,_repeatableOrder:{...a._repeatableOrder,[i]:d},_repeatableNextKey:{...a._repeatableNextKey,[i]:p+1}}),f}})))}var oe=createContext(null);function S(){let r=useContext(oe);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 dt(r){let e=S();return useStore(e,t=>t.errors[r]??De)}function ut(r){let e=S();return useStore(e,t=>t.touched[r]??false)}function ct(r){let e=S();return useStore(e,t=>t.validationStates[r]??"idle")}var mt={visible:true,disabled:false,required:false,readonly:false};function $(r){let e=S();return useStore(e,t=>t._fieldConditions[r]??mt)}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 ft(){let r=S();return useStore(r,e=>e.isSubmitting)}function pt(){let r=S();return useStore(r,e=>e.isValid)}function Ft(){let r=S();return useStore(r,e=>e.isDirty)}function gt(){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 bt=[];function pe(r){let e=S();return useStore(e,t=>t._repeatableOrder[r]??bt)}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 Ct(){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 ie(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=ie(r.visible,e),i=ie(r.disabled,e),o=ie(r.required,e),s=ie(r.readonly,e);return {visible:t??true,disabled:i??false,required:o??false,readonly:s??false}}function fr(r,e={}){let{conditions:t,skip:i=false}=e,o=H(),s=$(r),a=useRef(null);if(i||!t)return s;let n=o.getState().values,u=Ke(n);if(a.current?.valuesHash===u)return a.current.result;let p=be(t,n);return a.current={result:p,valuesHash:u},p}function pr(){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 u=e.current.get(o);if(u)return u.result;let p=be(s,a);return e.current.set(o,{result:p,valuesHash:n}),p},[r])}function Fr(r,e){let t=H(),i=$(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 Ue({formConfig:r,formValues:e,repeatableOrder:t}){let i=useMemo(()=>{let m={};for(let l of r.allFields)l.conditions&&(m[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(c=>c.id));for(let c of F)for(let R of d.allFields){if(!R.conditions)continue;let b=V(l,c,R.id);m[b]=Y(R.conditions,l,c,g);}}return m},[r.allFields,r.repeatableFields,t]),o=useMemo(()=>Object.keys(i).length>0,[i]),s=Te(o?i:{},o?e:{}),a=useCallback(m=>s[m],[s]),n=useCallback(m=>{let l=s[m];return l?l.visible:true},[s]),u=useCallback(m=>{let l=s[m];return l?l.disabled:false},[s]),p=useCallback(m=>{let l=s[m];return l?l.required:false},[s]),f=useCallback(m=>{let l=s[m];return l?l.readonly:false},[s]);return useMemo(()=>({fieldConditions:s,hasConditionalFields:o,getFieldCondition:a,isFieldVisible:n,isFieldDisabled:u,isFieldRequired:p,isFieldReadonly:f}),[s,o,a,n,u,p,f])}function $e({store:r,onSubmit:e,validateForm:t}){let i=useRef(e);return i.current=e,{submit:useCallback(async s=>{s?.preventDefault();let a=r.getState();if(a.isSubmitting)return false;a._setSubmitting(true);try{if(!(await t()).isValid)return a._setSubmitting(!1),!1;let u=r.getState(),f=Object.keys(u._repeatableConfigs).length>0?de(u.values,u._repeatableConfigs,u._repeatableOrder):u.values;return i.current&&await i.current(f),a._setSubmitting(!1),!0}catch(n){return a._setSubmitting(false),console.error("Form submission error:",n),false}},[r,t])}}function ne(){return {isValid:true,errors:[]}}function Le({formConfig:r,store:e,conditionsHelpers:t}){let i=useRef(r),o=useRef(t);i.current=r,o.current=t;let s=useCallback(async(n,u)=>{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(c=>c.id===d.fieldId);g&&(p={...g,id:n});}}}let f=e.getState();if(!p)return ne();if(!o.current.isFieldVisible(n))return f._setErrors(n,[]),f._setValidationState(n,"valid"),ne();if(!p.validation||!hasUnifiedValidation(p.validation)){let d=o.current.isFieldRequired(n),F=u!==void 0?u:f.values[n];if(d&&isEmptyValue(F)){let g={isValid:false,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"}]};return f._setErrors(n,g.errors),f._setValidationState(n,"invalid"),g}return f._setErrors(n,[]),f._setValidationState(n,"valid"),ne()}let m=u!==void 0?u:f.values[n],l=createValidationContext({fieldId:n,formId:i.current.id,allFormData:{...f.values,[n]:m}});f._setValidationState(n,"validating");try{let d=await validateWithUnifiedConfig(p.validation,m,l);if(o.current.isFieldRequired(n)&&isEmptyValue(m)&&!d.errors.some(c=>c.code==="REQUIRED"||c.message.toLowerCase().includes("required"))){let c={isValid:!1,errors:[{message:"This field is required",code:"CONDITIONAL_REQUIRED"},...d.errors]};return f._setErrors(n,c.errors),f._setValidationState(n,"invalid"),c}return f._setErrors(n,d.errors),f._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 f._setErrors(n,F.errors),f._setValidationState(n,"invalid"),F}},[e]),a=useCallback(async()=>{let n=e.getState(),u=i.current.allFields.filter(c=>{if(!o.current.isFieldVisible(c.id))return false;let b=c.validation&&hasUnifiedValidation(c.validation),k=o.current.isFieldRequired(c.id);return b||k}),p=i.current.allFields.filter(c=>!o.current.isFieldVisible(c.id));for(let c of p)n._setErrors(c.id,[]),n._setValidationState(c.id,"valid");let f=await Promise.all(u.map(c=>s(c.id))),m=f.some(c=>!c.isValid),l=i.current.repeatableFields??{},d=[];for(let[c,R]of Object.entries(l)){let b=n._repeatableOrder[c]??[];for(let k of b)for(let O of R.allFields){let P=V(c,k,O.id);if(!o.current.isFieldVisible(P)){n._setErrors(P,[]),n._setValidationState(P,"valid");continue}let D=await s(P);d.push(D);}R.min!==void 0&&b.length<R.min&&d.push({isValid:false,errors:[{message:`At least ${R.min} item(s) required`,code:"REPEATABLE_MIN_COUNT",path:c}]});}let F=d.some(c=>!c.isValid);m=m||F;let g=ne();if(i.current.validation&&hasUnifiedValidation(i.current.validation)){let c=Object.keys(n.values).reduce((b,k)=>(o.current.isFieldVisible(k)&&(b[k]=n.values[k]),b),{}),R=createValidationContext({formId:i.current.id,allFormData:c});try{g=await validateFormWithUnifiedConfig(i.current.validation,c,R);}catch(b){g={isValid:false,errors:[{message:b instanceof Error?b.message:"Form validation failed",code:"FORM_VALIDATION_ERROR"}]};}}return {isValid:!m&&g.isValid,errors:[...f.flatMap(c=>c.errors),...d.flatMap(c=>c.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(c=>{let R=V(r,l,c.id),b=c.conditions?Y(c.conditions,r,l,s):void 0;return {...c,id:R,conditions:b}}),g=o.rows.map(c=>({...c,fields:c.fields.map(R=>{let b=V(r,l,R.id),k=R.conditions?Y(R.conditions,r,l,s):void 0;return {...R,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]),u=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]),f=useCallback(l=>{e.getState()._removeRepeatableItem(r,l);},[e,r]),m=useCallback((l,d)=>{e.getState()._moveRepeatableItem(r,l,d);},[e,r]);return {items:a,append:p,remove:f,move:m,canAdd:n,canRemove:u,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]),u=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]),f=useCallback(l=>{!i.current||!e||i.current.start(l,{formId:r.id,renderCount:o.current});},[e,r.id]),m=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:u,trackFieldChange:p,startPerformanceTracking:f,endPerformanceTracking:m}}var Ye=createContext(null);function E(){let r=useContext(Ye);if(!r)throw new Error("useFormConfigContext must be used within a FormProvider");return r}function xe({children:r,formConfig:e,defaultValues:t={},onSubmit:i,onFieldChange:o,className:s}){let[a]=useState(()=>{let _=e.repeatableFields??{},C={...t},h={},y={};if(Object.keys(_).some(v=>Array.isArray(t[v]))){let v=re(t,_);C=v.values,h=v.order,y=v.nextKeys;}for(let[v,B]of Object.entries(_))if(!h[v]){let Z=B.min??0,K=[],U=y[v]??0;for(let N=0;N<Z;N++){let W=`k${U}`;K.push(W);for(let ee of B.allFields){let tt=V(v,W,ee.id);C[tt]=B.defaultValue?.[ee.id]??void 0;}U++;}h[v]=K,y[v]=U;}let M=ue(C),x=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),u=useRef(o);u.current=o,useEffect(()=>u.current?a.subscribe(C=>C.values,(C,h)=>{for(let y of Object.keys(C))C[y]!==h[y]&&u.current?.(y,C[y],C);}):void 0,[a]),useEffect(()=>{if(n.current!==e.id){n.current=e.id;let _=e.repeatableFields??{},C={...t},h={},y={};if(Object.keys(_).some(x=>Array.isArray(t[x]))){let x=re(t,_);C=x.values,h=x.order,y=x.nextKeys;}for(let[x,v]of Object.entries(_))if(!h[x]){let B=v.min??0,Z=[],K=y[x]??0;for(let U=0;U<B;U++){let N=`k${K}`;Z.push(N);for(let W of v.allFields){let ee=V(x,N,W.id);C[ee]=v.defaultValue?.[W.id]??void 0;}K++;}h[x]=Z,y[x]=K;}a.getState()._reset(C);let M=a.getState();for(let[x,v]of Object.entries(_))M._setRepeatableConfig(x,v);a.setState({_repeatableOrder:h,_repeatableNextKey:y});}},[e.id,e.repeatableFields,a,t]);let[p,f]=useState(()=>a.getState().values);useEffect(()=>a.subscribe(C=>C.values,C=>f(C)),[a]);let[m,l]=useState(()=>a.getState()._repeatableOrder);useEffect(()=>a.subscribe(C=>C._repeatableOrder,C=>l(C)),[a]);let{fieldConditions:d,hasConditionalFields:F,getFieldCondition:g,isFieldVisible:c,isFieldDisabled:R,isFieldRequired:b,isFieldReadonly:k}=Ue({formConfig:e,formValues:p,repeatableOrder:m});useEffect(()=>{for(let[_,C]of Object.entries(d)){let h={visible:C.visible,disabled:C.disabled,required:C.required,readonly:C.readonly};a.getState()._setFieldConditions(_,h);}},[d,a]);let O=useMemo(()=>({hasConditionalFields:F,getFieldCondition:g,isFieldVisible:c,isFieldDisabled:R,isFieldRequired:b,isFieldReadonly:k}),[F,g,c,R,b,k]),{validateField:P,validateForm:D}=Le({formConfig:e,store:a,conditionsHelpers:O}),{submit:q}=$e({store:a,onSubmit:i,validateForm:D}),X=useMemo(()=>({formConfig:e,conditionsHelpers:O,validateField:P,validateForm:D,submit:q}),[e,O,P,D,q]);return jsx(oe.Provider,{value:a,children:jsx(Ye.Provider,{value:X,children:jsx("form",{onSubmit:q,className:s,noValidate:true,children:r})})})}function Et({formConfig:r,defaultValues:e,onSubmit:t,onFieldChange:i,className:o,children:s}){let a=useMemo(()=>r instanceof T?r.build():r,[r]);return jsx(xe,{formConfig:a,defaultValues:e,onSubmit:t,onFieldChange:i,className:o,children:s})}var Q=Ot.memo(function({fieldId:e,fieldConfig:t,disabled:i=false,customProps:o={},className:s,forceVisible:a=false}){let{formConfig:n,validateField:u,conditionsHelpers:p}=E(),f=ce(e),m=me(e),l=$(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(x=>x.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 c=n.config.getComponent(g.componentId);if(!c)throw new Error(`Component with ID "${g.componentId}" not found`);let R=m.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||m.touched)&&await u(e,h);},[e,d,u,g.validation?.validateOnChange,m.touched]),O=useCallback(async()=>{m.touched||F(),g.validation?.validateOnBlur!==false&&await u(e);},[e,m.touched,F,u,g.validation?.validateOnBlur]),P=useMemo(()=>({...c.defaultProps??{},...g.props,...o,disabled:b.isFieldDisabled,required:b.isFieldRequired,readOnly:b.isFieldReadonly}),[c.defaultProps,g.props,o,b.isFieldDisabled,b.isFieldRequired,b.isFieldReadonly]),D=useMemo(()=>({id:e,props:P,value:f,onChange:k,onBlur:O,disabled:b.isFieldDisabled,error:m.errors,isValidating:R,touched:m.touched}),[e,P,f,k,O,b.isFieldDisabled,m.errors,R,m.touched]);if(!b.isVisible)return null;let q=c.renderer(D),X=n.renderConfig?.fieldRenderer,_=c.useFieldRenderer!==false,C=X&&_?X({children:q,id:e,...P,error:m.errors,isValidating:R,touched:m.touched}):q;return jsx("div",{className:s,"data-field-id":e,"data-field-type":c.type,"data-field-visible":b.isVisible,"data-field-disabled":b.isFieldDisabled,"data-field-required":b.isFieldRequired,"data-field-readonly":b.isFieldReadonly,children:C})});var Qe=Ot.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]),u=useMemo(()=>({row:e,children:n,className:t}),[e,n,t]);return a.length===0?null:jsx(ComponentRendererWrapper,{name:"FormRow",renderer:o.renderConfig?.rowRenderer,props:u,...i,children:n})}),le=Qe;var ke=Ot.memo(function({item:e,index:t,total:i,canRemove:o,canMoveUp:s,canMoveDown:a,onRemove:n,onMoveUp:u,onMoveDown:p}){let{formConfig:f}=E(),m=useMemo(()=>new Map(e.allFields.map(F=>[F.id,F])),[e.allFields]),l=useMemo(()=>e.rows.map(F=>jsx(le,{row:F,children:F.fields.map(g=>jsx(Q,{fieldId:g.id,fieldConfig:m.get(g.id)},g.id))},F.id)),[e.rows,m]),d=f.renderConfig?.repeatableItemRenderer;return d?d({item:e,index:t,total:i,canRemove:o,canMoveUp:s,canMoveDown:a,onRemove:n,onMoveUp:u,onMoveDown:p,children:l}):jsx("div",{"data-repeatable-item":e.key,"data-repeatable-index":t,children:l})});var Ee=Ot.memo(function({repeatableId:e,repeatableConfig:t,className:i}){let{formConfig:o}=E(),{items:s,append:a,remove:n,move:u,canAdd:p,canRemove:f}=ve(e),m=useMemo(()=>s.map((d,F)=>jsx(ke,{item:d,index:F,total:s.length,canRemove:f,canMoveUp:F>0,canMoveDown:F<s.length-1,onRemove:()=>n(d.key),onMoveUp:()=>u(F,F-1),onMoveDown:()=>u(F,F+1)},d.key)),[s,f,n,u]),l=o.renderConfig?.repeatableRenderer;return l?l({repeatableId:e,items:s,canAdd:p,canRemove:f,onAdd:()=>a(),min:t.min,max:t.max,children:m}):jsxs("div",{className:i,"data-repeatable-id":e,children:[m,p&&jsx("button",{type:"button",onClick:()=>a(),"data-repeatable-add":e,children:"Add"})]})});var It=Ot.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(le,{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 Lt=Ot.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{Et as Form,It as FormBody,T as FormBuilder,Q as FormField,xe as FormProvider,Qe as FormRow,oe as FormStoreContext,Lt as FormSubmitButton,j as RepeatableBuilder,Ee as RepeatableField,ke as RepeatableItem,ue as createFormStore,re as flattenRepeatableValues,T as form,de as structureFormValues,er as useConditionEvaluation,pr as useConditionEvaluator,Fe as useFieldActions,$ as useFieldConditions,fr as useFieldConditionsLazy,Fr as useFieldConditionsWithRefresh,dt as useFieldErrors,me as useFieldState,ut as useFieldTouched,ct as useFieldValidationState,ce as useFieldValue,Ct as useFormActions,Ue as useFormConditions,E as useFormConfigContext,Ft as useFormDirty,xt as useFormMonitoring,S as useFormStore,H as useFormStoreApi,$e as useFormSubmissionWithStore,fe as useFormSubmitState,ft as useFormSubmitting,pt as useFormValid,Le as useFormValidationWithStore,gt 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.2",
|
|
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.2"
|
|
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
|
},
|