@dynamicforms/vue-forms 0.12.0 → 0.16.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 +1524 -1147
- package/dist/dynamicforms-vue-forms.js.map +1 -1
- package/dist/index.d.ts +392 -72
- package/package.json +16 -4
- package/readme.md +49 -25
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dynamicforms/vue-forms",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.16.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
|
-
"author": "Jure
|
|
7
|
+
"author": "Jure Erzno\u017enik",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist/*"
|
|
10
10
|
],
|
|
@@ -61,23 +61,35 @@
|
|
|
61
61
|
"vue": "^3.5.2"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
+
"@eslint/js": "^10.0.1",
|
|
64
65
|
"@types/lodash-es": "^4.17.12",
|
|
65
66
|
"@types/markdown-it-attrs": "^4.1.3",
|
|
66
|
-
"@types/node": "^
|
|
67
|
+
"@types/node": "^26.2.0",
|
|
68
|
+
"@typescript-eslint/eslint-plugin": "^8.67.0",
|
|
69
|
+
"@typescript-eslint/parser": "^8.67.0",
|
|
67
70
|
"@vitejs/plugin-vue": "^6",
|
|
68
71
|
"@vitest/coverage-v8": "^3",
|
|
69
72
|
"@vue/test-utils": "^2.2.4",
|
|
70
73
|
"@vue/tsconfig": "^0.7.0",
|
|
71
74
|
"baseline-browser-mapping": "^2.8.31",
|
|
72
|
-
"eslint
|
|
75
|
+
"eslint": "^10.4.0",
|
|
76
|
+
"eslint-config-prettier": "^10.1.8",
|
|
77
|
+
"eslint-config-velis": "^3.0.0",
|
|
78
|
+
"eslint-import-resolver-typescript": "^4.4.5",
|
|
79
|
+
"eslint-plugin-import-x": "^4.17.1",
|
|
80
|
+
"eslint-plugin-prettier": "^5.5.6",
|
|
81
|
+
"eslint-plugin-unicorn": "^73.0.0",
|
|
82
|
+
"eslint-plugin-vue": "^10.10.0",
|
|
73
83
|
"jsdom": "^26.0.0",
|
|
74
84
|
"markdown-it-attrs": "^4.3.1",
|
|
85
|
+
"prettier": "^3.9.6",
|
|
75
86
|
"rollup-plugin-visualizer": "^5.14.0",
|
|
76
87
|
"typescript": "^5",
|
|
77
88
|
"vite": "^7",
|
|
78
89
|
"vite-plugin-dts": "^4",
|
|
79
90
|
"vite-plugin-eslint": "^1.8.1",
|
|
80
91
|
"vitest": "^3",
|
|
92
|
+
"vue-eslint-parser": "^10.4.1",
|
|
81
93
|
"vue-markdown-render": "^2.3.0",
|
|
82
94
|
"vue-tsc": "^2"
|
|
83
95
|
}
|
package/readme.md
CHANGED
|
@@ -22,19 +22,27 @@ mechanism applies at every level of a nested form.
|
|
|
22
22
|
|
|
23
23
|
## Features
|
|
24
24
|
|
|
25
|
-
- **UI-agnostic**:
|
|
25
|
+
- **UI-agnostic**: a logic layer for form state, validation and dynamic behaviour. Any Vue components render it,
|
|
26
|
+
your own included
|
|
27
|
+
- **Transactional**: every mutating operation is atomic — events are announced once, over the net change, and a
|
|
28
|
+
handler that throws leaves the form exactly as it was. `transaction()` makes several writes one operation, and
|
|
29
|
+
`tx.rollback()` withdraws one without an error
|
|
30
|
+
- **Lists that scale**: a `List` is meant to hold thousands of rows. Writing one field of one row costs that row
|
|
31
|
+
and the depth it sits at, a `push()` costs one row, and reading `value` or `valid` again costs nothing
|
|
32
|
+
- **Declared once, bound per record**: the `Group` handed to `new List(template)` is the declaration every row is
|
|
33
|
+
built from. One validator instance and one conditional rule serve every row, and each row answers for itself
|
|
26
34
|
- **Reactive**: every member of a field, group or list is a tracked read — assign a property directly, with no
|
|
27
35
|
`ref` to unwrap and no computed mirror to keep in sync
|
|
28
|
-
- **Nested structures**:
|
|
29
|
-
- **Event system**:
|
|
30
|
-
- **
|
|
31
|
-
|
|
32
|
-
- **
|
|
36
|
+
- **Nested structures**: fields, groups and lists compose recursively, at every level
|
|
37
|
+
- **Event system**: every change travels through an action pipeline; a handler may pass it on, reshape it or stop it
|
|
38
|
+
- **Validation**: built-in validators, custom synchronous and asynchronous rules, cross-field comparisons, and
|
|
39
|
+
errors that carry a `code` and render as text, markdown or a component of your own
|
|
40
|
+
- **Conditional logic**: visibility, enablement and values declared as statements over other fields
|
|
41
|
+
- **Extended properties**: a field carries whatever your UI renders it with — a label, a hint, a width — declared
|
|
42
|
+
as a second type argument, checked by the compiler and read through `extra`
|
|
43
|
+
- **Display modes**: control field visibility with different display modes (Full, Hidden, Invisible, Suppress)
|
|
44
|
+
- **TypeScript support**: full type definitions, and a group's value type inferred from the fields it holds
|
|
33
45
|
- **Lightweight**: `vue` (^3.5.2) is the only peer dependency and `lodash-es` the only runtime one
|
|
34
|
-
- **Field types**: Core field types (Field, Action, Group, List) to represent any data structure
|
|
35
|
-
- **Validation**: Comprehensive validation system with built-in validators and extensible error handling
|
|
36
|
-
- **Conditional logic**: Dynamic form behavior based on field values and conditions
|
|
37
|
-
- **Display modes**: Control field visibility with different display modes (Full, Hidden, Invisible, Suppress)
|
|
38
46
|
|
|
39
47
|
## Installation
|
|
40
48
|
|
|
@@ -63,6 +71,10 @@ The import must be a named one — the default export is a namespace of the libr
|
|
|
63
71
|
(`MdString`). Rendering those requires a globally registered `vue-markdown` component; set the option to `false` if you
|
|
64
72
|
want plain text instead.
|
|
65
73
|
|
|
74
|
+
The configuration is module-global rather than per app, and `getConfig()`, `setConfig()` and the `FormsConfig` type
|
|
75
|
+
are exported beside the plugin, so the options can be read and written where there is no app to install a plugin
|
|
76
|
+
on.
|
|
77
|
+
|
|
66
78
|
## Basic Usage Example
|
|
67
79
|
|
|
68
80
|
Every form element is created with the constructor: `new Field({ ... })`, `new Action({ ... })`,
|
|
@@ -211,13 +223,22 @@ const validatedForm = new Group({
|
|
|
211
223
|
```
|
|
212
224
|
|
|
213
225
|
Validators run eagerly — a field is validated the moment it is created, so a form built from empty required fields is
|
|
214
|
-
invalid immediately. Use `field.touched` to decide when to show the errors in the UI.
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
226
|
+
invalid immediately. Use `field.touched` to decide when to show the errors in the UI. `Required` trims a string
|
|
227
|
+
before it measures it, so a value of spaces alone is no value; `new Validators.Required({ trim: false })` keeps the
|
|
228
|
+
spaces where they are part of what the field holds.
|
|
229
|
+
|
|
230
|
+
Every error a built-in validator produces carries a `code` — `required`, `pattern`, `min-length`, … — so a program
|
|
231
|
+
reacting to one particular failure need not match message text that is translated and configurable.
|
|
232
|
+
|
|
233
|
+
A validation function may return a `Promise`. `field.validating` is `true` while such a run is pending — on the
|
|
234
|
+
field and on every container above it, so a form answers for the whole tree — and the verdict applied to the field
|
|
235
|
+
is always the one belonging to the newest run, so a slow check cannot overwrite a faster one started after it. The
|
|
236
|
+
function receives an `AbortSignal` as its fourth argument, which aborts the moment the run's verdict stops
|
|
237
|
+
counting, so the request behind it can be called off. `form.busy` is the same question with the `Action.execute()`
|
|
238
|
+
runs below it included — what a submit button binds to. A validator message given as a `Ref` or `computed` is
|
|
239
|
+
resolved when the message is rendered, so a translated message follows a locale switch without revalidating.
|
|
240
|
+
`field.clearValidators()` drops the validators, empties the errors and cancels whatever validation is still in
|
|
241
|
+
flight.
|
|
221
242
|
|
|
222
243
|
## Messages Widget Component
|
|
223
244
|
|
|
@@ -326,7 +347,8 @@ const contactsList = new List(contactTemplate);
|
|
|
326
347
|
contactsList.push({ name: 'John Doe', email: 'john@example.com', phone: '123-456-7890' });
|
|
327
348
|
contactsList.push({ name: 'Jane Doe', email: 'jane@example.com', phone: '987-654-3210' });
|
|
328
349
|
|
|
329
|
-
// Access list items: get()
|
|
350
|
+
// Access list items: items hands out the rows, get() answers undefined for an invalid index
|
|
351
|
+
console.log(contactsList.length); // 2
|
|
330
352
|
const firstContact = contactsList.get(0)!;
|
|
331
353
|
console.log(firstContact.fields.name.value); // 'John Doe'
|
|
332
354
|
|
|
@@ -337,8 +359,9 @@ firstContact.fields.email.value = 'john.doe@example.com';
|
|
|
337
359
|
contactsList.remove(1);
|
|
338
360
|
```
|
|
339
361
|
|
|
340
|
-
Every list mutation is tracked, so a `v-for` over `contactsList.
|
|
341
|
-
`remove()`, `pop()` and `clear()` without any extra wiring.
|
|
362
|
+
Every list mutation is tracked, so a `v-for` over `contactsList.items` re-renders on `push()`, `insert()`,
|
|
363
|
+
`remove()`, `pop()` and `clear()` without any extra wiring. `items` hands out a frozen array of the live rows,
|
|
364
|
+
rebuilt once per change of the set.
|
|
342
365
|
|
|
343
366
|
## TypeScript Support
|
|
344
367
|
|
|
@@ -388,10 +411,11 @@ const email: string = userForm.fields.email.value;
|
|
|
388
411
|
const age: number = userForm.fields.age.value;
|
|
389
412
|
const darkMode: boolean = userForm.fields.preferences.fields.darkMode.value;
|
|
390
413
|
|
|
391
|
-
// The serialized value is typed too, member by member
|
|
414
|
+
// The serialized value is typed too, member by member. Every member is optional, because a disabled field is
|
|
415
|
+
// left out of the object the group builds
|
|
392
416
|
const values = userForm.value!;
|
|
393
|
-
const emailFromValue: string = values.email;
|
|
394
|
-
const prefs: { darkMode
|
|
417
|
+
const emailFromValue: string | undefined = values.email;
|
|
418
|
+
const prefs: { darkMode?: boolean; notifications?: boolean } | null | undefined = values.preferences;
|
|
395
419
|
|
|
396
420
|
// Type safety prevents errors
|
|
397
421
|
// userForm.fields.age.value = 'not a number'; // Error: Type 'string' is not assignable to type 'number'
|
|
@@ -443,8 +467,8 @@ For more detailed documentation and examples, check out the [documentation](http
|
|
|
443
467
|
declarations, transactions, where validity comes from and how a `List` builds its rows.
|
|
444
468
|
|
|
445
469
|
Upgrading an existing project? The
|
|
446
|
-
[migration guide](https://docs.velis.si/dynamicforms/vue-forms/guide/migration) walks the journey from 0.6.1
|
|
447
|
-
|
|
470
|
+
[migration guide](https://docs.velis.si/dynamicforms/vue-forms/guide/migration) walks the journey from 0.6.1
|
|
471
|
+
onwards, silent breaks first, and keeps a section per release for a project crossing one.
|
|
448
472
|
|
|
449
473
|
## Conclusion
|
|
450
474
|
|