remix-validated-form 4.3.0 → 4.3.1-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.
Files changed (137) hide show
  1. package/.turbo/turbo-build.log +15 -9
  2. package/README.md +1 -0
  3. package/browser/internal/logic/getRadioChecked.js +10 -0
  4. package/browser/internal/reset.d.ts +0 -0
  5. package/browser/internal/reset.js +0 -0
  6. package/browser/unreleased/formStateHooks.d.ts +0 -0
  7. package/browser/unreleased/formStateHooks.js +0 -0
  8. package/dist/remix-validated-form.cjs.js +1 -0
  9. package/dist/remix-validated-form.es.js +3535 -0
  10. package/dist/remix-validated-form.umd.js +1 -0
  11. package/{build → dist/types}/ValidatedForm.d.ts +0 -0
  12. package/{build → dist/types}/hooks.d.ts +0 -0
  13. package/{build → dist/types}/index.d.ts +0 -0
  14. package/{build → dist/types}/internal/MultiValueMap.d.ts +0 -0
  15. package/{build → dist/types}/internal/constants.d.ts +0 -0
  16. package/{build → dist/types}/internal/flatten.d.ts +0 -0
  17. package/{build → dist/types}/internal/formContext.d.ts +0 -0
  18. package/{build → dist/types}/internal/getInputProps.d.ts +0 -0
  19. package/{build → dist/types}/internal/hooks.d.ts +0 -0
  20. package/{build → dist/types}/internal/hydratable.d.ts +0 -0
  21. package/{build → dist/types}/internal/logic/getCheckboxChecked.d.ts +0 -0
  22. package/{build → dist/types}/internal/logic/getRadioChecked.d.ts +0 -0
  23. package/{build → dist/types}/internal/reset.d.ts +0 -0
  24. package/{build → dist/types}/internal/state/atomUtils.d.ts +0 -0
  25. package/{build → dist/types}/internal/state/controlledFields.d.ts +0 -0
  26. package/{build → dist/types}/internal/state.d.ts +0 -0
  27. package/{build → dist/types}/internal/submissionCallbacks.d.ts +0 -0
  28. package/{build → dist/types}/internal/util.d.ts +0 -0
  29. package/{build → dist/types}/server.d.ts +0 -0
  30. package/{build → dist/types}/unreleased/formStateHooks.d.ts +0 -0
  31. package/{build → dist/types}/userFacingFormContext.d.ts +0 -0
  32. package/{build → dist/types}/validation/createValidator.d.ts +0 -0
  33. package/{build → dist/types}/validation/types.d.ts +0 -0
  34. package/package.json +8 -7
  35. package/src/internal/getInputProps.test.ts +251 -0
  36. package/src/internal/logic/getRadioChecked.ts +11 -0
  37. package/src/validation/validation.test.ts +304 -0
  38. package/tsconfig.json +4 -1
  39. package/vite.config.ts +7 -0
  40. package/.turbo/turbo-test.log +0 -11
  41. package/browser/components.d.ts +0 -7
  42. package/browser/components.js +0 -10
  43. package/browser/internal/SingleTypeMultiValueMap.d.ts +0 -9
  44. package/browser/internal/SingleTypeMultiValueMap.js +0 -41
  45. package/browser/internal/customState.d.ts +0 -105
  46. package/browser/internal/customState.js +0 -46
  47. package/browser/internal/hooks-valtio.d.ts +0 -18
  48. package/browser/internal/hooks-valtio.js +0 -110
  49. package/browser/internal/hooks-zustand.d.ts +0 -16
  50. package/browser/internal/hooks-zustand.js +0 -100
  51. package/browser/internal/immerMiddleware.d.ts +0 -6
  52. package/browser/internal/immerMiddleware.js +0 -7
  53. package/browser/internal/logic/elementUtils.d.ts +0 -3
  54. package/browser/internal/logic/elementUtils.js +0 -3
  55. package/browser/internal/logic/getCheckboxChecked copy.d.ts +0 -1
  56. package/browser/internal/logic/getCheckboxChecked copy.js +0 -9
  57. package/browser/internal/logic/setFieldValue.d.ts +0 -1
  58. package/browser/internal/logic/setFieldValue.js +0 -40
  59. package/browser/internal/logic/setInputValueInForm.d.ts +0 -1
  60. package/browser/internal/logic/setInputValueInForm.js +0 -77
  61. package/browser/internal/setFieldValue.d.ts +0 -20
  62. package/browser/internal/setFieldValue.js +0 -83
  63. package/browser/internal/setFormValues.d.ts +0 -2
  64. package/browser/internal/setFormValues.js +0 -26
  65. package/browser/internal/state/setFieldValue.d.ts +0 -0
  66. package/browser/internal/state/setFieldValue.js +0 -1
  67. package/browser/internal/state-valtio.d.ts +0 -62
  68. package/browser/internal/state-valtio.js +0 -69
  69. package/browser/internal/state-zustand.d.ts +0 -47
  70. package/browser/internal/state-zustand.js +0 -85
  71. package/browser/internal/test.d.ts +0 -0
  72. package/browser/internal/test.js +0 -15
  73. package/browser/internal/useMultiValueMap.d.ts +0 -1
  74. package/browser/internal/useMultiValueMap.js +0 -11
  75. package/browser/internal/watch.d.ts +0 -18
  76. package/browser/internal/watch.js +0 -122
  77. package/browser/lowLevelHooks.d.ts +0 -0
  78. package/browser/lowLevelHooks.js +0 -1
  79. package/browser/test-data/testFormData.d.ts +0 -15
  80. package/browser/test-data/testFormData.js +0 -46
  81. package/browser/types.d.ts +0 -1
  82. package/browser/types.js +0 -1
  83. package/browser/validation/validation.test.d.ts +0 -1
  84. package/browser/validation/validation.test.js +0 -274
  85. package/browser/validation/withYup.d.ts +0 -6
  86. package/browser/validation/withYup.js +0 -40
  87. package/browser/validation/withZod.d.ts +0 -6
  88. package/browser/validation/withZod.js +0 -50
  89. package/build/ValidatedForm.js +0 -261
  90. package/build/hooks.js +0 -91
  91. package/build/index.js +0 -18
  92. package/build/internal/MultiValueMap.js +0 -48
  93. package/build/internal/SingleTypeMultiValueMap.d.ts +0 -8
  94. package/build/internal/SingleTypeMultiValueMap.js +0 -45
  95. package/build/internal/constants.js +0 -7
  96. package/build/internal/flatten.js +0 -14
  97. package/build/internal/formContext.js +0 -5
  98. package/build/internal/getInputProps.js +0 -58
  99. package/build/internal/hooks-valtio.d.ts +0 -18
  100. package/build/internal/hooks-valtio.js +0 -128
  101. package/build/internal/hooks-zustand.d.ts +0 -16
  102. package/build/internal/hooks-zustand.js +0 -117
  103. package/build/internal/hooks.js +0 -128
  104. package/build/internal/hydratable.js +0 -17
  105. package/build/internal/immerMiddleware.d.ts +0 -6
  106. package/build/internal/immerMiddleware.js +0 -14
  107. package/build/internal/logic/elementUtils.d.ts +0 -3
  108. package/build/internal/logic/elementUtils.js +0 -9
  109. package/build/internal/logic/getCheckboxChecked.js +0 -13
  110. package/build/internal/logic/getRadioChecked.js +0 -9
  111. package/build/internal/logic/setFieldValue.d.ts +0 -1
  112. package/build/internal/logic/setFieldValue.js +0 -47
  113. package/build/internal/logic/setInputValueInForm.d.ts +0 -1
  114. package/build/internal/logic/setInputValueInForm.js +0 -84
  115. package/build/internal/reset.js +0 -19
  116. package/build/internal/setFormValues.d.ts +0 -2
  117. package/build/internal/setFormValues.js +0 -33
  118. package/build/internal/state/atomUtils.js +0 -13
  119. package/build/internal/state/controlledFields.js +0 -103
  120. package/build/internal/state-valtio.d.ts +0 -62
  121. package/build/internal/state-valtio.js +0 -83
  122. package/build/internal/state-zustand.d.ts +0 -47
  123. package/build/internal/state-zustand.js +0 -91
  124. package/build/internal/state.js +0 -71
  125. package/build/internal/submissionCallbacks.js +0 -17
  126. package/build/internal/test.d.ts +0 -1
  127. package/build/internal/test.js +0 -12
  128. package/build/internal/util.js +0 -41
  129. package/build/internal/watch.d.ts +0 -20
  130. package/build/internal/watch.js +0 -126
  131. package/build/server.js +0 -32
  132. package/build/types.d.ts +0 -1
  133. package/build/types.js +0 -2
  134. package/build/unreleased/formStateHooks.js +0 -59
  135. package/build/userFacingFormContext.js +0 -30
  136. package/build/validation/createValidator.js +0 -45
  137. package/build/validation/types.js +0 -2
