@dynamicforms/vue-forms 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dynamicforms-vue-forms.js +786 -635
- package/dist/dynamicforms-vue-forms.js.map +1 -1
- package/dist/dynamicforms-vue-forms.umd.cjs +2 -2
- package/dist/dynamicforms-vue-forms.umd.cjs.map +1 -1
- package/dist/index.d.cts +60 -0
- package/dist/index.d.ts +60 -0
- package/package.json +11 -4
- package/readme.md +14 -2
package/dist/index.d.cts
CHANGED
|
@@ -178,14 +178,46 @@ export declare abstract class FieldBase<T = any> {
|
|
|
178
178
|
private _enabled;
|
|
179
179
|
get enabled(): boolean;
|
|
180
180
|
set enabled(newValue: boolean);
|
|
181
|
+
/**
|
|
182
|
+
* While true, validate() on this field does nothing at all: it neither recomputes _valid nor emits
|
|
183
|
+
* ValidChangedAction, and nothing climbs to the parent. A container that walks its members one by one - assigning
|
|
184
|
+
* them a value, or revalidating them - sets it for the duration of the walk, so a member's upward climb cannot
|
|
185
|
+
* announce the verdict of a half-applied value or of a half-revalidated set, and clears it in a finally block
|
|
186
|
+
* followed by its own validate(). That call recomputes over the finished state and emits the net transition, so
|
|
187
|
+
* the deferral cannot swallow a real change.
|
|
188
|
+
*/
|
|
189
|
+
protected suppressValidation: boolean;
|
|
190
|
+
/**
|
|
191
|
+
* While true, a validity change is still recomputed and still announced on this field, but it is not passed up to
|
|
192
|
+
* the parent: the climb is only remembered and carried out by flushParentValidityClimb(). A setter that notifies
|
|
193
|
+
* the parent of the new value itself holds it over the notifying region, so the parent forms its verdict once,
|
|
194
|
+
* over the finished value, instead of once over the value being replaced and once over the value replacing it.
|
|
195
|
+
*/
|
|
196
|
+
protected suppressParentValidityClimb: boolean;
|
|
197
|
+
private parentValidityClimbPending;
|
|
198
|
+
/** Carries out the climb that a validity change made while suppressParentValidityClimb was held, if there was one. */
|
|
199
|
+
protected flushParentValidityClimb(): void;
|
|
181
200
|
validate(revalidate?: boolean): void;
|
|
182
201
|
get valid(): boolean;
|
|
183
202
|
get fullValue(): any;
|
|
184
203
|
get isChanged(): boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Registers the actions a constructor received in its parameters: registration without the eager trigger,
|
|
206
|
+
* because a constructor ends with a single this.actions.triggerEager(...) over the finished value, and an
|
|
207
|
+
* eager action registered here would otherwise also run once per registration, over a half-built field.
|
|
208
|
+
*/
|
|
209
|
+
protected registerInitialActions(actions: FieldActionBase[]): void;
|
|
185
210
|
registerAction(action: FieldActionBase): this;
|
|
186
211
|
triggerAction<T2 extends FieldActionBase>(actionClass: (abstract new (...args: any[]) => T2) & {
|
|
187
212
|
classIdentifier: symbol;
|
|
188
213
|
}, ...params: any[]): any;
|
|
214
|
+
private _validationEpoch;
|
|
215
|
+
/**
|
|
216
|
+
* Generation counter of the validators attached to this field. A Validator reads it when a run starts and drops
|
|
217
|
+
* a result whose epoch no longer matches, so a validation still in flight when clearValidators() is called
|
|
218
|
+
* cannot push an error onto a field that no longer has the validator that produced it.
|
|
219
|
+
*/
|
|
220
|
+
get validationEpoch(): number;
|
|
189
221
|
clearValidators(): void;
|
|
190
222
|
}
|
|
191
223
|
|
|
@@ -279,6 +311,14 @@ export declare class Group<T extends GenericFieldsInterface = GenericFieldsInter
|
|
|
279
311
|
field<K extends keyof T>(fieldName: K): T[K] | null;
|
|
280
312
|
get fields(): T;
|
|
281
313
|
get value(): GroupValue<T>;
|
|
314
|
+
/**
|
|
315
|
+
* Writes the members that the given value carries, without letting any single member announce the state in
|
|
316
|
+
* between: the group's own value notification and its own validation are both held back for the duration, so
|
|
317
|
+
* neither a ValueChangedAction nor a ValidChangedAction reports a verdict over a half-applied value. The
|
|
318
|
+
* caller decides what to announce once the whole value is in place. Both flags are restored to what they were,
|
|
319
|
+
* so a caller that is itself holding the group back stays in charge.
|
|
320
|
+
*/
|
|
321
|
+
private assignMembers;
|
|
282
322
|
set value(newValue: GroupValueInput<T>);
|
|
283
323
|
get touched(): boolean;
|
|
284
324
|
set touched(touched: boolean);
|
|
@@ -339,6 +379,11 @@ export declare class List<T extends GenericFieldsInterface = GenericFieldsInterf
|
|
|
339
379
|
private _previousValue;
|
|
340
380
|
constructor(itemTemplate?: Group<T>, params?: Partial<IFieldConstructorParams<ListValue>>);
|
|
341
381
|
private processSetValueItem;
|
|
382
|
+
/**
|
|
383
|
+
* Builds the item that fills a gap left by an insert beyond the end of the list: a copy of the item template with
|
|
384
|
+
* the template's own values, or an empty group when the list has no template.
|
|
385
|
+
*/
|
|
386
|
+
private createPaddingItem;
|
|
342
387
|
private setValueInternal;
|
|
343
388
|
get value(): ListValue;
|
|
344
389
|
set value(newValue: Record<string, any>[]);
|
|
@@ -543,11 +588,26 @@ export declare type ValidationFunctionResult = ValidationError[] | null;
|
|
|
543
588
|
*/
|
|
544
589
|
export declare class Validator<T = any> extends ValueChangedAction {
|
|
545
590
|
private readonly source;
|
|
591
|
+
/**
|
|
592
|
+
* Sequence number of the newest run of this validator, per field. Every execution takes the next number, and a
|
|
593
|
+
* result is applied only while its number is still the newest one, so a slow run cannot overwrite the verdict of
|
|
594
|
+
* a faster one that started after it. The key is the field because one validator instance may be registered on
|
|
595
|
+
* several fields, each with a sequence of its own.
|
|
596
|
+
*/
|
|
597
|
+
private readonly runs;
|
|
546
598
|
/**
|
|
547
599
|
* Creates a new validator
|
|
548
600
|
* @param validationFn Function that validates the field value and returns errors or null
|
|
549
601
|
*/
|
|
550
602
|
constructor(validationFn: ValidationFunction<T>);
|
|
603
|
+
/**
|
|
604
|
+
* Returns the error instance this validator owns and may later withdraw: the argument itself when it carries no
|
|
605
|
+
* ownership stamp or already carries this validator's, a copy of it when it belongs to another validator. The
|
|
606
|
+
* stamp is not configurable, so an instance one validator owns can never be reassigned to another; the copy is
|
|
607
|
+
* what makes an error instance shareable between validators, and it keeps the prototype and every own property,
|
|
608
|
+
* including the non-enumerable ones, so it renders exactly like the instance it was made from.
|
|
609
|
+
*/
|
|
610
|
+
private claim;
|
|
551
611
|
static get classIdentifier(): symbol;
|
|
552
612
|
get eager(): boolean;
|
|
553
613
|
protected replacePlaceholdersFunction(text: RenderContentRef, replace: Record<string, any>): RenderContentRef;
|
package/dist/index.d.ts
CHANGED
|
@@ -178,14 +178,46 @@ export declare abstract class FieldBase<T = any> {
|
|
|
178
178
|
private _enabled;
|
|
179
179
|
get enabled(): boolean;
|
|
180
180
|
set enabled(newValue: boolean);
|
|
181
|
+
/**
|
|
182
|
+
* While true, validate() on this field does nothing at all: it neither recomputes _valid nor emits
|
|
183
|
+
* ValidChangedAction, and nothing climbs to the parent. A container that walks its members one by one - assigning
|
|
184
|
+
* them a value, or revalidating them - sets it for the duration of the walk, so a member's upward climb cannot
|
|
185
|
+
* announce the verdict of a half-applied value or of a half-revalidated set, and clears it in a finally block
|
|
186
|
+
* followed by its own validate(). That call recomputes over the finished state and emits the net transition, so
|
|
187
|
+
* the deferral cannot swallow a real change.
|
|
188
|
+
*/
|
|
189
|
+
protected suppressValidation: boolean;
|
|
190
|
+
/**
|
|
191
|
+
* While true, a validity change is still recomputed and still announced on this field, but it is not passed up to
|
|
192
|
+
* the parent: the climb is only remembered and carried out by flushParentValidityClimb(). A setter that notifies
|
|
193
|
+
* the parent of the new value itself holds it over the notifying region, so the parent forms its verdict once,
|
|
194
|
+
* over the finished value, instead of once over the value being replaced and once over the value replacing it.
|
|
195
|
+
*/
|
|
196
|
+
protected suppressParentValidityClimb: boolean;
|
|
197
|
+
private parentValidityClimbPending;
|
|
198
|
+
/** Carries out the climb that a validity change made while suppressParentValidityClimb was held, if there was one. */
|
|
199
|
+
protected flushParentValidityClimb(): void;
|
|
181
200
|
validate(revalidate?: boolean): void;
|
|
182
201
|
get valid(): boolean;
|
|
183
202
|
get fullValue(): any;
|
|
184
203
|
get isChanged(): boolean;
|
|
204
|
+
/**
|
|
205
|
+
* Registers the actions a constructor received in its parameters: registration without the eager trigger,
|
|
206
|
+
* because a constructor ends with a single this.actions.triggerEager(...) over the finished value, and an
|
|
207
|
+
* eager action registered here would otherwise also run once per registration, over a half-built field.
|
|
208
|
+
*/
|
|
209
|
+
protected registerInitialActions(actions: FieldActionBase[]): void;
|
|
185
210
|
registerAction(action: FieldActionBase): this;
|
|
186
211
|
triggerAction<T2 extends FieldActionBase>(actionClass: (abstract new (...args: any[]) => T2) & {
|
|
187
212
|
classIdentifier: symbol;
|
|
188
213
|
}, ...params: any[]): any;
|
|
214
|
+
private _validationEpoch;
|
|
215
|
+
/**
|
|
216
|
+
* Generation counter of the validators attached to this field. A Validator reads it when a run starts and drops
|
|
217
|
+
* a result whose epoch no longer matches, so a validation still in flight when clearValidators() is called
|
|
218
|
+
* cannot push an error onto a field that no longer has the validator that produced it.
|
|
219
|
+
*/
|
|
220
|
+
get validationEpoch(): number;
|
|
189
221
|
clearValidators(): void;
|
|
190
222
|
}
|
|
191
223
|
|
|
@@ -279,6 +311,14 @@ export declare class Group<T extends GenericFieldsInterface = GenericFieldsInter
|
|
|
279
311
|
field<K extends keyof T>(fieldName: K): T[K] | null;
|
|
280
312
|
get fields(): T;
|
|
281
313
|
get value(): GroupValue<T>;
|
|
314
|
+
/**
|
|
315
|
+
* Writes the members that the given value carries, without letting any single member announce the state in
|
|
316
|
+
* between: the group's own value notification and its own validation are both held back for the duration, so
|
|
317
|
+
* neither a ValueChangedAction nor a ValidChangedAction reports a verdict over a half-applied value. The
|
|
318
|
+
* caller decides what to announce once the whole value is in place. Both flags are restored to what they were,
|
|
319
|
+
* so a caller that is itself holding the group back stays in charge.
|
|
320
|
+
*/
|
|
321
|
+
private assignMembers;
|
|
282
322
|
set value(newValue: GroupValueInput<T>);
|
|
283
323
|
get touched(): boolean;
|
|
284
324
|
set touched(touched: boolean);
|
|
@@ -339,6 +379,11 @@ export declare class List<T extends GenericFieldsInterface = GenericFieldsInterf
|
|
|
339
379
|
private _previousValue;
|
|
340
380
|
constructor(itemTemplate?: Group<T>, params?: Partial<IFieldConstructorParams<ListValue>>);
|
|
341
381
|
private processSetValueItem;
|
|
382
|
+
/**
|
|
383
|
+
* Builds the item that fills a gap left by an insert beyond the end of the list: a copy of the item template with
|
|
384
|
+
* the template's own values, or an empty group when the list has no template.
|
|
385
|
+
*/
|
|
386
|
+
private createPaddingItem;
|
|
342
387
|
private setValueInternal;
|
|
343
388
|
get value(): ListValue;
|
|
344
389
|
set value(newValue: Record<string, any>[]);
|
|
@@ -543,11 +588,26 @@ export declare type ValidationFunctionResult = ValidationError[] | null;
|
|
|
543
588
|
*/
|
|
544
589
|
export declare class Validator<T = any> extends ValueChangedAction {
|
|
545
590
|
private readonly source;
|
|
591
|
+
/**
|
|
592
|
+
* Sequence number of the newest run of this validator, per field. Every execution takes the next number, and a
|
|
593
|
+
* result is applied only while its number is still the newest one, so a slow run cannot overwrite the verdict of
|
|
594
|
+
* a faster one that started after it. The key is the field because one validator instance may be registered on
|
|
595
|
+
* several fields, each with a sequence of its own.
|
|
596
|
+
*/
|
|
597
|
+
private readonly runs;
|
|
546
598
|
/**
|
|
547
599
|
* Creates a new validator
|
|
548
600
|
* @param validationFn Function that validates the field value and returns errors or null
|
|
549
601
|
*/
|
|
550
602
|
constructor(validationFn: ValidationFunction<T>);
|
|
603
|
+
/**
|
|
604
|
+
* Returns the error instance this validator owns and may later withdraw: the argument itself when it carries no
|
|
605
|
+
* ownership stamp or already carries this validator's, a copy of it when it belongs to another validator. The
|
|
606
|
+
* stamp is not configurable, so an instance one validator owns can never be reassigned to another; the copy is
|
|
607
|
+
* what makes an error instance shareable between validators, and it keeps the prototype and every own property,
|
|
608
|
+
* including the non-enumerable ones, so it renders exactly like the instance it was made from.
|
|
609
|
+
*/
|
|
610
|
+
private claim;
|
|
551
611
|
static get classIdentifier(): symbol;
|
|
552
612
|
get eager(): boolean;
|
|
553
613
|
protected replacePlaceholdersFunction(text: RenderContentRef, replace: Record<string, any>): RenderContentRef;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamicforms/vue-forms",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "A lightweight Vue.js library for form state management with built-in validation, conditional logic, nested structures, and a rich event system. UI-agnostic and TypeScript-first.",
|
|
7
7
|
"author": "Jure Erznožnik",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
],
|
|
30
30
|
"scripts": {
|
|
31
31
|
"build": "vite build && node -e \"require('fs').copyFileSync('dist/index.d.ts', 'dist/index.d.cts')\"",
|
|
32
|
+
"prepack": "npm run build",
|
|
32
33
|
"test": "vitest run --coverage",
|
|
33
34
|
"lint": "eslint src --fix && vue-tsc --noEmit",
|
|
34
35
|
"lint:ci": "eslint src && vue-tsc --noEmit",
|
|
@@ -52,13 +53,19 @@
|
|
|
52
53
|
"homepage": "https://docs.velis.si/dynamicforms/vue-forms",
|
|
53
54
|
"repository": {
|
|
54
55
|
"type": "git",
|
|
55
|
-
"url": "git+https://github.com/
|
|
56
|
+
"url": "git+https://github.com/dynamicforms/vue-forms.git"
|
|
56
57
|
},
|
|
57
58
|
"bugs": {
|
|
58
|
-
"url": "https://github.com/
|
|
59
|
+
"url": "https://github.com/dynamicforms/vue-forms/issues"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=18"
|
|
63
|
+
},
|
|
64
|
+
"dependencies": {
|
|
65
|
+
"lodash": "^4.17.21",
|
|
66
|
+
"lodash-es": "^4.17.21"
|
|
59
67
|
},
|
|
60
68
|
"peerDependencies": {
|
|
61
|
-
"lodash-es": "^4.17.12",
|
|
62
69
|
"vue": "^3.4"
|
|
63
70
|
},
|
|
64
71
|
"devDependencies": {
|
package/readme.md
CHANGED
|
@@ -18,7 +18,7 @@ mechanism applies at every level of a nested form.
|
|
|
18
18
|
- **Fields that react to each other**: Conditional visibility, enablement and values are declared as statements over other fields, and an action pipeline lets a handler intercept, transform or abort an event.
|
|
19
19
|
- **Reactive & Type-Safe**: Fields, groups and lists are Vue reactive objects, and a group's value type is inferred from the fields it holds, nested structures included.
|
|
20
20
|
- **Structural serialization**: A group's value is the shape of its fields, and `Group.createFromFormData()` turns a plain object back into a form.
|
|
21
|
-
- **Lightweight**:
|
|
21
|
+
- **Lightweight**: `vue` (^3.4) is the only peer dependency, and `lodash` and `lodash-es` — one per build — the only runtime ones.
|
|
22
22
|
|
|
23
23
|
## Features
|
|
24
24
|
|
|
@@ -28,7 +28,8 @@ mechanism applies at every level of a nested form.
|
|
|
28
28
|
- **Nested structures**: Support for complex data with nested fields and groups
|
|
29
29
|
- **Event system**: Rich event handling for field changes, validation, and more
|
|
30
30
|
- **TypeScript support**: Full type definitions for excellent developer experience
|
|
31
|
-
- **Lightweight**:
|
|
31
|
+
- **Lightweight**: `vue` (^3.4) is the only peer dependency; the runtime dependencies are `lodash` and `lodash-es`,
|
|
32
|
+
one for each of the two builds
|
|
32
33
|
- **Field types**: Core field types (Field, Action, Group, List) to represent any data structure
|
|
33
34
|
- **Validation**: Comprehensive validation system with built-in validators and extensible error handling
|
|
34
35
|
- **Conditional logic**: Dynamic form behavior based on field values and conditions
|
|
@@ -40,6 +41,11 @@ mechanism applies at every level of a nested form.
|
|
|
40
41
|
npm install @dynamicforms/vue-forms
|
|
41
42
|
```
|
|
42
43
|
|
|
44
|
+
The package ships two builds of the same source: an ESM build that imports `lodash-es` and a CJS/UMD build that
|
|
45
|
+
requires `lodash`. Both lodash packages are declared as dependencies and install with the package, so an `import` and
|
|
46
|
+
a `require()` both resolve without further setup. Node 18 or newer is required, and `vue` (^3.4) is the only peer
|
|
47
|
+
dependency. Type definitions ship for both entry points; the stylesheet is `@dynamicforms/vue-forms/style.css`.
|
|
48
|
+
|
|
43
49
|
## Setup
|
|
44
50
|
|
|
45
51
|
The library ships a Vue plugin for its global options:
|
|
@@ -185,6 +191,12 @@ const validatedForm = new Group({
|
|
|
185
191
|
Validators run eagerly — a field is validated the moment it is created, so a form built from empty required fields is
|
|
186
192
|
invalid immediately. Use `field.touched` to decide when to show the errors in the UI.
|
|
187
193
|
|
|
194
|
+
A validation function may return a `Promise`. `field.validating` is `true` while such a run is pending, and the
|
|
195
|
+
verdict applied to the field is always the one belonging to the newest run, so a slow check cannot overwrite a faster
|
|
196
|
+
one started after it. A validator message given as a `Ref` or `computed` is resolved when the message is rendered, so
|
|
197
|
+
a translated message follows a locale switch without revalidating. `field.clearValidators()` drops the validators,
|
|
198
|
+
empties the errors and cancels whatever validation is still in flight.
|
|
199
|
+
|
|
188
200
|
## Messages Widget Component
|
|
189
201
|
|
|
190
202
|
The library includes a `messages-widget` Vue component for displaying validation errors and messages:
|