@bach.software/vue-dynamic-form 0.5.0 → 0.6.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.
Files changed (41) hide show
  1. package/dist/examples.cjs.js +6 -6
  2. package/dist/examples.cjs.js.map +1 -1
  3. package/dist/examples.es.js +1643 -280
  4. package/dist/examples.es.js.map +1 -1
  5. package/dist/mapEachMetadataItem-Cxfn86Z2.cjs +2 -0
  6. package/dist/mapEachMetadataItem-Cxfn86Z2.cjs.map +1 -0
  7. package/dist/mapEachMetadataItem-Dvf3R5jd.js +1159 -0
  8. package/dist/mapEachMetadataItem-Dvf3R5jd.js.map +1 -0
  9. package/dist/src/components/DynamicFormItemWizard.vue.d.ts +25 -0
  10. package/dist/src/components/DynamicFormTemplate.vue.d.ts +97 -4
  11. package/dist/src/components/__tests__/DynamicFormItem.unmount.logic.test.d.ts +1 -0
  12. package/dist/src/components/__tests__/DynamicFormItemArray.analytics.test.d.ts +1 -0
  13. package/dist/src/components/__tests__/DynamicFormItemArray.test-helpers.d.ts +9 -0
  14. package/dist/src/components/__tests__/DynamicFormItemChoice.analytics.test.d.ts +1 -0
  15. package/dist/src/components/__tests__/DynamicFormItemChoice.test-helpers.d.ts +100 -0
  16. package/dist/src/components/__tests__/DynamicFormItemWizard.analytics.test.d.ts +1 -0
  17. package/dist/src/components/__tests__/DynamicFormItemWizard.logic.test.d.ts +1 -0
  18. package/dist/src/components/__tests__/DynamicFormItemWizard.test-helpers.d.ts +24 -0
  19. package/dist/src/components/__tests__/DynamicFormItemWizard.validation.test.d.ts +1 -0
  20. package/dist/src/components/__tests__/DynamicFormTemplate.test.d.ts +1 -0
  21. package/dist/src/core/validation.d.ts +8 -2
  22. package/dist/src/examples/KeepValuesOnUnmountTestCase.vue.d.ts +2 -0
  23. package/dist/src/examples/TestForm.vue.d.ts +1 -0
  24. package/dist/src/examples/TestFormTemplate.vue.d.ts +48 -13
  25. package/dist/src/examples/__tests__/TestFormTemplate.testcases.d.ts +2317 -7
  26. package/dist/src/examples/index.d.ts +1 -0
  27. package/dist/src/index.d.ts +1 -0
  28. package/dist/src/types/DynamicFormItemProps.d.ts +39 -2
  29. package/dist/src/types/DynamicFormSettings.d.ts +16 -0
  30. package/dist/src/types/FieldMetadata.d.ts +123 -1
  31. package/dist/src/utils/__tests__/deepCloneValue.test.d.ts +1 -0
  32. package/dist/src/utils/__tests__/mapEachMetadataItem.test.d.ts +1 -1
  33. package/dist/src/utils/deepCloneValue.d.ts +10 -0
  34. package/dist/tsconfig.tsbuildinfo +1 -1
  35. package/dist/vue-dynamic-form.cjs.js +1 -1
  36. package/dist/vue-dynamic-form.es.js +28 -27
  37. package/package.json +6 -4
  38. package/dist/mapEachMetadataItem-B4xJvJDj.js +0 -732
  39. package/dist/mapEachMetadataItem-B4xJvJDj.js.map +0 -1
  40. package/dist/mapEachMetadataItem-CDDSsb0c.cjs +0 -6
  41. package/dist/mapEachMetadataItem-CDDSsb0c.cjs.map +0 -1
@@ -1,3 +1,4 @@
1
1
  export * from './__tests__/TestFormTemplate.testcases';
2
+ export { default as KeepValuesOnUnmountTestCase } from './KeepValuesOnUnmountTestCase.vue';
2
3
  export { default as TestFormTemplate } from './TestFormTemplate.vue';
3
4
  export { type Metadata } from './TestFormTemplate.vue';
@@ -2,6 +2,7 @@ export { default as DynamicForm } from './components/DynamicForm.vue';
2
2
  export { default as DynamicFormItem } from './components/DynamicFormItem.vue';