@@ -1,274 +0,0 @@
1
- import * as yup from "yup";
2
- import { z } from "zod";
3
- import { withYup } from "..";
4
- import { objectFromPathEntries } from "../internal/flatten";
5
- import { TestFormData } from "../test-data/testFormData";
6
- import { withZod } from "./withZod";
7
- const validationTestCases = [
8
- {
9
- name: "yup",
10
- validator: withYup(yup.object({
11
- firstName: yup.string().required(),
12
- lastName: yup.string().required(),
13
- age: yup.number(),
14
- address: yup
15
- .object({
16
- streetAddress: yup.string().required(),
17
- city: yup.string().required(),
18
- country: yup.string().required(),
19
- })
20
- .required(),
21
- pets: yup.array().of(yup.object({
22
- animal: yup.string().required(),
23
- name: yup.string().required(),
24
- })),
25
- })),
26
- },
27
- {
28
- name: "zod",
29
- validator: withZod(z.object({
30
- firstName: z.string().nonempty(),
31
- lastName: z.string().nonempty(),
32
- age: z.optional(z.number()),
33
- address: z.preprocess((value) => (value == null ? {} : value), z.object({
34
- streetAddress: z.string().nonempty(),
35
- city: z.string().nonempty(),
36
- country: z.string().nonempty(),
37
- })),
38
- pets: z
39
- .object({
40
- animal: z.string().nonempty(),
41
- name: z.string().nonempty(),
42
- })
43
- .array()
44
- .optional(),
45
- })),
46
- },
47
- ];
48
- // Not going to enforce exact error strings here
49
- const anyString = expect.any(String);
50
- describe("Validation", () => {
51
- describe.each(validationTestCases)("Adapter for $name", ({ validator }) => {
52
- describe("validate", () => {
53
- it("should return the data when valid", () => {
54
- const person = {
55
- firstName: "John",
56
- lastName: "Doe",
57
- age: 30,
58
- address: {
59
- streetAddress: "123 Main St",
60
- city: "Anytown",
61
- country: "USA",
62
- },
63
- pets: [{ animal: "dog", name: "Fido" }],
64
- };
65
- expect(validator.validate(person)).toEqual({
66
- data: person,
67
- error: undefined,
68
- });
69
- });
70
- it("should return field errors when invalid", () => {
71
- const obj = { age: "hi!", pets: [{ animal: "dog" }] };
72
- expect(validator.validate(obj)).toEqual({
73
- data: undefined,
74
- error: {
75
- firstName: anyString,
76
- lastName: anyString,
77
- age: anyString,
78
- "address.city": anyString,
79
- "address.country": anyString,
80
- "address.streetAddress": anyString,
81
- "pets[0].name": anyString,
82
- _submittedData: obj,
83
- },
84
- });
85
- });
86
- it("should unflatten data when validating", () => {
87
- const data = {
88
- firstName: "John",
89
- lastName: "Doe",
90
- age: 30,
91
- "address.streetAddress": "123 Main St",
92
- "address.city": "Anytown",
93
- "address.country": "USA",
94
- "pets[0].animal": "dog",
95
- "pets[0].name": "Fido",
96
- };
97
- expect(validator.validate(data)).toEqual({
98
- data: {
99
- firstName: "John",
100
- lastName: "Doe",
101
- age: 30,
102
- address: {
103
- streetAddress: "123 Main St",
104
- city: "Anytown",
105
- country: "USA",
106
- },
107
- pets: [{ animal: "dog", name: "Fido" }],
108
- },
109
- error: undefined,
110
- });
111
- });
112
- it("should accept FormData directly and return errors", () => {
113
- const formData = new TestFormData();
114
- formData.set("firstName", "John");
115
- formData.set("lastName", "Doe");
116
- formData.set("address.streetAddress", "123 Main St");
117
- formData.set("address.country", "USA");
118
- formData.set("pets[0].animal", "dog");
119
- expect(validator.validate(formData)).toEqual({
120
- data: undefined,
121
- error: {
122
- "address.city": anyString,
123
- "pets[0].name": anyString,
124
- _submittedData: objectFromPathEntries([...formData.entries()]),
125
- },
126
- });
127
- });
128
- it("should accept FormData directly and return valid data", () => {
129
- const formData = new TestFormData();
130
- formData.set("firstName", "John");
131
- formData.set("lastName", "Doe");
132
- formData.set("address.streetAddress", "123 Main St");
133
- formData.set("address.country", "USA");
134
- formData.set("address.city", "Anytown");
135
- formData.set("pets[0].animal", "dog");
136
- formData.set("pets[0].name", "Fido");
137
- expect(validator.validate(formData)).toEqual({
138
- data: {
139
- firstName: "John",
140
- lastName: "Doe",
141
- address: {
142
- streetAddress: "123 Main St",
143
- country: "USA",
144
- city: "Anytown",
145
- },
146
- pets: [{ animal: "dog", name: "Fido" }],
147
- },
148
- error: undefined,
149
- });
150
- });
151
- });
152
- describe("validateField", () => {
153
- it("should not return an error if field is valid", () => {
154
- const person = {
155
- firstName: "John",
156
- lastName: {}, // invalid, but we should only be validating firstName
157
- };
158
- expect(validator.validateField(person, "firstName")).toEqual({
159
- error: undefined,
160
- });
161
- });
162
- it("should not return an error if a nested field is valid", () => {
163
- const person = {
164
- firstName: "John",
165
- lastName: {},
166
- address: {
167
- streetAddress: "123 Main St",
168
- city: "Anytown",
169
- country: "USA",
170
- },
171
- pets: [{ animal: "dog", name: "Fido" }],
172
- };
173
- expect(validator.validateField(person, "address.streetAddress")).toEqual({
174
- error: undefined,
175
- });
176
- expect(validator.validateField(person, "address.city")).toEqual({
177
- error: undefined,
178
- });
179
- expect(validator.validateField(person, "address.country")).toEqual({
180
- error: undefined,
181
- });
182
- expect(validator.validateField(person, "pets[0].animal")).toEqual({
183
- error: undefined,
184
- });
185
- expect(validator.validateField(person, "pets[0].name")).toEqual({
186
- error: undefined,
187
- });
188
- });
189
- it("should return an error if field is invalid", () => {
190
- const person = {
191
- firstName: "John",
192
- lastName: {},
193
- address: {
194
- streetAddress: "123 Main St",
195
- city: 1234,
196
- },
197
- };
198
- expect(validator.validateField(person, "lastName")).toEqual({
199
- error: anyString,
200
- });
201
- });
202
- it("should return an error if a nested field is invalid", () => {
203
- const person = {
204
- firstName: "John",
205
- lastName: {},
206
- address: {
207
- streetAddress: "123 Main St",
208
- city: 1234,
209
- },
210
- pets: [{ animal: "dog" }],
211
- };
212
- expect(validator.validateField(person, "address.country")).toEqual({
213
- error: anyString,
214
- });
215
- expect(validator.validateField(person, "pets[0].name")).toEqual({
216
- error: anyString,
217
- });
218
- });
219
- });
220
- });
221
- });
222
- describe("withZod", () => {
223
- it("returns coherent errors for complex schemas", () => {
224
- const schema = z.union([
225
- z.object({
226
- type: z.literal("foo"),
227
- foo: z.string(),
228
- }),
229
- z.object({
230
- type: z.literal("bar"),
231
- bar: z.string(),
232
- }),
233
- ]);
234
- const obj = {
235
- type: "foo",
236
- bar: 123,
237
- foo: 123,
238
- };
239
- expect(withZod(schema).validate(obj)).toEqual({
240
- data: undefined,
241
- error: {
242
- type: anyString,
243
- bar: anyString,
244
- foo: anyString,
245
- _submittedData: obj,
246
- },
247
- });
248
- });
249
- it("returns errors for fields that are unions", () => {
250
- const schema = z.object({
251
- field1: z.union([z.literal("foo"), z.literal("bar")]),
252
- field2: z.union([z.literal("foo"), z.literal("bar")]),
253
- });
254
- const obj = {
255
- field1: "a value",
256
- // field2 missing
257
- };
258
- const validator = withZod(schema);
259
- expect(validator.validate(obj)).toEqual({
260
- data: undefined,
261
- error: {
262
- field1: anyString,
263
- field2: anyString,
264
- _submittedData: obj,
265
- },
266
- });
267
- expect(validator.validateField(obj, "field1")).toEqual({
268
- error: anyString,
269
- });
270
- expect(validator.validateField(obj, "field2")).toEqual({
271
- error: anyString,
272
- });
273
- });
274
- });
@@ -1,6 +0,0 @@
1
- import type { AnyObjectSchema, InferType } from "yup";
2
- import { Validator } from "./types";
3
- /**
4
- * Create a `Validator` using a `yup` schema.
5
- */
6
- export declare const withYup: <Schema extends AnyObjectSchema>(validationSchema: Schema) => Validator<InferType<Schema>>;
@@ -1,40 +0,0 @@
1
- import { createValidator } from "./createValidator";
2
- const validationErrorToFieldErrors = (error) => {
3
- const fieldErrors = {};
4
- error.inner.forEach((innerError) => {
5
- if (!innerError.path)
6
- return;
7
- fieldErrors[innerError.path] = innerError.message;
8
- });
9
- return fieldErrors;
10
- };
11
- /**
12
- * Create a `Validator` using a `yup` schema.
13
- */
14
- export const withYup = (validationSchema) => {
15
- return createValidator({
16
- validate: (data) => {
17
- try {
18
- const validated = validationSchema.validateSync(data, {
19
- abortEarly: false,
20
- });
21
- return { data: validated, error: undefined };
22
- }
23
- catch (err) {
24
- return {
25
- error: validationErrorToFieldErrors(err),
26
- data: undefined,
27
- };
28
- }
29
- },
30
- validateField: (data, field) => {
31
- try {
32
- validationSchema.validateSyncAt(field, data);
33
- return {};
34
- }
35
- catch (err) {
36
- return { error: err.message };
37
- }
38
- },
39
- });
40
- };
@@ -1,6 +0,0 @@
1
- import type { z } from "zod";
2
- import { Validator } from "..";
3
- /**
4
- * Create a validator using a `zod` schema.
5
- */
6
- export declare function withZod<T>(zodSchema: z.Schema<T>): Validator<T>;
@@ -1,50 +0,0 @@
1
- import isEqual from "lodash/isEqual";
2
- import toPath from "lodash/toPath";
3
- import { createValidator } from "./createValidator";
4
- const getIssuesForError = (err) => {
5
- return err.issues.flatMap((issue) => {
6
- if ("unionErrors" in issue) {
7
- return issue.unionErrors.flatMap((err) => getIssuesForError(err));
8
- }
9
- else {
10
- return [issue];
11
- }
12
- });
13
- };
14
- function pathToString(array) {
15
- return array.reduce(function (string, item) {
16
- var prefix = string === "" ? "" : ".";
17
- return string + (isNaN(Number(item)) ? prefix + item : "[" + item + "]");
18
- }, "");
19
- }
20
- /**
21
- * Create a validator using a `zod` schema.
22
- */
23
- export function withZod(zodSchema) {
24
- return createValidator({
25
- validate: (value) => {
26
- const result = zodSchema.safeParse(value);
27
- if (result.success)
28
- return { data: result.data, error: undefined };
29
- const fieldErrors = {};
30
- getIssuesForError(result.error).forEach((issue) => {
31
- const path = pathToString(issue.path);
32
- if (!fieldErrors[path])
33
- fieldErrors[path] = issue.message;
34
- });
35
- return { error: fieldErrors, data: undefined };
36
- },
37
- validateField: (data, field) => {
38
- var _a;
39
- const result = zodSchema.safeParse(data);
40
- if (result.success)
41
- return { error: undefined };
42
- return {
43
- error: (_a = getIssuesForError(result.error).find((issue) => {
44
- const allPathsAsString = issue.path.map((p) => `${p}`);
45
- return isEqual(allPathsAsString, toPath(field));
46
- })) === null || _a === void 0 ? void 0 : _a.message,
47
- };
48
- },
49
- });
50
- }
@@ -1,261 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
- Object.defineProperty(o, "default", { enumerable: true, value: v });
11
- }) : function(o, v) {
12
- o["default"] = v;
13
- });
14
- var __importStar = (this && this.__importStar) || function (mod) {
15
- if (mod && mod.__esModule) return mod;
16
- var result = {};
17
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
- __setModuleDefault(result, mod);
19
- return result;
20
- };
21
- var __importDefault = (this && this.__importDefault) || function (mod) {
22
- return (mod && mod.__esModule) ? mod : { "default": mod };
23
- };
24
- Object.defineProperty(exports, "__esModule", { value: true });
25
- exports.ValidatedForm = void 0;
26
- const jsx_runtime_1 = require("react/jsx-runtime");
27
- const react_1 = require("@remix-run/react");
28
- const uniq_1 = __importDefault(require("lodash/uniq"));
29
- const react_2 = __importStar(require("react"));
30
- const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
31
- const hooks_1 = require("./hooks");
32
- const constants_1 = require("./internal/constants");
33
- const formContext_1 = require("./internal/formContext");
34
- const hooks_2 = require("./internal/hooks");
35
- const MultiValueMap_1 = require("./internal/MultiValueMap");
36
- const reset_1 = require("./internal/reset");
37
- const state_1 = require("./internal/state");
38
- const controlledFields_1 = require("./internal/state/controlledFields");
39
- const submissionCallbacks_1 = require("./internal/submissionCallbacks");
40
- const util_1 = require("./internal/util");
41
- const getDataFromForm = (el) => new FormData(el);
42
- function nonNull(value) {
43
- return value !== null;
44
- }
45
- const focusFirstInvalidInput = (fieldErrors, customFocusHandlers, formElement) => {
46
- var _a;
47
- const namesInOrder = [...formElement.elements]
48
- .map((el) => {
49
- const input = el instanceof RadioNodeList ? el[0] : el;
50
- if (input instanceof HTMLInputElement)
51
- return input.name;
52
- return null;
53
- })
54
- .filter(nonNull)
55
- .filter((name) => name in fieldErrors);
56
- const uniqueNamesInOrder = (0, uniq_1.default)(namesInOrder);
57
- for (const fieldName of uniqueNamesInOrder) {
58
- if (customFocusHandlers.has(fieldName)) {
59
- customFocusHandlers.getAll(fieldName).forEach((handler) => {
60
- handler();
61
- });
62
- break;
63
- }
64
- const elem = formElement.elements.namedItem(fieldName);
65
- if (!elem)
66
- continue;
67
- if (elem instanceof RadioNodeList) {
68
- const selectedRadio = (_a = [...elem]
69
- .filter((item) => item instanceof HTMLInputElement)
70
- .find((item) => item.value === elem.value)) !== null && _a !== void 0 ? _a : elem[0];
71
- if (selectedRadio && selectedRadio instanceof HTMLInputElement) {
72
- selectedRadio.focus();
73
- break;
74
- }
75
- }
76
- if (elem instanceof HTMLInputElement) {
77
- if (elem.type === "hidden") {
78
- continue;
79
- }
80
- elem.focus();
81
- break;
82
- }
83
- }
84
- };
85
- const useFormId = (providedId) => {
86
- // We can use a `Symbol` here because we only use it after hydration
87
- const [symbolId] = (0, react_2.useState)(() => Symbol("remix-validated-form-id"));
88
- return providedId !== null && providedId !== void 0 ? providedId : symbolId;
89
- };
90
- /**
91
- * Use a component to access the state so we don't cause
92
- * any extra rerenders of the whole form.
93
- */
94
- const FormResetter = ({ resetAfterSubmit, formRef, }) => {
95
- const isSubmitting = (0, hooks_1.useIsSubmitting)();
96
- const isValid = (0, hooks_1.useIsValid)();
97
- (0, submissionCallbacks_1.useSubmitComplete)(isSubmitting, () => {
98
- var _a;
99
- if (isValid && resetAfterSubmit) {
100
- (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.reset();
101
- }
102
- });
103
- return null;
104
- };
105
- function formEventProxy(event) {
106
- let defaultPrevented = false;
107
- return new Proxy(event, {
108
- get: (target, prop) => {
109
- if (prop === "preventDefault") {
110
- return () => {
111
- defaultPrevented = true;
112
- };
113
- }
114
- if (prop === "defaultPrevented") {
115
- return defaultPrevented;
116
- }
117
- return target[prop];
118
- },
119
- });
120
- }
121
- /**
122
- * The primary form component of `remix-validated-form`.
123
- */
124
- function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues: unMemoizedDefaults, formRef: formRefProp, onReset, subaction, resetAfterSubmit = false, disableFocusOnError, method, replace, id, ...rest }) {
125
- var _a;
126
- const formId = useFormId(id);
127
- const providedDefaultValues = (0, util_1.useDeepEqualsMemo)(unMemoizedDefaults);
128
- const contextValue = (0, react_2.useMemo)(() => ({
129
- formId,
130
- action,
131
- subaction,
132
- defaultValuesProp: providedDefaultValues,
133
- fetcher,
134
- }), [action, fetcher, formId, providedDefaultValues, subaction]);
135
- const backendError = (0, hooks_2.useErrorResponseForForm)(contextValue);
136
- const backendDefaultValues = (0, hooks_2.useDefaultValuesFromLoader)(contextValue);
137
- const hasActiveSubmission = (0, hooks_2.useHasActiveFormSubmit)(contextValue);
138
- const formRef = (0, react_2.useRef)(null);
139
- const Form = (_a = fetcher === null || fetcher === void 0 ? void 0 : fetcher.Form) !== null && _a !== void 0 ? _a : react_1.Form;
140
- const submit = (0, react_1.useSubmit)();
141
- const setFieldErrors = (0, hooks_2.useFormUpdateAtom)((0, state_1.fieldErrorsAtom)(formId));
142
- const setFieldError = (0, hooks_2.useFormUpdateAtom)((0, state_1.setFieldErrorAtom)(formId));
143
- const reset = (0, hooks_2.useFormUpdateAtom)((0, reset_1.resetAtom)(formId));
144
- const startSubmit = (0, hooks_2.useFormUpdateAtom)((0, state_1.startSubmitAtom)(formId));
145
- const endSubmit = (0, hooks_2.useFormUpdateAtom)((0, state_1.endSubmitAtom)(formId));
146
- const syncFormProps = (0, hooks_2.useFormUpdateAtom)((0, state_1.formPropsAtom)(formId));
147
- const setHydrated = (0, hooks_2.useFormUpdateAtom)((0, state_1.isHydratedAtom)(formId));
148
- const setFormElementInState = (0, hooks_2.useFormUpdateAtom)((0, state_1.formElementAtom)(formId));
149
- (0, react_2.useEffect)(() => {
150
- setHydrated(true);
151
- return () => (0, state_1.cleanupFormState)(formId);
152
- }, [formId, setHydrated]);
153
- const awaitValue = (0, controlledFields_1.useAwaitValue)(formId);
154
- const validateField = (0, react_2.useCallback)(async (field) => {
155
- (0, tiny_invariant_1.default)(formRef.current, "Cannot find reference to form");
156
- await awaitValue(field);
157
- const { error } = await validator.validateField(getDataFromForm(formRef.current), field);
158
- if (error) {
159
- setFieldError({ field, error });
160
- return error;
161
- }
162
- else {
163
- setFieldError({ field, error: undefined });
164
- return null;
165
- }
166
- }, [awaitValue, setFieldError, validator]);
167
- const customFocusHandlers = (0, MultiValueMap_1.useMultiValueMap)();
168
- const registerReceiveFocus = (0, react_2.useCallback)((fieldName, handler) => {
169
- customFocusHandlers().add(fieldName, handler);
170
- return () => {
171
- customFocusHandlers().remove(fieldName, handler);
172
- };
173
- }, [customFocusHandlers]);
174
- (0, util_1.useIsomorphicLayoutEffect)(() => {
175
- var _a;
176
- syncFormProps({
177
- action,
178
- defaultValues: (_a = providedDefaultValues !== null && providedDefaultValues !== void 0 ? providedDefaultValues : backendDefaultValues) !== null && _a !== void 0 ? _a : {},
179
- subaction,
180
- validateField,
181
- registerReceiveFocus,
182
- });
183
- }, [
184
- action,
185
- providedDefaultValues,
186
- registerReceiveFocus,
187
- subaction,
188
- syncFormProps,
189
- validateField,
190
- backendDefaultValues,
191
- ]);
192
- (0, react_2.useEffect)(() => {
193
- var _a;
194
- setFieldErrors((_a = backendError === null || backendError === void 0 ? void 0 : backendError.fieldErrors) !== null && _a !== void 0 ? _a : {});
195
- }, [backendError === null || backendError === void 0 ? void 0 : backendError.fieldErrors, setFieldErrors, setFieldError]);
196
- (0, submissionCallbacks_1.useSubmitComplete)(hasActiveSubmission, () => {
197
- endSubmit();
198
- });
199
- let clickedButtonRef = react_2.default.useRef();
200
- (0, react_2.useEffect)(() => {
201
- let form = formRef.current;
202
- if (!form)
203
- return;
204
- function handleClick(event) {
205
- if (!(event.target instanceof HTMLElement))
206
- return;
207
- let submitButton = event.target.closest("button,input[type=submit]");
208
- if (submitButton &&
209
- submitButton.form === form &&
210
- submitButton.type === "submit") {
211
- clickedButtonRef.current = submitButton;
212
- }
213
- }
214
- window.addEventListener("click", handleClick, { capture: true });
215
- return () => {
216
- window.removeEventListener("click", handleClick, { capture: true });
217
- };
218
- }, []);
219
- const handleSubmit = async (e) => {
220
- startSubmit();
221
- const result = await validator.validate(getDataFromForm(e.currentTarget));
222
- if (result.error) {
223
- endSubmit();
224
- setFieldErrors(result.error.fieldErrors);
225
- if (!disableFocusOnError) {
226
- focusFirstInvalidInput(result.error.fieldErrors, customFocusHandlers(), formRef.current);
227
- }
228
- }
229
- else {
230
- const eventProxy = formEventProxy(e);
231
- await (onSubmit === null || onSubmit === void 0 ? void 0 : onSubmit(result.data, eventProxy));
232
- if (eventProxy.defaultPrevented) {
233
- endSubmit();
234
- return;
235
- }
236
- // We deviate from the remix code here a bit because of our async submit.
237
- // In remix's `FormImpl`, they use `event.currentTarget` to get the form,
238
- // but we already have the form in `formRef.current` so we can just use that.
239
- // If we use `event.currentTarget` here, it will break because `currentTarget`
240
- // will have changed since the start of the submission.
241
- if (fetcher)
242
- fetcher.submit(clickedButtonRef.current || formRef.current);
243
- else
244
- submit(clickedButtonRef.current || formRef.current, {
245
- method,
246
- replace,
247
- });
248
- clickedButtonRef.current = null;
249
- }
250
- };
251
- return ((0, jsx_runtime_1.jsx)(Form, { ref: (0, util_1.mergeRefs)([formRef, formRefProp, setFormElementInState]), ...rest, id: id, action: action, method: method, replace: replace, onSubmit: (e) => {
252
- e.preventDefault();
253
- handleSubmit(e);
254
- }, onReset: (event) => {
255
- onReset === null || onReset === void 0 ? void 0 : onReset(event);
256
- if (event.defaultPrevented)
257
- return;
258
- reset();
259
- }, children: (0, jsx_runtime_1.jsxs)(formContext_1.InternalFormContext.Provider, { value: contextValue, children: [(0, jsx_runtime_1.jsx)(FormResetter, { formRef: formRef, resetAfterSubmit: resetAfterSubmit }, void 0), subaction && ((0, jsx_runtime_1.jsx)("input", { type: "hidden", value: subaction, name: "subaction" }, void 0)), id && (0, jsx_runtime_1.jsx)("input", { type: "hidden", value: id, name: constants_1.FORM_ID_FIELD }, void 0), children] }, void 0) }, void 0));
260
- }
261
- exports.ValidatedForm = ValidatedForm;