@formisch/solid 0.1.1 → 0.3.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 +22 -10
- package/dist/dev.js +77 -54
- package/dist/dev.jsx +85 -60
- package/dist/index.d.ts +18 -15
- package/dist/index.js +77 -54
- package/dist/index.jsx +85 -60
- package/package.json +4 -8
package/dist/index.jsx
CHANGED
|
@@ -104,6 +104,7 @@ function copyItemState(fromInternalFieldStore, toInternalFieldStore) {
|
|
|
104
104
|
}
|
|
105
105
|
function resetItemState(internalFieldStore, initialInput) {
|
|
106
106
|
batch(() => {
|
|
107
|
+
internalFieldStore.errors.value = null;
|
|
107
108
|
if (internalFieldStore.kind === "array") {
|
|
108
109
|
internalFieldStore.isTouched.value = false;
|
|
109
110
|
internalFieldStore.isDirty.value = false;
|
|
@@ -260,6 +261,7 @@ function setFieldInput(internalFieldStore, input) {
|
|
|
260
261
|
} else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) setFieldInput(internalFieldStore.children[key], input[key]);
|
|
261
262
|
else {
|
|
262
263
|
internalFieldStore.input.value = input;
|
|
264
|
+
internalFieldStore.isTouched.value = true;
|
|
263
265
|
const startInput = untrack(() => internalFieldStore.startInput.value);
|
|
264
266
|
internalFieldStore.isDirty.value = startInput !== input && (startInput !== void 0 || input !== "" && !Number.isNaN(input));
|
|
265
267
|
}
|
|
@@ -288,12 +290,12 @@ function walkFieldStore(internalFieldStore, callback) {
|
|
|
288
290
|
if (internalFieldStore.kind === "array") for (let index = 0; index < untrack(() => internalFieldStore.items.value).length; index++) walkFieldStore(internalFieldStore.children[index], callback);
|
|
289
291
|
else if (internalFieldStore.kind === "object") for (const key in internalFieldStore.children) walkFieldStore(internalFieldStore.children[key], callback);
|
|
290
292
|
}
|
|
291
|
-
function createFormStore(config,
|
|
293
|
+
function createFormStore(config, parse) {
|
|
292
294
|
const store = {};
|
|
293
295
|
initializeFieldStore(store, config.schema, config.initialInput, []);
|
|
294
|
-
store.
|
|
295
|
-
store.
|
|
296
|
-
store.
|
|
296
|
+
store.validate = config.validate ?? "submit";
|
|
297
|
+
store.revalidate = config.revalidate ?? "input";
|
|
298
|
+
store.parse = parse;
|
|
297
299
|
store.isSubmitting = createSignal(false);
|
|
298
300
|
store.isSubmitted = createSignal(false);
|
|
299
301
|
store.isValidating = createSignal(false);
|
|
@@ -302,7 +304,7 @@ function createFormStore(config, validate2) {
|
|
|
302
304
|
async function validateFormInput(internalFormStore, config) {
|
|
303
305
|
internalFormStore.validators++;
|
|
304
306
|
internalFormStore.isValidating.value = true;
|
|
305
|
-
const result = await internalFormStore.
|
|
307
|
+
const result = await internalFormStore.parse(untrack(() => getFieldInput(internalFormStore)));
|
|
306
308
|
let rootErrors;
|
|
307
309
|
let nestedErrors;
|
|
308
310
|
if (result.issues) {
|
|
@@ -342,7 +344,7 @@ async function validateFormInput(internalFormStore, config) {
|
|
|
342
344
|
return result;
|
|
343
345
|
}
|
|
344
346
|
function validateIfRequired(internalFormStore, internalFieldStore, validationModes) {
|
|
345
|
-
if (validationModes === (internalFormStore.
|
|
347
|
+
if (validationModes === (internalFormStore.validate === "initial" || (internalFormStore.validate === "submit" ? untrack(() => internalFormStore.isSubmitted.value) : untrack(() => getFieldBool(internalFieldStore, "errors"))) ? internalFormStore.revalidate : internalFormStore.validate)) validateFormInput(internalFormStore);
|
|
346
348
|
}
|
|
347
349
|
var INTERNAL = "~internal";
|
|
348
350
|
|
|
@@ -365,6 +367,22 @@ function getErrors(form, config) {
|
|
|
365
367
|
function getInput(form, config) {
|
|
366
368
|
return getFieldInput(config?.path ? getFieldStore(form[INTERNAL], config.path) : form[INTERNAL]);
|
|
367
369
|
}
|
|
370
|
+
function handleSubmit(form, handler) {
|
|
371
|
+
return async (event) => {
|
|
372
|
+
event.preventDefault();
|
|
373
|
+
const internalFormStore = form[INTERNAL];
|
|
374
|
+
internalFormStore.isSubmitted.value = true;
|
|
375
|
+
internalFormStore.isSubmitting.value = true;
|
|
376
|
+
try {
|
|
377
|
+
const result = await validateFormInput(internalFormStore, { shouldFocus: true });
|
|
378
|
+
if (result.success) await handler(result.output, event);
|
|
379
|
+
} catch (error) {
|
|
380
|
+
internalFormStore.errors.value = [error instanceof Error ? error.message : "An unknown error has occurred."];
|
|
381
|
+
} finally {
|
|
382
|
+
internalFormStore.isSubmitting.value = false;
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
}
|
|
368
386
|
function insert(form, config) {
|
|
369
387
|
const internalFormStore = form[INTERNAL];
|
|
370
388
|
const internalArrayStore = getFieldStore(internalFormStore, config.path);
|
|
@@ -456,7 +474,7 @@ function reset(form, config) {
|
|
|
456
474
|
});
|
|
457
475
|
if (!config?.path) {
|
|
458
476
|
if (!config?.keepSubmitted) internalFormStore.isSubmitted.value = false;
|
|
459
|
-
if (internalFormStore.
|
|
477
|
+
if (internalFormStore.validate === "initial") validateFormInput(internalFormStore);
|
|
460
478
|
}
|
|
461
479
|
});
|
|
462
480
|
});
|
|
@@ -468,7 +486,6 @@ function setInput(form, config) {
|
|
|
468
486
|
batch(() => {
|
|
469
487
|
const internalFormStore = form[INTERNAL];
|
|
470
488
|
const internalFieldStore = config.path ? getFieldStore(internalFormStore, config.path) : internalFormStore;
|
|
471
|
-
setFieldBool(internalFieldStore, "isTouched", true);
|
|
472
489
|
setFieldInput(internalFieldStore, config.input);
|
|
473
490
|
validateIfRequired(internalFormStore, internalFieldStore, "input");
|
|
474
491
|
});
|
|
@@ -497,12 +514,22 @@ function validate(form, config) {
|
|
|
497
514
|
}
|
|
498
515
|
|
|
499
516
|
// src/primitives/createForm/createForm.ts
|
|
517
|
+
import { createMemo } from "solid-js";
|
|
500
518
|
import * as v2 from "valibot";
|
|
501
519
|
function createForm(config) {
|
|
502
520
|
const internalFormStore = createFormStore(
|
|
503
521
|
config,
|
|
504
522
|
async (input) => v2.safeParseAsync(config.schema, input)
|
|
505
523
|
);
|
|
524
|
+
const getIsTouched = createMemo(
|
|
525
|
+
() => getFieldBool(internalFormStore, "isTouched")
|
|
526
|
+
);
|
|
527
|
+
const getIsDirty = createMemo(
|
|
528
|
+
() => getFieldBool(internalFormStore, "isDirty")
|
|
529
|
+
);
|
|
530
|
+
const getIsValid = createMemo(
|
|
531
|
+
() => !getFieldBool(internalFormStore, "errors")
|
|
532
|
+
);
|
|
506
533
|
const form = {
|
|
507
534
|
[INTERNAL]: internalFormStore,
|
|
508
535
|
get isSubmitting() {
|
|
@@ -515,26 +542,26 @@ function createForm(config) {
|
|
|
515
542
|
return internalFormStore.isValidating.value;
|
|
516
543
|
},
|
|
517
544
|
get isTouched() {
|
|
518
|
-
return
|
|
545
|
+
return getIsTouched();
|
|
519
546
|
},
|
|
520
547
|
get isDirty() {
|
|
521
|
-
return
|
|
548
|
+
return getIsDirty();
|
|
522
549
|
},
|
|
523
550
|
get isValid() {
|
|
524
|
-
return
|
|
551
|
+
return getIsValid();
|
|
525
552
|
},
|
|
526
553
|
get errors() {
|
|
527
554
|
return internalFormStore.errors.value;
|
|
528
555
|
}
|
|
529
556
|
};
|
|
530
|
-
if (config.
|
|
557
|
+
if (config.validate === "initial") {
|
|
531
558
|
validateFormInput(form[INTERNAL]);
|
|
532
559
|
}
|
|
533
560
|
return form;
|
|
534
561
|
}
|
|
535
562
|
|
|
536
563
|
// src/primitives/useField/useField.ts
|
|
537
|
-
import { createMemo, onCleanup } from "solid-js";
|
|
564
|
+
import { createMemo as createMemo2, onCleanup } from "solid-js";
|
|
538
565
|
|
|
539
566
|
// src/utils/unwrap/unwrap.ts
|
|
540
567
|
function unwrap(value) {
|
|
@@ -546,28 +573,38 @@ function unwrap(value) {
|
|
|
546
573
|
|
|
547
574
|
// src/primitives/useField/useField.ts
|
|
548
575
|
function useField(form, config) {
|
|
549
|
-
const getInternalFormStore =
|
|
550
|
-
const getInternalFieldStore =
|
|
576
|
+
const getInternalFormStore = createMemo2(() => unwrap(form)[INTERNAL]);
|
|
577
|
+
const getInternalFieldStore = createMemo2(
|
|
551
578
|
() => getFieldStore(getInternalFormStore(), unwrap(config).path)
|
|
552
579
|
);
|
|
580
|
+
const getInput2 = createMemo2(() => getFieldInput(getInternalFieldStore()));
|
|
581
|
+
const getIsTouched = createMemo2(
|
|
582
|
+
() => getFieldBool(getInternalFieldStore(), "isTouched")
|
|
583
|
+
);
|
|
584
|
+
const getIsDirty = createMemo2(
|
|
585
|
+
() => getFieldBool(getInternalFieldStore(), "isDirty")
|
|
586
|
+
);
|
|
587
|
+
const getIsValid = createMemo2(
|
|
588
|
+
() => !getFieldBool(getInternalFieldStore(), "errors")
|
|
589
|
+
);
|
|
553
590
|
return {
|
|
554
591
|
get path() {
|
|
555
592
|
return unwrap(config).path;
|
|
556
593
|
},
|
|
557
594
|
get input() {
|
|
558
|
-
return
|
|
595
|
+
return getInput2();
|
|
559
596
|
},
|
|
560
597
|
get errors() {
|
|
561
598
|
return getInternalFieldStore().errors.value;
|
|
562
599
|
},
|
|
563
600
|
get isTouched() {
|
|
564
|
-
return
|
|
601
|
+
return getIsTouched();
|
|
565
602
|
},
|
|
566
603
|
get isDirty() {
|
|
567
|
-
return
|
|
604
|
+
return getIsDirty();
|
|
568
605
|
},
|
|
569
606
|
get isValid() {
|
|
570
|
-
return
|
|
607
|
+
return getIsValid();
|
|
571
608
|
},
|
|
572
609
|
props: {
|
|
573
610
|
get name() {
|
|
@@ -594,11 +631,10 @@ function useField(form, config) {
|
|
|
594
631
|
},
|
|
595
632
|
onInput(event) {
|
|
596
633
|
const internalFieldStore = getInternalFieldStore();
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
internalFieldStore
|
|
634
|
+
setFieldInput(
|
|
635
|
+
internalFieldStore,
|
|
636
|
+
getElementInput(event.currentTarget, internalFieldStore)
|
|
600
637
|
);
|
|
601
|
-
setFieldInput(internalFieldStore, nextValue);
|
|
602
638
|
validateIfRequired(getInternalFormStore(), internalFieldStore, "input");
|
|
603
639
|
},
|
|
604
640
|
onChange() {
|
|
@@ -620,14 +656,23 @@ function useField(form, config) {
|
|
|
620
656
|
}
|
|
621
657
|
|
|
622
658
|
// src/primitives/useFieldArray/useFieldArray.ts
|
|
623
|
-
import { createMemo as
|
|
659
|
+
import { createMemo as createMemo3 } from "solid-js";
|
|
624
660
|
function useFieldArray(form, config) {
|
|
625
|
-
const getInternalFieldStore =
|
|
661
|
+
const getInternalFieldStore = createMemo3(
|
|
626
662
|
() => getFieldStore(
|
|
627
663
|
unwrap(form)[INTERNAL],
|
|
628
664
|
unwrap(config).path
|
|
629
665
|
)
|
|
630
666
|
);
|
|
667
|
+
const getIsTouched = createMemo3(
|
|
668
|
+
() => getFieldBool(getInternalFieldStore(), "isTouched")
|
|
669
|
+
);
|
|
670
|
+
const getIsDirty = createMemo3(
|
|
671
|
+
() => getFieldBool(getInternalFieldStore(), "isDirty")
|
|
672
|
+
);
|
|
673
|
+
const getIsValid = createMemo3(
|
|
674
|
+
() => !getFieldBool(getInternalFieldStore(), "errors")
|
|
675
|
+
);
|
|
631
676
|
return {
|
|
632
677
|
get path() {
|
|
633
678
|
return unwrap(config).path;
|
|
@@ -639,35 +684,33 @@ function useFieldArray(form, config) {
|
|
|
639
684
|
return getInternalFieldStore().errors.value;
|
|
640
685
|
},
|
|
641
686
|
get isTouched() {
|
|
642
|
-
return
|
|
687
|
+
return getIsTouched();
|
|
643
688
|
},
|
|
644
689
|
get isDirty() {
|
|
645
|
-
return
|
|
690
|
+
return getIsDirty();
|
|
646
691
|
},
|
|
647
692
|
get isValid() {
|
|
648
|
-
return
|
|
693
|
+
return getIsValid();
|
|
649
694
|
}
|
|
650
695
|
};
|
|
651
696
|
}
|
|
652
697
|
|
|
653
698
|
// src/components/Field/Field.tsx
|
|
654
699
|
function Field(props) {
|
|
655
|
-
const field = useField(
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
}
|
|
660
|
-
return <>{props.render(field)}</>;
|
|
700
|
+
const field = useField(
|
|
701
|
+
() => props.of,
|
|
702
|
+
() => ({ path: props.path })
|
|
703
|
+
);
|
|
704
|
+
return <>{props.children(field)}</>;
|
|
661
705
|
}
|
|
662
706
|
|
|
663
707
|
// src/components/FieldArray/FieldArray.tsx
|
|
664
708
|
function FieldArray(props) {
|
|
665
|
-
const field = useFieldArray(
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
}
|
|
670
|
-
return <>{props.render(field)}</>;
|
|
709
|
+
const field = useFieldArray(
|
|
710
|
+
() => props.of,
|
|
711
|
+
() => ({ path: props.path })
|
|
712
|
+
);
|
|
713
|
+
return <>{props.children(field)}</>;
|
|
671
714
|
}
|
|
672
715
|
|
|
673
716
|
// src/components/Form/Form.tsx
|
|
@@ -680,26 +723,7 @@ function Form(props) {
|
|
|
680
723
|
ref={(element) => {
|
|
681
724
|
props.of[INTERNAL].element = element;
|
|
682
725
|
}}
|
|
683
|
-
onSubmit={
|
|
684
|
-
event.preventDefault();
|
|
685
|
-
const internalFormStore = props.of[INTERNAL];
|
|
686
|
-
internalFormStore.isSubmitted.value = true;
|
|
687
|
-
internalFormStore.isSubmitting.value = true;
|
|
688
|
-
try {
|
|
689
|
-
const result = await validateFormInput(internalFormStore, {
|
|
690
|
-
shouldFocus: true
|
|
691
|
-
});
|
|
692
|
-
if (result.success) {
|
|
693
|
-
await props.onSubmit(result.output, event);
|
|
694
|
-
}
|
|
695
|
-
} catch (error) {
|
|
696
|
-
internalFormStore.errors.value = [
|
|
697
|
-
error instanceof Error ? error.message : "An unknown error has occurred."
|
|
698
|
-
];
|
|
699
|
-
} finally {
|
|
700
|
-
internalFormStore.isSubmitting.value = false;
|
|
701
|
-
}
|
|
702
|
-
}}
|
|
726
|
+
onSubmit={(event) => handleSubmit(props.of, props.onSubmit)(event)}
|
|
703
727
|
/>;
|
|
704
728
|
}
|
|
705
729
|
export {
|
|
@@ -711,6 +735,7 @@ export {
|
|
|
711
735
|
getAllErrors,
|
|
712
736
|
getErrors,
|
|
713
737
|
getInput,
|
|
738
|
+
handleSubmit,
|
|
714
739
|
insert,
|
|
715
740
|
move,
|
|
716
741
|
remove,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formisch/solid",
|
|
3
3
|
"description": "The modular and type-safe form library for SolidJS",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Fabian Hiller",
|
|
7
7
|
"homepage": "https://formisch.dev",
|
|
@@ -11,13 +11,10 @@
|
|
|
11
11
|
},
|
|
12
12
|
"keywords": [
|
|
13
13
|
"solidjs",
|
|
14
|
-
"
|
|
14
|
+
"form",
|
|
15
15
|
"typescript",
|
|
16
16
|
"schema",
|
|
17
|
-
"validation"
|
|
18
|
-
"parsing",
|
|
19
|
-
"bundle-size",
|
|
20
|
-
"type-safe"
|
|
17
|
+
"validation"
|
|
21
18
|
],
|
|
22
19
|
"type": "module",
|
|
23
20
|
"main": "./dist/index.js",
|
|
@@ -48,8 +45,7 @@
|
|
|
48
45
|
},
|
|
49
46
|
"scripts": {
|
|
50
47
|
"test": "vitest --typecheck",
|
|
51
|
-
"
|
|
52
|
-
"lint": "eslint \"src/**/*.ts*\" && tsc --noEmit && deno check ./src/index.ts",
|
|
48
|
+
"lint": "eslint \"src/**/*.ts*\" && tsc --noEmit",
|
|
53
49
|
"lint.fix": "eslint \"src/**/*.ts*\" --fix",
|
|
54
50
|
"format": "prettier --write ./src",
|
|
55
51
|
"format.check": "prettier --check ./src",
|