3
3
  export { default as DynamicFormItemArray } from './components/DynamicFormItemArray.vue';
4
4
  export { default as DynamicFormItemChoice } from './components/DynamicFormItemChoice.vue';
5
+ export { default as DynamicFormItemWizard } from './components/DynamicFormItemWizard.vue';
5
6
  export { default as DynamicFormTemplate } from './components/DynamicFormTemplate.vue';
6
7
  export * from './core/defineMetadata';
7
8
  export * from './core/useDynamicForm';
@@ -3,8 +3,8 @@ import type { FieldMetadata } from '../types/FieldMetadata';
3
3
  import type { InternalFieldMetadata } from '../types/InternalFieldMetadata';
4
4
  export interface DynamicFormItemProps<InternalMetadata extends InternalFieldMetadata<FieldMetadata>> {
5
5
  template: DefineComponent<object, object, {
6
- default?: ((props: object) => any) | undefined;
7
- attributes?: ((props: object) => any) | undefined;
6
+ default?: ((props: object) => any);
7
+ attributes?: ((props: object) => any);
8
8
  }>;
9
9
  fieldMetadata: InternalMetadata;
10
10
  /**
@@ -39,6 +39,20 @@ export interface DynamicFormItemProps<InternalMetadata extends InternalFieldMeta
39
39
  * mark all children optional if no value has been filled in.
40
40
  */
41
41
  partOfArrayField?: boolean;
42
+ /**
43
+ * In case this field is one of the parent's `attributes`, it is conditionally mounted based on
44
+ * whether the parent has a value. This marks it so unmount cleanup can tell it apart from a
45
+ * regular field and consult `attributeOwnerHasValue` before deciding whether to clear it.
46
+ */
47
+ partOfAttributeField?: boolean;
48
+ /**
49
+ * For an item marked `partOfAttributeField`, a getter that reports whether the owning field
50
+ * currently has a main value. Called at unmount time: when it returns false, the attribute's
51
+ * value is cleared regardless of `keepValuesOnUnmount`/`keepValueOnUnmount`, since it belongs
52
+ * to a value that is itself gone; when it returns true, the attribute follows the same keep
53
+ * flags as any other field.
54
+ */
55
+ attributeOwnerHasValue?: () => boolean;
42
56
  /**
43
57
  * In case this field is part of a choice field, it needs to co-operate with that choice field and
44
58
  * behave differently in some situations.
@@ -49,6 +63,29 @@ export interface DynamicFormItemProps<InternalMetadata extends InternalFieldMeta
49
63
  * In case the field is a child of a choice field, it needs to be made repeatable and we can set this to true.
50
64
  */
51
65
  isArrayOverride?: 'auto' | 'array' | 'single';
66
+ /**
67
+ * In case this item is a single occurrence of a repeatable explicit choice branch
68
+ * (`DynamicFormItemChoice`, `maxOccurs > 1`), the branch (choice child) `name` it belongs to.
69
+ * When set, it drives the `-choice-array-item` slot type suffix (mirroring `partOfArrayField`'s
70
+ * `-array-item` suffix) and is forwarded to the slot as `branchKey`.
71
+ */
72
+ branchKey?: string;
73
+ /**
74
+ * In case this item is a single occurrence of a repeatable explicit choice branch
75
+ * (`DynamicFormItemChoice`, `maxOccurs > 1`), this occurrence's zero-based position across
76
+ * every branch's active occurrences (not just its own branch). Forwarded to the slot as
77
+ * `globalIndex`. `undefined` everywhere else.
78
+ */
79
+ globalIndex?: number;
80
+ /**
81
+ * In case this item is a single occurrence of a repeatable explicit choice branch
82
+ * (`DynamicFormItemChoice`, `maxOccurs > 1`), the instance-local, ephemeral position at which
83
+ * this occurrence was added this session (1-based, in add-press order across every branch).
84
+ * `undefined` for an occurrence that existed before this session (loaded saved data) or that
85
+ * was never added through `addChoiceOccurrence`. Forwarded to the slot as `insertionOrder`;
86
+ * never written to form values.
87
+ */
88
+ insertionOrder?: number;
52
89
  /**
53
90
  * In case this item is part of a field array, we need some extra info
54
91
  */
@@ -59,8 +59,24 @@ export type DynamicFormSettings<ExtendedProperties extends object = object> = {
59
59
  required?: ValidationMessage;
60
60
  /** XSD: minOccurs. Available placeholders: `{field}`, `{0}` or `{min}` (min number of filled items) */
61
61
  minOccurs?: ValidationMessage;
62
+ /** XSD: maxOccurs. Available placeholders: `{field}`, `{0}` or `{max}` (max number of allowed items) */
63
+ maxOccurs?: ValidationMessage;
62
64
  /** XSD: choiceMinOccurs. Available placeholders: `{field}`, `{0}` or `{min}` (min number of filled items) */
63
65
  choiceMinOccurs?: ValidationMessage;
66
+ /** XSD: choiceMaxOccurs. Available placeholders: `{field}`, `{0}` or `{max}` (max number of choice occurrences allowed) */
67
+ choiceMaxOccurs?: ValidationMessage;
68
+ /**
69
+ * Non-XSD: maxOccursTotal, a choice branch's opt-in cap on its own total raw occurrence
70
+ * count. Available placeholders: `{field}`, `{0}` or `{max}` (the offending branch's cap).
71
+ *
72
+ * This is a single choice-level aggregate, not a per-branch error: `{field}` resolves to the
73
+ * choice's own anchor label, never the branch's, so the message cannot name which branch is
74
+ * over its cap. When several branches breach their own `maxOccursTotal` at once, `{max}`/`{0}`
75
+ * carry the cap of the first offending branch in declaration order only, not every breached
76
+ * cap. A single-branch choice skips choice-level validation entirely; there a breach surfaces
77
+ * as the branch's own `maxOccurs` message instead.
78
+ */
79
+ maxOccursTotal?: ValidationMessage;
64
80
  /** XSD: minLength. Available placeholders: `{field}`, `{0}` or `{length}` (min length) */
65
81
  minLength?: ValidationMessage;
66
82
  /** XSD: maxLength. Available placeholders: `{field}`, `{0}` or `{length}` (max length) */
@@ -28,6 +28,28 @@ export type FieldMetadata<ExtendedFieldTypes extends string = string, ExtendedPr
28
28
  * { name: 'emails', type: 'text', maxOccurs: 5, minOccurs: 1, autoAddMinOccurs: false }
29
29
  */
30
30
  autoAddMinOccurs?: boolean;
31
+ /**
32
+ * Opt-in, non-XSD cap on the total number of raw occurrences a `choice` branch may reach
33
+ * across the whole choice, independent of the occurrence batching `maxOccurs` drives. Only
34
+ * meaningful on a node that is itself an entry inside a `choice` array.
35
+ *
36
+ * Plain `<xs:choice>` has no equivalent: XSD only bounds how many times the choice itself
37
+ * repeats and how many items a single repetition of a branch may hold, never a branch's
38
+ * running total across the whole choice. Use this when a per-kind ceiling ("at most 3 of this
39
+ * kind") is a real requirement layered on top of correct XSD batching.
40
+ *
41
+ * - With `explicitChoiceSelection: true`, the branch's add affordance disables once its own
42
+ * raw item count reaches this value, regardless of the shared choice budget.
43
+ * - Without it (auto mode), the branch's rendered array headroom is capped at this value even
44
+ * when the shared choice budget would otherwise allow more.
45
+ *
46
+ * If set lower than `maxOccurs`, the cap simply limits the effective per-iteration reach.
47
+ * It can only ever tighten the branch's declared `maxOccurs`: a value above it has no effect.
48
+ *
49
+ * Read from static metadata only: it is excluded from `ComputedPropsFieldType`, so a
50
+ * `computedProps` function cannot flip occurrence capacity mid-form.
51
+ */
52
+ maxOccursTotal?: number;
31
53
  /**
32
54
  * Simple restrictions to the data, these are available in XSD and can be applied easily.
33
55
  * For more advance validation use the validation property.
@@ -117,6 +139,86 @@ export type FieldMetadata<ExtendedFieldTypes extends string = string, ExtendedPr
117
139
  *
118
140
  */
119
141
  choice?: FieldMetadata<ExtendedFieldTypes, ExtendedProperties>[];
142
+ /**
143
+ * Opt into selection-first rendering for a `choice` node. When true, no branch is shown until
144
+ * the template calls the `-choice` slot's (`-choice-array` for `maxOccurs > 1`)
145
+ * `addChoiceOccurrence(branchKey)`; the engine clears a
146
+ * deselected branch from the form values on switch. When false or omitted (the default), the
147
+ * current value-driven behaviour is unchanged: every branch renders and siblings disable once
148
+ * one holds a value.
149
+ *
150
+ * Has no effect on non-choice nodes. Read from static metadata only: it is excluded from
151
+ * `ComputedPropsFieldType`, so a `computedProps` function cannot flip it at runtime.
152
+ */
153
+ explicitChoiceSelection?: boolean;
154
+ /**
155
+ * Opt into preserve-on-switch for a `maxOccurs: 1` explicit choice (only meaningful together
156
+ * with `explicitChoiceSelection: true`). When true, switching away from a branch (calling
157
+ * `addChoiceOccurrence` for a different branch) deep-clones that branch's current values into
158
+ * an ephemeral, instance-local stash before clearing it as usual; switching back to that branch
159
+ * restores the stashed values via `setFieldValue`. The stash is never written to the form
160
+ * `values`, exactly like the selection state itself.
161
+ *
162
+ * When false or omitted (the default), switching clears the branch with nothing preserved,
163
+ * matching the baseline clear-on-switch behaviour.
164
+ *
165
+ * Restored fields start with fresh touched/validation state (as if freshly re-entered), and no
166
+ * error renders immediately after restore even if a required field is still empty.
167
+ *
168
+ * Has no effect on non-choice nodes, on `maxOccurs > 1` explicit choices, or when
169
+ * `explicitChoiceSelection` is not set. Read reactively from the metadata, so it may be
170
+ * flipped live through the metadata prop; it is excluded from `ComputedPropsFieldType`, so
171
+ * the metadata prop (not `computedProps`) is the channel for changing it.
172
+ */
173
+ preserveOnSwitch?: boolean;
174
+ /**
175
+ * How a repeatable explicit choice (`explicitChoiceSelection: true`, `maxOccurs > 1`) orders
176
+ * its occurrences on screen:
177
+ *
178
+ * - `'grouped'` (default): grouped by branch, in declaration order.
179
+ * - `'added'`: in the order the user clicked "add", mixing branches. Occurrences from loaded
180
+ * data render first, in grouped order. This order is display-only and resets on reload;
181
+ * combine with `preserveOrder` to keep it.
182
+ *
183
+ * May be changed while the form is mounted; the list re-sorts in place. Ignored everywhere
184
+ * else, and not settable from `computedProps`.
185
+ */
186
+ displayOrder?: 'grouped' | 'added';
187
+ /**
188
+ * Stores the add-order of a repeatable explicit choice (`explicitChoiceSelection: true`,
189
+ * `maxOccurs > 1`) in the form values, so it survives a reload: each occurrence gets a
190
+ * numeric `order` field (1..N across all branches, renumbered on removal, normalized at
191
+ * mount for loaded data). Use together with `displayOrder: 'added'`, which then sorts by
192
+ * this field.
193
+ *
194
+ * Note that `order` becomes part of your submitted values, next to the occurrence's own
195
+ * fields. Don't declare a child named `order` yourself; the engine owns that key.
196
+ *
197
+ * Requires every branch to have `children`, since the `order` field needs an object to live
198
+ * in. If any branch is a bare scalar, the flag is ignored for the whole choice, with a
199
+ * warning in development.
200
+ *
201
+ * May be changed while the form is mounted: turning it on adds `order` to the existing
202
+ * occurrences, seeding it from this session's add-order (so an interleaved display keeps the
203
+ * sequence the user built) and from grouped position for occurrences not added this session;
204
+ * turning it off removes it. Ignored everywhere else, and not settable from `computedProps`.
205
+ */
206
+ preserveOrder?: boolean;
207
+ /**
208
+ * Marks this node as a wizard: a first-class shape rendered through `DynamicFormItemWizard`,
209
+ * checked before `choice`/array/parent detection. Its pages are its `children` in declaration
210
+ * order; a non-empty `choice` or a `maxOccurs > 1` on the same node is ignored (a dev-mode
211
+ * `console.warn` fires once per ignored property). A choice of wizards or a repeated wizard is
212
+ * expressed by nesting `wizard: true` nodes inside a plain `choice` branch or an array item's
213
+ * children instead.
214
+ *
215
+ * `true` selects the defaults (linear: `next` blocks on an invalid page, forward `gotoStep` jumps
216
+ * refused); an object form opts into per-wizard configuration (see `WizardConfig`).
217
+ *
218
+ * Static, render-mode-defining metadata: excluded from `ComputedPropsFieldType` so
219
+ * `computedProps` cannot flip it mid-form.
220
+ */
221
+ wizard?: boolean | WizardConfig;
120
222
  /**
121
223
  * Attributes are additional metadata that can be attached to a field.
122
224
  * These attributes can be used to provide extra information about the field,
@@ -178,7 +280,27 @@ export type FieldMetadata<ExtendedFieldTypes extends string = string, ExtendedPr
178
280
  */
179
281
  computeOnChildValueChange?: boolean;
180
282
  } & ExtendedProperties;
181
- export type ComputedPropsFieldType<ExtendedFieldTypes extends string = string, ExtendedProperties extends object = object> = Omit<FieldMetadata<ExtendedFieldTypes, ExtendedProperties>, 'name' | 'path' | 'parent' | 'children' | 'choice' | 'attributes' | 'fieldOptions' | 'computedProps' | 'maxOccurs' | 'isComplexType' | 'computeOnChildValueChange'> & Readonly<{
283
+ /** Per-wizard navigation gating. `wizard: true` resolves to `{ allowForwardJump: false, validateOnJump: true }`. */
284
+ export interface WizardConfig {
285
+ /**
286
+ * Make the wizard non-linear: forward movement (both `next` and forward `gotoStep`) surfaces page
287
+ * errors but never blocks, so the user can move ahead with an invalid page. Default `false`, a
288
+ * linear wizard where `next` blocks on an invalid page and forward `gotoStep` jumps are refused.
289
+ */
290
+ allowForwardJump?: boolean;
291
+ /**
292
+ * Run validation on non-blocking forward movement so page errors surface. Default `true`; set
293
+ * `false` for a silent jump that moves without validating. No effect on a linear `next` (which
294
+ * always validates to decide whether to block) or on backward navigation (always silent).
295
+ */
296
+ validateOnJump?: boolean;
297
+ }
298
+ /** Call-time overrides for a single `gotoStep` call, taking priority over the wizard's own `WizardConfig`. */
299
+ export interface WizardGotoStepOptions {
300
+ allowForwardJump?: boolean;
301
+ validateOnJump?: boolean;
302
+ }
303
+ export type ComputedPropsFieldType<ExtendedFieldTypes extends string = string, ExtendedProperties extends object = object> = Omit<FieldMetadata<ExtendedFieldTypes, ExtendedProperties>, 'name' | 'path' | 'parent' | 'children' | 'choice' | 'attributes' | 'fieldOptions' | 'computedProps' | 'maxOccurs' | 'maxOccursTotal' | 'isComplexType' | 'computeOnChildValueChange' | 'explicitChoiceSelection' | 'preserveOnSwitch' | 'displayOrder' | 'preserveOrder' | 'wizard'> & Readonly<{
182
304
  name: string;
183
305
  path: string;
184
306
  parent: FieldMetadata<ExtendedFieldTypes, ExtendedProperties>;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,7 +1,7 @@
1
1
  import type { GetMetadataType } from '../../types/GetMetadataType';
2
2
  export type TestMetadata = GetMetadataType<typeof _metadata>;
3
3
  declare const _metadata: {
4
- fieldTypes: readonly ("default" | "text")[];
4
+ fieldTypes: readonly ("text" | "default")[];
5
5
  extendedProperties: {
6
6
  visited?: boolean;
7
7
  visitOrder?: number;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Deep-clones a form value tree (plain objects, arrays, Dates and primitives) into plain,
3
+ * non-reactive data.
4
+ *
5
+ * Exists because `structuredClone` cannot be used on values read from the vee-validate form
6
+ * context: those are Vue reactive Proxies (any branch value that is an object, i.e. a node with
7
+ * `children`), and `structuredClone` throws a `DataCloneError` on a Proxy. Recursively copying
8
+ * by property instead reads *through* the proxy, so the clone comes out raw.
9
+ */
10
+ export declare function deepCloneValue<T>(value: T): T;