remix-validated-form 4.1.0 → 4.1.4-beta.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/.turbo/turbo-build.log +2 -2
- package/browser/ValidatedForm.d.ts +1 -1
- package/browser/ValidatedForm.js +12 -6
- package/browser/internal/MultiValueMap.js +3 -3
- package/browser/internal/hooks-valtio.d.ts +18 -0
- package/browser/internal/hooks-valtio.js +110 -0
- package/browser/internal/hooks-zustand.d.ts +16 -0
- package/browser/internal/hooks-zustand.js +100 -0
- package/browser/internal/hydratable.d.ts +14 -0
- package/browser/internal/hydratable.js +14 -0
- package/browser/internal/immerMiddleware.d.ts +6 -0
- package/browser/internal/immerMiddleware.js +7 -0
- package/browser/internal/logic/getCheckboxChecked copy.d.ts +1 -0
- package/browser/internal/logic/getCheckboxChecked copy.js +9 -0
- package/browser/internal/logic/getCheckboxChecked.d.ts +1 -0
- package/browser/internal/logic/getCheckboxChecked.js +9 -0
- package/browser/internal/logic/getRadioChecked.d.ts +1 -0
- package/browser/internal/logic/getRadioChecked.js +5 -0
- package/browser/internal/logic/setFieldValue.d.ts +1 -0
- package/browser/internal/logic/setFieldValue.js +40 -0
- package/browser/internal/logic/setInputValueInForm.d.ts +1 -0
- package/browser/internal/logic/setInputValueInForm.js +120 -0
- package/browser/internal/setFieldValue.d.ts +20 -0
- package/browser/internal/setFieldValue.js +83 -0
- package/browser/internal/setFormValues.d.ts +2 -0
- package/browser/internal/setFormValues.js +26 -0
- package/browser/internal/state/atomUtils.d.ts +38 -0
- package/browser/internal/state/atomUtils.js +5 -0
- package/browser/internal/state/controlledFields.d.ts +66 -0
- package/browser/internal/state/controlledFields.js +93 -0
- package/browser/internal/state/setFieldValue.d.ts +0 -0
- package/browser/internal/state/setFieldValue.js +1 -0
- package/browser/internal/state-valtio.d.ts +62 -0
- package/browser/internal/state-valtio.js +69 -0
- package/browser/internal/state-zustand.d.ts +47 -0
- package/browser/internal/state-zustand.js +85 -0
- package/browser/internal/util.d.ts +1 -0
- package/browser/internal/util.js +12 -1
- package/build/ValidatedForm.d.ts +1 -1
- package/build/ValidatedForm.js +11 -5
- package/build/internal/MultiValueMap.js +2 -2
- package/build/internal/hooks-valtio.d.ts +18 -0
- package/build/internal/hooks-valtio.js +128 -0
- package/build/internal/hooks-zustand.d.ts +16 -0
- package/build/internal/hooks-zustand.js +117 -0
- package/build/internal/hydratable.d.ts +14 -0
- package/build/internal/hydratable.js +17 -0
- package/build/internal/immerMiddleware.d.ts +6 -0
- package/build/internal/immerMiddleware.js +14 -0
- package/build/internal/logic/getCheckboxChecked.d.ts +1 -0
- package/build/internal/logic/getCheckboxChecked.js +13 -0
- package/build/internal/logic/getRadioChecked.d.ts +1 -0
- package/build/internal/logic/getRadioChecked.js +9 -0
- package/build/internal/logic/setFieldValue.d.ts +1 -0
- package/build/internal/logic/setFieldValue.js +47 -0
- package/build/internal/logic/setInputValueInForm.d.ts +1 -0
- package/build/internal/logic/setInputValueInForm.js +127 -0
- package/build/internal/setFormValues.d.ts +2 -0
- package/build/internal/setFormValues.js +33 -0
- package/build/internal/state/atomUtils.d.ts +38 -0
- package/build/internal/state/atomUtils.js +13 -0
- package/build/internal/state/controlledFields.d.ts +66 -0
- package/build/internal/state/controlledFields.js +95 -0
- package/build/internal/state-valtio.d.ts +62 -0
- package/build/internal/state-valtio.js +83 -0
- package/build/internal/state-zustand.d.ts +47 -0
- package/build/internal/state-zustand.js +91 -0
- package/build/internal/util.d.ts +1 -0
- package/build/internal/util.js +16 -1
- package/package.json +5 -5
- package/src/ValidatedForm.tsx +13 -5
- package/src/internal/MultiValueMap.ts +3 -3
- package/src/internal/util.ts +13 -1
@@ -0,0 +1,83 @@
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
5
|
+
};
|
6
|
+
var _ValidatedFormData_data, _ValidatedFormData_updates, _a;
|
7
|
+
import { useAtomCallback } from "jotai/utils";
|
8
|
+
import groupBy from "lodash/groupBy";
|
9
|
+
import mapValues from "lodash/mapValues";
|
10
|
+
import invariant from "tiny-invariant";
|
11
|
+
import { setInputValueInForm } from "./logic/setInputValueInForm";
|
12
|
+
import { MultiValueMap } from "./MultiValueMap";
|
13
|
+
import { ATOM_SCOPE, formElementAtom } from "./state";
|
14
|
+
import { controlledFieldsAtom, setControlledFieldValueAtom, } from "./state/controlledFields";
|
15
|
+
class ValidatedFormData {
|
16
|
+
constructor(formData, customData) {
|
17
|
+
_ValidatedFormData_data.set(this, new MultiValueMap());
|
18
|
+
_ValidatedFormData_updates.set(this, new Map());
|
19
|
+
// API to mimic form data
|
20
|
+
this.get = (fieldName) => __classPrivateFieldGet(this, _ValidatedFormData_data, "f").getAll(fieldName)[0];
|
21
|
+
this.getAll = (fieldName) => __classPrivateFieldGet(this, _ValidatedFormData_data, "f").getAll(fieldName);
|
22
|
+
this.has = (fieldName) => __classPrivateFieldGet(this, _ValidatedFormData_data, "f").has(fieldName);
|
23
|
+
this.append = (fieldName, value) => {
|
24
|
+
__classPrivateFieldGet(this, _ValidatedFormData_data, "f").add(fieldName, value);
|
25
|
+
};
|
26
|
+
this.delete = (fieldName) => {
|
27
|
+
__classPrivateFieldGet(this, _ValidatedFormData_data, "f").delete(fieldName);
|
28
|
+
__classPrivateFieldGet(this, _ValidatedFormData_updates, "f").set(fieldName, true);
|
29
|
+
};
|
30
|
+
this.set = (fieldName, value) => {
|
31
|
+
__classPrivateFieldGet(this, _ValidatedFormData_data, "f").delete(fieldName);
|
32
|
+
__classPrivateFieldGet(this, _ValidatedFormData_data, "f").add(fieldName, value);
|
33
|
+
__classPrivateFieldGet(this, _ValidatedFormData_updates, "f").set(fieldName, true);
|
34
|
+
};
|
35
|
+
this.entries = () => __classPrivateFieldGet(this, _ValidatedFormData_data, "f").entries();
|
36
|
+
this.values = () => __classPrivateFieldGet(this, _ValidatedFormData_data, "f").values();
|
37
|
+
this[_a] = () => __classPrivateFieldGet(this, _ValidatedFormData_data, "f").entries();
|
38
|
+
// Custom APIs
|
39
|
+
this.setRepeated = (fieldName, value) => {
|
40
|
+
__classPrivateFieldGet(this, _ValidatedFormData_data, "f").delete(fieldName);
|
41
|
+
value.forEach((val) => __classPrivateFieldGet(this, _ValidatedFormData_data, "f").add(fieldName, val));
|
42
|
+
__classPrivateFieldGet(this, _ValidatedFormData_updates, "f").set(fieldName, true);
|
43
|
+
};
|
44
|
+
for (const [key, value] of formData.entries()) {
|
45
|
+
__classPrivateFieldGet(this, _ValidatedFormData_data, "f").add(key, value);
|
46
|
+
}
|
47
|
+
Object.entries(customData).forEach(([name, values]) => {
|
48
|
+
__classPrivateFieldGet(this, _ValidatedFormData_data, "f").delete(name);
|
49
|
+
values.forEach((value) => __classPrivateFieldGet(this, _ValidatedFormData_data, "f").add(name, value));
|
50
|
+
});
|
51
|
+
}
|
52
|
+
*changedFields() {
|
53
|
+
for (const updatedField of __classPrivateFieldGet(this, _ValidatedFormData_updates, "f").keys()) {
|
54
|
+
const value = this.getAll(updatedField);
|
55
|
+
yield [updatedField, value];
|
56
|
+
}
|
57
|
+
}
|
58
|
+
}
|
59
|
+
_ValidatedFormData_data = new WeakMap(), _ValidatedFormData_updates = new WeakMap(), _a = Symbol.iterator;
|
60
|
+
export const useSetFormValues = (formId) => useAtomCallback(async (get, set, update) => {
|
61
|
+
var _b;
|
62
|
+
const form = get(formElementAtom(formId));
|
63
|
+
invariant(form, "Unable to access form element when setting field value. This is likely a bug in remix-validated-form.");
|
64
|
+
const formData = new FormData(form);
|
65
|
+
const controlledFields = get(controlledFieldsAtom(formId));
|
66
|
+
const controlledData = mapValues(groupBy(controlledFields, (field) => field.name), (val) => val.map((field) => get(field.valueAtom)));
|
67
|
+
const validatedFormData = new ValidatedFormData(formData, controlledData);
|
68
|
+
update(validatedFormData);
|
69
|
+
for (const [field, value] of validatedFormData.changedFields()) {
|
70
|
+
const relevantFields = controlledFields.filter(({ name }) => name === field);
|
71
|
+
if (relevantFields.length === 0) {
|
72
|
+
setInputValueInForm(form, field, value);
|
73
|
+
return;
|
74
|
+
}
|
75
|
+
for (const [index, field] of relevantFields.entries()) {
|
76
|
+
const itemValue = (_b = value[index]) !== null && _b !== void 0 ? _b : "";
|
77
|
+
await set(setControlledFieldValueAtom, {
|
78
|
+
internalFieldId: field.internalId,
|
79
|
+
value: itemValue,
|
80
|
+
});
|
81
|
+
}
|
82
|
+
}
|
83
|
+
}, ATOM_SCOPE);
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { useAtomCallback } from "jotai/utils";
|
2
|
+
import { useCallback } from "react";
|
3
|
+
import invariant from "tiny-invariant";
|
4
|
+
import { setInputValueInForm } from "./logic/setInputValueInForm";
|
5
|
+
import { ATOM_SCOPE, formElementAtom } from "./state";
|
6
|
+
import { controlledFieldsAtom, setControlledFieldValueAtom, } from "./state/controlledFields";
|
7
|
+
export const useSetFormValues = (formId) => useAtomCallback(useCallback(async (get, set, updatedValues) => {
|
8
|
+
const form = get(formElementAtom(formId));
|
9
|
+
invariant(form, "Unable to access form element when setting field value. This is likely a bug in remix-validated-form.");
|
10
|
+
const controlledFields = get(controlledFieldsAtom(formId));
|
11
|
+
const updatePromises = [];
|
12
|
+
for (const [field, value] of Object.entries(updatedValues)) {
|
13
|
+
const isControlled = !!controlledFields[field];
|
14
|
+
if (isControlled) {
|
15
|
+
updatePromises.push(set(setControlledFieldValueAtom, {
|
16
|
+
field,
|
17
|
+
formId,
|
18
|
+
value,
|
19
|
+
}));
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
setInputValueInForm(form, field, Array.isArray(value) ? value : [value]);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
await Promise.all(updatePromises);
|
26
|
+
}, [formId]), ATOM_SCOPE);
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { Atom } from "jotai";
|
2
|
+
export declare type InternalFormId = string | symbol;
|
3
|
+
export declare const formAtomFamily: <T>(data: T) => {
|
4
|
+
(param: InternalFormId): Atom<T> & {
|
5
|
+
write: (get: {
|
6
|
+
<Value>(atom: Atom<Value | Promise<Value>>): Value;
|
7
|
+
<Value_1>(atom: Atom<Promise<Value_1>>): Value_1;
|
8
|
+
<Value_2>(atom: Atom<Value_2>): Value_2 extends Promise<infer V> ? V : Value_2;
|
9
|
+
} & {
|
10
|
+
<Value_3>(atom: Atom<Value_3 | Promise<Value_3>>, options: {
|
11
|
+
unstable_promise: true;
|
12
|
+
}): Value_3 | Promise<Value_3>;
|
13
|
+
<Value_4>(atom: Atom<Promise<Value_4>>, options: {
|
14
|
+
unstable_promise: true;
|
15
|
+
}): Value_4 | Promise<Value_4>;
|
16
|
+
<Value_5>(atom: Atom<Value_5>, options: {
|
17
|
+
unstable_promise: true;
|
18
|
+
}): (Value_5 extends Promise<infer V> ? V : Value_5) | Promise<Value_5 extends Promise<infer V> ? V : Value_5>;
|
19
|
+
}, set: {
|
20
|
+
<Value_6, Result extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_6, undefined, Result>): Result;
|
21
|
+
<Value_7, Update, Result_1 extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_7, Update, Result_1>, update: Update): Result_1;
|
22
|
+
}, update: T | ((prev: T) => T)) => void;
|
23
|
+
onMount?: (<S extends import("jotai/core/atom").SetAtom<T | ((prev: T) => T), void>>(setAtom: S) => void | (() => void)) | undefined;
|
24
|
+
} & {
|
25
|
+
init: T;
|
26
|
+
};
|
27
|
+
remove(param: InternalFormId): void;
|
28
|
+
setShouldRemove(shouldRemove: ((createdAt: number, param: InternalFormId) => boolean) | null): void;
|
29
|
+
};
|
30
|
+
export declare type FieldAtomKey = {
|
31
|
+
formId: InternalFormId;
|
32
|
+
field: string;
|
33
|
+
};
|
34
|
+
export declare const fieldAtomFamily: <T extends Atom<unknown>>(func: (key: FieldAtomKey) => T) => {
|
35
|
+
(param: FieldAtomKey): T;
|
36
|
+
remove(param: FieldAtomKey): void;
|
37
|
+
setShouldRemove(shouldRemove: ((createdAt: number, param: FieldAtomKey) => boolean) | null): void;
|
38
|
+
};
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { PrimitiveAtom } from "jotai";
|
2
|
+
import { InternalFormId } from "./atomUtils";
|
3
|
+
export declare type ValueSerializer = (val: unknown) => string;
|
4
|
+
export declare const controlledFieldsAtom: {
|
5
|
+
(param: InternalFormId): import("jotai").Atom<Record<string, PrimitiveAtom<unknown>>> & {
|
6
|
+
write: (get: {
|
7
|
+
<Value>(atom: import("jotai").Atom<Value | Promise<Value>>): Value;
|
8
|
+
<Value_1>(atom: import("jotai").Atom<Promise<Value_1>>): Value_1;
|
9
|
+
<Value_2>(atom: import("jotai").Atom<Value_2>): Value_2 extends Promise<infer V> ? V : Value_2;
|
10
|
+
} & {
|
11
|
+
<Value_3>(atom: import("jotai").Atom<Value_3 | Promise<Value_3>>, options: {
|
12
|
+
unstable_promise: true;
|
13
|
+
}): Value_3 | Promise<Value_3>;
|
14
|
+
<Value_4>(atom: import("jotai").Atom<Promise<Value_4>>, options: {
|
15
|
+
unstable_promise: true;
|
16
|
+
}): Value_4 | Promise<Value_4>;
|
17
|
+
<Value_5>(atom: import("jotai").Atom<Value_5>, options: {
|
18
|
+
unstable_promise: true;
|
19
|
+
}): (Value_5 extends Promise<infer V> ? V : Value_5) | Promise<Value_5 extends Promise<infer V> ? V : Value_5>;
|
20
|
+
}, set: {
|
21
|
+
<Value_6, Result extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_6, undefined, Result>): Result;
|
22
|
+
<Value_7, Update, Result_1 extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_7, Update, Result_1>, update: Update): Result_1;
|
23
|
+
}, update: Record<string, PrimitiveAtom<unknown>> | ((prev: Record<string, PrimitiveAtom<unknown>>) => Record<string, PrimitiveAtom<unknown>>)) => void;
|
24
|
+
onMount?: (<S extends (update: Record<string, PrimitiveAtom<unknown>> | ((prev: Record<string, PrimitiveAtom<unknown>>) => Record<string, PrimitiveAtom<unknown>>)) => void>(setAtom: S) => void | (() => void)) | undefined;
|
25
|
+
} & {
|
26
|
+
init: Record<string, PrimitiveAtom<unknown>>;
|
27
|
+
};
|
28
|
+
remove(param: InternalFormId): void;
|
29
|
+
setShouldRemove(shouldRemove: ((createdAt: number, param: InternalFormId) => boolean) | null): void;
|
30
|
+
};
|
31
|
+
export declare const setControlledFieldValueAtom: import("jotai").Atom<null> & {
|
32
|
+
write: (get: {
|
33
|
+
<Value>(atom: import("jotai").Atom<Value | Promise<Value>>): Value;
|
34
|
+
<Value_1>(atom: import("jotai").Atom<Promise<Value_1>>): Value_1;
|
35
|
+
<Value_2>(atom: import("jotai").Atom<Value_2>): Value_2 extends Promise<infer V> ? V : Value_2;
|
36
|
+
} & {
|
37
|
+
<Value_3>(atom: import("jotai").Atom<Value_3 | Promise<Value_3>>, options: {
|
38
|
+
unstable_promise: true;
|
39
|
+
}): Value_3 | Promise<Value_3>;
|
40
|
+
<Value_4>(atom: import("jotai").Atom<Promise<Value_4>>, options: {
|
41
|
+
unstable_promise: true;
|
42
|
+
}): Value_4 | Promise<Value_4>;
|
43
|
+
<Value_5>(atom: import("jotai").Atom<Value_5>, options: {
|
44
|
+
unstable_promise: true;
|
45
|
+
}): (Value_5 extends Promise<infer V> ? V : Value_5) | Promise<Value_5 extends Promise<infer V> ? V : Value_5>;
|
46
|
+
}, set: {
|
47
|
+
<Value_6, Result extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_6, undefined, Result>): Result;
|
48
|
+
<Value_7, Update, Result_1 extends void | Promise<void>>(atom: import("jotai").WritableAtom<Value_7, Update, Result_1>, update: Update): Result_1;
|
49
|
+
}, update: {
|
50
|
+
formId: InternalFormId;
|
51
|
+
field: string;
|
52
|
+
value: unknown;
|
53
|
+
}) => Promise<void>;
|
54
|
+
onMount?: (<S extends (update: {
|
55
|
+
formId: InternalFormId;
|
56
|
+
field: string;
|
57
|
+
value: unknown;
|
58
|
+
}) => Promise<void>>(setAtom: S) => void | (() => void)) | undefined;
|
59
|
+
} & {
|
60
|
+
init: null;
|
61
|
+
};
|
62
|
+
export declare const useAllControlledFields: (formId: InternalFormId) => Record<string, PrimitiveAtom<unknown>>;
|
63
|
+
export declare const useControlledFieldValue: (formId: InternalFormId, field: string) => any;
|
64
|
+
export declare const useControllableValue: (formId: InternalFormId, field: string) => readonly [any, (value: unknown) => Promise<void>];
|
65
|
+
export declare const useFieldSerializer: (formId: InternalFormId, field: string) => ValueSerializer;
|
66
|
+
export declare const useRegisterFieldSerializer: (formId: InternalFormId, field: string, serializer?: ValueSerializer | undefined) => void;
|
@@ -0,0 +1,93 @@
|
|
1
|
+
import { atom } from "jotai";
|
2
|
+
import omit from "lodash/omit";
|
3
|
+
import { useCallback, useEffect } from "react";
|
4
|
+
import { useFieldDefaultValue, useFormAtomValue, useFormAtom, useFormUpdateAtom, } from "../hooks";
|
5
|
+
import { isHydratedAtom } from "../state";
|
6
|
+
import { fieldAtomFamily, formAtomFamily, } from "./atomUtils";
|
7
|
+
export const controlledFieldsAtom = formAtomFamily({});
|
8
|
+
const refCountAtom = fieldAtomFamily(() => atom(0));
|
9
|
+
const fieldValueAtom = fieldAtomFamily(() => atom(undefined));
|
10
|
+
const fieldValueHydratedAtom = fieldAtomFamily(() => atom(false));
|
11
|
+
const pendingValidateAtom = fieldAtomFamily(() => atom(undefined));
|
12
|
+
const valueSerializerAtom = fieldAtomFamily(() => atom({
|
13
|
+
serializer: (value) => {
|
14
|
+
if (value === undefined)
|
15
|
+
return "";
|
16
|
+
return JSON.stringify(value);
|
17
|
+
},
|
18
|
+
}));
|
19
|
+
const registerAtom = atom(null, (get, set, { formId, field }) => {
|
20
|
+
set(refCountAtom({ formId, field }), (prev) => prev + 1);
|
21
|
+
const newRefCount = get(refCountAtom({ formId, field }));
|
22
|
+
// We don't set hydrated here because it gets set when we know
|
23
|
+
// we have the right default values
|
24
|
+
if (newRefCount === 1) {
|
25
|
+
set(controlledFieldsAtom(formId), (prev) => ({
|
26
|
+
...prev,
|
27
|
+
[field]: fieldValueAtom({ formId, field }),
|
28
|
+
}));
|
29
|
+
}
|
30
|
+
});
|
31
|
+
const unregisterAtom = atom(null, (get, set, { formId, field }) => {
|
32
|
+
set(refCountAtom({ formId, field }), (prev) => prev - 1);
|
33
|
+
const newRefCount = get(refCountAtom({ formId, field }));
|
34
|
+
if (newRefCount === 0) {
|
35
|
+
set(controlledFieldsAtom(formId), (prev) => omit(prev, field));
|
36
|
+
fieldValueAtom.remove({ formId, field });
|
37
|
+
pendingValidateAtom.remove({ formId, field });
|
38
|
+
fieldValueHydratedAtom.remove({ formId, field });
|
39
|
+
}
|
40
|
+
});
|
41
|
+
export const setControlledFieldValueAtom = atom(null, async (_get, set, { formId, field, value, }) => {
|
42
|
+
set(fieldValueAtom({ formId, field }), value);
|
43
|
+
const pending = pendingValidateAtom({ formId, field });
|
44
|
+
await new Promise((resolve) => set(pending, resolve));
|
45
|
+
set(pending, undefined);
|
46
|
+
});
|
47
|
+
export const useAllControlledFields = (formId) => useFormAtomValue(controlledFieldsAtom(formId));
|
48
|
+
export const useControlledFieldValue = (formId, field) => {
|
49
|
+
const fieldAtom = fieldValueAtom({ formId, field });
|
50
|
+
const [value, setValue] = useFormAtom(fieldAtom);
|
51
|
+
const defaultValue = useFieldDefaultValue(field, { formId });
|
52
|
+
const isHydrated = useFormAtomValue(isHydratedAtom(formId));
|
53
|
+
const [isFieldHydrated, setIsFieldHydrated] = useFormAtom(fieldValueHydratedAtom({ formId, field }));
|
54
|
+
useEffect(() => {
|
55
|
+
if (isHydrated && !isFieldHydrated) {
|
56
|
+
setValue(defaultValue);
|
57
|
+
setIsFieldHydrated(true);
|
58
|
+
}
|
59
|
+
}, [
|
60
|
+
defaultValue,
|
61
|
+
field,
|
62
|
+
formId,
|
63
|
+
isFieldHydrated,
|
64
|
+
isHydrated,
|
65
|
+
setIsFieldHydrated,
|
66
|
+
setValue,
|
67
|
+
]);
|
68
|
+
return isFieldHydrated ? value : defaultValue;
|
69
|
+
};
|
70
|
+
export const useControllableValue = (formId, field) => {
|
71
|
+
const pending = useFormAtomValue(pendingValidateAtom({ formId, field }));
|
72
|
+
useEffect(() => {
|
73
|
+
pending === null || pending === void 0 ? void 0 : pending();
|
74
|
+
}, [pending]);
|
75
|
+
const register = useFormUpdateAtom(registerAtom);
|
76
|
+
const unregister = useFormUpdateAtom(unregisterAtom);
|
77
|
+
useEffect(() => {
|
78
|
+
register({ formId, field });
|
79
|
+
return () => unregister({ formId, field });
|
80
|
+
}, [field, formId, register, unregister]);
|
81
|
+
const setControlledFieldValue = useFormUpdateAtom(setControlledFieldValueAtom);
|
82
|
+
const setValue = useCallback((value) => setControlledFieldValue({ formId, field, value }), [field, formId, setControlledFieldValue]);
|
83
|
+
const value = useControlledFieldValue(formId, field);
|
84
|
+
return [value, setValue];
|
85
|
+
};
|
86
|
+
export const useFieldSerializer = (formId, field) => useFormAtomValue(valueSerializerAtom({ formId, field })).serializer;
|
87
|
+
export const useRegisterFieldSerializer = (formId, field, serializer) => {
|
88
|
+
const setSerializer = useFormUpdateAtom(valueSerializerAtom({ formId, field }));
|
89
|
+
useEffect(() => {
|
90
|
+
if (serializer)
|
91
|
+
setSerializer({ serializer });
|
92
|
+
}, [serializer, setSerializer]);
|
93
|
+
};
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
"use strict";
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import { FieldErrors, TouchedFields } from "..";
|
2
|
+
export declare type InternalFormState = {
|
3
|
+
hydrated: boolean;
|
4
|
+
fieldErrors: FieldErrors;
|
5
|
+
isSubmitting: boolean;
|
6
|
+
hasBeenSubmitted: boolean;
|
7
|
+
touchedFields: TouchedFields;
|
8
|
+
action?: string;
|
9
|
+
subaction?: string;
|
10
|
+
defaultValues: {
|
11
|
+
[fieldName: string]: any;
|
12
|
+
};
|
13
|
+
validateField: (fieldName: string) => Promise<string | null>;
|
14
|
+
registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
|
15
|
+
setFieldValue: (fieldName: string, value: unknown) => void;
|
16
|
+
};
|
17
|
+
declare type SyncFormArgs = {
|
18
|
+
defaultValues?: {
|
19
|
+
[fieldName: string]: any;
|
20
|
+
};
|
21
|
+
action?: string;
|
22
|
+
subaction?: string;
|
23
|
+
validateField: InternalFormState["validateField"];
|
24
|
+
registerReceiveFocus: InternalFormState["registerReceiveFocus"];
|
25
|
+
setFieldValueForForm: InternalFormState["setFieldValue"];
|
26
|
+
};
|
27
|
+
declare type StoreState = {
|
28
|
+
forms: {
|
29
|
+
[formId: string | symbol]: InternalFormState;
|
30
|
+
};
|
31
|
+
};
|
32
|
+
export declare const state: StoreState;
|
33
|
+
export declare const registerFormSlice: (formId: string | symbol, { registerReceiveFocus, setFieldValueForForm, validateField, action, defaultValues, subaction, }: SyncFormArgs) => void;
|
34
|
+
export declare const unregisterFormSlice: (formId: string | symbol) => void;
|
35
|
+
export declare const useFormData: (formId: string | symbol) => {
|
36
|
+
readonly hydrated: boolean;
|
37
|
+
readonly fieldErrors: {
|
38
|
+
readonly [x: string]: string;
|
39
|
+
};
|
40
|
+
readonly isSubmitting: boolean;
|
41
|
+
readonly hasBeenSubmitted: boolean;
|
42
|
+
readonly touchedFields: {
|
43
|
+
readonly [x: string]: boolean;
|
44
|
+
};
|
45
|
+
readonly action?: string | undefined;
|
46
|
+
readonly subaction?: string | undefined;
|
47
|
+
readonly defaultValues: {
|
48
|
+
readonly [x: string]: any;
|
49
|
+
};
|
50
|
+
readonly validateField: (fieldName: string) => Promise<string | null>;
|
51
|
+
readonly registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
|
52
|
+
readonly setFieldValue: (fieldName: string, value: unknown) => void;
|
53
|
+
};
|
54
|
+
export declare const startSubmit: (formId: string | symbol) => void;
|
55
|
+
export declare const endSubmit: (formId: string | symbol) => void;
|
56
|
+
export declare const sync: (formId: string | symbol, { defaultValues, action, subaction, registerReceiveFocus, validateField, setFieldValueForForm, }: SyncFormArgs) => void;
|
57
|
+
export declare const clearError: (formId: string | symbol, fieldName: string) => void;
|
58
|
+
export declare const addError: (formId: string | symbol, fieldName: string, error: string) => void;
|
59
|
+
export declare const setTouched: (formId: string | symbol, fieldName: string, touched: boolean) => void;
|
60
|
+
export declare const reset: (formId: string | symbol) => void;
|
61
|
+
export declare const setFieldErrors: (formId: string | symbol, fieldErrors: FieldErrors) => void;
|
62
|
+
export {};
|
@@ -0,0 +1,69 @@
|
|
1
|
+
import { proxy, useSnapshot } from "valtio";
|
2
|
+
export const state = proxy({ forms: {} });
|
3
|
+
export const registerFormSlice = (formId, { registerReceiveFocus, setFieldValueForForm, validateField, action, defaultValues, subaction, }) => {
|
4
|
+
state.forms[formId] = {
|
5
|
+
hydrated: true,
|
6
|
+
defaultValues: defaultValues !== null && defaultValues !== void 0 ? defaultValues : {},
|
7
|
+
fieldErrors: {},
|
8
|
+
hasBeenSubmitted: false,
|
9
|
+
isSubmitting: false,
|
10
|
+
touchedFields: {},
|
11
|
+
registerReceiveFocus,
|
12
|
+
setFieldValue: setFieldValueForForm,
|
13
|
+
validateField,
|
14
|
+
action,
|
15
|
+
subaction,
|
16
|
+
};
|
17
|
+
};
|
18
|
+
export const unregisterFormSlice = (formId) => {
|
19
|
+
delete state.forms[formId];
|
20
|
+
};
|
21
|
+
const unhydratedFormState = {
|
22
|
+
hydrated: false,
|
23
|
+
fieldErrors: {},
|
24
|
+
isSubmitting: false,
|
25
|
+
hasBeenSubmitted: false,
|
26
|
+
touchedFields: {},
|
27
|
+
defaultValues: {},
|
28
|
+
validateField: () => Promise.resolve(null),
|
29
|
+
registerReceiveFocus: () => () => { },
|
30
|
+
setFieldValue: () => { },
|
31
|
+
};
|
32
|
+
export const useFormData = (formId) => {
|
33
|
+
var _a;
|
34
|
+
const snapshot = useSnapshot(state);
|
35
|
+
return (_a = snapshot.forms[formId]) !== null && _a !== void 0 ? _a : unhydratedFormState;
|
36
|
+
};
|
37
|
+
export const startSubmit = (formId) => {
|
38
|
+
state.forms[formId].isSubmitting = true;
|
39
|
+
state.forms[formId].hasBeenSubmitted = true;
|
40
|
+
};
|
41
|
+
export const endSubmit = (formId) => {
|
42
|
+
state.forms[formId].isSubmitting = false;
|
43
|
+
};
|
44
|
+
export const sync = (formId, { defaultValues, action, subaction, registerReceiveFocus, validateField, setFieldValueForForm, }) => {
|
45
|
+
state.forms[formId].defaultValues = defaultValues !== null && defaultValues !== void 0 ? defaultValues : {};
|
46
|
+
state.forms[formId].action = action;
|
47
|
+
state.forms[formId].subaction = subaction;
|
48
|
+
state.forms[formId].registerReceiveFocus = registerReceiveFocus;
|
49
|
+
state.forms[formId].validateField = validateField;
|
50
|
+
state.forms[formId].hydrated = true;
|
51
|
+
state.forms[formId].setFieldValue = setFieldValueForForm;
|
52
|
+
};
|
53
|
+
export const clearError = (formId, fieldName) => {
|
54
|
+
delete state.forms[formId].fieldErrors[fieldName];
|
55
|
+
};
|
56
|
+
export const addError = (formId, fieldName, error) => {
|
57
|
+
state.forms[formId].fieldErrors[fieldName] = error;
|
58
|
+
};
|
59
|
+
export const setTouched = (formId, fieldName, touched) => {
|
60
|
+
state.forms[formId].touchedFields[fieldName] = touched;
|
61
|
+
};
|
62
|
+
export const reset = (formId) => {
|
63
|
+
state.forms[formId].fieldErrors = {};
|
64
|
+
state.forms[formId].touchedFields = {};
|
65
|
+
state.forms[formId].hasBeenSubmitted = false;
|
66
|
+
};
|
67
|
+
export const setFieldErrors = (formId, fieldErrors) => {
|
68
|
+
state.forms[formId].fieldErrors = fieldErrors;
|
69
|
+
};
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { FieldErrors, TouchedFields } from "..";
|
2
|
+
export declare type InternalFormState = {
|
3
|
+
hydrated: boolean;
|
4
|
+
fieldErrors: FieldErrors;
|
5
|
+
isSubmitting: boolean;
|
6
|
+
hasBeenSubmitted: boolean;
|
7
|
+
touchedFields: TouchedFields;
|
8
|
+
action?: string;
|
9
|
+
subaction?: string;
|
10
|
+
defaultValues: {
|
11
|
+
[fieldName: string]: any;
|
12
|
+
};
|
13
|
+
validateField: (fieldName: string) => Promise<string | null>;
|
14
|
+
registerReceiveFocus: (fieldName: string, handler: () => void) => () => void;
|
15
|
+
setFieldValue: (fieldName: string, value: unknown) => void;
|
16
|
+
};
|
17
|
+
declare type Helpers = {
|
18
|
+
startSubmit: () => void;
|
19
|
+
endSubmit: () => void;
|
20
|
+
sync: (args: SyncFormArgs) => void;
|
21
|
+
clearError: (name: string) => void;
|
22
|
+
addError: (name: string, error: string) => void;
|
23
|
+
setTouched: (name: string, touched: boolean) => void;
|
24
|
+
reset: () => void;
|
25
|
+
setFieldErrors: (fieldErrors: FieldErrors) => void;
|
26
|
+
register: (init: SyncFormArgs) => void;
|
27
|
+
unregister: () => void;
|
28
|
+
};
|
29
|
+
declare type SyncFormArgs = {
|
30
|
+
defaultValues?: {
|
31
|
+
[fieldName: string]: any;
|
32
|
+
};
|
33
|
+
action?: string;
|
34
|
+
subaction?: string;
|
35
|
+
validateField: InternalFormState["validateField"];
|
36
|
+
registerReceiveFocus: InternalFormState["registerReceiveFocus"];
|
37
|
+
setFieldValueForForm: InternalFormState["setFieldValue"];
|
38
|
+
};
|
39
|
+
declare type StoreState = {
|
40
|
+
forms: {
|
41
|
+
[formId: string | symbol]: InternalFormState;
|
42
|
+
};
|
43
|
+
form: (formId: string | symbol) => InternalFormState;
|
44
|
+
helpers: (formId: string | symbol) => Helpers;
|
45
|
+
};
|
46
|
+
export declare const useStore: import("zustand").UseBoundStore<StoreState, import("zustand").StoreApi<StoreState>>;
|
47
|
+
export {};
|
@@ -0,0 +1,85 @@
|
|
1
|
+
import create from "zustand";
|
2
|
+
import { immer } from "./immerMiddleware";
|
3
|
+
const unhydratedFormState = {
|
4
|
+
hydrated: false,
|
5
|
+
fieldErrors: {},
|
6
|
+
isSubmitting: false,
|
7
|
+
hasBeenSubmitted: false,
|
8
|
+
touchedFields: {},
|
9
|
+
defaultValues: {},
|
10
|
+
validateField: () => Promise.resolve(null),
|
11
|
+
registerReceiveFocus: () => () => { },
|
12
|
+
setFieldValue: () => { },
|
13
|
+
// clearError: () => {},
|
14
|
+
// addError: () => {},
|
15
|
+
// setTouched: () => {},
|
16
|
+
// reset: () => {},
|
17
|
+
// startSubmit: () => {},
|
18
|
+
// endSubmit: () => {},
|
19
|
+
// sync: () => {},
|
20
|
+
// setFieldErrors: () => {},
|
21
|
+
};
|
22
|
+
export const useStore = create(immer((set, get) => ({
|
23
|
+
forms: {},
|
24
|
+
form: (formId) => { var _a; return (_a = get().forms[formId]) !== null && _a !== void 0 ? _a : unhydratedFormState; },
|
25
|
+
helpers: (formId) => ({
|
26
|
+
clearError: (name) => set((state) => {
|
27
|
+
delete state.forms[formId].fieldErrors[name];
|
28
|
+
}),
|
29
|
+
addError: (name, error) => set((state) => {
|
30
|
+
state.forms[formId].fieldErrors[name] = error;
|
31
|
+
}),
|
32
|
+
setTouched: (name, touched) => set((state) => {
|
33
|
+
state.forms[formId].touchedFields[name] = touched;
|
34
|
+
}),
|
35
|
+
reset: () => set((state) => {
|
36
|
+
state.forms[formId].fieldErrors = {};
|
37
|
+
state.forms[formId].touchedFields = {};
|
38
|
+
state.forms[formId].hasBeenSubmitted = false;
|
39
|
+
}),
|
40
|
+
startSubmit: () => set((state) => {
|
41
|
+
state.forms[formId].hasBeenSubmitted = true;
|
42
|
+
state.forms[formId].isSubmitting = true;
|
43
|
+
}),
|
44
|
+
endSubmit: () => set((state) => {
|
45
|
+
state.forms[formId].isSubmitting = false;
|
46
|
+
}),
|
47
|
+
setFieldErrors: (fieldErrors) => {
|
48
|
+
set((state) => {
|
49
|
+
state.forms[formId].fieldErrors = fieldErrors;
|
50
|
+
});
|
51
|
+
},
|
52
|
+
sync: ({ defaultValues, action, subaction, validateField, registerReceiveFocus, setFieldValueForForm, }) => set((state) => {
|
53
|
+
state.forms[formId].defaultValues = defaultValues !== null && defaultValues !== void 0 ? defaultValues : {};
|
54
|
+
state.forms[formId].action = action;
|
55
|
+
state.forms[formId].subaction = subaction;
|
56
|
+
state.forms[formId].registerReceiveFocus = registerReceiveFocus;
|
57
|
+
state.forms[formId].validateField = validateField;
|
58
|
+
state.forms[formId].hydrated = true;
|
59
|
+
state.forms[formId].setFieldValue = setFieldValueForForm;
|
60
|
+
}),
|
61
|
+
unregister: () => {
|
62
|
+
set((state) => {
|
63
|
+
delete state.forms[formId];
|
64
|
+
});
|
65
|
+
},
|
66
|
+
register: ({ defaultValues, action, subaction, validateField, registerReceiveFocus, setFieldValueForForm, }) => {
|
67
|
+
set((state) => {
|
68
|
+
state.forms[formId] = {
|
69
|
+
defaultValues: defaultValues !== null && defaultValues !== void 0 ? defaultValues : {},
|
70
|
+
setFieldValue: setFieldValueForForm,
|
71
|
+
registerReceiveFocus,
|
72
|
+
validateField,
|
73
|
+
action,
|
74
|
+
subaction,
|
75
|
+
hydrated: true,
|
76
|
+
fieldErrors: {},
|
77
|
+
isSubmitting: false,
|
78
|
+
hasBeenSubmitted: false,
|
79
|
+
touchedFields: {},
|
80
|
+
// helpers
|
81
|
+
};
|
82
|
+
});
|
83
|
+
},
|
84
|
+
}),
|
85
|
+
})));
|
@@ -2,3 +2,4 @@ import type React from "react";
|
|
2
2
|
export declare const omit: (obj: any, ...keys: string[]) => any;
|
3
3
|
export declare const mergeRefs: <T = any>(refs: (React.MutableRefObject<T> | React.LegacyRef<T> | undefined)[]) => (instance: T | null) => void;
|
4
4
|
export declare const useIsomorphicLayoutEffect: typeof React.useEffect;
|
5
|
+
export declare const useDeepEqualsMemo: <T>(item: T) => T;
|
package/browser/internal/util.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
import
|
1
|
+
import isEqual from "lodash/isEqual";
|
2
|
+
import { useEffect, useLayoutEffect, useRef } from "react";
|
2
3
|
export const omit = (obj, ...keys) => {
|
3
4
|
const result = { ...obj };
|
4
5
|
for (const key of keys) {
|
@@ -19,3 +20,13 @@ export const mergeRefs = (refs) => {
|
|
19
20
|
};
|
20
21
|
};
|
21
22
|
export const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect;
|
23
|
+
export const useDeepEqualsMemo = (item) => {
|
24
|
+
const ref = useRef(item);
|
25
|
+
const areEqual = ref.current === item || isEqual(ref.current, item);
|
26
|
+
useEffect(() => {
|
27
|
+
if (!areEqual) {
|
28
|
+
ref.current = item;
|
29
|
+
}
|
30
|
+
});
|
31
|
+
return areEqual ? ref.current : item;
|
32
|
+
};
|
package/build/ValidatedForm.d.ts
CHANGED
@@ -47,4 +47,4 @@ export declare type FormProps<DataType> = {
|
|
47
47
|
/**
|
48
48
|
* The primary form component of `remix-validated-form`.
|
49
49
|
*/
|
50
|
-
export declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues:
|
50
|
+
export declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues: unMemoizedDefaults, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, id, ...rest }: FormProps<DataType>): JSX.Element;
|