@void/react 0.21.9 → 0.23.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/README.md +10 -5
- package/dist/action-4EvQppia.mjs +7 -0
- package/dist/{index-Cx2A1XEu.d.mts → index-D5M3zrVB.d.mts} +14 -18
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -3
- package/dist/runtime/index.d.mts +2 -2
- package/dist/runtime/index.mjs +3 -3
- package/dist/runtime/pages-client.mjs +1 -1
- package/dist/{runtime-W1oiBA4P.mjs → runtime-D_Wv52MZ.mjs} +26 -4
- package/package.json +6 -6
- package/dist/action-Bq1iSG6m.mjs +0 -29
package/README.md
CHANGED
|
@@ -39,7 +39,7 @@ Main runtime APIs live at `@void/react`:
|
|
|
39
39
|
- `useParams()`
|
|
40
40
|
- `useNavigation()`
|
|
41
41
|
- `useShared()`
|
|
42
|
-
- `useForm()`
|
|
42
|
+
- `Form` and `useForm()`
|
|
43
43
|
- `useIslandForm()`
|
|
44
44
|
- `action()`
|
|
45
45
|
|
|
@@ -110,7 +110,7 @@ export interface Props {
|
|
|
110
110
|
inside the form for button pending state.
|
|
111
111
|
|
|
112
112
|
```tsx
|
|
113
|
-
import { useForm } from '@void/react';
|
|
113
|
+
import { Form, useForm } from '@void/react';
|
|
114
114
|
import { useFormStatus } from 'react-dom';
|
|
115
115
|
|
|
116
116
|
function SubmitButton() {
|
|
@@ -122,16 +122,15 @@ export default function CreateUser() {
|
|
|
122
122
|
const form = useForm('/users', { name: '', email: '' });
|
|
123
123
|
|
|
124
124
|
return (
|
|
125
|
-
<form
|
|
125
|
+
<Form form={form}>
|
|
126
126
|
<input
|
|
127
127
|
name="name"
|
|
128
128
|
value={form.data.name}
|
|
129
129
|
onChange={(e) => form.setData('name', e.target.value)}
|
|
130
130
|
/>
|
|
131
131
|
{form.errors.name && <span>{form.errors.name}</span>}
|
|
132
|
-
{form.error && <p>{form.error.message}</p>}
|
|
133
132
|
<SubmitButton />
|
|
134
|
-
</
|
|
133
|
+
</Form>
|
|
135
134
|
);
|
|
136
135
|
}
|
|
137
136
|
```
|
|
@@ -217,6 +216,12 @@ import { defineHandler } from 'void';
|
|
|
217
216
|
export const loader = defineHandler(() => ({ title: 'Home' }));
|
|
218
217
|
```
|
|
219
218
|
|
|
219
|
+
## Form
|
|
220
|
+
|
|
221
|
+
Use `Form` with `useForm()` to display action and validation errors automatically. It preserves controlled input after a failed submission and announces errors accessibly. Structured usage-limit responses display the application's message and an optional retry time. Submissions are blocked while pending and before that deadline; inputs remain editable and requests are never retried automatically.
|
|
222
|
+
|
|
223
|
+
`method` selects `post` (the default), `put`, `patch`, or `delete`. Use `renderError` to customize action-error presentation (a snippet in Svelte). Native forms remain available when you manage error presentation yourself.
|
|
224
|
+
|
|
220
225
|
## Tests
|
|
221
226
|
|
|
222
227
|
- `test/unit/link.test.ts`
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createActionClient } from "void/pages-client";
|
|
2
|
+
//#region src/runtime/action.ts
|
|
3
|
+
const client = createActionClient();
|
|
4
|
+
const action = client.action;
|
|
5
|
+
const setActionRouter = client.setActionRouter;
|
|
6
|
+
//#endregion
|
|
7
|
+
export { setActionRouter as n, action as t };
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import { AnchorHTMLAttributes, ButtonHTMLAttributes, Context, JSX } from "react";
|
|
2
|
-
import {
|
|
1
|
+
import { AnchorHTMLAttributes, ButtonHTMLAttributes, Context, FormHTMLAttributes, JSX, ReactElement, ReactNode } from "react";
|
|
2
|
+
import { ActionClient, FormMethod, FormModel, NavigationState, VoidActionError, VoidRouter } from "void/pages-client";
|
|
3
3
|
import { CloudContextVariables, InferProps } from "void";
|
|
4
|
-
import { ActionFormOptions, ActionUrl, ResolveActionBody
|
|
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
|
+
declare function Form({ form, method, renderError, children, ...props }: FormProps): ReactElement;
|
|
12
|
+
//#endregion
|
|
5
13
|
//#region src/runtime/link.d.ts
|
|
6
14
|
type PrefetchStrategy = "hover" | "click" | "mount" | "visible";
|
|
7
15
|
type LinkMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
@@ -79,20 +87,8 @@ type IslandFormReturn<T extends Record<string, unknown>> = Readonly<{
|
|
|
79
87
|
declare function useIslandForm<T extends Record<string, unknown>>(defaults: T): IslandFormReturn<T>;
|
|
80
88
|
//#endregion
|
|
81
89
|
//#region src/runtime/action.d.ts
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
type ActionOptions<U extends string> = {
|
|
85
|
-
data?: ResolveActionBody<U>;
|
|
86
|
-
method?: ActionMethod;
|
|
87
|
-
} & (HasParams<U> extends true ? {
|
|
88
|
-
params: ResolveActionParams<U>;
|
|
89
|
-
} : {
|
|
90
|
-
params?: never;
|
|
91
|
-
});
|
|
92
|
-
/** Called during app initialization to store the router instance */
|
|
93
|
-
declare function setActionRouter(router: VoidRouter): void;
|
|
94
|
-
/** Programmatic one-shot page action call with Inertia page update */
|
|
95
|
-
declare function action<U extends ActionUrl & string>(url: U, ...args: HasParams<U> extends true ? [options: ActionOptions<U>] : [options?: ActionOptions<U>]): Promise<ActionResult>;
|
|
90
|
+
declare const action: ActionClient["action"];
|
|
91
|
+
declare const setActionRouter: ActionClient["setActionRouter"];
|
|
96
92
|
//#endregion
|
|
97
93
|
//#region src/runtime/context.d.ts
|
|
98
94
|
declare const SharedContext: Context<Record<string, unknown> | null>;
|
|
@@ -104,4 +100,4 @@ declare const NavigationContext: Context<NavigationState>;
|
|
|
104
100
|
//#region src/runtime/index.d.ts
|
|
105
101
|
type Deferred<T> = Promise<T>;
|
|
106
102
|
//#endregion
|
|
107
|
-
export { ParamsContext as a, action as c, useNavigation as d, useForm as f, Link as g, useRouter as h, NavigationContext as i, setActionRouter as l, useParams as m, InferProps as n, RouterContext as o, useShared as p, ErrorsContext as r, SharedContext as s, Deferred as t, useIslandForm as u };
|
|
103
|
+
export { Form as _, ParamsContext as a, action as c, useNavigation as d, useForm as f, Link as g, useRouter as h, NavigationContext as i, setActionRouter as l, useParams as m, InferProps as n, RouterContext as o, useShared as p, ErrorsContext as r, SharedContext as s, Deferred as t, useIslandForm as u };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { 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-
|
|
2
|
-
export { Deferred, ErrorsContext, type InferProps, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|
|
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 };
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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-D_Wv52MZ.mjs";
|
|
1
2
|
import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "./context-B0GL7p5m.mjs";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export { ErrorsContext, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|
|
3
|
+
import { n as setActionRouter, t as action } from "./action-4EvQppia.mjs";
|
|
4
|
+
export { ErrorsContext, Form, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|
package/dist/runtime/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { 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-
|
|
2
|
-
export { Deferred, ErrorsContext, type InferProps, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|
|
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 };
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
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-D_Wv52MZ.mjs";
|
|
1
2
|
import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "../context-B0GL7p5m.mjs";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
export { ErrorsContext, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|
|
3
|
+
import { n as setActionRouter, t as action } from "../action-4EvQppia.mjs";
|
|
4
|
+
export { ErrorsContext, Form, Link, NavigationContext, ParamsContext, RouterContext, SharedContext, action, setActionRouter, useForm, useIslandForm, useNavigation, useParams, useRouter, useShared };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext, t as ErrorsContext } from "../context-B0GL7p5m.mjs";
|
|
2
|
-
import { n as setActionRouter } from "../action-
|
|
2
|
+
import { n as setActionRouter } from "../action-4EvQppia.mjs";
|
|
3
3
|
import { n as DEFERRED_SHELL_END_ID } from "../deferred-shell-DOuP2YAo.mjs";
|
|
4
4
|
import React, { Suspense, startTransition, use, useCallback, useEffect, useInsertionEffect, useLayoutEffect, useMemo, useReducer, useTransition } from "react";
|
|
5
5
|
import { createPageNavigationEngine, createRouterFacade, idleNavigationState, isAbortError, isCallSiteActionError } from "void/pages-client";
|
|
@@ -1,8 +1,30 @@
|
|
|
1
1
|
import { a as SharedContext, i as RouterContext, n as NavigationContext, r as ParamsContext } from "./context-B0GL7p5m.mjs";
|
|
2
|
-
import "./action-
|
|
2
|
+
import "./action-4EvQppia.mjs";
|
|
3
3
|
import { useActionState, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
-
import { VoidActionError, categorizeActionError, isAbortError, isEqualFormValue, ssrProxy, submitAction } from "void/pages-client";
|
|
5
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
+
import { VoidActionError, categorizeActionError, formErrorMessage, formRetryMessage, formSubmissionBlocked, isAbortError, isEqualFormValue, 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
|
|
6
28
|
//#region src/runtime/link.tsx
|
|
7
29
|
function appendQueryValue(params, key, value) {
|
|
8
30
|
if (value === null || value === void 0) return false;
|
|
@@ -497,4 +519,4 @@ function useIslandForm(defaults) {
|
|
|
497
519
|
};
|
|
498
520
|
}
|
|
499
521
|
//#endregion
|
|
500
|
-
export { useParams as a, useShared as i, useNavigation as n, useRouter as o, useForm as r, Link as s, useIslandForm as t };
|
|
522
|
+
export { useParams as a, Form as c, useShared as i, useNavigation as n, useRouter as o, useForm as r, Link as s, useIslandForm as t };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/voidzero-dev/void.git",
|
|
@@ -35,21 +35,21 @@
|
|
|
35
35
|
"@vitejs/plugin-react": "^6.1.1"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^26.
|
|
38
|
+
"@types/node": "^26.6.3",
|
|
39
39
|
"@types/react": "^19.3.0",
|
|
40
40
|
"@types/react-dom": "^19.3.0",
|
|
41
41
|
"pathslash": "^0.1.0",
|
|
42
42
|
"react": "^19.3.0",
|
|
43
43
|
"react-dom": "^19.3.0",
|
|
44
|
-
"vite": "npm:@voidzero-dev/vite-plus-core@0.
|
|
45
|
-
"vite-plus": "0.
|
|
46
|
-
"void": "0.
|
|
44
|
+
"vite": "npm:@voidzero-dev/vite-plus-core@1.0.0",
|
|
45
|
+
"vite-plus": "1.0.0",
|
|
46
|
+
"void": "0.23.0"
|
|
47
47
|
},
|
|
48
48
|
"peerDependencies": {
|
|
49
49
|
"react": "^19.0.0",
|
|
50
50
|
"react-dom": "^19.0.0",
|
|
51
51
|
"vite": "^8.0.0",
|
|
52
|
-
"void": "0.
|
|
52
|
+
"void": "0.23.0"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "vp pack",
|
package/dist/action-Bq1iSG6m.mjs
DELETED
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { submitAction } from "void/pages-client";
|
|
2
|
-
//#region src/runtime/action.ts
|
|
3
|
-
let _router = null;
|
|
4
|
-
/** Called during app initialization to store the router instance */
|
|
5
|
-
function setActionRouter(router) {
|
|
6
|
-
_router = router;
|
|
7
|
-
}
|
|
8
|
-
/** Programmatic one-shot page action call with Inertia page update */
|
|
9
|
-
async function action(url, ...args) {
|
|
10
|
-
if (!_router) throw new Error("action(): called before router initialization.");
|
|
11
|
-
const options = args[0];
|
|
12
|
-
const data = options?.data;
|
|
13
|
-
const method = options?.method || "POST";
|
|
14
|
-
let resolvedUrl = url;
|
|
15
|
-
let actionQuery = "";
|
|
16
|
-
const qIdx = resolvedUrl.indexOf("?");
|
|
17
|
-
if (qIdx !== -1) {
|
|
18
|
-
actionQuery = resolvedUrl.slice(qIdx);
|
|
19
|
-
resolvedUrl = resolvedUrl.slice(0, qIdx);
|
|
20
|
-
}
|
|
21
|
-
if (options?.params) for (const [key, value] of Object.entries(options.params)) resolvedUrl = resolvedUrl.replace(`:${key}`, encodeURIComponent(value));
|
|
22
|
-
resolvedUrl = resolvedUrl + actionQuery;
|
|
23
|
-
return submitAction(_router, resolvedUrl, {
|
|
24
|
-
method,
|
|
25
|
-
data
|
|
26
|
-
});
|
|
27
|
-
}
|
|
28
|
-
//#endregion
|
|
29
|
-
export { setActionRouter as n, action as t };
|