@formisch/react 0.5.0 → 0.7.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 +483 -156
- package/dist/index.js +225 -163
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -123,11 +123,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
123
123
|
else {
|
|
124
124
|
internalFieldStore.schema = schema;
|
|
125
125
|
internalFieldStore.name = JSON.stringify(path);
|
|
126
|
+
internalFieldStore.path = path;
|
|
126
127
|
const initialElements = [];
|
|
127
128
|
internalFieldStore.initialElements = initialElements;
|
|
128
129
|
internalFieldStore.elements = initialElements;
|
|
129
130
|
internalFieldStore.errors = /* @__PURE__ */ createSignal(null);
|
|
130
131
|
internalFieldStore.isTouched = /* @__PURE__ */ createSignal(false);
|
|
132
|
+
internalFieldStore.isEdited = /* @__PURE__ */ createSignal(false);
|
|
131
133
|
internalFieldStore.isDirty = /* @__PURE__ */ createSignal(false);
|
|
132
134
|
if (schema.type === "array" || schema.type === "loose_tuple" || schema.type === "strict_tuple" || schema.type === "tuple") {
|
|
133
135
|
if (internalFieldStore.kind && internalFieldStore.kind !== "array") throw new Error(`Store initialized as "${internalFieldStore.kind}" cannot be reinitialized as "array"`);
|
|
@@ -137,16 +139,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
137
139
|
if (schema.type === "array") {
|
|
138
140
|
if (initialInput) for (let index = 0; index < initialInput.length; index++) {
|
|
139
141
|
internalFieldStore.children[index] = {};
|
|
140
|
-
|
|
141
|
-
initializeFieldStore(internalFieldStore.children[index], schema.item, initialInput[index], path);
|
|
142
|
-
path.pop();
|
|
142
|
+
initializeFieldStore(internalFieldStore.children[index], schema.item, initialInput[index], [...path, index]);
|
|
143
143
|
}
|
|
144
144
|
} else for (let index = 0; index < schema.items.length; index++) {
|
|
145
145
|
internalFieldStore.children[index] = {};
|
|
146
|
-
|
|
147
|
-
initializeFieldStore(internalFieldStore.children[index], schema.items[index], initialInput?.[index], path);
|
|
148
|
-
path.pop();
|
|
146
|
+
initializeFieldStore(internalFieldStore.children[index], schema.items[index], initialInput?.[index], [...path, index]);
|
|
149
147
|
}
|
|
148
|
+
internalFieldStore.isNullish = nullish;
|
|
150
149
|
const arrayInput = nullish && initialInput == null ? initialInput : true;
|
|
151
150
|
internalFieldStore.initialInput = /* @__PURE__ */ createSignal(arrayInput);
|
|
152
151
|
internalFieldStore.startInput = /* @__PURE__ */ createSignal(arrayInput);
|
|
@@ -163,10 +162,9 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
163
162
|
internalFieldStore.children ??= {};
|
|
164
163
|
for (const key in schema.entries) {
|
|
165
164
|
internalFieldStore.children[key] ??= {};
|
|
166
|
-
|
|
167
|
-
initializeFieldStore(internalFieldStore.children[key], schema.entries[key], initialInput?.[key], path);
|
|
168
|
-
path.pop();
|
|
165
|
+
initializeFieldStore(internalFieldStore.children[key], schema.entries[key], initialInput?.[key], [...path, key]);
|
|
169
166
|
}
|
|
167
|
+
internalFieldStore.isNullish = nullish;
|
|
170
168
|
const objectInput = nullish && initialInput == null ? initialInput : true;
|
|
171
169
|
internalFieldStore.initialInput = /* @__PURE__ */ createSignal(objectInput);
|
|
172
170
|
internalFieldStore.startInput = /* @__PURE__ */ createSignal(objectInput);
|
|
@@ -186,8 +184,9 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
186
184
|
/**
|
|
187
185
|
* Copies the deeply nested state (signal values) from one field store to
|
|
188
186
|
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
189
|
-
* `isTouched`, `isDirty`, and for arrays `startItems` and `items`
|
|
190
|
-
* Recursively walks through the field stores and copies all signal
|
|
187
|
+
* `isTouched`, `isEdited`, `isDirty`, and for arrays `startItems` and `items`
|
|
188
|
+
* properties. Recursively walks through the field stores and copies all signal
|
|
189
|
+
* values.
|
|
191
190
|
*
|
|
192
191
|
* @param fromInternalFieldStore The source field store to copy from.
|
|
193
192
|
* @param toInternalFieldStore The destination field store to copy to.
|
|
@@ -200,19 +199,16 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
200
199
|
toInternalFieldStore.startInput.value = fromInternalFieldStore.startInput.value;
|
|
201
200
|
toInternalFieldStore.input.value = fromInternalFieldStore.input.value;
|
|
202
201
|
toInternalFieldStore.isTouched.value = fromInternalFieldStore.isTouched.value;
|
|
202
|
+
toInternalFieldStore.isEdited.value = fromInternalFieldStore.isEdited.value;
|
|
203
203
|
toInternalFieldStore.isDirty.value = fromInternalFieldStore.isDirty.value;
|
|
204
204
|
if (fromInternalFieldStore.kind === "array" && toInternalFieldStore.kind === "array") {
|
|
205
205
|
const fromItems = fromInternalFieldStore.items.value;
|
|
206
206
|
toInternalFieldStore.startItems.value = fromInternalFieldStore.startItems.value;
|
|
207
207
|
toInternalFieldStore.items.value = fromItems;
|
|
208
|
-
let path;
|
|
209
208
|
for (let index = 0; index < fromItems.length; index++) {
|
|
210
209
|
if (!toInternalFieldStore.children[index]) {
|
|
211
|
-
path ??= JSON.parse(toInternalFieldStore.name);
|
|
212
210
|
toInternalFieldStore.children[index] = {};
|
|
213
|
-
|
|
214
|
-
initializeFieldStore(toInternalFieldStore.children[index], toInternalFieldStore.schema.item, void 0, path);
|
|
215
|
-
path.pop();
|
|
211
|
+
initializeFieldStore(toInternalFieldStore.children[index], toInternalFieldStore.schema.item, void 0, [...toInternalFieldStore.path, index]);
|
|
216
212
|
}
|
|
217
213
|
copyItemState(fromInternalFieldStore.children[index], toInternalFieldStore.children[index]);
|
|
218
214
|
}
|
|
@@ -222,45 +218,61 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
222
218
|
}
|
|
223
219
|
/**
|
|
224
220
|
* Resets the state of a field store (signal values) deeply nested. Sets
|
|
225
|
-
* `elements` to empty array, `errors` to `null`, `isTouched
|
|
226
|
-
* `false`, and `startInput`, `input`, `startItems`, and `items` to
|
|
227
|
-
* input value. Keeps the `initialInput` and `initialItems` state
|
|
228
|
-
* form reset functionality.
|
|
221
|
+
* `elements` to empty array, `errors` to `null`, `isTouched`, `isEdited` and
|
|
222
|
+
* `isDirty` to `false`, and `startInput`, `input`, `startItems`, and `items` to
|
|
223
|
+
* the new input value. Keeps the `initialInput` and `initialItems` state
|
|
224
|
+
* unchanged for form reset functionality.
|
|
229
225
|
*
|
|
230
226
|
* @param internalFieldStore The field store to reset.
|
|
231
|
-
* @param
|
|
227
|
+
* @param input The new input value (can be any type including array or object).
|
|
228
|
+
* @param keepStart Whether to keep `startInput` and `startItems` as the dirty
|
|
229
|
+
* baseline instead of resetting them to the new input. Used when a field store
|
|
230
|
+
* is reused for an in-place edit so its dirty state is detected correctly.
|
|
232
231
|
*/
|
|
233
|
-
function resetItemState(internalFieldStore,
|
|
232
|
+
function resetItemState(internalFieldStore, input, keepStart = false) {
|
|
234
233
|
batch(() => {
|
|
235
|
-
|
|
234
|
+
const elements = [];
|
|
235
|
+
if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
|
|
236
|
+
internalFieldStore.elements = elements;
|
|
236
237
|
internalFieldStore.errors.value = null;
|
|
237
238
|
internalFieldStore.isTouched.value = false;
|
|
239
|
+
internalFieldStore.isEdited.value = false;
|
|
238
240
|
internalFieldStore.isDirty.value = false;
|
|
239
241
|
if (internalFieldStore.kind === "array" || internalFieldStore.kind === "object") {
|
|
240
|
-
const objectInput =
|
|
241
|
-
internalFieldStore.startInput.value = objectInput;
|
|
242
|
+
const objectInput = internalFieldStore.isNullish && input == null ? input : true;
|
|
243
|
+
if (!keepStart) internalFieldStore.startInput.value = objectInput;
|
|
242
244
|
internalFieldStore.input.value = objectInput;
|
|
243
|
-
if (internalFieldStore.kind === "array")
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
245
|
+
if (internalFieldStore.kind === "array") {
|
|
246
|
+
const isTuple = internalFieldStore.schema.type !== "array";
|
|
247
|
+
if (input || isTuple) {
|
|
248
|
+
const length = isTuple ? internalFieldStore.children.length : input.length;
|
|
249
|
+
const newItems = Array.from({ length }, createId);
|
|
250
|
+
if (!keepStart) internalFieldStore.startItems.value = newItems;
|
|
251
|
+
internalFieldStore.items.value = newItems;
|
|
252
|
+
for (let index = 0; index < length; index++) {
|
|
253
|
+
const itemInput = input?.[index];
|
|
254
|
+
if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], itemInput, keepStart);
|
|
255
|
+
else {
|
|
256
|
+
internalFieldStore.children[index] = {};
|
|
257
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, itemInput, [...internalFieldStore.path, index]);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
if (!keepStart) internalFieldStore.startItems.value = [];
|
|
262
|
+
internalFieldStore.items.value = [];
|
|
263
|
+
}
|
|
264
|
+
} else for (const key in internalFieldStore.children) resetItemState(internalFieldStore.children[key], input?.[key], keepStart);
|
|
253
265
|
} else {
|
|
254
|
-
internalFieldStore.startInput.value =
|
|
255
|
-
internalFieldStore.input.value =
|
|
266
|
+
if (!keepStart) internalFieldStore.startInput.value = input;
|
|
267
|
+
internalFieldStore.input.value = input;
|
|
256
268
|
}
|
|
257
269
|
});
|
|
258
270
|
}
|
|
259
271
|
/**
|
|
260
272
|
* Swaps the deeply nested state (signal values) between two field stores. This
|
|
261
273
|
* includes the `elements`, `errors`, `startInput`, `input`, `isTouched`,
|
|
262
|
-
* `isDirty`, and for arrays `startItems` and `items` properties.
|
|
263
|
-
* walks through the field stores and swaps all signal values.
|
|
274
|
+
* `isEdited`, `isDirty`, and for arrays `startItems` and `items` properties.
|
|
275
|
+
* Recursively walks through the field stores and swaps all signal values.
|
|
264
276
|
*
|
|
265
277
|
* @param firstInternalFieldStore The first field store to swap.
|
|
266
278
|
* @param secondInternalFieldStore The second field store to swap.
|
|
@@ -283,6 +295,9 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
283
295
|
const tempIsTouched = firstInternalFieldStore.isTouched.value;
|
|
284
296
|
firstInternalFieldStore.isTouched.value = secondInternalFieldStore.isTouched.value;
|
|
285
297
|
secondInternalFieldStore.isTouched.value = tempIsTouched;
|
|
298
|
+
const tempIsEdited = firstInternalFieldStore.isEdited.value;
|
|
299
|
+
firstInternalFieldStore.isEdited.value = secondInternalFieldStore.isEdited.value;
|
|
300
|
+
secondInternalFieldStore.isEdited.value = tempIsEdited;
|
|
286
301
|
const tempIsDirty = firstInternalFieldStore.isDirty.value;
|
|
287
302
|
firstInternalFieldStore.isDirty.value = secondInternalFieldStore.isDirty.value;
|
|
288
303
|
secondInternalFieldStore.isDirty.value = tempIsDirty;
|
|
@@ -295,22 +310,14 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
295
310
|
firstInternalFieldStore.items.value = secondItems;
|
|
296
311
|
secondInternalFieldStore.items.value = firstItems;
|
|
297
312
|
const maxLength = Math.max(firstItems.length, secondItems.length);
|
|
298
|
-
let firstPath;
|
|
299
|
-
let secondPath;
|
|
300
313
|
for (let index = 0; index < maxLength; index++) {
|
|
301
314
|
if (!firstInternalFieldStore.children[index]) {
|
|
302
|
-
firstPath ??= JSON.parse(firstInternalFieldStore.name);
|
|
303
315
|
firstInternalFieldStore.children[index] = {};
|
|
304
|
-
|
|
305
|
-
initializeFieldStore(firstInternalFieldStore.children[index], firstInternalFieldStore.schema.item, void 0, firstPath);
|
|
306
|
-
firstPath.pop();
|
|
316
|
+
initializeFieldStore(firstInternalFieldStore.children[index], firstInternalFieldStore.schema.item, void 0, [...firstInternalFieldStore.path, index]);
|
|
307
317
|
}
|
|
308
318
|
if (!secondInternalFieldStore.children[index]) {
|
|
309
|
-
secondPath ??= JSON.parse(secondInternalFieldStore.name);
|
|
310
319
|
secondInternalFieldStore.children[index] = {};
|
|
311
|
-
|
|
312
|
-
initializeFieldStore(secondInternalFieldStore.children[index], secondInternalFieldStore.schema.item, void 0, secondPath);
|
|
313
|
-
secondPath.pop();
|
|
320
|
+
initializeFieldStore(secondInternalFieldStore.children[index], secondInternalFieldStore.schema.item, void 0, [...secondInternalFieldStore.path, index]);
|
|
314
321
|
}
|
|
315
322
|
swapItemState(firstInternalFieldStore.children[index], secondInternalFieldStore.children[index]);
|
|
316
323
|
}
|
|
@@ -319,6 +326,51 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
319
326
|
});
|
|
320
327
|
}
|
|
321
328
|
/**
|
|
329
|
+
* Focuses the first focusable element of a field store. The elements are tried
|
|
330
|
+
* in order and the first one that actually receives focus wins, so detached,
|
|
331
|
+
* disabled or hidden elements are skipped. The browser decides focusability,
|
|
332
|
+
* which is read back via the element's root `activeElement` so elements in a
|
|
333
|
+
* shadow root or another document are handled correctly.
|
|
334
|
+
*
|
|
335
|
+
* Hint: A `display: none` or `hidden` element is correctly skipped in real
|
|
336
|
+
* browsers, but jsdom has no layout and focuses it anyway, so that case cannot
|
|
337
|
+
* be covered by unit tests.
|
|
338
|
+
*
|
|
339
|
+
* @param internalFieldStore The field store to focus.
|
|
340
|
+
*
|
|
341
|
+
* @returns Whether an element was focused.
|
|
342
|
+
*/
|
|
343
|
+
function focusFieldElement(internalFieldStore) {
|
|
344
|
+
for (const element of internalFieldStore.elements) {
|
|
345
|
+
element.focus();
|
|
346
|
+
if (element.getRootNode().activeElement === element) return true;
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Walks through the field store and all nested children, calling the callback
|
|
352
|
+
* for each field store in depth-first order. The callback may return `true` to
|
|
353
|
+
* stop the walk early, in which case `walkFieldStore` returns `true` as well.
|
|
354
|
+
*
|
|
355
|
+
* The walk reads array `items` reactively, so a reactive caller subscribes to
|
|
356
|
+
* structural changes naturally. Imperative callers that must not subscribe
|
|
357
|
+
* (e.g. when invoked inside an effect) should wrap the call in `untrack`.
|
|
358
|
+
*
|
|
359
|
+
* @param internalFieldStore The field store to walk.
|
|
360
|
+
* @param callback The callback to invoke for each field store. Return `true` to stop the walk early.
|
|
361
|
+
*
|
|
362
|
+
* @returns Whether the walk was stopped early by the callback.
|
|
363
|
+
*/
|
|
364
|
+
function walkFieldStore(internalFieldStore, callback) {
|
|
365
|
+
if (callback(internalFieldStore)) return true;
|
|
366
|
+
if (internalFieldStore.kind === "array") {
|
|
367
|
+
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (walkFieldStore(internalFieldStore.children[index], callback)) return true;
|
|
368
|
+
} else if (internalFieldStore.kind === "object") {
|
|
369
|
+
for (const key in internalFieldStore.children) if (walkFieldStore(internalFieldStore.children[key], callback)) return true;
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
322
374
|
* Returns whether the specified boolean property is true for the field store
|
|
323
375
|
* or any of its nested children. Recursively checks arrays and objects.
|
|
324
376
|
*
|
|
@@ -329,16 +381,7 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
329
381
|
*/
|
|
330
382
|
/* @__NO_SIDE_EFFECTS__ */
|
|
331
383
|
function getFieldBool(internalFieldStore, type) {
|
|
332
|
-
|
|
333
|
-
if (internalFieldStore.kind === "array") {
|
|
334
|
-
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
|
|
335
|
-
return false;
|
|
336
|
-
}
|
|
337
|
-
if (internalFieldStore.kind == "object") {
|
|
338
|
-
for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
|
|
339
|
-
return false;
|
|
340
|
-
}
|
|
341
|
-
return false;
|
|
384
|
+
return walkFieldStore(internalFieldStore, (internalFieldStore$1) => Boolean(internalFieldStore$1[type].value));
|
|
342
385
|
}
|
|
343
386
|
/**
|
|
344
387
|
* Returns only the dirty input of the field store. Arrays are treated as
|
|
@@ -456,11 +499,11 @@ function getFieldStore(internalFormStore, path) {
|
|
|
456
499
|
*/
|
|
457
500
|
function setFieldBool(internalFieldStore, type, bool) {
|
|
458
501
|
batch(() => {
|
|
459
|
-
|
|
460
|
-
internalFieldStore
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
502
|
+
untrack(() => {
|
|
503
|
+
walkFieldStore(internalFieldStore, (internalFieldStore$1) => {
|
|
504
|
+
internalFieldStore$1[type].value = bool;
|
|
505
|
+
});
|
|
506
|
+
});
|
|
464
507
|
});
|
|
465
508
|
}
|
|
466
509
|
/**
|
|
@@ -472,23 +515,21 @@ function setFieldBool(internalFieldStore, type, bool) {
|
|
|
472
515
|
*/
|
|
473
516
|
function setNestedInput(internalFieldStore, input) {
|
|
474
517
|
internalFieldStore.isTouched.value = true;
|
|
518
|
+
internalFieldStore.isEdited.value = true;
|
|
475
519
|
if (internalFieldStore.kind === "array") {
|
|
476
520
|
const arrayInput = input ?? [];
|
|
477
521
|
const items = internalFieldStore.items.value;
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], path);
|
|
486
|
-
path.pop();
|
|
487
|
-
}
|
|
522
|
+
const length = internalFieldStore.schema.type === "array" ? arrayInput.length : internalFieldStore.children.length;
|
|
523
|
+
if (length < items.length) internalFieldStore.items.value = items.slice(0, length);
|
|
524
|
+
else if (length > items.length) {
|
|
525
|
+
for (let index = items.length; index < length; index++) if (internalFieldStore.children[index]) resetItemState(internalFieldStore.children[index], arrayInput[index], true);
|
|
526
|
+
else {
|
|
527
|
+
internalFieldStore.children[index] = {};
|
|
528
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, arrayInput[index], [...internalFieldStore.path, index]);
|
|
488
529
|
}
|
|
489
|
-
internalFieldStore.items.value = [...items, ...
|
|
530
|
+
internalFieldStore.items.value = [...items, ...Array.from({ length: length - items.length }, createId)];
|
|
490
531
|
}
|
|
491
|
-
for (let index = 0; index <
|
|
532
|
+
for (let index = 0; index < length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
|
|
492
533
|
internalFieldStore.input.value = input == null ? input : true;
|
|
493
534
|
internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== internalFieldStore.items.value.length;
|
|
494
535
|
} else if (internalFieldStore.kind === "object") {
|
|
@@ -532,38 +573,22 @@ function setFieldInput(internalFormStore, path, input) {
|
|
|
532
573
|
function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
533
574
|
batch(() => {
|
|
534
575
|
if (internalFieldStore.kind === "array") {
|
|
535
|
-
internalFieldStore.
|
|
576
|
+
internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
|
|
536
577
|
const initialArrayInput = initialInput ?? [];
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
path.push(index);
|
|
542
|
-
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], path);
|
|
543
|
-
path.pop();
|
|
544
|
-
}
|
|
578
|
+
const length = internalFieldStore.schema.type === "array" ? initialArrayInput.length : internalFieldStore.children.length;
|
|
579
|
+
if (length > internalFieldStore.children.length) for (let index = internalFieldStore.children.length; index < length; index++) {
|
|
580
|
+
internalFieldStore.children[index] = {};
|
|
581
|
+
initializeFieldStore(internalFieldStore.children[index], internalFieldStore.schema.item, initialArrayInput[index], [...internalFieldStore.path, index]);
|
|
545
582
|
}
|
|
546
|
-
internalFieldStore.initialItems.value =
|
|
583
|
+
internalFieldStore.initialItems.value = Array.from({ length }, createId);
|
|
547
584
|
for (let index = 0; index < internalFieldStore.children.length; index++) setInitialFieldInput(internalFieldStore.children[index], initialArrayInput[index]);
|
|
548
585
|
} else if (internalFieldStore.kind === "object") {
|
|
549
|
-
internalFieldStore.
|
|
586
|
+
internalFieldStore.initialInput.value = initialInput == null ? initialInput : true;
|
|
550
587
|
for (const key in internalFieldStore.children) setInitialFieldInput(internalFieldStore.children[key], initialInput?.[key]);
|
|
551
588
|
} else internalFieldStore.initialInput.value = initialInput;
|
|
552
589
|
});
|
|
553
590
|
}
|
|
554
591
|
/**
|
|
555
|
-
* Walks through the field store and all nested children, calling the callback
|
|
556
|
-
* for each field store in depth-first order.
|
|
557
|
-
*
|
|
558
|
-
* @param internalFieldStore The field store to walk.
|
|
559
|
-
* @param callback The callback to invoke for each field store.
|
|
560
|
-
*/
|
|
561
|
-
function walkFieldStore(internalFieldStore, callback) {
|
|
562
|
-
callback(internalFieldStore);
|
|
563
|
-
if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) walkFieldStore(internalFieldStore.children[index], callback);
|
|
564
|
-
else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) walkFieldStore(internalFieldStore.children[key], callback);
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
592
|
* Creates a new internal form store from the provided configuration.
|
|
568
593
|
* Initializes the field store hierarchy, sets validation modes, and
|
|
569
594
|
* creates form state signals.
|
|
@@ -598,44 +623,51 @@ function createFormStore(config, parse) {
|
|
|
598
623
|
async function validateFormInput(internalFormStore, config) {
|
|
599
624
|
internalFormStore.validators++;
|
|
600
625
|
internalFormStore.isValidating.value = true;
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
const path
|
|
608
|
-
|
|
609
|
-
const
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
const name = JSON.stringify(path);
|
|
616
|
-
const fieldErrors = nestedErrors[name];
|
|
617
|
-
if (fieldErrors) fieldErrors.push(issue.message);
|
|
618
|
-
else nestedErrors[name] = [issue.message];
|
|
619
|
-
} else if (rootErrors) rootErrors.push(issue.message);
|
|
620
|
-
else rootErrors = [issue.message];
|
|
621
|
-
}
|
|
622
|
-
let shouldFocus = config?.shouldFocus ?? false;
|
|
623
|
-
batch(() => {
|
|
624
|
-
walkFieldStore(internalFormStore, (internalFieldStore) => {
|
|
625
|
-
if (internalFieldStore.name === "[]") internalFieldStore.errors.value = rootErrors ?? null;
|
|
626
|
-
else {
|
|
627
|
-
const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
|
|
628
|
-
internalFieldStore.errors.value = fieldErrors;
|
|
629
|
-
if (shouldFocus && fieldErrors) {
|
|
630
|
-
internalFieldStore.elements[0]?.focus();
|
|
631
|
-
shouldFocus = false;
|
|
626
|
+
try {
|
|
627
|
+
const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
|
|
628
|
+
let rootErrors;
|
|
629
|
+
let nestedErrors;
|
|
630
|
+
if (result.issues) {
|
|
631
|
+
nestedErrors = {};
|
|
632
|
+
for (const issue of result.issues) if (issue.path) {
|
|
633
|
+
const path = [];
|
|
634
|
+
for (const pathItem of issue.path) {
|
|
635
|
+
const key = pathItem.key;
|
|
636
|
+
const keyType = typeof key;
|
|
637
|
+
const itemType = pathItem.type;
|
|
638
|
+
if (keyType !== "string" && keyType !== "number" || itemType === "map" || itemType === "set") break;
|
|
639
|
+
path.push(key);
|
|
632
640
|
}
|
|
633
|
-
|
|
641
|
+
const name = JSON.stringify(path);
|
|
642
|
+
const fieldErrors = nestedErrors[name];
|
|
643
|
+
if (fieldErrors) fieldErrors.push(issue.message);
|
|
644
|
+
else nestedErrors[name] = [issue.message];
|
|
645
|
+
} else if (rootErrors) rootErrors.push(issue.message);
|
|
646
|
+
else rootErrors = [issue.message];
|
|
647
|
+
}
|
|
648
|
+
let shouldFocus = config?.shouldFocus ?? false;
|
|
649
|
+
batch(() => {
|
|
650
|
+
untrack(() => {
|
|
651
|
+
walkFieldStore(internalFormStore, (internalFieldStore) => {
|
|
652
|
+
if (internalFieldStore.path.length === 0) internalFieldStore.errors.value = rootErrors ?? null;
|
|
653
|
+
else {
|
|
654
|
+
const fieldErrors = nestedErrors?.[internalFieldStore.name] ?? null;
|
|
655
|
+
internalFieldStore.errors.value = fieldErrors;
|
|
656
|
+
if (shouldFocus && fieldErrors && focusFieldElement(internalFieldStore)) shouldFocus = false;
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
});
|
|
660
|
+
internalFormStore.validators--;
|
|
661
|
+
internalFormStore.isValidating.value = internalFormStore.validators > 0;
|
|
634
662
|
});
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
663
|
+
return result;
|
|
664
|
+
} catch (error) {
|
|
665
|
+
batch(() => {
|
|
666
|
+
internalFormStore.validators--;
|
|
667
|
+
internalFormStore.isValidating.value = internalFormStore.validators > 0;
|
|
668
|
+
});
|
|
669
|
+
throw error;
|
|
670
|
+
}
|
|
639
671
|
}
|
|
640
672
|
/**
|
|
641
673
|
* Validates the form input if required based on the validation mode and form
|
|
@@ -657,7 +689,7 @@ const INTERNAL = "~internal";
|
|
|
657
689
|
//#endregion
|
|
658
690
|
//#region ../../packages/methods/dist/index.react.js
|
|
659
691
|
/**
|
|
660
|
-
* Focuses the first input element of a field. This is useful for
|
|
692
|
+
* Focuses the first focusable input element of a field. This is useful for
|
|
661
693
|
* programmatically setting focus to a specific field, such as after
|
|
662
694
|
* validation errors or user interactions.
|
|
663
695
|
*
|
|
@@ -665,27 +697,29 @@ const INTERNAL = "~internal";
|
|
|
665
697
|
* @param config The focus field configuration.
|
|
666
698
|
*/
|
|
667
699
|
function focus(form, config) {
|
|
668
|
-
getFieldStore(form[INTERNAL], config.path)
|
|
700
|
+
focusFieldElement(getFieldStore(form[INTERNAL], config.path));
|
|
701
|
+
}
|
|
702
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
703
|
+
function getDeepErrorEntries(form, config) {
|
|
704
|
+
const entries = [];
|
|
705
|
+
walkFieldStore(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], (internalFieldStore) => {
|
|
706
|
+
const errors = internalFieldStore.errors.value;
|
|
707
|
+
if (errors) entries.push({
|
|
708
|
+
path: internalFieldStore.path,
|
|
709
|
+
errors
|
|
710
|
+
});
|
|
711
|
+
});
|
|
712
|
+
return entries;
|
|
669
713
|
}
|
|
670
|
-
/**
|
|
671
|
-
* Retrieves all error messages from all fields in the form by walking through
|
|
672
|
-
* the entire field store tree. This is useful for displaying a summary of all
|
|
673
|
-
* validation errors across the form.
|
|
674
|
-
*
|
|
675
|
-
* @param form The form store to retrieve errors from.
|
|
676
|
-
*
|
|
677
|
-
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
678
|
-
*/
|
|
679
714
|
/* @__NO_SIDE_EFFECTS__ */
|
|
680
|
-
function
|
|
681
|
-
let
|
|
682
|
-
walkFieldStore(form[INTERNAL], (internalFieldStore) => {
|
|
683
|
-
if (internalFieldStore.kind === "array") internalFieldStore.items.value;
|
|
715
|
+
function getDeepErrors(form, config) {
|
|
716
|
+
let deepErrors = null;
|
|
717
|
+
walkFieldStore(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], (internalFieldStore) => {
|
|
684
718
|
const errors = internalFieldStore.errors.value;
|
|
685
|
-
if (errors) if (
|
|
686
|
-
else
|
|
719
|
+
if (errors) if (deepErrors) deepErrors.push(...errors);
|
|
720
|
+
else deepErrors = [...errors];
|
|
687
721
|
});
|
|
688
|
-
return
|
|
722
|
+
return deepErrors;
|
|
689
723
|
}
|
|
690
724
|
/* @__NO_SIDE_EFFECTS__ */
|
|
691
725
|
function getDirtyInput(form, config) {
|
|
@@ -693,9 +727,8 @@ function getDirtyInput(form, config) {
|
|
|
693
727
|
}
|
|
694
728
|
/* @__NO_SIDE_EFFECTS__ */
|
|
695
729
|
function getDirtyPaths(form, config) {
|
|
696
|
-
config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
|
|
697
730
|
const paths = [];
|
|
698
|
-
config?.path
|
|
731
|
+
config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL];
|
|
699
732
|
return paths;
|
|
700
733
|
}
|
|
701
734
|
/* @__NO_SIDE_EFFECTS__ */
|
|
@@ -750,25 +783,38 @@ function insert(form, config) {
|
|
|
750
783
|
internalArrayStore.items.value = newItems;
|
|
751
784
|
for (let index = items.length; index > insertIndex; index--) {
|
|
752
785
|
if (!internalArrayStore.children[index]) {
|
|
753
|
-
const path = JSON.parse(internalArrayStore.name);
|
|
754
786
|
internalArrayStore.children[index] = {};
|
|
755
|
-
|
|
756
|
-
initializeFieldStore(internalArrayStore.children[index], internalArrayStore.schema.item, void 0, path);
|
|
787
|
+
initializeFieldStore(internalArrayStore.children[index], internalArrayStore.schema.item, void 0, [...internalArrayStore.path, index]);
|
|
757
788
|
}
|
|
758
789
|
copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
759
790
|
}
|
|
760
791
|
if (!internalArrayStore.children[insertIndex]) {
|
|
761
|
-
const path = JSON.parse(internalArrayStore.name);
|
|
762
792
|
internalArrayStore.children[insertIndex] = {};
|
|
763
|
-
|
|
764
|
-
initializeFieldStore(internalArrayStore.children[insertIndex], internalArrayStore.schema.item, config.initialInput, path);
|
|
793
|
+
initializeFieldStore(internalArrayStore.children[insertIndex], internalArrayStore.schema.item, config.initialInput, [...internalArrayStore.path, insertIndex]);
|
|
765
794
|
} else resetItemState(internalArrayStore.children[insertIndex], config.initialInput);
|
|
766
795
|
internalArrayStore.input.value = true;
|
|
767
796
|
internalArrayStore.isTouched.value = true;
|
|
797
|
+
internalArrayStore.isEdited.value = true;
|
|
768
798
|
internalArrayStore.isDirty.value = true;
|
|
769
799
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
770
800
|
});
|
|
771
801
|
}
|
|
802
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
803
|
+
function isDirty(form, config) {
|
|
804
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isDirty");
|
|
805
|
+
}
|
|
806
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
807
|
+
function isEdited(form, config) {
|
|
808
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isEdited");
|
|
809
|
+
}
|
|
810
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
811
|
+
function isTouched(form, config) {
|
|
812
|
+
return getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "isTouched");
|
|
813
|
+
}
|
|
814
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
815
|
+
function isValid(form, config) {
|
|
816
|
+
return !getFieldBool(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL], "errors");
|
|
817
|
+
}
|
|
772
818
|
/**
|
|
773
819
|
* Moves an item from one index to another within a field array. All items
|
|
774
820
|
* between the source and destination indices are shifted accordingly.
|
|
@@ -791,6 +837,7 @@ function move(form, config) {
|
|
|
791
837
|
else for (let index = config.from; index > config.to; index--) copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
792
838
|
copyItemState(tempInternalFieldStore, internalArrayStore.children[config.to]);
|
|
793
839
|
internalArrayStore.isTouched.value = true;
|
|
840
|
+
internalArrayStore.isEdited.value = true;
|
|
794
841
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
795
842
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
796
843
|
});
|
|
@@ -854,6 +901,7 @@ function remove(form, config) {
|
|
|
854
901
|
internalArrayStore.items.value = newItems;
|
|
855
902
|
for (let index = config.at; index < items.length - 1; index++) copyItemState(internalArrayStore.children[index + 1], internalArrayStore.children[index]);
|
|
856
903
|
internalArrayStore.isTouched.value = true;
|
|
904
|
+
internalArrayStore.isEdited.value = true;
|
|
857
905
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
858
906
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
859
907
|
});
|
|
@@ -874,6 +922,7 @@ function replace(form, config) {
|
|
|
874
922
|
internalArrayStore.items.value = newItems;
|
|
875
923
|
resetItemState(internalArrayStore.children[config.at], config.initialInput);
|
|
876
924
|
internalArrayStore.isTouched.value = true;
|
|
925
|
+
internalArrayStore.isEdited.value = true;
|
|
877
926
|
internalArrayStore.isDirty.value = true;
|
|
878
927
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
879
928
|
});
|
|
@@ -888,6 +937,7 @@ function reset(form, config) {
|
|
|
888
937
|
internalFieldStore$1.elements = internalFieldStore$1.initialElements;
|
|
889
938
|
if (!config?.keepErrors) internalFieldStore$1.errors.value = null;
|
|
890
939
|
if (!config?.keepTouched) internalFieldStore$1.isTouched.value = false;
|
|
940
|
+
if (!config?.keepEdited) internalFieldStore$1.isEdited.value = false;
|
|
891
941
|
internalFieldStore$1.startInput.value = internalFieldStore$1.initialInput.value;
|
|
892
942
|
if (!config?.keepInput) internalFieldStore$1.input.value = internalFieldStore$1.initialInput.value;
|
|
893
943
|
if (internalFieldStore$1.kind === "array") {
|
|
@@ -948,6 +998,7 @@ function swap(form, config) {
|
|
|
948
998
|
internalArrayStore.items.value = newItems;
|
|
949
999
|
swapItemState(internalArrayStore.children[config.at], internalArrayStore.children[config.and]);
|
|
950
1000
|
internalArrayStore.isTouched.value = true;
|
|
1001
|
+
internalArrayStore.isEdited.value = true;
|
|
951
1002
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
952
1003
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
953
1004
|
});
|
|
@@ -1001,7 +1052,9 @@ function useField(form, config) {
|
|
|
1001
1052
|
const internalFieldStore = getFieldStore(internalFormStore, config.path);
|
|
1002
1053
|
useEffect(() => {
|
|
1003
1054
|
return () => {
|
|
1004
|
-
|
|
1055
|
+
const elements = internalFieldStore.elements.filter((element) => element.isConnected);
|
|
1056
|
+
if (internalFieldStore.elements === internalFieldStore.initialElements) internalFieldStore.initialElements = elements;
|
|
1057
|
+
internalFieldStore.elements = elements;
|
|
1005
1058
|
};
|
|
1006
1059
|
}, [internalFieldStore]);
|
|
1007
1060
|
return useMemo(() => ({
|
|
@@ -1015,6 +1068,9 @@ function useField(form, config) {
|
|
|
1015
1068
|
get isTouched() {
|
|
1016
1069
|
return getFieldBool(internalFieldStore, "isTouched");
|
|
1017
1070
|
},
|
|
1071
|
+
get isEdited() {
|
|
1072
|
+
return getFieldBool(internalFieldStore, "isEdited");
|
|
1073
|
+
},
|
|
1018
1074
|
get isDirty() {
|
|
1019
1075
|
return getFieldBool(internalFieldStore, "isDirty");
|
|
1020
1076
|
},
|
|
@@ -1066,6 +1122,9 @@ function useFieldArray(form, config) {
|
|
|
1066
1122
|
get isTouched() {
|
|
1067
1123
|
return getFieldBool(internalFieldStore, "isTouched");
|
|
1068
1124
|
},
|
|
1125
|
+
get isEdited() {
|
|
1126
|
+
return getFieldBool(internalFieldStore, "isEdited");
|
|
1127
|
+
},
|
|
1069
1128
|
get isDirty() {
|
|
1070
1129
|
return getFieldBool(internalFieldStore, "isDirty");
|
|
1071
1130
|
},
|
|
@@ -1098,6 +1157,9 @@ function useForm(config) {
|
|
|
1098
1157
|
get isTouched() {
|
|
1099
1158
|
return getFieldBool(internalFormStore, "isTouched");
|
|
1100
1159
|
},
|
|
1160
|
+
get isEdited() {
|
|
1161
|
+
return getFieldBool(internalFormStore, "isEdited");
|
|
1162
|
+
},
|
|
1101
1163
|
get isDirty() {
|
|
1102
1164
|
return getFieldBool(internalFormStore, "isDirty");
|
|
1103
1165
|
},
|
|
@@ -1158,4 +1220,4 @@ function Form({ of, onSubmit, ...other }) {
|
|
|
1158
1220
|
}
|
|
1159
1221
|
|
|
1160
1222
|
//#endregion
|
|
1161
|
-
export { Field, FieldArray, Form, focus,
|
|
1223
|
+
export { Field, FieldArray, 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 };
|