remix-validated-form 4.5.0 → 4.5.3
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 +8 -9
- package/browser/ValidatedForm.js +4 -4
- package/browser/internal/hooks.d.ts +1 -0
- package/browser/internal/hooks.js +1 -0
- package/browser/internal/logic/nestedObjectToPathObject.d.ts +0 -0
- package/browser/internal/logic/nestedObjectToPathObject.js +0 -0
- package/browser/internal/state/arrayUtil.d.ts +6 -0
- package/browser/internal/state/arrayUtil.js +236 -7
- package/browser/internal/state/createFormStore.d.ts +1 -0
- package/browser/internal/state/createFormStore.js +6 -0
- package/browser/internal/state/fieldArray.d.ts +18 -11
- package/browser/internal/state/fieldArray.js +44 -21
- package/browser/server.d.ts +2 -2
- package/browser/server.js +1 -1
- package/browser/unreleased/formStateHooks.d.ts +4 -0
- package/browser/unreleased/formStateHooks.js +4 -1
- package/browser/userFacingFormContext.d.ts +4 -0
- package/browser/userFacingFormContext.js +3 -1
- package/dist/remix-validated-form.cjs.js +12 -3
- package/dist/remix-validated-form.cjs.js.map +1 -1
- package/dist/remix-validated-form.es.js +45 -11
- package/dist/remix-validated-form.es.js.map +1 -1
- package/dist/remix-validated-form.umd.js +12 -3
- package/dist/remix-validated-form.umd.js.map +1 -1
- package/dist/types/internal/hooks.d.ts +1 -0
- package/dist/types/internal/state/createFormStore.d.ts +1 -0
- package/dist/types/server.d.ts +2 -2
- package/dist/types/unreleased/formStateHooks.d.ts +4 -0
- package/dist/types/userFacingFormContext.d.ts +4 -0
- package/package.json +2 -4
- package/src/ValidatedForm.tsx +5 -4
- package/src/internal/hooks.ts +3 -0
- package/src/internal/state/createFormStore.ts +12 -1
- package/src/server.ts +2 -2
- package/src/unreleased/formStateHooks.ts +8 -0
- package/src/userFacingFormContext.ts +7 -0
- package/src/validation/validation.test.ts +7 -7
- package/vite.config.ts +1 -1
@@ -31,3 +31,4 @@ export declare const useResetFormElement: (formId: InternalFormId) => () => void
|
|
31
31
|
export declare const useSubmitForm: (formId: InternalFormId) => () => void;
|
32
32
|
export declare const useFormActionProp: (formId: InternalFormId) => string | undefined;
|
33
33
|
export declare const useFormSubactionProp: (formId: InternalFormId) => string | undefined;
|
34
|
+
export declare const useFormValues: (formId: InternalFormId) => () => FormData;
|
@@ -42,6 +42,7 @@ export declare type FormState = {
|
|
42
42
|
validate: () => Promise<ValidationResult<unknown>>;
|
43
43
|
resetFormElement: () => void;
|
44
44
|
submit: () => void;
|
45
|
+
getValues: () => FormData;
|
45
46
|
};
|
46
47
|
export declare const useRootFormStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<FormStoreState>, "setState"> & {
|
47
48
|
setState(nextStateOrUpdater: FormStoreState | Partial<FormStoreState> | ((state: WritableDraft<FormStoreState>) => void), shouldReplace?: boolean | undefined): void;
|
package/dist/types/server.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { FORM_DEFAULTS_FIELD } from "./internal/constants";
|
2
|
-
import { ValidatorError } from "./validation/types";
|
2
|
+
import { ValidatorError, ValidationErrorResponseData } from "./validation/types";
|
3
3
|
/**
|
4
4
|
* Takes the errors from a `Validator` and returns a `Response`.
|
5
5
|
* When you return this from your action, `ValidatedForm` on the frontend will automatically
|
@@ -14,7 +14,7 @@ import { ValidatorError } from "./validation/types";
|
|
14
14
|
* if (result.error) return validationError(result.error, result.submittedData);
|
15
15
|
* ```
|
16
16
|
*/
|
17
|
-
export declare function validationError(error: ValidatorError, repopulateFields?: unknown, init?: ResponseInit):
|
17
|
+
export declare function validationError(error: ValidatorError, repopulateFields?: unknown, init?: ResponseInit): import("@remix-run/server-runtime").TypedResponse<ValidationErrorResponseData>;
|
18
18
|
export declare type FormDefaults = {
|
19
19
|
[formDefaultsKey: `${typeof FORM_DEFAULTS_FIELD}_${string}`]: any;
|
20
20
|
};
|
@@ -51,6 +51,10 @@ export declare type FormHelpers = {
|
|
51
51
|
* _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
|
52
52
|
*/
|
53
53
|
submit: () => void;
|
54
|
+
/**
|
55
|
+
* Returns the current form values as FormData
|
56
|
+
*/
|
57
|
+
getValues: () => FormData;
|
54
58
|
};
|
55
59
|
/**
|
56
60
|
* Returns helpers that can be used to update the form state.
|
@@ -74,6 +74,10 @@ export declare type FormContextValue = {
|
|
74
74
|
* _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
|
75
75
|
*/
|
76
76
|
submit: () => void;
|
77
|
+
/**
|
78
|
+
* Returns the current form values as FormData
|
79
|
+
*/
|
80
|
+
getValues: () => FormData;
|
77
81
|
};
|
78
82
|
/**
|
79
83
|
* Provides access to some of the internal state of the form.
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "remix-validated-form",
|
3
|
-
"version": "4.5.
|
3
|
+
"version": "4.5.3",
|
4
4
|
"description": "Form component and utils for easy form validation in remix",
|
5
5
|
"browser": "./dist/remix-validated-form.cjs.js",
|
6
6
|
"main": "./dist/remix-validated-form.umd.js",
|
@@ -34,12 +34,10 @@
|
|
34
34
|
],
|
35
35
|
"peerDependencies": {
|
36
36
|
"@remix-run/react": "1.x",
|
37
|
-
"@remix-run/server-runtime": "1.x",
|
38
37
|
"react": "^17.0.2 || ^18.0.0"
|
39
38
|
},
|
40
39
|
"devDependencies": {
|
41
|
-
"@remix-run/react": "^1.
|
42
|
-
"@remix-run/server-runtime": "^1.2.1",
|
40
|
+
"@remix-run/react": "^1.6.5",
|
43
41
|
"@types/lodash": "^4.14.178",
|
44
42
|
"@types/react": "^18.0.9",
|
45
43
|
"fetch-blob": "^3.1.3",
|
package/src/ValidatedForm.tsx
CHANGED
@@ -98,7 +98,8 @@ const focusFirstInvalidInput = (
|
|
98
98
|
const namesInOrder = [...formElement.elements]
|
99
99
|
.map((el) => {
|
100
100
|
const input = el instanceof RadioNodeList ? el[0] : el;
|
101
|
-
if (input instanceof
|
101
|
+
if (input instanceof HTMLElement && "name" in input)
|
102
|
+
return (input as any).name;
|
102
103
|
return null;
|
103
104
|
})
|
104
105
|
.filter(nonNull)
|
@@ -129,8 +130,8 @@ const focusFirstInvalidInput = (
|
|
129
130
|
}
|
130
131
|
}
|
131
132
|
|
132
|
-
if (elem instanceof
|
133
|
-
if (elem.type === "hidden") {
|
133
|
+
if (elem instanceof HTMLElement) {
|
134
|
+
if (elem instanceof HTMLInputElement && elem.type === "hidden") {
|
134
135
|
continue;
|
135
136
|
}
|
136
137
|
|
@@ -329,7 +330,7 @@ export function ValidatedForm<DataType>({
|
|
329
330
|
// If we use `event.currentTarget` here, it will break because `currentTarget`
|
330
331
|
// will have changed since the start of the submission.
|
331
332
|
if (fetcher) fetcher.submit(submitter || e.currentTarget);
|
332
|
-
else submit(submitter || target, {
|
333
|
+
else submit(submitter || target, { replace });
|
333
334
|
}
|
334
335
|
};
|
335
336
|
|
package/src/internal/hooks.ts
CHANGED
@@ -204,3 +204,6 @@ export const useFormActionProp = (formId: InternalFormId) =>
|
|
204
204
|
|
205
205
|
export const useFormSubactionProp = (formId: InternalFormId) =>
|
206
206
|
useFormStore(formId, (state) => state.formProps?.subaction);
|
207
|
+
|
208
|
+
export const useFormValues = (formId: InternalFormId) =>
|
209
|
+
useFormStore(formId, (state) => state.getValues);
|
@@ -51,6 +51,7 @@ export type FormState = {
|
|
51
51
|
validate: () => Promise<ValidationResult<unknown>>;
|
52
52
|
resetFormElement: () => void;
|
53
53
|
submit: () => void;
|
54
|
+
getValues: () => FormData;
|
54
55
|
};
|
55
56
|
|
56
57
|
const noOp = () => {};
|
@@ -84,6 +85,7 @@ const defaultFormState: FormState = {
|
|
84
85
|
},
|
85
86
|
|
86
87
|
resetFormElement: noOp,
|
88
|
+
getValues: () => new FormData(),
|
87
89
|
};
|
88
90
|
|
89
91
|
const createFormState = (
|
@@ -125,7 +127,6 @@ const createFormState = (
|
|
125
127
|
set((state) => {
|
126
128
|
delete state.fieldErrors[fieldName];
|
127
129
|
}),
|
128
|
-
|
129
130
|
reset: () =>
|
130
131
|
set((state) => {
|
131
132
|
state.fieldErrors = {};
|
@@ -208,6 +209,16 @@ const createFormState = (
|
|
208
209
|
formElement.submit();
|
209
210
|
},
|
210
211
|
|
212
|
+
getValues: () => {
|
213
|
+
const formElement = get().formElement;
|
214
|
+
invariant(
|
215
|
+
formElement,
|
216
|
+
"Cannot find reference to form. This is probably a bug in remix-validated-form."
|
217
|
+
);
|
218
|
+
|
219
|
+
return new FormData(formElement);
|
220
|
+
},
|
221
|
+
|
211
222
|
resetFormElement: () => get().formElement?.reset(),
|
212
223
|
});
|
213
224
|
|
package/src/server.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { json } from "
|
1
|
+
import { json } from "remix";
|
2
2
|
import {
|
3
3
|
formDefaultValuesKey,
|
4
4
|
FORM_DEFAULTS_FIELD,
|
@@ -26,7 +26,7 @@ export function validationError(
|
|
26
26
|
error: ValidatorError,
|
27
27
|
repopulateFields?: unknown,
|
28
28
|
init?: ResponseInit
|
29
|
-
)
|
29
|
+
) {
|
30
30
|
return json<ValidationErrorResponseData>(
|
31
31
|
{
|
32
32
|
fieldErrors: error.fieldErrors,
|
@@ -19,6 +19,7 @@ import {
|
|
19
19
|
useFormActionProp,
|
20
20
|
useFormSubactionProp,
|
21
21
|
useSubmitForm,
|
22
|
+
useFormValues,
|
22
23
|
} from "../internal/hooks";
|
23
24
|
import {
|
24
25
|
FieldErrors,
|
@@ -118,6 +119,10 @@ export type FormHelpers = {
|
|
118
119
|
* _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
|
119
120
|
*/
|
120
121
|
submit: () => void;
|
122
|
+
/**
|
123
|
+
* Returns the current form values as FormData
|
124
|
+
*/
|
125
|
+
getValues: () => FormData;
|
121
126
|
};
|
122
127
|
|
123
128
|
/**
|
@@ -134,6 +139,7 @@ export const useFormHelpers = (formId?: string): FormHelpers => {
|
|
134
139
|
const setFieldErrors = useSetFieldErrors(formContext.formId);
|
135
140
|
const reset = useResetFormElement(formContext.formId);
|
136
141
|
const submit = useSubmitForm(formContext.formId);
|
142
|
+
const getValues = useFormValues(formContext.formId);
|
137
143
|
return useMemo(
|
138
144
|
() => ({
|
139
145
|
setTouched,
|
@@ -143,6 +149,7 @@ export const useFormHelpers = (formId?: string): FormHelpers => {
|
|
143
149
|
clearAllErrors: () => setFieldErrors({}),
|
144
150
|
reset,
|
145
151
|
submit,
|
152
|
+
getValues,
|
146
153
|
}),
|
147
154
|
[
|
148
155
|
clearError,
|
@@ -152,6 +159,7 @@ export const useFormHelpers = (formId?: string): FormHelpers => {
|
|
152
159
|
submit,
|
153
160
|
validate,
|
154
161
|
validateField,
|
162
|
+
getValues,
|
155
163
|
]
|
156
164
|
);
|
157
165
|
};
|
@@ -83,6 +83,10 @@ export type FormContextValue = {
|
|
83
83
|
* _Note_: This is equivalent to clicking a button element with `type="submit"` or calling formElement.submit().
|
84
84
|
*/
|
85
85
|
submit: () => void;
|
86
|
+
/**
|
87
|
+
* Returns the current form values as FormData
|
88
|
+
*/
|
89
|
+
getValues: () => FormData;
|
86
90
|
};
|
87
91
|
|
88
92
|
/**
|
@@ -100,6 +104,7 @@ export const useFormContext = (formId?: string): FormContextValue => {
|
|
100
104
|
validate,
|
101
105
|
reset,
|
102
106
|
submit,
|
107
|
+
getValues,
|
103
108
|
} = useFormHelpers(formId);
|
104
109
|
|
105
110
|
const registerReceiveFocus = useRegisterReceiveFocus(context.formId);
|
@@ -124,6 +129,7 @@ export const useFormContext = (formId?: string): FormContextValue => {
|
|
124
129
|
validate,
|
125
130
|
reset,
|
126
131
|
submit,
|
132
|
+
getValues,
|
127
133
|
}),
|
128
134
|
[
|
129
135
|
clearAllErrors,
|
@@ -135,6 +141,7 @@ export const useFormContext = (formId?: string): FormContextValue => {
|
|
135
141
|
submit,
|
136
142
|
validate,
|
137
143
|
validateField,
|
144
|
+
getValues,
|
138
145
|
]
|
139
146
|
);
|
140
147
|
};
|
@@ -56,21 +56,21 @@ const validationTestCases: ValidationTestCase[] = [
|
|
56
56
|
name: "zod",
|
57
57
|
validator: withZod(
|
58
58
|
z.object({
|
59
|
-
firstName: z.string().
|
60
|
-
lastName: z.string().
|
59
|
+
firstName: z.string().min(1),
|
60
|
+
lastName: z.string().min(1),
|
61
61
|
age: z.optional(z.number()),
|
62
62
|
address: z.preprocess(
|
63
63
|
(value) => (value == null ? {} : value),
|
64
64
|
z.object({
|
65
|
-
streetAddress: z.string().
|
66
|
-
city: z.string().
|
67
|
-
country: z.string().
|
65
|
+
streetAddress: z.string().min(1),
|
66
|
+
city: z.string().min(1),
|
67
|
+
country: z.string().min(1),
|
68
68
|
})
|
69
69
|
),
|
70
70
|
pets: z
|
71
71
|
.object({
|
72
|
-
animal: z.string().
|
73
|
-
name: z.string().
|
72
|
+
animal: z.string().min(1),
|
73
|
+
name: z.string().min(1),
|
74
74
|
})
|
75
75
|
.array()
|
76
76
|
.optional(),
|
package/vite.config.ts
CHANGED