remix-validated-form 4.0.0 → 4.0.2

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.
@@ -1,9 +1,9 @@
1
1
  $ npm run build:browser && npm run build:main
2
2
 
3
- > remix-validated-form@3.4.2 build:browser
3
+ > remix-validated-form@4.0.1-beta.1 build:browser
4
4
  > tsc --module ESNext --outDir ./browser
5
5
 
6
6
 
7
- > remix-validated-form@3.4.2 build:main
7
+ > remix-validated-form@4.0.1-beta.1 build:main
8
8
  > tsc --module CommonJS --outDir ./build
9
9
 
@@ -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, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }: FormProps<DataType>): JSX.Element;
50
+ export declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }: FormProps<DataType>): JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Form as RemixForm, useActionData, useFormAction, useSubmit, useTransition, } from "@remix-run/react";
2
+ import { Form as RemixForm, useActionData, useSubmit, useTransition, } from "@remix-run/react";
3
3
  import uniq from "lodash/uniq";
4
4
  import React, { useEffect, useMemo, useRef, useState, } from "react";
5
5
  import invariant from "tiny-invariant";
@@ -30,19 +30,18 @@ function useFieldErrors(fieldErrorsFromBackend) {
30
30
  }, [fieldErrorsFromBackend]);
31
31
  return [fieldErrors, setFieldErrors];
32
32
  }
