@void/react 0.26.1 → 0.28.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/dist/index.d.mts CHANGED
@@ -1,2 +1,103 @@
1
- import { _ as Form, a as ParamsContext, c as action, d as useNavigation, f as useForm, g as Link, h as useRouter, i as NavigationContext, l as setActionRouter, m as useParams, n as InferProps, o as RouterContext, p as useShared, r as ErrorsContext, s as SharedContext, t as Deferred, u as useIslandForm } from "./index-D5M3zrVB.mjs";
2
- export { Deferred, ErrorsContext, Form, type InferProps, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
1
+ import { AnchorHTMLAttributes, ButtonHTMLAttributes, Context, FormHTMLAttributes, JSX, ReactElement, ReactNode } from "react";
2
+ import { ActionClient, FormMethod, FormModel, NavigationState, VoidActionError, VoidRouter } from "void/pages-client";
3
+ import { CloudContextVariables, InferProps } from "void";
4
+ import { ActionFormOptions, ActionUrl, ResolveActionBody } from "void/routes";
5
+ //#region src/runtime/form.d.ts
6
+ type FormProps = Omit<FormHTMLAttributes<HTMLFormElement>, "action" | "onSubmit" | "method" | "onError"> & {
7
+ form: FormModel<[FormData]>;
8
+ method?: FormMethod;
9
+ renderError?: (error: VoidActionError) => ReactNode;
10
+ };
11
+ export declare function Form({ form, method, renderError, children, ...props }: FormProps): ReactElement;
12
+ //#endregion
13
+ //#region src/runtime/link.d.ts
14
+ type PrefetchStrategy = "hover" | "click" | "mount" | "visible";
15
+ type LinkMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
16
+ type CommonProps = {
17
+ href: string;
18
+ method?: LinkMethod | Lowercase<LinkMethod>;
19
+ data?: Record<string, unknown>;
20
+ preserveScroll?: boolean;
21
+ preserveState?: boolean;
22
+ replace?: boolean;
23
+ reloadDocument?: boolean;
24
+ viewTransition?: boolean;
25
+ prefetch?: boolean | PrefetchStrategy | ReadonlyArray<PrefetchStrategy>;
26
+ cacheFor?: number | string | [string, string];
27
+ onNavigate?: (event: {
28
+ preventDefault(): void;
29
+ }) => void;
30
+ };
31
+ type LinkProps = CommonProps & Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof CommonProps> & Omit<ButtonHTMLAttributes<HTMLButtonElement>, keyof CommonProps>;
32
+ export declare function Link({ href, method, data, preserveScroll, preserveState, replace, reloadDocument, viewTransition, prefetch: prefetchProp, cacheFor, onNavigate, children, style, onClick: onClickProp, onMouseEnter: onMouseEnterProp, onMouseLeave: onMouseLeaveProp, onFocus: onFocusProp, onMouseDown: onMouseDownProp, onTouchStart: onTouchStartProp, ...rest }: LinkProps): JSX.Element;
33
+ //#endregion
34
+ //#region src/runtime/use-router.d.ts
35
+ export declare function useRouter(): VoidRouter;
36
+ //#endregion
37
+ //#region src/runtime/use-params.d.ts
38
+ export declare function useParams<T extends Record<string, string> = Record<string, string>>(): T;
39
+ //#endregion
40
+ //#region src/runtime/use-shared.d.ts
41
+ export declare function useShared<T = CloudContextVariables["shared"]>(): T;
42
+ //#endregion
43
+ //#region src/runtime/use-form.d.ts
44
+ export declare function useForm<U extends ActionUrl & string>(url: U, defaults: ResolveActionBody<U>, options?: ActionFormOptions<U>): {
45
+ data: ResolveActionBody<U>;
46
+ setData: <K extends keyof ResolveActionBody<U> & string>(field: K, value: ResolveActionBody<U>[K]) => void;
47
+ errors: Partial<Record<keyof ResolveActionBody<U> & string, string>>;
48
+ error: VoidActionError | null;
49
+ pending: boolean;
50
+ hasChanges: boolean;
51
+ wasSuccessful: boolean;
52
+ recentlySuccessful: boolean;
53
+ post(formData: FormData): void;
54
+ put(formData: FormData): void;
55
+ patch(formData: FormData): void;
56
+ delete(formData: FormData): void;
57
+ reset(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
58
+ clearErrors(...fields: ReadonlyArray<keyof ResolveActionBody<U> & string>): void;
59
+ clearError(): void;
60
+ };
61
+ //#endregion
62
+ //#region src/runtime/use-navigation.d.ts
63
+ export declare function useNavigation(): NavigationState;
64
+ //#endregion
65
+ //#region src/runtime/use-island-form.d.ts
66
+ type IslandFormReturn<T extends Record<string, unknown>> = Readonly<{
67
+ data: T;
68
+ setData: <K extends keyof T & string>(field: K, value: T[K]) => void;
69
+ errors: Record<string, string>;
70
+ error: VoidActionError | null;
71
+ pending: boolean;
72
+ hasChanges: boolean;
73
+ wasSuccessful: boolean;
74
+ recentlySuccessful: boolean;
75
+ reset: (...fields: ReadonlyArray<keyof T & string>) => void;
76
+ clearErrors: (...fields: ReadonlyArray<string>) => void;
77
+ clearError: () => void;
78
+ post: (url: string) => Promise<void>;
79
+ put: (url: string) => Promise<void>;
80
+ patch: (url: string) => Promise<void>;
81
+ delete: (url: string) => Promise<void>;
82
+ }>;
83
+ /**
84
+ * Form hook for island pages. Uses fetch + page reload instead of the
85
+ * Void Router (which is not available in island mode).
86
+ */
87
+ export declare function useIslandForm<T extends Record<string, unknown>>(defaults: T): IslandFormReturn<T>;
88
+ //#endregion
89
+ //#region src/runtime/action.d.ts
90
+ export declare const action: ActionClient["action"];
91
+ export declare const setActionRouter: ActionClient["setActionRouter"];
92
+ //#endregion
93
+ //#region src/runtime/context.d.ts
94
+ export declare const SharedContext: Context<Record<string, unknown> | null>;
95
+ export declare const ErrorsContext: Context<Record<string, string> | null>;
96
+ export declare const ParamsContext: Context<Record<string, string> | null>;
97
+ export declare const RouterContext: Context<VoidRouter | null>;
98
+ export declare const NavigationContext: Context<NavigationState>;
99
+ //#endregion
100
+ //#region src/runtime/index.d.ts
101
+ export type Deferred<T> = Promise<T>;
102
+ //#endregion
103
+ export type { InferProps };
package/dist/index.mjs CHANGED
@@ -1,4 +1,463 @@
1
- import { a as useParams, c as Form, i as useShared, n as useNavigation, o as useRouter, r as useForm, s as Link, t as useIslandForm } from "./runtime--GesOVur.mjs";
2
1
  import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "./context-B0GL7p5m.mjs";
3
2
  import { n as setActionRouter, t as action } from "./action-4EvQppia.mjs";
3
+ import { useActionState, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
4
+ import { VoidActionError, categorizeActionError, formErrorMessage, formRetryMessage, formSubmissionBlocked, getValidationErrors, interpolateRouteParams, isAbortError, isEqualFormValue, isModifiedEvent, mergeDataIntoHref, scheduleFormRetry, ssrProxy, submitAction } from "void/pages-client";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/runtime/form.tsx
7
+ function Form({ form, method = "post", renderError, children, ...props }) {
8
+ const [now, setNow] = useState(Date.now);
9
+ useEffect(() => scheduleFormRetry(form.error, () => setNow(Date.now())), [form.error]);
10
+ const message = formErrorMessage(form);
11
+ const retry = formRetryMessage(form.error);
12
+ const blocked = formSubmissionBlocked(form, now);
13
+ return /* @__PURE__ */ jsxs("form", {
14
+ ...props,
15
+ action: form[method],
16
+ "aria-busy": form.pending,
17
+ onSubmit: (event) => {
18
+ if (blocked) event.preventDefault();
19
+ },
20
+ children: [message && /* @__PURE__ */ jsxs("div", {
21
+ role: "alert",
22
+ "data-void-form-error": true,
23
+ children: [form.error && renderError ? renderError(form.error) : message, retry && /* @__PURE__ */ jsx("p", { children: retry })]
24
+ }), children]
25
+ });
26
+ }
27
+ //#endregion
28
+ //#region src/runtime/link.tsx
29
+ function Link({ href, method = "GET", data, preserveScroll = false, preserveState = false, replace = false, reloadDocument = false, viewTransition, prefetch: prefetchProp = false, cacheFor, onNavigate, children, style, onClick: onClickProp, onMouseEnter: onMouseEnterProp, onMouseLeave: onMouseLeaveProp, onFocus: onFocusProp, onMouseDown: onMouseDownProp, onTouchStart: onTouchStartProp, ...rest }) {
30
+ const router = useContext(RouterContext);
31
+ const elementRef = useRef(null);
32
+ const hoverTimerRef = useRef(null);
33
+ const normalizedMethod = useMemo(() => method.toUpperCase(), [method]);
34
+ const isGet = normalizedMethod === "GET";
35
+ const hrefWithData = useMemo(() => isGet ? mergeDataIntoHref(href, data) : href, [
36
+ data,
37
+ href,
38
+ isGet
39
+ ]);
40
+ const strategies = useMemo(() => {
41
+ if (prefetchProp === false || reloadDocument) return [];
42
+ if (prefetchProp === true) return ["hover"];
43
+ if (typeof prefetchProp === "string") return [prefetchProp];
44
+ return prefetchProp;
45
+ }, [prefetchProp, reloadDocument]);
46
+ const hoverDelay = router?._hoverDelay ?? 75;
47
+ if (prefetchProp !== false && !isGet) throw new Error("Link: prefetch only supports GET links. Remove `prefetch` or use method=\"GET\".");
48
+ if (reloadDocument && !isGet) throw new Error("Link: reloadDocument only supports GET links.");
49
+ const doPrefetch = useCallback(() => {
50
+ if (!router?.prefetch) return;
51
+ const current = window.location.pathname + window.location.search;
52
+ if (hrefWithData === current) return;
53
+ const options = {};
54
+ if (cacheFor !== void 0) options.cacheFor = cacheFor;
55
+ router.prefetch(hrefWithData, options);
56
+ }, [
57
+ cacheFor,
58
+ hrefWithData,
59
+ router
60
+ ]);
61
+ const shouldHandleClick = useCallback((event) => {
62
+ if (reloadDocument || !router || event.defaultPrevented || isModifiedEvent(event)) return false;
63
+ if (event.currentTarget.hasAttribute("download")) return false;
64
+ if (new URL(hrefWithData, window.location.origin).origin !== window.location.origin) return false;
65
+ return true;
66
+ }, [
67
+ hrefWithData,
68
+ reloadDocument,
69
+ router
70
+ ]);
71
+ const onClick = useCallback((event) => {
72
+ onClickProp?.(event);
73
+ if (!shouldHandleClick(event)) return;
74
+ let navigatePrevented = false;
75
+ onNavigate?.({ preventDefault() {
76
+ navigatePrevented = true;
77
+ } });
78
+ if (navigatePrevented) {
79
+ event.preventDefault();
80
+ return;
81
+ }
82
+ event.preventDefault();
83
+ router?.visit(hrefWithData, {
84
+ method: normalizedMethod,
85
+ data: isGet ? void 0 : data,
86
+ preserveScroll,
87
+ preserveState,
88
+ replace,
89
+ viewTransition
90
+ });
91
+ }, [
92
+ data,
93
+ hrefWithData,
94
+ isGet,
95
+ normalizedMethod,
96
+ onClickProp,
97
+ onNavigate,
98
+ preserveScroll,
99
+ preserveState,
100
+ replace,
101
+ router,
102
+ shouldHandleClick,
103
+ viewTransition
104
+ ]);
105
+ const onMouseEnter = useCallback((event) => {
106
+ onMouseEnterProp?.(event);
107
+ if (event.defaultPrevented) return;
108
+ if (hoverTimerRef.current) clearTimeout(hoverTimerRef.current);
109
+ hoverTimerRef.current = setTimeout(doPrefetch, hoverDelay);
110
+ }, [
111
+ doPrefetch,
112
+ hoverDelay,
113
+ onMouseEnterProp
114
+ ]);
115
+ const onMouseLeave = useCallback((event) => {
116
+ onMouseLeaveProp?.(event);
117
+ if (hoverTimerRef.current) {
118
+ clearTimeout(hoverTimerRef.current);
119
+ hoverTimerRef.current = null;
120
+ }
121
+ }, [onMouseLeaveProp]);
122
+ const onFocus = useCallback((event) => {
123
+ onFocusProp?.(event);
124
+ if (!event.defaultPrevented) doPrefetch();
125
+ }, [doPrefetch, onFocusProp]);
126
+ const onMouseDown = useCallback((event) => {
127
+ onMouseDownProp?.(event);
128
+ if (!event.defaultPrevented) doPrefetch();
129
+ }, [doPrefetch, onMouseDownProp]);
130
+ const onTouchStart = useCallback((event) => {
131
+ onTouchStartProp?.(event);
132
+ if (!event.defaultPrevented) doPrefetch();
133
+ }, [doPrefetch, onTouchStartProp]);
134
+ useEffect(() => {
135
+ if (strategies.length === 0) return;
136
+ if (strategies.includes("mount")) doPrefetch();
137
+ let observer = null;
138
+ if (strategies.includes("visible") && elementRef.current) {
139
+ observer = new IntersectionObserver((entries) => {
140
+ if (entries.some((e) => e.isIntersecting)) {
141
+ doPrefetch();
142
+ observer?.disconnect();
143
+ observer = null;
144
+ }
145
+ }, { rootMargin: "0px" });
146
+ observer.observe(elementRef.current);
147
+ }
148
+ return () => {
149
+ if (hoverTimerRef.current) {
150
+ clearTimeout(hoverTimerRef.current);
151
+ hoverTimerRef.current = null;
152
+ }
153
+ if (observer) observer.disconnect();
154
+ };
155
+ }, [doPrefetch, strategies]);
156
+ const eventHandlers = useMemo(() => {
157
+ const handlers = {
158
+ onClick,
159
+ onFocus: onFocusProp,
160
+ onMouseDown: onMouseDownProp,
161
+ onMouseEnter: onMouseEnterProp,
162
+ onMouseLeave: onMouseLeaveProp,
163
+ onTouchStart: onTouchStartProp
164
+ };
165
+ if (strategies.includes("hover")) {
166
+ handlers.onMouseEnter = onMouseEnter;
167
+ handlers.onMouseLeave = onMouseLeave;
168
+ handlers.onFocus = onFocus;
169
+ handlers.onTouchStart = onTouchStart;
170
+ }
171
+ if (strategies.includes("click")) {
172
+ handlers.onMouseDown = onMouseDown;
173
+ handlers.onTouchStart = onTouchStart;
174
+ }
175
+ return handlers;
176
+ }, [
177
+ onClick,
178
+ onFocus,
179
+ onFocusProp,
180
+ onMouseDown,
181
+ onMouseDownProp,
182
+ onMouseEnter,
183
+ onMouseEnterProp,
184
+ onMouseLeave,
185
+ onMouseLeaveProp,
186
+ onTouchStart,
187
+ onTouchStartProp,
188
+ strategies
189
+ ]);
190
+ const resolvedStyle = useMemo(() => ({
191
+ touchAction: "manipulation",
192
+ ...style
193
+ }), [style]);
194
+ if (isGet) return /* @__PURE__ */ jsx("a", {
195
+ ref: elementRef,
196
+ href: hrefWithData,
197
+ style: resolvedStyle,
198
+ ...rest,
199
+ ...eventHandlers,
200
+ children
201
+ });
202
+ return /* @__PURE__ */ jsx("button", {
203
+ ref: elementRef,
204
+ type: "button",
205
+ style: resolvedStyle,
206
+ ...rest,
207
+ ...eventHandlers,
208
+ children
209
+ });
210
+ }
211
+ //#endregion
212
+ //#region src/runtime/use-router.ts
213
+ function useRouter() {
214
+ return useContext(RouterContext) ?? ssrProxy;
215
+ }
216
+ //#endregion
217
+ //#region src/runtime/use-params.ts
218
+ function useParams() {
219
+ const params = useContext(ParamsContext);
220
+ if (params === null) throw new Error("useParams(): must be used inside a Void page.");
221
+ return params;
222
+ }
223
+ //#endregion
224
+ //#region src/runtime/use-shared.ts
225
+ function useShared() {
226
+ const shared = useContext(SharedContext);
227
+ if (!shared) throw new Error("useShared(): must be used inside a Void page.");
228
+ return shared;
229
+ }
230
+ //#endregion
231
+ //#region src/runtime/use-form.ts
232
+ function useForm(url, defaults, options) {
233
+ const router = useContext(RouterContext);
234
+ const [data, setDataState] = useState(() => ({ ...defaults }));
235
+ const [defaultsState, setDefaultsState] = useState(() => ({ ...defaults }));
236
+ const [errors, setErrors] = useState({});
237
+ const [error, setError] = useState(null);
238
+ const [wasSuccessful, setWasSuccessful] = useState(false);
239
+ const [recentlySuccessful, setRecentlySuccessful] = useState(false);
240
+ const defaultsRef = useRef(defaultsState);
241
+ const dataRef = useRef(data);
242
+ const successTimeoutRef = useRef(null);
243
+ const hasChanges = useMemo(() => {
244
+ return Object.keys(defaultsState).some((key) => !isEqualFormValue(data[key], defaultsState[key]));
245
+ }, [data, defaultsState]);
246
+ const setData = useCallback((field, value) => {
247
+ dataRef.current = {
248
+ ...dataRef.current,
249
+ [field]: value
250
+ };
251
+ setDataState(dataRef.current);
252
+ }, []);
253
+ const reset = useCallback((...fields) => {
254
+ if (fields.length === 0) dataRef.current = { ...defaultsRef.current };
255
+ else {
256
+ const next = { ...dataRef.current };
257
+ for (const field of fields) next[field] = defaultsRef.current[field];
258
+ dataRef.current = next;
259
+ }
260
+ setDataState(dataRef.current);
261
+ }, []);
262
+ const clearErrors = useCallback((...fields) => {
263
+ if (fields.length === 0) setErrors({});
264
+ else setErrors((prev) => {
265
+ const next = { ...prev };
266
+ for (const field of fields) delete next[field];
267
+ return next;
268
+ });
269
+ }, []);
270
+ const clearError = useCallback(() => {
271
+ setError(null);
272
+ }, []);
273
+ const [, dispatchAction, pending] = useActionState(async (previousSuccessVersion, payload) => {
274
+ if (!router) throw new Error("useForm(): requires the Void Router.");
275
+ const method = payload instanceof FormData ? "POST" : payload.method;
276
+ setWasSuccessful(false);
277
+ setRecentlySuccessful(false);
278
+ if (successTimeoutRef.current) {
279
+ clearTimeout(successTimeoutRef.current);
280
+ successTimeoutRef.current = null;
281
+ }
282
+ setErrors({});
283
+ setError(null);
284
+ let result;
285
+ try {
286
+ result = await submitAction(router, interpolateRouteParams(url, options?.params), {
287
+ method,
288
+ data: { ...dataRef.current },
289
+ _resolveOnShell: true
290
+ });
291
+ } catch (submitError) {
292
+ if (isAbortError(submitError)) return previousSuccessVersion;
293
+ throw submitError;
294
+ }
295
+ if (!result.ok) {
296
+ const validationErrors = getValidationErrors(result.error.body);
297
+ if (validationErrors) setErrors(validationErrors);
298
+ else setError(result.error);
299
+ return previousSuccessVersion;
300
+ }
301
+ const nextDefaults = { ...dataRef.current };
302
+ defaultsRef.current = nextDefaults;
303
+ setDefaultsState(nextDefaults);
304
+ setWasSuccessful(true);
305
+ setRecentlySuccessful(true);
306
+ successTimeoutRef.current = setTimeout(() => {
307
+ setRecentlySuccessful(false);
308
+ successTimeoutRef.current = null;
309
+ }, 2e3);
310
+ return previousSuccessVersion + 1;
311
+ }, 0);
312
+ useEffect(() => {
313
+ return () => {
314
+ if (successTimeoutRef.current) {
315
+ clearTimeout(successTimeoutRef.current);
316
+ successTimeoutRef.current = null;
317
+ }
318
+ };
319
+ }, []);
320
+ const dispatchMethodAction = useCallback((method) => (_formData) => {
321
+ dispatchAction({ method });
322
+ }, [dispatchAction]);
323
+ return {
324
+ data,
325
+ setData,
326
+ errors,
327
+ error,
328
+ pending,
329
+ hasChanges,
330
+ wasSuccessful,
331
+ recentlySuccessful,
332
+ post: dispatchAction,
333
+ put: useMemo(() => dispatchMethodAction("PUT"), [dispatchMethodAction]),
334
+ patch: useMemo(() => dispatchMethodAction("PATCH"), [dispatchMethodAction]),
335
+ delete: useMemo(() => dispatchMethodAction("DELETE"), [dispatchMethodAction]),
336
+ reset,
337
+ clearErrors,
338
+ clearError
339
+ };
340
+ }
341
+ //#endregion
342
+ //#region src/runtime/use-navigation.ts
343
+ function useNavigation() {
344
+ return useContext(NavigationContext);
345
+ }
346
+ //#endregion
347
+ //#region src/runtime/use-island-form.ts
348
+ async function readActionErrorBody(response) {
349
+ const contentType = response.headers.get("content-type") || "";
350
+ try {
351
+ if (contentType.includes("application/json")) return await response.json();
352
+ return await response.text() || null;
353
+ } catch {
354
+ return null;
355
+ }
356
+ }
357
+ /**
358
+ * Form hook for island pages. Uses fetch + page reload instead of the
359
+ * Void Router (which is not available in island mode).
360
+ */
361
+ function useIslandForm(defaults) {
362
+ const [data, setDataState] = useState(() => ({ ...defaults }));
363
+ const [defaultsState, setDefaultsState] = useState(() => ({ ...defaults }));
364
+ const [errors, setErrors] = useState({});
365
+ const [error, setError] = useState(null);
366
+ const [pending, setPending] = useState(false);
367
+ const [wasSuccessful, setWasSuccessful] = useState(false);
368
+ const [recentlySuccessful, setRecentlySuccessful] = useState(false);
369
+ const defaultsRef = useRef(defaultsState);
370
+ const successTimeoutRef = useRef(null);
371
+ const hasChanges = useMemo(() => {
372
+ return Object.keys(defaultsState).some((key) => !isEqualFormValue(data[key], defaultsState[key]));
373
+ }, [data, defaultsState]);
374
+ const setData = useCallback((field, value) => {
375
+ setDataState((prev) => ({
376
+ ...prev,
377
+ [field]: value
378
+ }));
379
+ }, []);
380
+ const reset = useCallback((...fields) => {
381
+ if (fields.length === 0) setDataState({ ...defaultsRef.current });
382
+ else setDataState((prev) => {
383
+ const next = { ...prev };
384
+ for (const field of fields) next[field] = defaultsRef.current[field];
385
+ return next;
386
+ });
387
+ }, []);
388
+ const clearErrors = useCallback((...fields) => {
389
+ if (fields.length === 0) setErrors({});
390
+ else setErrors((prev) => {
391
+ const next = { ...prev };
392
+ for (const field of fields) delete next[field];
393
+ return next;
394
+ });
395
+ }, []);
396
+ const clearError = useCallback(() => {
397
+ setError(null);
398
+ }, []);
399
+ const submit = useCallback(async (url, method) => {
400
+ setPending(true);
401
+ setWasSuccessful(false);
402
+ setRecentlySuccessful(false);
403
+ if (successTimeoutRef.current) clearTimeout(successTimeoutRef.current);
404
+ setErrors({});
405
+ setError(null);
406
+ try {
407
+ const response = await fetch(url, {
408
+ method,
409
+ headers: { "Content-Type": "application/json" },
410
+ body: JSON.stringify(data)
411
+ });
412
+ if (response.redirected) {
413
+ window.location.href = response.url;
414
+ return;
415
+ }
416
+ if (response.ok) {
417
+ setWasSuccessful(true);
418
+ setRecentlySuccessful(true);
419
+ const nextDefaults = { ...data };
420
+ defaultsRef.current = nextDefaults;
421
+ setDefaultsState(nextDefaults);
422
+ successTimeoutRef.current = setTimeout(() => setRecentlySuccessful(false), 2e3);
423
+ window.location.reload();
424
+ } else {
425
+ const body = await readActionErrorBody(response);
426
+ const actionError = new VoidActionError({
427
+ body,
428
+ status: response.status,
429
+ statusText: response.statusText,
430
+ url
431
+ });
432
+ const validationErrors = response.status === 422 ? getValidationErrors(body) : null;
433
+ if (validationErrors) {
434
+ setErrors(validationErrors);
435
+ return;
436
+ }
437
+ if (categorizeActionError(response.status) === "boundary") throw actionError;
438
+ setError(actionError);
439
+ }
440
+ } finally {
441
+ setPending(false);
442
+ }
443
+ }, [data]);
444
+ return {
445
+ data,
446
+ setData,
447
+ errors,
448
+ error,
449
+ pending,
450
+ hasChanges,
451
+ wasSuccessful,
452
+ recentlySuccessful,
453
+ reset,
454
+ clearErrors,
455
+ clearError,
456
+ post: (url) => submit(url, "POST"),
457
+ put: (url) => submit(url, "PUT"),
458
+ patch: (url) => submit(url, "PATCH"),
459
+ delete: (url) => submit(url, "DELETE")
460
+ };
461
+ }
462
+ //#endregion
4
463
  export { ErrorsContext, Form, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };