@formisch/qwik 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +701 -24
- package/dist/index.qwik.js +277 -35
- package/package.json +3 -3
package/dist/index.qwik.js
CHANGED
|
@@ -3,16 +3,40 @@ import { $, Slot, component$, createComputed$, createSignal, implicit$FirstArg,
|
|
|
3
3
|
import { jsx } from "@qwik.dev/core/jsx-runtime";
|
|
4
4
|
|
|
5
5
|
//#region ../../packages/core/dist/index.qwik.js
|
|
6
|
+
/**
|
|
7
|
+
* The current framework being used.
|
|
8
|
+
*/
|
|
6
9
|
const framework = "qwik";
|
|
10
|
+
/**
|
|
11
|
+
* Creates a unique identifier string.
|
|
12
|
+
*
|
|
13
|
+
* @returns The unique identifier.
|
|
14
|
+
*/
|
|
15
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
7
16
|
function createId() {
|
|
8
17
|
return Math.random().toString(36).slice(2);
|
|
9
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Batches multiple signal updates into a single update cycle. This is a
|
|
21
|
+
* no-op in Qwik as batching is not needed.
|
|
22
|
+
*
|
|
23
|
+
* @param fn The function to execute.
|
|
24
|
+
*
|
|
25
|
+
* @returns The return value of the function.
|
|
26
|
+
*/
|
|
10
27
|
function batch(fn) {
|
|
11
28
|
return fn();
|
|
12
29
|
}
|
|
13
30
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
31
|
+
* Initializes a field store recursively based on the schema structure. Handles
|
|
32
|
+
* array, object, and value schemas, setting up all necessary signals and
|
|
33
|
+
* children. Supports wrapped schemas and schema options.
|
|
34
|
+
*
|
|
35
|
+
* @param internalFieldStore The partial field store to initialize.
|
|
36
|
+
* @param schema The Valibot schema defining the field structure.
|
|
37
|
+
* @param initialInput The initial input value.
|
|
38
|
+
* @param path The path to the field in the form.
|
|
39
|
+
* @param nullish Whether the schema is wrapped in a nullish schema.
|
|
16
40
|
*/
|
|
17
41
|
function initializeFieldStore(internalFieldStore, schema, initialInput, path, nullish = false) {
|
|
18
42
|
if (framework === "qwik" && schema.type === "lazy" || schema.type === "object_with_rest" || schema.type === "record" || schema.type === "tuple_with_rest" || schema.type === "promise") throw new Error(`"${schema.type}" schema is not supported`);
|
|
@@ -83,13 +107,13 @@ function initializeFieldStore(internalFieldStore, schema, initialInput, path, nu
|
|
|
83
107
|
}
|
|
84
108
|
}
|
|
85
109
|
/**
|
|
86
|
-
* Copies the deeply nested state (signal values) from one
|
|
87
|
-
* This includes the `
|
|
110
|
+
* Copies the deeply nested state (signal values) from one field store to
|
|
111
|
+
* another. This includes the `elements`, `errors`, `startInput`, `input`,
|
|
112
|
+
* `isTouched`, `isDirty`, and for arrays `startItems` and `items` properties.
|
|
88
113
|
* Recursively walks through the field stores and copies all signal values.
|
|
89
114
|
*
|
|
90
|
-
* @param
|
|
91
|
-
* @param
|
|
92
|
-
* @param toIndex - The destination index to copy to
|
|
115
|
+
* @param fromInternalFieldStore The source field store to copy from.
|
|
116
|
+
* @param toInternalFieldStore The destination field store to copy to.
|
|
93
117
|
*/
|
|
94
118
|
function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
95
119
|
batch(() => {
|
|
@@ -120,13 +144,14 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
120
144
|
});
|
|
121
145
|
}
|
|
122
146
|
/**
|
|
123
|
-
* Resets the state of
|
|
124
|
-
*
|
|
125
|
-
* `
|
|
126
|
-
* Keeps the `initialInput` and `initialItems` state unchanged for
|
|
147
|
+
* Resets the state of a field store (signal values) deeply nested. Sets
|
|
148
|
+
* `elements` to empty array, `errors` to `null`, `isTouched` and `isDirty` to
|
|
149
|
+
* `false`, and `startInput`, `input`, `startItems`, and `items` to the new
|
|
150
|
+
* input value. Keeps the `initialInput` and `initialItems` state unchanged for
|
|
151
|
+
* form reset functionality.
|
|
127
152
|
*
|
|
128
|
-
* @param internalFieldStore
|
|
129
|
-
* @param initialInput
|
|
153
|
+
* @param internalFieldStore The field store to reset.
|
|
154
|
+
* @param initialInput The new input value (can be any type including array or object).
|
|
130
155
|
*/
|
|
131
156
|
function resetItemState(internalFieldStore, initialInput) {
|
|
132
157
|
batch(() => {
|
|
@@ -155,12 +180,13 @@ function resetItemState(internalFieldStore, initialInput) {
|
|
|
155
180
|
});
|
|
156
181
|
}
|
|
157
182
|
/**
|
|
158
|
-
* Swaps the deeply nested state (signal values) between two field stores.
|
|
159
|
-
*
|
|
160
|
-
*
|
|
183
|
+
* Swaps the deeply nested state (signal values) between two field stores. This
|
|
184
|
+
* includes the `elements`, `errors`, `startInput`, `input`, `isTouched`,
|
|
185
|
+
* `isDirty`, and for arrays `startItems` and `items` properties. Recursively
|
|
186
|
+
* walks through the field stores and swaps all signal values.
|
|
161
187
|
*
|
|
162
|
-
* @param firstInternalFieldStore
|
|
163
|
-
* @param secondInternalFieldStore
|
|
188
|
+
* @param firstInternalFieldStore The first field store to swap.
|
|
189
|
+
* @param secondInternalFieldStore The second field store to swap.
|
|
164
190
|
*/
|
|
165
191
|
function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
166
192
|
batch(() => {
|
|
@@ -215,11 +241,21 @@ function swapItemState(firstInternalFieldStore, secondInternalFieldStore) {
|
|
|
215
241
|
});
|
|
216
242
|
});
|
|
217
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Returns the current input of the field store. For arrays and objects,
|
|
246
|
+
* recursively collects input from all children. Returns `null` or `undefined`
|
|
247
|
+
* for nullish array/object inputs, or the primitive value for value fields.
|
|
248
|
+
*
|
|
249
|
+
* @param internalFieldStore The field store to get input from.
|
|
250
|
+
*
|
|
251
|
+
* @returns The field input.
|
|
252
|
+
*/
|
|
253
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
218
254
|
function getFieldInput(internalFieldStore) {
|
|
219
255
|
if (internalFieldStore.kind === "array") {
|
|
220
256
|
if (internalFieldStore.input.value) {
|
|
221
257
|
const value = [];
|
|
222
|
-
for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = getFieldInput(internalFieldStore.children[index]);
|
|
258
|
+
for (let index = 0; index < internalFieldStore.items.value.length; index++) value[index] = /* @__PURE__ */ getFieldInput(internalFieldStore.children[index]);
|
|
223
259
|
return value;
|
|
224
260
|
}
|
|
225
261
|
return internalFieldStore.input.value;
|
|
@@ -227,7 +263,7 @@ function getFieldInput(internalFieldStore) {
|
|
|
227
263
|
if (internalFieldStore.kind === "object") {
|
|
228
264
|
if (internalFieldStore.input.value) {
|
|
229
265
|
const value = {};
|
|
230
|
-
for (const key in internalFieldStore.children) value[key] = getFieldInput(internalFieldStore.children[key]);
|
|
266
|
+
for (const key in internalFieldStore.children) value[key] = /* @__PURE__ */ getFieldInput(internalFieldStore.children[key]);
|
|
231
267
|
return value;
|
|
232
268
|
}
|
|
233
269
|
return internalFieldStore.input.value;
|
|
@@ -235,13 +271,15 @@ function getFieldInput(internalFieldStore) {
|
|
|
235
271
|
return internalFieldStore.input.value;
|
|
236
272
|
}
|
|
237
273
|
/**
|
|
238
|
-
* Returns the current input of the element.
|
|
274
|
+
* Returns the current input of the element. Handles special cases for select
|
|
275
|
+
* multiple, checkbox groups, radio groups, and file inputs.
|
|
239
276
|
*
|
|
240
277
|
* @param element The field element.
|
|
241
|
-
* @param
|
|
278
|
+
* @param internalFieldStore The internal field store.
|
|
242
279
|
*
|
|
243
280
|
* @returns The element input.
|
|
244
281
|
*/
|
|
282
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
245
283
|
function getElementInput(element, internalFieldStore) {
|
|
246
284
|
if (element.options && element.multiple) return [...element.options].filter((option) => option.selected && !option.disabled).map((option) => option.value);
|
|
247
285
|
if (element.type === "checkbox") {
|
|
@@ -250,7 +288,7 @@ function getElementInput(element, internalFieldStore) {
|
|
|
250
288
|
return element.checked;
|
|
251
289
|
}
|
|
252
290
|
if (element.type === "radio") {
|
|
253
|
-
const prevValue = untrack(() => getFieldInput(internalFieldStore));
|
|
291
|
+
const prevValue = untrack(() => /* @__PURE__ */ getFieldInput(internalFieldStore));
|
|
254
292
|
if (element.checked) return [...prevValue, element.value];
|
|
255
293
|
return prevValue.filter((value) => value !== element.value);
|
|
256
294
|
}
|
|
@@ -260,23 +298,51 @@ function getElementInput(element, internalFieldStore) {
|
|
|
260
298
|
}
|
|
261
299
|
return element.value;
|
|
262
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Returns whether the specified boolean property is true for the field store
|
|
303
|
+
* or any of its nested children. Recursively checks arrays and objects.
|
|
304
|
+
*
|
|
305
|
+
* @param internalFieldStore The field store to check.
|
|
306
|
+
* @param type The boolean property type to check.
|
|
307
|
+
*
|
|
308
|
+
* @returns Whether the property is true.
|
|
309
|
+
*/
|
|
310
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
263
311
|
function getFieldBool(internalFieldStore, type) {
|
|
264
312
|
if (internalFieldStore[type].value) return true;
|
|
265
313
|
if (internalFieldStore.kind === "array") {
|
|
266
|
-
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (getFieldBool(internalFieldStore.children[index], type)) return true;
|
|
314
|
+
for (let index = 0; index < internalFieldStore.items.value.length; index++) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[index], type)) return true;
|
|
267
315
|
return false;
|
|
268
316
|
}
|
|
269
317
|
if (internalFieldStore.kind == "object") {
|
|
270
|
-
for (const key in internalFieldStore.children) if (getFieldBool(internalFieldStore.children[key], type)) return true;
|
|
318
|
+
for (const key in internalFieldStore.children) if (/* @__PURE__ */ getFieldBool(internalFieldStore.children[key], type)) return true;
|
|
271
319
|
return false;
|
|
272
320
|
}
|
|
273
321
|
return false;
|
|
274
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* Returns the field store at the specified path by traversing the form store's
|
|
325
|
+
* children hierarchy.
|
|
326
|
+
*
|
|
327
|
+
* @param internalFormStore The form store to traverse.
|
|
328
|
+
* @param path The path to the field store.
|
|
329
|
+
*
|
|
330
|
+
* @returns The field store.
|
|
331
|
+
*/
|
|
332
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
275
333
|
function getFieldStore(internalFormStore, path) {
|
|
276
334
|
let internalFieldStore = internalFormStore;
|
|
277
335
|
for (const key of path) internalFieldStore = internalFieldStore.children[key];
|
|
278
336
|
return internalFieldStore;
|
|
279
337
|
}
|
|
338
|
+
/**
|
|
339
|
+
* Sets the specified boolean property for the field store and all nested
|
|
340
|
+
* children. Recursively updates arrays and objects.
|
|
341
|
+
*
|
|
342
|
+
* @param internalFieldStore The field store to update.
|
|
343
|
+
* @param type The boolean property type to set.
|
|
344
|
+
* @param bool The boolean value to set.
|
|
345
|
+
*/
|
|
280
346
|
function setFieldBool(internalFieldStore, type, bool) {
|
|
281
347
|
batch(() => {
|
|
282
348
|
if (internalFieldStore.kind === "array") {
|
|
@@ -286,6 +352,13 @@ function setFieldBool(internalFieldStore, type, bool) {
|
|
|
286
352
|
else internalFieldStore[type].value = bool;
|
|
287
353
|
});
|
|
288
354
|
}
|
|
355
|
+
/**
|
|
356
|
+
* Sets the input for a nested field store and all its children, updating
|
|
357
|
+
* touched and dirty states accordingly. Handles dynamic array resizing.
|
|
358
|
+
*
|
|
359
|
+
* @param internalFieldStore The field store to update.
|
|
360
|
+
* @param input The new input value.
|
|
361
|
+
*/
|
|
289
362
|
function setNestedInput(internalFieldStore, input) {
|
|
290
363
|
internalFieldStore.isTouched.value = true;
|
|
291
364
|
if (internalFieldStore.kind === "array") {
|
|
@@ -304,7 +377,7 @@ function setNestedInput(internalFieldStore, input) {
|
|
|
304
377
|
}
|
|
305
378
|
internalFieldStore.items.value = [...items, ...arrayInput.slice(items.length).map(createId)];
|
|
306
379
|
}
|
|
307
|
-
for (let index = 0; index <
|
|
380
|
+
for (let index = 0; index < arrayInput.length; index++) setNestedInput(internalFieldStore.children[index], arrayInput[index]);
|
|
308
381
|
internalFieldStore.input.value = input == null ? input : true;
|
|
309
382
|
internalFieldStore.isDirty.value = internalFieldStore.startInput.value !== internalFieldStore.input.value || internalFieldStore.startItems.value.length !== items.length;
|
|
310
383
|
} else if (internalFieldStore.kind === "object") {
|
|
@@ -317,6 +390,14 @@ function setNestedInput(internalFieldStore, input) {
|
|
|
317
390
|
internalFieldStore.isDirty.value = startInput !== input && (startInput !== void 0 || input !== "" && !Number.isNaN(input));
|
|
318
391
|
}
|
|
319
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* Sets the input for a field at the specified path in the form store,
|
|
395
|
+
* traversing the path and updating all parent fields along the way.
|
|
396
|
+
*
|
|
397
|
+
* @param internalFormStore The form store containing the field.
|
|
398
|
+
* @param path The path to the field.
|
|
399
|
+
* @param input The new input value.
|
|
400
|
+
*/
|
|
320
401
|
function setFieldInput(internalFormStore, path, input) {
|
|
321
402
|
batch(() => {
|
|
322
403
|
untrack(() => {
|
|
@@ -329,6 +410,14 @@ function setFieldInput(internalFormStore, path, input) {
|
|
|
329
410
|
});
|
|
330
411
|
});
|
|
331
412
|
}
|
|
413
|
+
/**
|
|
414
|
+
* Sets the initial input for a field store and all its children recursively.
|
|
415
|
+
* For arrays, initializes missing children if needed. Updates `initialInput`
|
|
416
|
+
* and `initialItems` properties.
|
|
417
|
+
*
|
|
418
|
+
* @param internalFieldStore The field store to update.
|
|
419
|
+
* @param initialInput The initial input value.
|
|
420
|
+
*/
|
|
332
421
|
function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
333
422
|
batch(() => {
|
|
334
423
|
if (internalFieldStore.kind === "array") {
|
|
@@ -351,14 +440,32 @@ function setInitialFieldInput(internalFieldStore, initialInput) {
|
|
|
351
440
|
} else internalFieldStore.initialInput.value = initialInput;
|
|
352
441
|
});
|
|
353
442
|
}
|
|
443
|
+
/**
|
|
444
|
+
* Walks through the field store and all nested children, calling the callback
|
|
445
|
+
* for each field store in depth-first order.
|
|
446
|
+
*
|
|
447
|
+
* @param internalFieldStore The field store to walk.
|
|
448
|
+
* @param callback The callback to invoke for each field store.
|
|
449
|
+
*/
|
|
354
450
|
function walkFieldStore(internalFieldStore, callback) {
|
|
355
451
|
callback(internalFieldStore);
|
|
356
452
|
if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) walkFieldStore(internalFieldStore.children[index], callback);
|
|
357
453
|
else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) walkFieldStore(internalFieldStore.children[key], callback);
|
|
358
454
|
}
|
|
455
|
+
/**
|
|
456
|
+
* Creates a new internal form store from the provided configuration.
|
|
457
|
+
* Initializes the field store hierarchy, sets validation modes, and
|
|
458
|
+
* creates form state signals.
|
|
459
|
+
*
|
|
460
|
+
* @param config The form configuration.
|
|
461
|
+
* @param parse The schema parse function.
|
|
462
|
+
*
|
|
463
|
+
* @returns The internal form store.
|
|
464
|
+
*/
|
|
359
465
|
function createFormStore(config, parse) {
|
|
360
466
|
const store = {};
|
|
361
467
|
initializeFieldStore(store, config.schema, config.initialInput, []);
|
|
468
|
+
store.validators = 0;
|
|
362
469
|
store.validate = config.validate ?? "submit";
|
|
363
470
|
store.revalidate = config.revalidate ?? "input";
|
|
364
471
|
store.parse = parse;
|
|
@@ -367,10 +474,20 @@ function createFormStore(config, parse) {
|
|
|
367
474
|
store.isValidating = createSignal(false);
|
|
368
475
|
return store;
|
|
369
476
|
}
|
|
477
|
+
/**
|
|
478
|
+
* Validates the form input using the configured Valibot schema. Parses the
|
|
479
|
+
* current form input, processes validation issues, assigns errors to fields,
|
|
480
|
+
* and optionally focuses the first field with an error.
|
|
481
|
+
*
|
|
482
|
+
* @param internalFormStore The form store to validate.
|
|
483
|
+
* @param config The validation configuration.
|
|
484
|
+
*
|
|
485
|
+
* @returns The Valibot validation result.
|
|
486
|
+
*/
|
|
370
487
|
async function validateFormInput(internalFormStore, config) {
|
|
371
488
|
internalFormStore.validators++;
|
|
372
489
|
internalFormStore.isValidating.value = true;
|
|
373
|
-
const result = await internalFormStore.parse(untrack(() => getFieldInput(internalFormStore)));
|
|
490
|
+
const result = await internalFormStore.parse(untrack(() => /* @__PURE__ */ getFieldInput(internalFormStore)));
|
|
374
491
|
let rootErrors;
|
|
375
492
|
let nestedErrors;
|
|
376
493
|
if (result.issues) {
|
|
@@ -409,16 +526,46 @@ async function validateFormInput(internalFormStore, config) {
|
|
|
409
526
|
});
|
|
410
527
|
return result;
|
|
411
528
|
}
|
|
529
|
+
/**
|
|
530
|
+
* Validates the form input if required based on the validation mode and form
|
|
531
|
+
* state. Determines whether to use initial validation mode, revalidation mode,
|
|
532
|
+
* or skip validation entirely.
|
|
533
|
+
*
|
|
534
|
+
* @param internalFormStore The form store to validate.
|
|
535
|
+
* @param internalFieldStore The field store that triggered validation.
|
|
536
|
+
* @param validationMode The validation mode that triggered this check.
|
|
537
|
+
*/
|
|
412
538
|
function validateIfRequired(internalFormStore, internalFieldStore, validationMode) {
|
|
413
|
-
if (validationMode === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
|
|
539
|
+
if (validationMode === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => /* @__PURE__ */ getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
|
|
414
540
|
}
|
|
541
|
+
/**
|
|
542
|
+
* Internal symbol constant.
|
|
543
|
+
*/
|
|
415
544
|
const INTERNAL = "~internal";
|
|
416
545
|
|
|
417
546
|
//#endregion
|
|
418
547
|
//#region ../../packages/methods/dist/index.qwik.js
|
|
419
|
-
|
|
420
|
-
|
|
548
|
+
/**
|
|
549
|
+
* Focuses the first input element of a field. This is useful for
|
|
550
|
+
* programmatically setting focus to a specific field, such as after
|
|
551
|
+
* validation errors or user interactions.
|
|
552
|
+
*
|
|
553
|
+
* @param form The form store containing the field.
|
|
554
|
+
* @param config The focus field configuration.
|
|
555
|
+
*/
|
|
556
|
+
function focus(form, config) {
|
|
557
|
+
getFieldStore(form[INTERNAL], config.path).elements[0]?.focus();
|
|
421
558
|
}
|
|
559
|
+
/**
|
|
560
|
+
* Retrieves all error messages from all fields in the form by walking through
|
|
561
|
+
* the entire field store tree. This is useful for displaying a summary of all
|
|
562
|
+
* validation errors across the form.
|
|
563
|
+
*
|
|
564
|
+
* @param form The form store to retrieve errors from.
|
|
565
|
+
*
|
|
566
|
+
* @returns A non-empty array of error messages, or null if no errors exist.
|
|
567
|
+
*/
|
|
568
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
422
569
|
function getAllErrors(form) {
|
|
423
570
|
let allErrors = null;
|
|
424
571
|
walkFieldStore(form[INTERNAL], (internalFieldStore) => {
|
|
@@ -428,12 +575,15 @@ function getAllErrors(form) {
|
|
|
428
575
|
});
|
|
429
576
|
return allErrors;
|
|
430
577
|
}
|
|
578
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
431
579
|
function getErrors(form, config) {
|
|
432
580
|
return (config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]).errors.value;
|
|
433
581
|
}
|
|
582
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
434
583
|
function getInput(form, config) {
|
|
435
584
|
return getFieldInput(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]);
|
|
436
585
|
}
|
|
586
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
437
587
|
function handleSubmit(form, handler) {
|
|
438
588
|
return async (event) => {
|
|
439
589
|
event.preventDefault();
|
|
@@ -450,6 +600,13 @@ function handleSubmit(form, handler) {
|
|
|
450
600
|
}
|
|
451
601
|
};
|
|
452
602
|
}
|
|
603
|
+
/**
|
|
604
|
+
* Inserts a new item into a field array at the specified index. All items at
|
|
605
|
+
* or after the insertion point are shifted up by one index.
|
|
606
|
+
*
|
|
607
|
+
* @param form The form store containing the field array.
|
|
608
|
+
* @param config The insert configuration specifying the path, index, and initial value.
|
|
609
|
+
*/
|
|
453
610
|
function insert(form, config) {
|
|
454
611
|
const internalFormStore = form[INTERNAL];
|
|
455
612
|
let internalFieldStore = internalFormStore;
|
|
@@ -477,6 +634,13 @@ function insert(form, config) {
|
|
|
477
634
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
478
635
|
});
|
|
479
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* Moves an item from one index to another within a field array. All items
|
|
639
|
+
* between the source and destination indices are shifted accordingly.
|
|
640
|
+
*
|
|
641
|
+
* @param form The form store containing the field array.
|
|
642
|
+
* @param config The move configuration specifying the path and source/destination indices.
|
|
643
|
+
*/
|
|
480
644
|
function move(form, config) {
|
|
481
645
|
const internalFormStore = form[INTERNAL];
|
|
482
646
|
const internalArrayStore = getFieldStore(internalFormStore, config.path);
|
|
@@ -487,15 +651,22 @@ function move(form, config) {
|
|
|
487
651
|
internalArrayStore.items.value = newItems;
|
|
488
652
|
const tempInternalFieldStore = {};
|
|
489
653
|
initializeFieldStore(tempInternalFieldStore, internalArrayStore.schema.item, void 0, []);
|
|
490
|
-
copyItemState(internalArrayStore.children[config.from
|
|
654
|
+
copyItemState(internalArrayStore.children[config.from], tempInternalFieldStore);
|
|
491
655
|
if (config.from < config.to) for (let index = config.from; index < config.to; index++) copyItemState(internalArrayStore.children[index + 1], internalArrayStore.children[index]);
|
|
492
656
|
else for (let index = config.from; index > config.to; index--) copyItemState(internalArrayStore.children[index - 1], internalArrayStore.children[index]);
|
|
493
|
-
copyItemState(tempInternalFieldStore, internalArrayStore.children[config.
|
|
657
|
+
copyItemState(tempInternalFieldStore, internalArrayStore.children[config.to]);
|
|
494
658
|
internalArrayStore.isTouched.value = true;
|
|
495
659
|
internalArrayStore.isDirty.value = internalArrayStore.startItems.value.join() !== newItems.join();
|
|
496
660
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
497
661
|
});
|
|
498
662
|
}
|
|
663
|
+
/**
|
|
664
|
+
* Removes an item from a field array at the specified index. All items after
|
|
665
|
+
* the removed item are shifted down by one index.
|
|
666
|
+
*
|
|
667
|
+
* @param form The form store containing the field array.
|
|
668
|
+
* @param config The remove configuration specifying the path and index.
|
|
669
|
+
*/
|
|
499
670
|
function remove(form, config) {
|
|
500
671
|
const internalFormStore = form[INTERNAL];
|
|
501
672
|
const internalArrayStore = getFieldStore(internalFormStore, config.path);
|
|
@@ -510,6 +681,12 @@ function remove(form, config) {
|
|
|
510
681
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
511
682
|
});
|
|
512
683
|
}
|
|
684
|
+
/**
|
|
685
|
+
* Replaces an item in a field array at the specified index with new initial input.
|
|
686
|
+
*
|
|
687
|
+
* @param form The form store containing the field array.
|
|
688
|
+
* @param config The replace configuration specifying the path, index, and initial input.
|
|
689
|
+
*/
|
|
513
690
|
function replace(form, config) {
|
|
514
691
|
const internalFormStore = form[INTERNAL];
|
|
515
692
|
const internalArrayStore = getFieldStore(internalFormStore, config.path);
|
|
@@ -565,9 +742,21 @@ function setInput(form, config) {
|
|
|
565
742
|
validateIfRequired(internalFormStore, config.path ? getFieldStore(internalFormStore, config.path) : internalFormStore, "input");
|
|
566
743
|
});
|
|
567
744
|
}
|
|
745
|
+
/**
|
|
746
|
+
* Programmatically requests form submission by calling the native
|
|
747
|
+
* `requestSubmit()` method on the underlying form element.
|
|
748
|
+
*
|
|
749
|
+
* @param form The form store to submit.
|
|
750
|
+
*/
|
|
568
751
|
function submit(form) {
|
|
569
752
|
form[INTERNAL].element?.requestSubmit();
|
|
570
753
|
}
|
|
754
|
+
/**
|
|
755
|
+
* Swaps two items in a field array by exchanging their positions.
|
|
756
|
+
*
|
|
757
|
+
* @param form The form store containing the field array.
|
|
758
|
+
* @param config The swap configuration specifying the path and indices to swap.
|
|
759
|
+
*/
|
|
571
760
|
function swap(form, config) {
|
|
572
761
|
const internalFormStore = form[INTERNAL];
|
|
573
762
|
const internalArrayStore = getFieldStore(internalFormStore, config.path);
|
|
@@ -584,25 +773,46 @@ function swap(form, config) {
|
|
|
584
773
|
validateIfRequired(internalFormStore, internalArrayStore, "input");
|
|
585
774
|
});
|
|
586
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* Validates the entire form input against its schema. Returns a safe parse result
|
|
778
|
+
* indicating success or failure with detailed issues. Optionally focuses the first
|
|
779
|
+
* field with validation errors.
|
|
780
|
+
*
|
|
781
|
+
* @param form The form store to validate.
|
|
782
|
+
* @param config The validate form configuration specifying focus behavior.
|
|
783
|
+
*
|
|
784
|
+
* @returns A promise resolving to the validation result.
|
|
785
|
+
*/
|
|
587
786
|
function validate(form, config) {
|
|
588
787
|
return validateFormInput(form[INTERNAL], config);
|
|
589
788
|
}
|
|
590
789
|
|
|
591
790
|
//#endregion
|
|
592
791
|
//#region src/hooks/usePathSignal/usePathSignal.ts
|
|
792
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
593
793
|
function isEqual(a, b) {
|
|
594
794
|
if (a.length !== b.length) return false;
|
|
595
795
|
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
596
796
|
return true;
|
|
597
797
|
}
|
|
798
|
+
/**
|
|
799
|
+
* Creates a readonly signal for a field path. Compares new path values for
|
|
800
|
+
* equality to avoid unnecessary updates to the signal.
|
|
801
|
+
*
|
|
802
|
+
* @param path The field path to wrap in a signal.
|
|
803
|
+
*
|
|
804
|
+
* @returns A readonly signal containing the field path.
|
|
805
|
+
*/
|
|
806
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
598
807
|
function usePathSignal(path) {
|
|
599
808
|
const signal = useSignal(path);
|
|
600
|
-
if (
|
|
809
|
+
if (!/* @__PURE__ */ isEqual(signal.value, path)) signal.value = path;
|
|
601
810
|
return signal;
|
|
602
811
|
}
|
|
603
812
|
|
|
604
813
|
//#endregion
|
|
605
814
|
//#region src/hooks/useField/useField.ts
|
|
815
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
606
816
|
function useField(form, config) {
|
|
607
817
|
const pathSignal = usePathSignal(config.path);
|
|
608
818
|
const internalFormStore = form[INTERNAL];
|
|
@@ -648,6 +858,7 @@ function useField(form, config) {
|
|
|
648
858
|
|
|
649
859
|
//#endregion
|
|
650
860
|
//#region src/hooks/useFieldArray/useFieldArray.ts
|
|
861
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
651
862
|
function useFieldArray(form, config) {
|
|
652
863
|
const pathSignal = usePathSignal(config.path);
|
|
653
864
|
const internalFieldStore = useComputed$(() => getFieldStore(form[INTERNAL], pathSignal.value));
|
|
@@ -663,6 +874,15 @@ function useFieldArray(form, config) {
|
|
|
663
874
|
|
|
664
875
|
//#endregion
|
|
665
876
|
//#region src/hooks/useResolvedQrl/useResolvedQrl.ts
|
|
877
|
+
/**
|
|
878
|
+
* Resolves a QRL to its value within a Qwik component. Caches the resolved value
|
|
879
|
+
* to avoid re-resolving on each render and throws a promise during resolution.
|
|
880
|
+
*
|
|
881
|
+
* @param qrl The QRL to resolve.
|
|
882
|
+
*
|
|
883
|
+
* @returns The resolved value from the QRL.
|
|
884
|
+
*/
|
|
885
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
666
886
|
function useResolvedQrl(qrl) {
|
|
667
887
|
const store = useSignal();
|
|
668
888
|
const resolved = qrl.resolved ?? store.value;
|
|
@@ -674,6 +894,7 @@ function useResolvedQrl(qrl) {
|
|
|
674
894
|
|
|
675
895
|
//#endregion
|
|
676
896
|
//#region src/hooks/useForm$/useForm$.ts
|
|
897
|
+
/* @__NO_SIDE_EFFECTS__ */
|
|
677
898
|
function useFormQrl(configQrl) {
|
|
678
899
|
const config = useResolvedQrl(configQrl);
|
|
679
900
|
const form = useConstant(() => {
|
|
@@ -698,12 +919,22 @@ function useFormQrl(configQrl) {
|
|
|
698
919
|
});
|
|
699
920
|
return form;
|
|
700
921
|
}
|
|
922
|
+
/**
|
|
923
|
+
* Creates a reactive form store from a form configuration. The form store
|
|
924
|
+
* manages form state and provides reactive properties.
|
|
925
|
+
*
|
|
926
|
+
* @returns The form store with reactive properties.
|
|
927
|
+
*/
|
|
701
928
|
const useForm$ = implicit$FirstArg(useFormQrl);
|
|
702
929
|
|
|
703
930
|
//#endregion
|
|
704
931
|
//#region src/components/Field/Field.tsx
|
|
705
932
|
/**
|
|
706
|
-
* Headless form field that provides reactive properties and state.
|
|
933
|
+
* Headless form field component that provides reactive properties and state.
|
|
934
|
+
* The field component takes a form store, path to field, and a render function
|
|
935
|
+
* that receives a field store to display field state and handle user interactions.
|
|
936
|
+
*
|
|
937
|
+
* @returns The UI of the field to be rendered.
|
|
707
938
|
*/
|
|
708
939
|
const Field = component$(({ of, path, render$ }) => {
|
|
709
940
|
const field = useField(of, { path });
|
|
@@ -714,7 +945,12 @@ const Field = component$(({ of, path, render$ }) => {
|
|
|
714
945
|
//#endregion
|
|
715
946
|
//#region src/components/FieldArray/FieldArray.tsx
|
|
716
947
|
/**
|
|
717
|
-
* Headless field array that provides reactive properties and state.
|
|
948
|
+
* Headless field array component that provides reactive properties and state.
|
|
949
|
+
* The field array component takes a form store, path to array field, and a render
|
|
950
|
+
* function that receives a field array store to manage array items and handle
|
|
951
|
+
* array operations.
|
|
952
|
+
*
|
|
953
|
+
* @returns The UI of the field array to be rendered.
|
|
718
954
|
*/
|
|
719
955
|
const FieldArray = component$(({ of, path, render$ }) => {
|
|
720
956
|
const field = useFieldArray(of, { path });
|
|
@@ -724,6 +960,12 @@ const FieldArray = component$(({ of, path, render$ }) => {
|
|
|
724
960
|
|
|
725
961
|
//#endregion
|
|
726
962
|
//#region src/components/Form/Form.tsx
|
|
963
|
+
/**
|
|
964
|
+
* Form component that manages form submission and applies internal state.
|
|
965
|
+
* Wraps form element and passes submission events to the provided handler.
|
|
966
|
+
*
|
|
967
|
+
* @returns The a native form element.
|
|
968
|
+
*/
|
|
727
969
|
const Form = component$(({ of, onSubmit$,...other }) => {
|
|
728
970
|
return /* @__PURE__ */ jsx("form", {
|
|
729
971
|
...other,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formisch/qwik",
|
|
3
3
|
"description": "The modular and type-safe form library for Qwik",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.1",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Fabian Hiller",
|
|
7
7
|
"homepage": "https://formisch.dev",
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"valibot": "^1.1.0",
|
|
50
50
|
"vite": "7.0.4",
|
|
51
51
|
"vite-tsconfig-paths": "^5.1.4",
|
|
52
|
-
"@formisch/core": "0.4.
|
|
53
|
-
"@formisch/methods": "0.
|
|
52
|
+
"@formisch/core": "0.4.1",
|
|
53
|
+
"@formisch/methods": "0.4.1"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@qwik.dev/core": ">=2",
|