@formisch/vue 0.8.0 → 0.10.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/README.md +2 -1
- package/dist/index.d.ts +484 -157
- package/dist/index.js +247 -181
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -57,11 +57,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
57
57
|
else {
|
|
58
58
|
internalFieldStore.schema = schema;
|
|
59
59
|
internalFieldStore.name = JSON.stringify(path);
|
|
60
|
+
internalFieldStore.path = path;
|
|
60
61
|
const initialElements = [];
|
|
61
62
|
internalFieldStore.initialElements = initialElements;
|
|
62
63
|
internalFieldStore.elements = initialElements;
|
|
63
64
|
internalFieldStore.errors = createSignal(null);
|
|
64
65
|
internalFieldStore.isTouched = createSignal(false);
|
|
66
|
+
internalFieldStore.isEdited = createSignal(false);
|
|
65
67
|
internalFieldStore.isDirty = createSignal(false);
|
|
66
68
|
if (schema.type === "array" || schema.type === "loose_tuple" || schema.type === "strict_tuple" || schema.type === "tuple") {
|
|
67
69
|
if (internalFieldStore.kind && internalFieldStore.kind !== "array") throw new Error(`Store initialized as "${internalFieldStore.kind}" cannot be reinitialized as "array"`);
|
|
@@ -71,16 +73,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
71
73
|
if (schema.type === "array") {
|
|
72
74
|
if (initialInput) for (let index = 0; index < initialInput.length; index++) {
|
|
73
75
|
internalFieldStore.children[index] = {};
|
|
74
|
-
|
|
75
|
-
initializeFieldStore(internalFieldStore.children[index], schema.item, initialInput[index], path);
|
|
76
|
-
path.pop();
|
|
76
|
+
initializeFieldStore(internalFieldStore.children[index], schema.item, initialInput[index], [...path, index]);
|
|
77
77
|
}
|
|
78
78
|
} else for (let index = 0; index < schema.items.length; index++) {
|
|
79
79
|
internalFieldStore.children[index] = {};
|
|
80
|
-
|
|
81
|
-
initializeFieldStore(internalFieldStore.children[index], schema.items[index], initialInput?.[index], path);
|
|
82
|
-
path.pop();
|
|
80
|
+
initializeFieldStore(internalFieldStore.children[index], schema.items[index], initialInput?.[index], [...path, index]);
|
|
83
81
|
}
|
|
82
|
+
internalFieldStore.isNullish = nullish;
|
|
84
83
|
const arrayInput = nullish && initialInput == null ? initialInput : true;
|
|
85
84
|
internalFieldStore.initialInput = createSignal(arrayInput);
|
|
86
85
|
internalFieldStore.startInput = createSignal(arrayInput);
|
|
@@ -97,10 +96,9 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
97
96
|
internalFieldStore.children ??= {};
|
|
98
97
|
for (const key in schema.entries) {
|
|
99
98
|
internalFieldStore.children[key] ??= {};
|
|
100
|
-
|
|
101
|
-
initializeFieldStore(internalFieldStore.children[key], schema.entries[key], initialInput?.[key], path);
|
|
102
|
-
path.pop();
|
|
99
|
+
initializeFieldStore(internalFieldStore.children[key], schema.entries[key], initialInput?.[key], [...path, key]);
|
|
103
100
|
}
|
|
101
|
+
internalFieldStore.isNullish = nullish;
|
|
104
102
|
const objectInput = nullish && initialInput == null ? initialInput : true;
|
|
105
103
|
internalFieldStore.initialInput = createSignal(objectInput);
|
|
106
104
|
internalFieldStore.startInput = createSignal(objectInput);
|
|
@@ -120,8 +118,9 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
120
118
|
/**
|
|
121
119
|
* Copies the deeply nested state (signal values) from one field store to
|
|
122
120
|
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
123
|
-
* `isTouched`, `isDirty`, and for arrays `startItems` and `items`
|
|
124
|
-
* Recursively walks through the field stores and copies all signal
|
|
121
|
+
* `isTouched`, `isEdited`, `isDirty`, and for arrays `startItems` and `items`
|
|
122
|
+
* properties. Recursively walks through the field stores and copies all signal
|
|
123
|
+
* values.
|
|
125
124
|
*
|
|
126
125
|
* @param fromInternalFieldStore The source field store to copy from.
|
|
127
126
|
* @param toInternalFieldStore The destination field store to copy to.
|
|
@@ -134,19 +133,16 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
134
133
|
toInternalFieldStore.startInput.value = fromInternalFieldStore.startInput.value;
|
|
135
134
|
toInternalFieldStore.input.value = fromInternalFieldStore.input.value;
|
|
136
135
|
toInternalFieldStore.isTouched.value = fromInternalFieldStore.isTouched.value;
|
|
136
|
+
toInternalFieldStore.isEdited.value = fromInternalFieldStore.isEdited.value;
|
|
137
137
|
toInternalFieldStore.isDirty.value = fromInternalFieldStore.isDirty.value;
|
|
138
138
|
if (fromInternalFieldStore.kind === "array" && toInternalFieldStore.kind === "array") {
|
|
139
139
|
const fromItems = fromInternalFieldStore.items.value;
|
|
140
140
|
toInternalFieldStore.startItems.value = fromInternalFieldStore.startItems.value;
|
|
141
141
|
toInternalFieldStore.items.value = fromItems;
|
|
142
|
-
let path;
|
|
143
142
|
for (let index = 0; index < fromItems.length; index++) {
|
|
144
143
|
if (!toInternalFieldStore.children[index]) {
|
|
145
|
-
path ??= JSON.parse(toInternalFieldStore.name);
|
|
146
144
|
toInternalFieldStore.children[index] = {};
|
|
147
|
-
|
|
148
|
-
initializeFieldStore(toInternalFieldStore.children[index], toInternalFieldStore.schema.item, void 0, path);
|
|
149
|
-
path.pop();
|
|
145
|
+
initializeFieldStore(toInternalFieldStore.children[index], toInternalFieldStore.schema.item, void 0, [...toInternalFieldStore.path, index]);
|
|
150
146
|
}
|
|
151
147
|
copyItemState(fromInternalFieldStore.children[index], toInternalFieldStore.children[index]);
|
|
152
148
|
}
|
|
@@ -156,45 +152,61 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
156
152
|
}
|
|
157
153
|
/**
|
|
158
154
|
* Resets the state of a field store (signal values) deeply nested. Sets
|
|
159
|
-
* `elements` to empty array, `errors` to `null`, `isTouched
|
|
160
|
-
* `false`, and `startInput`, `input`, `startItems`, and `items` to
|
|
161
|
-
* input value. Keeps the `initialInput` and `initialItems` state
|
|
162
|
-
* form reset functionality.
|
|
155
|
+
* `elements` to empty array, `errors` to `null`, `isTouched`, `isEdited` and
|
|
156
|
+
* `isDirty` to `false`, and `startInput`, `input`, `startItems`, and `items` to
|
|
157
|
+
* the new input value. Keeps the `initialInput` and `initialItems` state
|
|
158
|
+
* unchanged for form reset functionality.
|
|
163
159
|
*
|
|
164
160
|
* @param internalFieldStore The field store to reset.
|
|
165
|
-
* @param
|
|
161
|
+
* @param input The new input value (can be any type including array or object).
|
|
162
|
+
* @param keepStart Whether to keep `startInput` and `startItems` as the dirty
|
|
163
|
+
* baseline instead of resetting them to the new input. Used when a field store
|
|
164
|
+
* is reused for an in-place edit so its dirty state is detected correctly.
|
|
166
165
|
*/
|
|
167
|
-
function resetItemState(internalFieldStore,
|
|
166
|
+
function resetItemState(internalFieldStore, input, keepStart = false) {
|
|
168
167
|
batch(() => {
|
|
169
|
-
|
|
168
|
+
const elements = [];
|
|
169
|
+
if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
|
|
170
|
+
internalFieldStore.elements = elements;
|
|
170
171
|
internalFieldStore.errors.value = null;
|
|
171
172
|
internalFieldStore.isTouched.value = false;
|
|
173
|
+
internalFieldStore.isEdited.value = false;
|
|
172
174
|
internalFieldStore.isDirty.value = false;
|
|
173
175
|
if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
|
|
174
|
-
const objectInput =
|
|
175
|
-
internalFieldStore.startInput.value = objectInput;
|
|
176
|
+
const objectInput = internalFieldStore.isNullish && input == null ? input : true;
|
|
177
|
+
if (!keepStart) internalFieldStore.startInput.value = objectInput;
|
|
176
178
|
internalFieldStore.input.value = objectInput;
|
|
177
|
-
if (internalFieldStore.kind === "array")
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
179
|
+
if (internalFieldStore.kind === "array") {
|
|
180
|
+
const isTuple = internalFieldStore.schema.type !== "array";
|
|
181
|
+
if (input || isTuple) {
|
|
182
|
+
const length = isTuple ? internalFieldStore.children.length : input.length;
|
|
183
|
+
const newItems = Array.from({ length }, createId);
|
|
184
|
+
if (!keepStart) internalFieldStore.startItems.value = newItems;
|
|
185
|
+
internalFieldStore.items.value = newItems;
|
|
186
|
+
for (let index = 0; index < length; index++) {
|
|
187
|
+
const itemInput = input?.[index];
|
|
188
|
+
if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], itemInput, keepStart);
|
|
189
|
+
else {
|
|
190
|
+
internalFieldStore.children[index] = {};
|
|
191
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, itemInput, [...internalFieldStore.path, index]);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
} else {
|
|
195
|
+
if (!keepStart) internalFieldStore.startItems.value = [];
|
|
196
|
+
internalFieldStore.items.value = [];
|
|
197
|
+
}
|
|
198
|
+
} else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
|
|
187
199
|
} else {
|
|
188
|
-
internalFieldStore.startInput.value =
|
|
189
|
-
internalFieldStore.input.value =
|
|
200
|
+
if (!keepStart) internalFieldStore.startInput.value = input;
|
|
201
|
+
internalFieldStore.input.value = input;
|
|
190
202
|
}
|
|
191
203
|
});
|
|
192
204
|
}
|
|
193
205
|
/**
|
|
194
206
|
* Swaps the deeply nested state (signal values) between two field stores. This
|
|
195
207
|
* includes the `elements`, `errors`, `startInput`, `input`, `isTouched`,
|
|
196
|
-
* `isDirty`, and for arrays `startItems` and `items` properties.
|
|
197
|
-
* walks through the field stores and swaps all signal values.
|
|
208
|
+
* `isEdited`, `isDirty`, and for arrays `startItems` and `items` properties.
|
|
209
|
+
* Recursively walks through the field stores and swaps all signal values.
|
|
198
210
|
*
|
|
199
211
|
* @param firstInternalFieldStore The first field store to swap.
|
|
200
212
|
* @param secondInternalFieldStore The second field store to swap.
|
|
@@ -217,6 +229,9 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
217
229
|
const tempIsTouched = firstInternalFieldStore.isTouched.value;
|
|
218
230
|
firstInternalFieldStore.isTouched.value = secondInternalFieldStore.isTouched.value;
|
|
219
231
|
secondInternalFieldStore.isTouched.value = tempIsTouched;
|
|
232
|
+
const tempIsEdited = firstInternalFieldStore.isEdited.value;
|
|
233
|
+
firstInternalFieldStore.isEdited.value = secondInternalFieldStore.isEdited.value;
|
|
234
|
+
secondInternalFieldStore.isEdited.value = tempIsEdited;
|
|
220
235
|
const tempIsDirty = firstInternalFieldStore.isDirty.value;
|
|
221
236
|
firstInternalFieldStore.isDirty.value = secondInternalFieldStore.isDirty.value;
|
|
222
237
|
secondInternalFieldStore.isDirty.value = tempIsDirty;
|
|
@@ -229,22 +244,14 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
229
244
|
firstInternalFieldStore.items.value = secondItems;
|
|
230
245
|
secondInternalFieldStore.items.value = firstItems;
|
|
231
246
|
const maxLength = Math.max(firstItems.length, secondItems.length);
|
|
232
|
-
let firstPath;
|
|
233
|
-
let secondPath;
|
|
234
247
|
for (let index = 0; index < maxLength; index++) {
|
|
235
248
|
if (!firstInternalFieldStore.children[index]) {
|
|
236
|
-
firstPath ??= JSON.parse(firstInternalFieldStore.name);
|
|
237
249
|
firstInternalFieldStore.children[index] = {};
|
|
238
|
-
|
|
239
|
-
initializeFieldStore(firstInternalFieldStore.children[index], firstInternalFieldStore.schema.item, void 0, firstPath);
|
|
240
|
-
firstPath.pop();
|
|
250
|
+
initializeFieldStore(firstInternalFieldStore.children[index], firstInternalFieldStore.schema.item, void 0, [...firstInternalFieldStore.path, index]);
|
|
241
251
|
}
|
|
242
252
|
if (!secondInternalFieldStore.children[index]) {
|
|
243
|
-
secondPath ??= JSON.parse(secondInternalFieldStore.name);
|
|
244
253
|
secondInternalFieldStore.children[index] = {};
|
|
245
|
-
|
|
246
|
-
initializeFieldStore(secondInternalFieldStore.children[index], secondInternalFieldStore.schema.item, void 0, secondPath);
|
|
247
|
-
secondPath.pop();
|
|
254
|
+
initializeFieldStore(secondInternalFieldStore.children[index], secondInternalFieldStore.schema.item, void 0, [...secondInternalFieldStore.path, index]);
|
|
248
255
|
}
|
|
249
256
|
swapItemState(firstInternalFieldStore.children[index], secondInternalFieldStore.children[index]);
|
|
250
257
|
}
|
|
@@ -253,6 +260,51 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
253
260
|
});
|
|
254
261
|
}
|
|
255
262
|
/**
|
|
263
|
+
* Focuses the first focusable element of a field store. The elements are tried
|
|
264
|
+
* in order and the first one that actually receives focus wins, so detached,
|
|
265
|
+
* disabled or hidden elements are skipped. The browser decides focusability,
|
|
266
|
+
* which is read back via the element's root `activeElement` so elements in a
|
|
267
|
+
* shadow root or another document are handled correctly.
|
|
268
|
+
*
|
|
269
|
+
* Hint: A `display: none` or `hidden` element is correctly skipped in real
|
|
270
|
+
* browsers, but jsdom has no layout and focuses it anyway, so that case cannot
|
|
271
|
+
* be covered by unit tests.
|
|
272
|
+
*
|
|
273
|
+
* @param internalFieldStore The field store to focus.
|
|
274
|
+
*
|
|
275
|
+
* @returns Whether an element was focused.
|
|
276
|
+
*/
|
|
277
|
+
function focusFieldElement(internalFieldStore) {
|
|
278
|
+
for (const element of internalFieldStore.elements) {
|
|
279
|
+
element.focus();
|
|
280
|
+
if (element.getRootNode().activeElement === element) return true;
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Walks through the field store and all nested children, calling the callback
|
|
286
|
+
* for each field store in depth-first order. The callback may return `true` to
|
|
287
|
+
* stop the walk early, in which case `walkFieldStore` returns `true` as well.
|
|
288
|
+
*
|
|
289
|
+
* The walk reads array `items` reactively, so a reactive caller subscribes to
|
|
290
|
+
* structural changes naturally. Imperative callers that must not subscribe
|
|
291
|
+
* (e.g. when invoked inside an effect) should wrap the call in `untrack`.
|
|
292
|
+
*
|
|
293
|
+
* @param internalFieldStore The field store to walk.
|
|
294
|
+
* @param callback The callback to invoke for each field store. Return `true` to stop the walk early.
|
|
295
|
+
*
|
|
296
|
+
* @returns Whether the walk was stopped early by the callback.
|
|
297
|
+
*/
|
|
298
|
+
function walkFieldStore(internalFieldStore, callback) {
|
|
299
|
+
if (callback(internalFieldStore)) return true;
|
|
300
|
+
if (internalFieldStore.kind === "array") {
|
|
301
|
+
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (walkFieldStore(internalFieldStore.children[index], callback)) return true;
|
|
302
|
+
} else if (internalFieldStore.kind === "object") {
|
|
303
|
+
for (const key in internalFieldStore.children) if (walkFieldStore(internalFieldStore.children[key], callback)) return true;
|
|
304
|
+
}
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
256
308
|
* Returns whether the specified boolean property is true for the field store
|
|
257
309
|
* or any of its nested children. Recursively checks arrays and objects.
|
|
258
310
|
*
|
|
@@ -263,16 +315,7 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
263
315
|
*/
|
|
264
316
|
/* @__NO_SIDE_EFFECTS__ */
|
|
265
317
|
function getFieldBool(internalFieldStore, type) {
|
|
266
|
-
|
|
267
|
-
if (internalFieldStore.kind === "array") {
|
|
268
|
-
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
|
|
269
|
-
return false;
|
|
270
|
-
}
|
|
271
|
-
if (internalFieldStore.kind == "object") {
|
|
272
|
-
for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
|
|
273
|
-
return false;
|
|
274
|
-
}
|
|
275
|
-
return false;
|
|
318
|
+
return walkFieldStore(internalFieldStore, (internalFieldStore$1) => Boolean(internalFieldStore$1[type].value));
|
|
276
319
|
}
|
|
277
320
|
/**
|
|
278
321
|
* Returns only the dirty input of the field store. Arrays are treated as
|
|
@@ -363,11 +406,11 @@ function getFieldStore(internalFormStore, path) {
|
|
|
363
406
|
*/
|
|
364
407
|
function setFieldBool(internalFieldStore, type, bool) {
|
|
365
408
|
batch(() => {
|
|
366
|
-
|
|
367
|
-
internalFieldStore
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
409
|
+
untrack(() => {
|
|
410
|
+
walkFieldStore(internalFieldStore, (internalFieldStore$1) => {
|
|
411
|
+
internalFieldStore$1[type].value = bool;
|
|
412
|
+
});
|
|
413
|
+
});
|
|
371
414
|
});
|
|
372
415
|
}
|
|
373
416
|
/**
|
|
@@ -379,23 +422,21 @@ function setFieldBool(internalFieldStore, type, bool) {
|
|
|
379
422
|
*/
|
|
380
423
|
function setNestedInput(internalFieldStore, input) {
|
|
381
424
|
internalFieldStore.isTouched.value = true;
|
|
425
|
+
internalFieldStore.isEdited.value = true;
|
|
382
426
|
if (internalFieldStore.kind === "array") {
|
|
383
427
|
const arrayInput = input ?? [];
|
|
384
428
|
const items = internalFieldStore.items.value;
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
|
|
393
|
-
path.pop();
|
|
394
|
-
}
|
|
429
|
+
const length = internalFieldStore.schema.type === "array" ? arrayInput.length : internalFieldStore.children.length;
|
|
430
|
+
if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
|
|
431
|
+
else if (length > items.length) {
|
|
432
|
+
for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], arrayInput[index], true);
|
|
433
|
+
else {
|
|
434
|
+
internalFieldStore.children[index] = {};
|
|
435
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], [...internalFieldStore.path, index]);
|
|
395
436
|
}
|
|
396
|
-
internalFieldStore.items.value = [...items, ...
|
|
437
|
+
internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, createId)];
|
|
397
438
|
}
|
|
398
|
-
for (let index = 0; index <
|
|
439
|
+
for (let index = 0; index < length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
|
|
399
440
|
internalFieldStore.input.value = input == null ? input : true;
|
|
400
441
|
internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== internalFieldStore.items.value.length;
|
|
401
442
|
} else if (internalFieldStore.kind === "object") {
|
|
@@ -439,38 +480,22 @@ function setFieldInput(internalFormStore, path, input) {
|
|
|
439
480
|
function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
440
481
|
batch(() => {
|
|
441
482
|
if (internalFieldStore.kind === "array") {
|
|
442
|
-
internalFieldStore.
|
|
483
|
+
internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
|
|
443
484
|
const initialArrayInput = initialInput ?? [];
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
path.push(index);
|
|
449
|
-
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], path);
|
|
450
|
-
path.pop();
|
|
451
|
-
}
|
|
485
|
+
const length = internalFieldStore.schema.type === "array" ? initialArrayInput.length : internalFieldStore.children.length;
|
|
486
|
+
if (length > internalFieldStore.children.length) for (let index = internalFieldStore.children.length; index < length; index++) {
|
|
487
|
+
internalFieldStore.children[index] = {};
|
|
488
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], [...internalFieldStore.path, index]);
|
|
452
489
|
}
|
|
453
|
-
internalFieldStore.initialItems.value =
|
|
490
|
+
internalFieldStore.initialItems.value = Array.from({ length }, createId);
|
|
454
491
|
for (let index = 0; index < internalFieldStore.children.length; index++) setInitialFieldInput(internalFieldStore.children[index], initialArrayInput[index]);
|
|
455
492
|
} else if (internalFieldStore.kind === "object") {
|
|
456
|
-
internalFieldStore.
|
|
493
|
+
internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
|
|
457
494
|
for (const key in internalFieldStore.children) setInitialFieldInput(internalFieldStore.children[key], initialInput?.[key]);
|
|
458
495
|
} else internalFieldStore.initialInput.value = initialInput;
|
|
459
496
|
});
|
|
460
497
|
}
|
|
461
498
|
/**
|
|
462
|
-
* Walks through the field store and all nested children, calling the callback
|
|
463
|
-
* for each field store in depth-first order.
|
|
464
|
-
*
|
|
465
|
-
* @param internalFieldStore The field store to walk.
|
|
466
|
-
* @param callback The callback to invoke for each field store.
|
|
467
|
-
*/
|
|
468
|
-
function walkFieldStore(internalFieldStore, callback) {
|
|
469
|
-
callback(internalFieldStore);
|
|
470
|
-
if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) walkFieldStore(internalFieldStore.children[index], callback);
|
|
471
|
-
else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) walkFieldStore(internalFieldStore.children[key], callback);
|
|
472
|
-
}
|
|
473
|
-
/**
|
|
474
499
|
* Creates a new internal form store from the provided configuration.
|
|
475
500
|
* Initializes the field store hierarchy, sets validation modes, and
|
|
476
501
|
* creates form state signals.
|
|
@@ -505,44 +530,51 @@ function createFormStore(config, parse) {
|
|
|
505
530
|
async function validateFormInput(internalFormStore, config) {
|
|
506
531
|
internalFormStore.validators++;
|
|
507
532
|
internalFormStore.isValidating.value = true;
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
const path
|
|
515
|
-
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const name = JSON.stringify(path);
|
|
523
|
-
const fieldErrors = nestedErrors[name];
|
|
524
|
-
if (fieldErrors) fieldErrors.push(issue.message);
|
|
525
|
-
else nestedErrors[name] = [issue.message];
|
|
526
|
-
} else if (rootErrors) rootErrors.push(issue.message);
|
|
527
|
-
else rootErrors = [issue.message];
|
|
528
|
-
}
|
|
529
|
-
let shouldFocus = config?.shouldFocus ?? false;
|
|
530
|
-
batch(() => {
|
|
531
|
-
walkFieldStore(internalFormStore, (internalFieldStore) => {
|
|
532
|
-
if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
|
|
533
|
-
else {
|
|
534
|
-
const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
|
|
535
|
-
internalFieldStore.errors.value = fieldErrors;
|
|
536
|
-
if (shouldFocus && fieldErrors) {
|
|
537
|
-
internalFieldStore.elements[0]?.focus();
|
|
538
|
-
shouldFocus = false;
|
|
533
|
+
try {
|
|
534
|
+
const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
|
|
535
|
+
let rootErrors;
|
|
536
|
+
let nestedErrors;
|
|
537
|
+
if (result.issues) {
|
|
538
|
+
nestedErrors = {};
|
|
539
|
+
for (const issue of result.issues) if (issue.path) {
|
|
540
|
+
const path = [];
|
|
541
|
+
for (const pathItem of issue.path) {
|
|
542
|
+
const key = pathItem.key;
|
|
543
|
+
const keyType = typeof key;
|
|
544
|
+
const itemType = pathItem.type;
|
|
545
|
+
if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
|
|
546
|
+
path.push(key);
|
|
539
547
|
}
|
|
540
|
-
|
|
548
|
+
const name = JSON.stringify(path);
|
|
549
|
+
const fieldErrors = nestedErrors[name];
|
|
550
|
+
if (fieldErrors) fieldErrors.push(issue.message);
|
|
551
|
+
else nestedErrors[name] = [issue.message];
|
|
552
|
+
} else if (rootErrors) rootErrors.push(issue.message);
|
|
553
|
+
else rootErrors = [issue.message];
|
|
554
|
+
}
|
|
555
|
+
let shouldFocus = config?.shouldFocus ?? false;
|
|
556
|
+
batch(() => {
|
|
557
|
+
untrack(() => {
|
|
558
|
+
walkFieldStore(internalFormStore, (internalFieldStore) => {
|
|
559
|
+
if (internalFieldStore.path.length === 0) internalFieldStore.errors.value = rootErrors ?? null;
|
|
560
|
+
else {
|
|
561
|
+
const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
|
|
562
|
+
internalFieldStore.errors.value = fieldErrors;
|
|
563
|
+
if (shouldFocus && fieldErrors && focusFieldElement(internalFieldStore)) shouldFocus = false;
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
});
|
|
567
|
+
internalFormStore.validators--;
|
|
568
|
+
internalFormStore.isValidating.value = internalFormStore.validators > 0;
|
|
541
569
|
});
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
570
|
+
return result;
|
|
571
|
+
} catch (error) {
|
|
572
|
+
batch(() => {
|
|
573
|
+
internalFormStore.validators--;
|
|
574
|
+
internalFormStore.isValidating.value = internalFormStore.validators > 0;
|
|
575
|
+
});
|
|
576
|
+
throw error;
|
|
577
|
+
}
|
|
546
578
|
}
|
|
547
579
|
/**
|
|
548
580
|
* Validates the form input if required based on the validation mode and form
|
|
@@ -564,7 +596,7 @@ const INTERNAL = "~internal";
|
|
|
564
596
|
//#endregion
|
|
565
597
|
//#region ../../packages/methods/dist/index.vue.js
|
|
566
598
|
/**
|
|
567
|
-
* Focuses the first input element of a field. This is useful for
|
|
599
|
+
* Focuses the first focusable input element of a field. This is useful for
|
|
568
600
|
* programmatically setting focus to a specific field, such as after
|
|
569
601
|
* validation errors or user interactions.
|
|
570
602
|
*
|
|
@@ -572,27 +604,29 @@ const INTERNAL = "~internal";
|
|
|
572
604
|
* @param config The focus field configuration.
|
|
573
605
|
*/
|
|
574
606
|
function focus(form, config) {
|
|
575
|
-
getFieldStore(form[INTERNAL], config.path)
|
|
607
|
+
focusFieldElement(getFieldStore(form[INTERNAL], config.path));
|
|
608
|
+
}
|
|
609
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
610
|
+
function getDeepErrorEntries(form, config) {
|
|
611
|
+
const entries = [];
|
|
612
|
+
walkFieldStore(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], (internalFieldStore) => {
|
|
613
|
+
const errors = internalFieldStore.errors.value;
|
|
614
|
+
if (errors) entries.push({
|
|
615
|
+
path: internalFieldStore.path,
|
|
616
|
+
errors
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
return entries;
|
|
576
620
|
}
|
|
577
|
-
/**
|
|
578
|
-
* Retrieves all error messages from all fields in the form by walking through
|
|
579
|
-
* the entire field store tree. This is useful for displaying a summary of all
|
|
580
|
-
* validation errors across the form.
|
|
581
|
-
*
|
|
582
|
-
* @param form The form store to retrieve errors from.
|
|
583
|
-
*
|
|
584
|
-
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
585
|
-
*/
|
|
586
621
|
/* @__NO_SIDE_EFFECTS__ */
|
|
587
|
-
function
|
|
588
|
-
let
|
|
589
|
-
walkFieldStore(form[INTERNAL], (internalFieldStore) => {
|
|
590
|
-
if (internalFieldStore.kind === "array") internalFieldStore.items.value;
|
|
622
|
+
function getDeepErrors(form, config) {
|
|
623
|
+
let deepErrors = null;
|
|
624
|
+
walkFieldStore(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], (internalFieldStore) => {
|
|
591
625
|
const errors = internalFieldStore.errors.value;
|
|
592
|
-
if (errors) if (
|
|
593
|
-
else
|
|
626
|
+
if (errors) if (deepErrors) deepErrors.push(...errors);
|
|
627
|
+
else deepErrors = [...errors];
|
|
594
628
|
});
|
|
595
|
-
return
|
|
629
|
+
return deepErrors;
|
|
596
630
|
}
|
|
597
631
|
/* @__NO_SIDE_EFFECTS__ */
|
|
598
632
|
function getDirtyInput(form, config) {
|
|
@@ -600,9 +634,8 @@ function getDirtyInput(form, config) {
|
|
|
600
634
|
}
|
|
601
635
|
/* @__NO_SIDE_EFFECTS__ */
|
|
602
636
|
function getDirtyPaths(form, config) {
|
|
603
|
-
config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
|
|
604
637
|
const paths = [];
|
|
605
|
-
config?.path
|
|
638
|
+
config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
|
|
606
639
|
return paths;
|
|
607
640
|
}
|
|
608
641
|
/* @__NO_SIDE_EFFECTS__ */
|
|
@@ -653,25 +686,38 @@ function insert(form, config) {
|
|
|
653
686
|
internalArrayStore.items.value = newItems;
|
|
654
687
|
for (let index = items.length; index > insertIndex; index--) {
|
|
655
688
|
if (!internalArrayStore.children[index]) {
|
|
656
|
-
const path = JSON.parse(internalArrayStore.name);
|
|
657
689
|
internalArrayStore.children[index] = {};
|
|
658
|
-
|
|
659
|
-
initializeFieldStore(internalArrayStore.children[index], internalArrayStore.schema.item, void 0, path);
|
|
690
|
+
initializeFieldStore(internalArrayStore.children[index], internalArrayStore.schema.item, void 0, [...internalArrayStore.path, index]);
|
|
660
691
|
}
|
|
661
692
|
copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
662
693
|
}
|
|
663
694
|
if (!internalArrayStore.children[insertIndex]) {
|
|
664
|
-
const path = JSON.parse(internalArrayStore.name);
|
|
665
695
|
internalArrayStore.children[insertIndex] = {};
|
|
666
|
-
|
|
667
|
-
initializeFieldStore(internalArrayStore.children[insertIndex], internalArrayStore.schema.item, config.initialInput, path);
|
|
696
|
+
initializeFieldStore(internalArrayStore.children[insertIndex], internalArrayStore.schema.item, config.initialInput, [...internalArrayStore.path, insertIndex]);
|
|
668
697
|
} else resetItemState(internalArrayStore.children[insertIndex], config.initialInput);
|
|
669
698
|
internalArrayStore.input.value = true;
|
|
670
699
|
internalArrayStore.isTouched.value = true;
|
|
700
|
+
internalArrayStore.isEdited.value = true;
|
|
671
701
|
internalArrayStore.isDirty.value = true;
|
|
672
702
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
673
703
|
});
|
|
674
704
|
}
|
|
705
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
706
|
+
function isDirty(form, config) {
|
|
707
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isDirty");
|
|
708
|
+
}
|
|
709
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
710
|
+
function isEdited(form, config) {
|
|
711
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isEdited");
|
|
712
|
+
}
|
|
713
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
714
|
+
function isTouched(form, config) {
|
|
715
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isTouched");
|
|
716
|
+
}
|
|
717
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
718
|
+
function isValid(form, config) {
|
|
719
|
+
return !getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "errors");
|
|
720
|
+
}
|
|
675
721
|
/**
|
|
676
722
|
* Moves an item from one index to another within a field array. All items
|
|
677
723
|
* between the source and destination indices are shifted accordingly.
|
|
@@ -694,6 +740,7 @@ function move(form, config) {
|
|
|
694
740
|
else for (let index = config.from; index > config.to; index--) copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
695
741
|
copyItemState(tempInternalFieldStore, internalArrayStore.children[config.to]);
|
|
696
742
|
internalArrayStore.isTouched.value = true;
|
|
743
|
+
internalArrayStore.isEdited.value = true;
|
|
697
744
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
698
745
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
699
746
|
});
|
|
@@ -757,6 +804,7 @@ function remove(form, config) {
|
|
|
757
804
|
internalArrayStore.items.value = newItems;
|
|
758
805
|
for (let index = config.at; index < items.length - 1; index++) copyItemState(internalArrayStore.children[index + 1], internalArrayStore.children[index]);
|
|
759
806
|
internalArrayStore.isTouched.value = true;
|
|
807
|
+
internalArrayStore.isEdited.value = true;
|
|
760
808
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
761
809
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
762
810
|
});
|
|
@@ -777,6 +825,7 @@ function replace(form, config) {
|
|
|
777
825
|
internalArrayStore.items.value = newItems;
|
|
778
826
|
resetItemState(internalArrayStore.children[config.at], config.initialInput);
|
|
779
827
|
internalArrayStore.isTouched.value = true;
|
|
828
|
+
internalArrayStore.isEdited.value = true;
|
|
780
829
|
internalArrayStore.isDirty.value = true;
|
|
781
830
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
782
831
|
});
|
|
@@ -791,6 +840,7 @@ function reset(form, config) {
|
|
|
791
840
|
internalFieldStore$1.elements = internalFieldStore$1.initialElements;
|
|
792
841
|
if (!config?.keepErrors) internalFieldStore$1.errors.value = null;
|
|
793
842
|
if (!config?.keepTouched) internalFieldStore$1.isTouched.value = false;
|
|
843
|
+
if (!config?.keepEdited) internalFieldStore$1.isEdited.value = false;
|
|
794
844
|
internalFieldStore$1.startInput.value = internalFieldStore$1.initialInput.value;
|
|
795
845
|
if (!config?.keepInput) internalFieldStore$1.input.value = internalFieldStore$1.initialInput.value;
|
|
796
846
|
if (internalFieldStore$1.kind === "array") {
|
|
@@ -849,6 +899,7 @@ function swap(form, config) {
|
|
|
849
899
|
internalArrayStore.items.value = newItems;
|
|
850
900
|
swapItemState(internalArrayStore.children[config.at], internalArrayStore.children[config.and]);
|
|
851
901
|
internalArrayStore.isTouched.value = true;
|
|
902
|
+
internalArrayStore.isEdited.value = true;
|
|
852
903
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
853
904
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
854
905
|
});
|
|
@@ -875,12 +926,16 @@ function useField(form, config) {
|
|
|
875
926
|
const internalFormStore = computed(() => toValue(form)[INTERNAL]);
|
|
876
927
|
const internalFieldStore = computed(() => getFieldStore(internalFormStore.value, path.value));
|
|
877
928
|
onUnmounted(() => {
|
|
878
|
-
|
|
929
|
+
const internalFieldStoreValue = internalFieldStore.value;
|
|
930
|
+
const elements = internalFieldStoreValue.elements.filter((element) => element.isConnected);
|
|
931
|
+
if (internalFieldStoreValue.elements === internalFieldStoreValue.initialElements) internalFieldStoreValue.initialElements = elements;
|
|
932
|
+
internalFieldStoreValue.elements = elements;
|
|
879
933
|
});
|
|
880
934
|
const input = computed(() => getFieldInput(internalFieldStore.value));
|
|
881
|
-
const isTouched = computed(() => getFieldBool(internalFieldStore.value, "isTouched"));
|
|
882
|
-
const
|
|
883
|
-
const
|
|
935
|
+
const isTouched$1 = computed(() => getFieldBool(internalFieldStore.value, "isTouched"));
|
|
936
|
+
const isEdited$1 = computed(() => getFieldBool(internalFieldStore.value, "isEdited"));
|
|
937
|
+
const isDirty$1 = computed(() => getFieldBool(internalFieldStore.value, "isDirty"));
|
|
938
|
+
const isValid$1 = computed(() => !getFieldBool(internalFieldStore.value, "errors"));
|
|
884
939
|
return {
|
|
885
940
|
get path() {
|
|
886
941
|
return path.value;
|
|
@@ -896,13 +951,16 @@ function useField(form, config) {
|
|
|
896
951
|
return internalFieldStore.value.errors.value;
|
|
897
952
|
},
|
|
898
953
|
get isTouched() {
|
|
899
|
-
return isTouched.value;
|
|
954
|
+
return isTouched$1.value;
|
|
955
|
+
},
|
|
956
|
+
get isEdited() {
|
|
957
|
+
return isEdited$1.value;
|
|
900
958
|
},
|
|
901
959
|
get isDirty() {
|
|
902
|
-
return isDirty.value;
|
|
960
|
+
return isDirty$1.value;
|
|
903
961
|
},
|
|
904
962
|
get isValid() {
|
|
905
|
-
return isValid.value;
|
|
963
|
+
return isValid$1.value;
|
|
906
964
|
},
|
|
907
965
|
props: {
|
|
908
966
|
get name() {
|
|
@@ -931,9 +989,10 @@ function useField(form, config) {
|
|
|
931
989
|
/* @__NO_SIDE_EFFECTS__ */
|
|
932
990
|
function useFieldArray(form, config) {
|
|
933
991
|
const internalFieldStore = computed(() => getFieldStore(toValue(form)[INTERNAL], toValue(config).path));
|
|
934
|
-
const isTouched = computed(() => getFieldBool(internalFieldStore.value, "isTouched"));
|
|
935
|
-
const
|
|
936
|
-
const
|
|
992
|
+
const isTouched$1 = computed(() => getFieldBool(internalFieldStore.value, "isTouched"));
|
|
993
|
+
const isEdited$1 = computed(() => getFieldBool(internalFieldStore.value, "isEdited"));
|
|
994
|
+
const isDirty$1 = computed(() => getFieldBool(internalFieldStore.value, "isDirty"));
|
|
995
|
+
const isValid$1 = computed(() => !getFieldBool(internalFieldStore.value, "errors"));
|
|
937
996
|
return {
|
|
938
997
|
get path() {
|
|
939
998
|
return toValue(config).path;
|
|
@@ -945,13 +1004,16 @@ function useFieldArray(form, config) {
|
|
|
945
1004
|
return internalFieldStore.value.errors.value;
|
|
946
1005
|
},
|
|
947
1006
|
get isTouched() {
|
|
948
|
-
return isTouched.value;
|
|
1007
|
+
return isTouched$1.value;
|
|
1008
|
+
},
|
|
1009
|
+
get isEdited() {
|
|
1010
|
+
return isEdited$1.value;
|
|
949
1011
|
},
|
|
950
1012
|
get isDirty() {
|
|
951
|
-
return isDirty.value;
|
|
1013
|
+
return isDirty$1.value;
|
|
952
1014
|
},
|
|
953
1015
|
get isValid() {
|
|
954
|
-
return isValid.value;
|
|
1016
|
+
return isValid$1.value;
|
|
955
1017
|
}
|
|
956
1018
|
};
|
|
957
1019
|
}
|
|
@@ -964,9 +1026,10 @@ function useForm(config) {
|
|
|
964
1026
|
onBeforeMount(async () => {
|
|
965
1027
|
if (config.validate === "initial") await validateFormInput(internalFormStore);
|
|
966
1028
|
});
|
|
967
|
-
const isTouched = computed(() => getFieldBool(internalFormStore, "isTouched"));
|
|
968
|
-
const
|
|
969
|
-
const
|
|
1029
|
+
const isTouched$1 = computed(() => getFieldBool(internalFormStore, "isTouched"));
|
|
1030
|
+
const isEdited$1 = computed(() => getFieldBool(internalFormStore, "isEdited"));
|
|
1031
|
+
const isDirty$1 = computed(() => getFieldBool(internalFormStore, "isDirty"));
|
|
1032
|
+
const isValid$1 = computed(() => !getFieldBool(internalFormStore, "errors"));
|
|
970
1033
|
return {
|
|
971
1034
|
[INTERNAL]: internalFormStore,
|
|
972
1035
|
get isSubmitting() {
|
|
@@ -979,13 +1042,16 @@ function useForm(config) {
|
|
|
979
1042
|
return internalFormStore.isValidating.value;
|
|
980
1043
|
},
|
|
981
1044
|
get isTouched() {
|
|
982
|
-
return isTouched.value;
|
|
1045
|
+
return isTouched$1.value;
|
|
1046
|
+
},
|
|
1047
|
+
get isEdited() {
|
|
1048
|
+
return isEdited$1.value;
|
|
983
1049
|
},
|
|
984
1050
|
get isDirty() {
|
|
985
|
-
return isDirty.value;
|
|
1051
|
+
return isDirty$1.value;
|
|
986
1052
|
},
|
|
987
1053
|
get isValid() {
|
|
988
|
-
return isValid.value;
|
|
1054
|
+
return isValid$1.value;
|
|
989
1055
|
},
|
|
990
1056
|
get errors() {
|
|
991
1057
|
return internalFormStore.errors.value;
|
|
@@ -1065,4 +1131,4 @@ var Form_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineCom
|
|
|
1065
1131
|
var Form_default = Form_vue_vue_type_script_setup_true_lang_default;
|
|
1066
1132
|
|
|
1067
1133
|
//#endregion
|
|
1068
|
-
export { Field_default as Field, FieldArray_default as FieldArray, Form_default as Form, focus,
|
|
1134
|
+
export { Field_default as Field, FieldArray_default as FieldArray, Form_default as Form, focus, getDeepErrorEntries, getDeepErrors, getDirtyInput, getDirtyPaths, getErrors, getInput, handleSubmit, insert, isDirty, isEdited, isTouched, isValid, move, pickDirty, remove, replace, reset, setErrors, setInput, submit, swap, useField, useFieldArray, useForm, validate };
|