@dynamicforms/vue-forms 0.6.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/dynamicforms-vue-forms.js +1893 -716
- 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 +827 -48
- package/dist/index.d.ts +827 -48
- package/package.json +11 -4
- package/readme.md +66 -16
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamicforms/vue-forms",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.11.0",
|
|
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
|
@@ -16,19 +16,22 @@ mechanism applies at every level of a nested form.
|
|
|
16
16
|
|
|
17
17
|
- **UI-Agnostic**: A logic layer for form state, validation and dynamic behaviour. Works with any Vue components, including your own.
|
|
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
|
-
- **Reactive & Type-Safe**:
|
|
19
|
+
- **Reactive & Type-Safe**: Every member of a field, group or list is a tracked read, 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
|
|
|
25
25
|
- **UI-agnostic**: Works with any Vue UI components or your custom ones
|
|
26
|
-
- **Reactive**:
|
|
27
|
-
|
|
26
|
+
- **Reactive**: every member of a field, group or list is a tracked read — assign a property directly, with no
|
|
27
|
+
`ref` to unwrap and no computed mirror to keep in sync
|
|
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
|
+
- **Transactional**: every mutating operation is atomic — events are announced once, over the net change, and a
|
|
31
|
+
handler that throws leaves the form exactly as it was
|
|
30
32
|
- **TypeScript support**: Full type definitions for excellent developer experience
|
|
31
|
-
- **Lightweight**:
|
|
33
|
+
- **Lightweight**: `vue` (^3.4) is the only peer dependency; the runtime dependencies are `lodash` and `lodash-es`,
|
|
34
|
+
one for each of the two builds
|
|
32
35
|
- **Field types**: Core field types (Field, Action, Group, List) to represent any data structure
|
|
33
36
|
- **Validation**: Comprehensive validation system with built-in validators and extensible error handling
|
|
34
37
|
- **Conditional logic**: Dynamic form behavior based on field values and conditions
|
|
@@ -40,6 +43,11 @@ mechanism applies at every level of a nested form.
|
|
|
40
43
|
npm install @dynamicforms/vue-forms
|
|
41
44
|
```
|
|
42
45
|
|
|
46
|
+
The package ships two builds of the same source: an ESM build that imports `lodash-es` and a CJS/UMD build that
|
|
47
|
+
requires `lodash`. Both lodash packages are declared as dependencies and install with the package, so an `import` and
|
|
48
|
+
a `require()` both resolve without further setup. Node 18 or newer is required, and `vue` (^3.4) is the only peer
|
|
49
|
+
dependency. Type definitions ship for both entry points; the stylesheet is `@dynamicforms/vue-forms/style.css`.
|
|
50
|
+
|
|
43
51
|
## Setup
|
|
44
52
|
|
|
45
53
|
The library ships a Vue plugin for its global options:
|
|
@@ -59,9 +67,10 @@ want plain text instead.
|
|
|
59
67
|
## Basic Usage Example
|
|
60
68
|
|
|
61
69
|
Every form element is created with the constructor: `new Field({ ... })`, `new Action({ ... })`,
|
|
62
|
-
`new Group({ ... })`, `new List(template)`.
|
|
70
|
+
`new Group({ ... })`, `new List(template)`. Every read through it is tracked from that moment on, so reading
|
|
63
71
|
`field.value` in a template tracks it and `field.value = x` re-renders — there is no `ref` to unwrap and no
|
|
64
|
-
computed mirror to maintain.
|
|
72
|
+
computed mirror to maintain. The element itself is not a Vue proxy, so watch what you read:
|
|
73
|
+
`watch(() => field.value, cb)`.
|
|
65
74
|
|
|
66
75
|
Here's a simple example of how to create and use a form with fields and groups:
|
|
67
76
|
|
|
@@ -99,9 +108,14 @@ const saveAction = new Action({
|
|
|
99
108
|
actions: [new ExecuteAction((field, supr, params) => { console.log('saving', params); })]
|
|
100
109
|
});
|
|
101
110
|
|
|
102
|
-
saveAction.execute({ form: personForm }); // 'saving { form: ... }'
|
|
111
|
+
await saveAction.execute({ form: personForm }); // 'saving { form: ... }'; saveAction.busy until it settles
|
|
103
112
|
```
|
|
104
113
|
|
|
114
|
+
`Action` is the one deliberate exception to "UI-agnostic": it names a label and an icon because it exists as the
|
|
115
|
+
element a form's submit and cancel hang on, and that minimal pair is what makes the concept legible. The shape is
|
|
116
|
+
minimal because a UI library is expected to extend it — [`@dynamicforms/vuetify-inputs`](https://docs.velis.si/dynamicforms/vuetify-inputs/examples/df-actions.html)
|
|
117
|
+
widens the value with render options and per-breakpoint variants on top of it.
|
|
118
|
+
|
|
105
119
|
## Events Example
|
|
106
120
|
|
|
107
121
|
The library provides a powerful event system for field changes and other actions:
|
|
@@ -135,6 +149,21 @@ const form = new Group({
|
|
|
135
149
|
Assigning `field.errors` directly replaces the whole array, including errors owned by validators — prefer a
|
|
136
150
|
`Validator` (see below) when all you want is to add a validation rule.
|
|
137
151
|
|
|
152
|
+
Events are announced when the operation carrying them finishes, over the net change. Wrap several writes in a
|
|
153
|
+
`transaction()` and they announce as one:
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { transaction } from '@dynamicforms/vue-forms';
|
|
157
|
+
|
|
158
|
+
// one ValueChangedAction on the form, not two
|
|
159
|
+
transaction(() => {
|
|
160
|
+
form.fields.email.value = 'janez@example.com';
|
|
161
|
+
form.fields.username.value = 'janez';
|
|
162
|
+
});
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
A throw out of the callback rolls the whole transaction back and rethrows, so a form is never left half-applied.
|
|
166
|
+
|
|
138
167
|
## Built-in Validators
|
|
139
168
|
|
|
140
169
|
The library provides several built-in validators for common validation scenarios:
|
|
@@ -185,6 +214,12 @@ const validatedForm = new Group({
|
|
|
185
214
|
Validators run eagerly — a field is validated the moment it is created, so a form built from empty required fields is
|
|
186
215
|
invalid immediately. Use `field.touched` to decide when to show the errors in the UI.
|
|
187
216
|
|
|
217
|
+
A validation function may return a `Promise`. `field.validating` is `true` while such a run is pending, and the
|
|
218
|
+
verdict applied to the field is always the one belonging to the newest run, so a slow check cannot overwrite a faster
|
|
219
|
+
one started after it. A validator message given as a `Ref` or `computed` is resolved when the message is rendered, so
|
|
220
|
+
a translated message follows a locale switch without revalidating. `field.clearValidators()` drops the validators,
|
|
221
|
+
empties the errors and cancels whatever validation is still in flight.
|
|
222
|
+
|
|
188
223
|
## Messages Widget Component
|
|
189
224
|
|
|
190
225
|
The library includes a `messages-widget` Vue component for displaying validation errors and messages:
|
|
@@ -365,18 +400,30 @@ const prefs: { darkMode: boolean; notifications: boolean } | null = values.prefe
|
|
|
365
400
|
|
|
366
401
|
The value shape is derived from the fields map by the exported `FieldsToValues<T>`, with `GroupValue<T>` and
|
|
367
402
|
`GroupValueInput<T>` as the group's read and write types, and `ListValue` for lists. Constructor parameters have
|
|
368
|
-
their own exported type, `
|
|
403
|
+
their own exported type, `IFieldParams<T, X>`, shared by all four element classes:
|
|
369
404
|
|
|
370
405
|
```typescript
|
|
371
|
-
import { Field,
|
|
406
|
+
import { Field, IFieldParams } from '@dynamicforms/vue-forms';
|
|
372
407
|
|
|
373
|
-
const defaults:
|
|
408
|
+
const defaults: IFieldParams<string> = { value: '', enabled: false };
|
|
374
409
|
const field = new Field(defaults);
|
|
375
410
|
```
|
|
376
411
|
|
|
377
412
|
It admits only the writable members — `value`, `originalValue`, `enabled`, `visibility`, `touched`, `errors`,
|
|
378
|
-
`validators` and `actions
|
|
379
|
-
compile error.
|
|
413
|
+
`validators` and `actions`, listed by `IFieldConstructorParams<T>`. Derived members such as `valid` and
|
|
414
|
+
`isChanged` are getters, and passing one is a compile error.
|
|
415
|
+
|
|
416
|
+
`X` is what an element carries beyond those: declare it, and the parameter object takes those properties too,
|
|
417
|
+
`extra` reads them back and `setExtendedValues()` writes them. It is how a form built from a server's description
|
|
418
|
+
carries the label, hint or css class a UI layer binds to its inputs:
|
|
419
|
+
|
|
420
|
+
```typescript
|
|
421
|
+
interface Presentation { label: string; hint?: string }
|
|
422
|
+
|
|
423
|
+
const name = new Field<string, Presentation>({ value: 'John', label: 'First name' });
|
|
424
|
+
name.extra.label; // 'First name', and a template reading it re-renders when it is written
|
|
425
|
+
name.setExtendedValues({ hint: 'as in your passport' });
|
|
426
|
+
```
|
|
380
427
|
|
|
381
428
|
`FieldBase<T>` is the abstract base of `Field`, `Action`, `Group` and `List`, and the type to use in your own
|
|
382
429
|
signatures whenever you accept any form element:
|
|
@@ -393,9 +440,12 @@ function isDirty(field: FieldBase): boolean {
|
|
|
393
440
|
|
|
394
441
|
For more detailed documentation and examples, check out the [documentation](https://docs.velis.si/dynamicforms/vue-forms).
|
|
395
442
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
443
|
+
[The model](https://docs.velis.si/dynamicforms/vue-forms/guide/model) is the whole library in one page: elements,
|
|
444
|
+
declarations, transactions, where validity comes from and how a `List` builds its rows.
|
|
445
|
+
|
|
446
|
+
Upgrading an existing project? The
|
|
447
|
+
[migration guide](https://docs.velis.si/dynamicforms/vue-forms/guide/migration) walks the journey from 0.6.1 to
|
|
448
|
+
0.10.x, silent breaks first, and keeps a section per release for a project crossing one.
|
|
399
449
|
|
|
400
450
|
## Conclusion
|
|
401
451
|
|