33
- const useIsSubmitting = (action, subaction, fetcher) => {
34
- const actionForCurrentPage = useFormAction();
35
- const pendingFormSubmit = useTransition().submission;
36
- if (fetcher)
37
- return fetcher.state === "submitting";
38
- if (!pendingFormSubmit)
39
- return false;
40
- const { formData, action: pendingAction } = pendingFormSubmit;
41
- const pendingSubAction = formData.get("subaction");
42
- const expectedAction = action !== null && action !== void 0 ? action : actionForCurrentPage;
43
- if (subaction)
44
- return expectedAction === pendingAction && subaction === pendingSubAction;
45
- return expectedAction === pendingAction && !pendingSubAction;
33
+ const useIsSubmitting = (fetcher) => {
34
+ const [isSubmitStarted, setSubmitStarted] = useState(false);
35
+ const transition = useTransition();
36
+ const hasActiveSubmission = fetcher
37
+ ? fetcher.state === "submitting"
38
+ : !!transition.submission;
39
+ const startSubmit = () => setSubmitStarted(true);
40
+ const endSubmit = () => setSubmitStarted(false);
41
+ useSubmitComplete(hasActiveSubmission, () => {
42
+ endSubmit();
43
+ });
44
+ return [isSubmitStarted, startSubmit, endSubmit];
46
45
  };
47
46
  const getDataFromForm = (el) => new FormData(el);
48
47
  /**
@@ -110,12 +109,11 @@ const focusFirstInvalidInput = (fieldErrors, customFocusHandlers, formElement) =
110
109
  /**
111
110
  * The primary form component of `remix-validated-form`.
112
111
  */
113
- export function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }) {
112
+ export function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }) {
114
113
  var _a;
115
114
  const backendError = useErrorResponseForThisForm(fetcher, subaction);
116
115
  const [fieldErrors, setFieldErrors] = useFieldErrors(backendError === null || backendError === void 0 ? void 0 : backendError.fieldErrors);
117
- const isSubmitting = useIsSubmitting(action, subaction, fetcher);
118
- const [isValidating, setIsValidating] = useState(false);
116
+ const [isSubmitting, startSubmit, endSubmit] = useIsSubmitting(fetcher);
119
117
  const defaultsToUse = useDefaultValues(backendError === null || backendError === void 0 ? void 0 : backendError.repopulateFields, defaultValues);
120
118
  const [touchedFields, setTouchedFields] = useState({});
121
119
  const [hasBeenSubmitted, setHasBeenSubmitted] = useState(false);
@@ -123,7 +121,7 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
123
121
  const formRef = useRef(null);
124
122
  useSubmitComplete(isSubmitting, () => {
125
123
  var _a;
126
- setIsValidating(false);
124
+ endSubmit();
127
125
  if (!backendError && resetAfterSubmit) {
128
126
  (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.reset();
129
127
  }
@@ -133,7 +131,7 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
133
131
  fieldErrors,
134
132
  action,
135
133
  defaultValues: defaultsToUse,
136
- isSubmitting: isValidating || isSubmitting,
134
+ isSubmitting,
137
135
  isValid: Object.keys(fieldErrors).length === 0,
138
136
  touchedFields,
139
137
  setFieldTouched: (fieldName, touched) => setTouchedFields((prev) => ({
@@ -179,7 +177,6 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
179
177
  fieldErrors,
180
178
  action,
181
179
  defaultsToUse,
182
- isValidating,
183
180
  isSubmitting,
184
181
  touchedFields,
185
182
  hasBeenSubmitted,
@@ -208,13 +205,13 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
208
205
  window.removeEventListener("click", handleClick);
209
206
  };
210
207
  }, []);
211
- return (_jsx(Form, { ref: mergeRefs([formRef, formRefProp]), ...rest, action: action, onSubmit: async (e) => {
208
+ return (_jsx(Form, { ref: mergeRefs([formRef, formRefProp]), ...rest, action: action, method: method, replace: replace, onSubmit: async (e) => {
212
209
  e.preventDefault();
213
210
  setHasBeenSubmitted(true);
214
- setIsValidating(true);
211
+ startSubmit();
215
212
  const result = await validator.validate(getDataFromForm(e.currentTarget));
216
213
  if (result.error) {
217
- setIsValidating(false);
214
+ endSubmit();
218
215
  setFieldErrors(result.error.fieldErrors);
219
216
  if (!disableFocusOnError) {
220
217
  focusFirstInvalidInput(result.error.fieldErrors, customFocusHandlers(), formRef.current);
@@ -225,7 +222,10 @@ export function ValidatedForm({ validator, onSubmit, children, fetcher, action,
225
222
  if (fetcher)
226
223
  fetcher.submit(clickedButtonRef.current || e.currentTarget);
227
224
  else
228
- submit(clickedButtonRef.current || e.currentTarget);
225
+ submit(clickedButtonRef.current || e.currentTarget, {
226
+ method,
227
+ replace,
228
+ });
229
229
  clickedButtonRef.current = null;
230
230
  }
231
231
  }, onReset: (event) => {
@@ -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, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }: FormProps<DataType>): JSX.Element;
50
+ export declare function ValidatedForm<DataType>({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }: FormProps<DataType>): JSX.Element;
@@ -55,19 +55,18 @@ function useFieldErrors(fieldErrorsFromBackend) {
55
55
  }, [fieldErrorsFromBackend]);
56
56
  return [fieldErrors, setFieldErrors];
57
57
  }
58
- const useIsSubmitting = (action, subaction, fetcher) => {
59
- const actionForCurrentPage = (0, react_1.useFormAction)();
60
- const pendingFormSubmit = (0, react_1.useTransition)().submission;
61
- if (fetcher)
62
- return fetcher.state === "submitting";
63
- if (!pendingFormSubmit)
64
- return false;
65
- const { formData, action: pendingAction } = pendingFormSubmit;
66
- const pendingSubAction = formData.get("subaction");
67
- const expectedAction = action !== null && action !== void 0 ? action : actionForCurrentPage;
68
- if (subaction)
69
- return expectedAction === pendingAction && subaction === pendingSubAction;
70
- return expectedAction === pendingAction && !pendingSubAction;
58
+ const useIsSubmitting = (fetcher) => {
59
+ const [isSubmitStarted, setSubmitStarted] = (0, react_2.useState)(false);
60
+ const transition = (0, react_1.useTransition)();
61
+ const hasActiveSubmission = fetcher
62
+ ? fetcher.state === "submitting"
63
+ : !!transition.submission;
64
+ const startSubmit = () => setSubmitStarted(true);
65
+ const endSubmit = () => setSubmitStarted(false);
66
+ (0, submissionCallbacks_1.useSubmitComplete)(hasActiveSubmission, () => {
67
+ endSubmit();
68
+ });
69
+ return [isSubmitStarted, startSubmit, endSubmit];
71
70
  };
72
71
  const getDataFromForm = (el) => new FormData(el);
73
72
  /**
@@ -135,12 +134,11 @@ const focusFirstInvalidInput = (fieldErrors, customFocusHandlers, formElement) =
135
134
  /**
136
135
  * The primary form component of `remix-validated-form`.
137
136
  */
138
- function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, ...rest }) {
137
+ function ValidatedForm({ validator, onSubmit, children, fetcher, action, defaultValues, formRef: formRefProp, onReset, subaction, resetAfterSubmit, disableFocusOnError, method, replace, ...rest }) {
139
138
  var _a;
140
139
  const backendError = useErrorResponseForThisForm(fetcher, subaction);
141
140
  const [fieldErrors, setFieldErrors] = useFieldErrors(backendError === null || backendError === void 0 ? void 0 : backendError.fieldErrors);
142
- const isSubmitting = useIsSubmitting(action, subaction, fetcher);
143
- const [isValidating, setIsValidating] = (0, react_2.useState)(false);
141
+ const [isSubmitting, startSubmit, endSubmit] = useIsSubmitting(fetcher);
144
142
  const defaultsToUse = useDefaultValues(backendError === null || backendError === void 0 ? void 0 : backendError.repopulateFields, defaultValues);
145
143
  const [touchedFields, setTouchedFields] = (0, react_2.useState)({});
146
144
  const [hasBeenSubmitted, setHasBeenSubmitted] = (0, react_2.useState)(false);
@@ -148,7 +146,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
148
146
  const formRef = (0, react_2.useRef)(null);
149
147
  (0, submissionCallbacks_1.useSubmitComplete)(isSubmitting, () => {
150
148
  var _a;
151
- setIsValidating(false);
149
+ endSubmit();
152
150
  if (!backendError && resetAfterSubmit) {
153
151
  (_a = formRef.current) === null || _a === void 0 ? void 0 : _a.reset();
154
152
  }
@@ -158,7 +156,7 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
158
156
  fieldErrors,
159
157
  action,
160
158
  defaultValues: defaultsToUse,
161
- isSubmitting: isValidating || isSubmitting,
159
+ isSubmitting,
162
160
  isValid: Object.keys(fieldErrors).length === 0,
163
161
  touchedFields,
164
162
  setFieldTouched: (fieldName, touched) => setTouchedFields((prev) => ({
@@ -204,7 +202,6 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
204
202
  fieldErrors,
205
203
  action,
206
204
  defaultsToUse,
207
- isValidating,
208
205
  isSubmitting,
209
206
  touchedFields,
210
207
  hasBeenSubmitted,
@@ -233,13 +230,13 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
233
230
  window.removeEventListener("click", handleClick);
234
231
  };
235
232
  }, []);
236
- return ((0, jsx_runtime_1.jsx)(Form, { ref: (0, util_1.mergeRefs)([formRef, formRefProp]), ...rest, action: action, onSubmit: async (e) => {
233
+ return ((0, jsx_runtime_1.jsx)(Form, { ref: (0, util_1.mergeRefs)([formRef, formRefProp]), ...rest, action: action, method: method, replace: replace, onSubmit: async (e) => {
237
234
  e.preventDefault();
238
235
  setHasBeenSubmitted(true);
239
- setIsValidating(true);
236
+ startSubmit();
240
237
  const result = await validator.validate(getDataFromForm(e.currentTarget));
241
238
  if (result.error) {
242
- setIsValidating(false);
239
+ endSubmit();
243
240
  setFieldErrors(result.error.fieldErrors);
244
241
  if (!disableFocusOnError) {
245
242
  focusFirstInvalidInput(result.error.fieldErrors, customFocusHandlers(), formRef.current);
@@ -250,7 +247,10 @@ function ValidatedForm({ validator, onSubmit, children, fetcher, action, default
250
247
  if (fetcher)
251
248
  fetcher.submit(clickedButtonRef.current || e.currentTarget);
252
249
  else
253
- submit(clickedButtonRef.current || e.currentTarget);
250
+ submit(clickedButtonRef.current || e.currentTarget, {
251
+ method,
252
+ replace,
253
+ });
254
254
  clickedButtonRef.current = null;
255
255
  }
256
256
  }, onReset: (event) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "remix-validated-form",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "description": "Form component and utils for easy form validation in remix",
5
5
  "browser": "./browser/index.js",
6
6
  "main": "./build/index.js",
@@ -6,6 +6,7 @@ import {
6
6
  useSubmit,
7
7
  useTransition,
8
8
  } from "@remix-run/react";
9
+ import { Fetcher } from "@remix-run/react/transition";
9
10
  import uniq from "lodash/uniq";
10
11
  import React, {
11
12
  ComponentProps,
@@ -106,22 +107,22 @@ function useFieldErrors(
106
107
  }
107
108
 
108
109
  const useIsSubmitting = (
109
- action?: string,
110
- subaction?: string,
111
- fetcher?: ReturnType<typeof useFetcher>
112
- ) => {
113
- const actionForCurrentPage = useFormAction();
114
- const pendingFormSubmit = useTransition().submission;
110
+ fetcher?: Fetcher
111
+ ): [boolean, () => void, () => void] => {
112
+ const [isSubmitStarted, setSubmitStarted] = useState(false);
113
+ const transition = useTransition();
114
+ const hasActiveSubmission = fetcher
115
+ ? fetcher.state === "submitting"
116
+ : !!transition.submission;
117
+
118
+ const startSubmit = () => setSubmitStarted(true);
119
+ const endSubmit = () => setSubmitStarted(false);
115
120
 
116
- if (fetcher) return fetcher.state === "submitting";
117
- if (!pendingFormSubmit) return false;
121
+ useSubmitComplete(hasActiveSubmission, () => {
122
+ endSubmit();
123
+ });
118
124
 
119
- const { formData, action: pendingAction } = pendingFormSubmit;
120
- const pendingSubAction = formData.get("subaction");
121
- const expectedAction = action ?? actionForCurrentPage;
122
- if (subaction)
123
- return expectedAction === pendingAction && subaction === pendingSubAction;
124
- return expectedAction === pendingAction && !pendingSubAction;
125
+ return [isSubmitStarted, startSubmit, endSubmit];
125
126
  };
126
127
 
127
128
  const getDataFromForm = (el: HTMLFormElement) => new FormData(el);
@@ -218,14 +219,16 @@ export function ValidatedForm<DataType>({
218
219
  subaction,
219
220
  resetAfterSubmit,
220
221
  disableFocusOnError,
222
+ method,
223
+ replace,
221
224
  ...rest
222
225
  }: FormProps<DataType>) {
223
226
  const backendError = useErrorResponseForThisForm(fetcher, subaction);
224
227
  const [fieldErrors, setFieldErrors] = useFieldErrors(
225
228
  backendError?.fieldErrors
226
229
  );
227
- const isSubmitting = useIsSubmitting(action, subaction, fetcher);
228
- const [isValidating, setIsValidating] = useState(false);
230
+ const [isSubmitting, startSubmit, endSubmit] = useIsSubmitting(fetcher);
231
+
229
232
  const defaultsToUse = useDefaultValues(
230
233
  backendError?.repopulateFields,
231
234
  defaultValues
@@ -235,7 +238,7 @@ export function ValidatedForm<DataType>({
235
238
  const submit = useSubmit();
236
239
  const formRef = useRef<HTMLFormElement>(null);
237
240
  useSubmitComplete(isSubmitting, () => {
238
- setIsValidating(false);
241
+ endSubmit();
239
242
  if (!backendError && resetAfterSubmit) {
240
243
  formRef.current?.reset();
241
244
  }
@@ -247,7 +250,7 @@ export function ValidatedForm<DataType>({
247
250
  fieldErrors,
248
251
  action,
249
252
  defaultValues: defaultsToUse,
250
- isSubmitting: isValidating || isSubmitting,
253
+ isSubmitting,
251
254
  isValid: Object.keys(fieldErrors).length === 0,
252
255
  touchedFields,
253
256
  setFieldTouched: (fieldName: string, touched: boolean) =>
@@ -296,7 +299,6 @@ export function ValidatedForm<DataType>({
296
299
  fieldErrors,
297
300
  action,
298
301
  defaultsToUse,
299
- isValidating,
300
302
  isSubmitting,
301
303
  touchedFields,
302
304
  hasBeenSubmitted,
@@ -339,15 +341,17 @@ export function ValidatedForm<DataType>({
339
341
  ref={mergeRefs([formRef, formRefProp])}
340
342
  {...rest}
341
343
  action={action}
344
+ method={method}
345
+ replace={replace}
342
346
  onSubmit={async (e) => {
343
347
  e.preventDefault();
344
348
  setHasBeenSubmitted(true);
345
- setIsValidating(true);
349
+ startSubmit();
346
350
  const result = await validator.validate(
347
351
  getDataFromForm(e.currentTarget)
348
352
  );
349
353
  if (result.error) {
350
- setIsValidating(false);
354
+ endSubmit();
351
355
  setFieldErrors(result.error.fieldErrors);
352
356
  if (!disableFocusOnError) {
353
357
  focusFirstInvalidInput(
@@ -360,7 +364,11 @@ export function ValidatedForm<DataType>({
360
364
  onSubmit && onSubmit(result.data, e);
361
365
  if (fetcher)
362
366
  fetcher.submit(clickedButtonRef.current || e.currentTarget);
363
- else submit(clickedButtonRef.current || e.currentTarget);
367
+ else
368
+ submit(clickedButtonRef.current || e.currentTarget, {
369
+ method,
370
+ replace,
371
+ });
364
372
  clickedButtonRef.current = null;
365
373
  }
366
374
  }}