@dynamicforms/vue-forms 0.6.1 → 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 +1807 -781
- 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 +797 -78
- package/dist/index.d.ts +797 -78
- package/package.json +1 -1
- package/readme.md +52 -14
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",
|
package/readme.md
CHANGED
|
@@ -16,17 +16,19 @@ 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
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
33
|
- **Lightweight**: `vue` (^3.4) is the only peer dependency; the runtime dependencies are `lodash` and `lodash-es`,
|
|
32
34
|
one for each of the two builds
|
|
@@ -65,9 +67,10 @@ want plain text instead.
|
|
|
65
67
|
## Basic Usage Example
|
|
66
68
|
|
|
67
69
|
Every form element is created with the constructor: `new Field({ ... })`, `new Action({ ... })`,
|
|
68
|
-
`new Group({ ... })`, `new List(template)`.
|
|
70
|
+
`new Group({ ... })`, `new List(template)`. Every read through it is tracked from that moment on, so reading
|
|
69
71
|
`field.value` in a template tracks it and `field.value = x` re-renders — there is no `ref` to unwrap and no
|
|
70
|
-
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)`.
|
|
71
74
|
|
|
72
75
|
Here's a simple example of how to create and use a form with fields and groups:
|
|
73
76
|
|
|
@@ -105,9 +108,14 @@ const saveAction = new Action({
|
|
|
105
108
|
actions: [new ExecuteAction((field, supr, params) => { console.log('saving', params); })]
|
|
106
109
|
});
|
|
107
110
|
|
|
108
|
-
saveAction.execute({ form: personForm }); // 'saving { form: ... }'
|
|
111
|
+
await saveAction.execute({ form: personForm }); // 'saving { form: ... }'; saveAction.busy until it settles
|
|
109
112
|
```
|
|
110
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
|
+
|
|
111
119
|
## Events Example
|
|
112
120
|
|
|
113
121
|
The library provides a powerful event system for field changes and other actions:
|
|
@@ -141,6 +149,21 @@ const form = new Group({
|
|
|
141
149
|
Assigning `field.errors` directly replaces the whole array, including errors owned by validators — prefer a
|
|
142
150
|
`Validator` (see below) when all you want is to add a validation rule.
|
|
143
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
|
+
|
|
144
167
|
## Built-in Validators
|
|
145
168
|
|
|
146
169
|
The library provides several built-in validators for common validation scenarios:
|
|
@@ -377,18 +400,30 @@ const prefs: { darkMode: boolean; notifications: boolean } | null = values.prefe
|
|
|
377
400
|
|
|
378
401
|
The value shape is derived from the fields map by the exported `FieldsToValues<T>`, with `GroupValue<T>` and
|
|
379
402
|
`GroupValueInput<T>` as the group's read and write types, and `ListValue` for lists. Constructor parameters have
|
|
380
|
-
their own exported type, `
|
|
403
|
+
their own exported type, `IFieldParams<T, X>`, shared by all four element classes:
|
|
381
404
|
|
|
382
405
|
```typescript
|
|
383
|
-
import { Field,
|
|
406
|
+
import { Field, IFieldParams } from '@dynamicforms/vue-forms';
|
|
384
407
|
|
|
385
|
-
const defaults:
|
|
408
|
+
const defaults: IFieldParams<string> = { value: '', enabled: false };
|
|
386
409
|
const field = new Field(defaults);
|
|
387
410
|
```
|
|
388
411
|
|
|
389
412
|
It admits only the writable members — `value`, `originalValue`, `enabled`, `visibility`, `touched`, `errors`,
|
|
390
|
-
`validators` and `actions
|
|
391
|
-
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
|
+
```
|
|
392
427
|
|
|
393
428
|
`FieldBase<T>` is the abstract base of `Field`, `Action`, `Group` and `List`, and the type to use in your own
|
|
394
429
|
signatures whenever you accept any form element:
|
|
@@ -405,9 +440,12 @@ function isDirty(field: FieldBase): boolean {
|
|
|
405
440
|
|
|
406
441
|
For more detailed documentation and examples, check out the [documentation](https://docs.velis.si/dynamicforms/vue-forms).
|
|
407
442
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
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.
|
|
411
449
|
|
|
412
450
|
## Conclusion
|
|
413
451
|
|