react-form-action 1.4.1 → 2.0.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 +176 -104
- package/dist/chunk-HL47DG4L.mjs +1 -0
- package/dist/client.d.mts +24 -55
- package/dist/client.mjs +1 -1
- package/dist/{createFormAction-4e719362.d.ts → createFormAction-277e6e21.d.ts} +2 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,28 +1,29 @@
|
|
|
1
1
|
# react-form-action
|
|
2
2
|
|
|
3
|
-
End-to-end typesafe success, error & validation state control for Next.js
|
|
3
|
+
End-to-end typesafe success, error & validation state control for Next.js form actions.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
7
|
**Action Creator**
|
|
8
8
|
|
|
9
|
-
- ✅ Provides `"invalid" | "success" | "failure"` response
|
|
9
|
+
- ✅ Provides envelope objects with `"initial" | "invalid" | "success" | "failure"` response types.
|
|
10
10
|
- ✅ Define generic payload for each of the response type.
|
|
11
11
|
|
|
12
|
-
**Form Action builder**
|
|
12
|
+
**tRPC-like Form Action builder**
|
|
13
13
|
|
|
14
|
-
- ✅
|
|
15
|
-
- ✅
|
|
14
|
+
- ✅ Define payload schema with the `.input(zodSchema)` to validate the `formData`
|
|
15
|
+
- ✅ Reuse business logic with the `.use(middleware)` method.
|
|
16
|
+
- ✅ Reuse error handling with the `.error(handler)`.
|
|
16
17
|
|
|
17
|
-
**
|
|
18
|
+
**React Context access with the `<Action action={myFormAction} />` component**
|
|
18
19
|
|
|
19
|
-
- ✅
|
|
20
|
-
- ✅ Computes progress
|
|
20
|
+
- ✅ The `useActionState()` accessible via the `useActionContext()` hook.
|
|
21
|
+
- ✅ Computes progress flags like `isInvalid`, `isSuccess` based on the envelope type.
|
|
21
22
|
|
|
22
|
-
**Context**
|
|
23
|
+
**Context-bound `<Form />` component**
|
|
23
24
|
|
|
24
|
-
- ✅
|
|
25
|
-
- ✅
|
|
25
|
+
- ✅ Reads the `action` from the `<Action />` context.
|
|
26
|
+
- ✅ Opt-out from the default form reset after action submit.
|
|
26
27
|
|
|
27
28
|
## Install
|
|
28
29
|
|
|
@@ -34,10 +35,95 @@ npm i react-form-action zod-form-data
|
|
|
34
35
|
<img alt="NPM Version" src="https://img.shields.io/npm/v/react-form-action?style=for-the-badge&labelColor=24292e">
|
|
35
36
|
</a>
|
|
36
37
|
|
|
38
|
+
## Getting Started
|
|
39
|
+
|
|
40
|
+
#### 1️⃣ Create a Server Action
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
// app/subscibe/action.ts
|
|
44
|
+
"use server";
|
|
45
|
+
|
|
46
|
+
import { formAction } from "react-form-action";
|
|
47
|
+
import { z } from "zod";
|
|
48
|
+
|
|
49
|
+
export const subscribeAction = formAction
|
|
50
|
+
.input(z.object({ email: z.string().email() }))
|
|
51
|
+
.run(async ({ input }) => {
|
|
52
|
+
return input.email;
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### 2️⃣ Create a Client Form Component
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
// app/subscibe/SubscribeForm.tsx
|
|
60
|
+
"use client";
|
|
61
|
+
|
|
62
|
+
import {
|
|
63
|
+
Action,
|
|
64
|
+
Form,
|
|
65
|
+
Pending,
|
|
66
|
+
useActionContext,
|
|
67
|
+
} from "react-form-action/client";
|
|
68
|
+
|
|
69
|
+
import { subscribeAction } from "./action";
|
|
70
|
+
|
|
71
|
+
const { FieldError } = createComponents(subscribeAction);
|
|
72
|
+
|
|
73
|
+
export function SubscribeForm() {
|
|
74
|
+
return (
|
|
75
|
+
<Action action={subscribeAction} initialData="">
|
|
76
|
+
<SuccessMessage />
|
|
77
|
+
<Form>
|
|
78
|
+
<input name="email" />
|
|
79
|
+
{/*💡 The name prop supports autocompletion */}
|
|
80
|
+
<FieldError name="email" />
|
|
81
|
+
</Form>
|
|
82
|
+
<SubmitButton />
|
|
83
|
+
<Pending>Please wait...</Pending>
|
|
84
|
+
</Action>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function SuccessMessage() {
|
|
89
|
+
// Pass action to cast the type of the "data" value
|
|
90
|
+
const { isSuccess, data } = useActionContext(subscribeAction);
|
|
91
|
+
|
|
92
|
+
return isSuccess && <p>Email {data} was registered.</p>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function SubmitButton() {
|
|
96
|
+
// no need for action when reading a generic value
|
|
97
|
+
const { isPending } = useActionContext();
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<button type="submit" disabled={isPending}>
|
|
101
|
+
{isPending ? "Submitting..." : "Submit"}
|
|
102
|
+
</button>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
#### 3️⃣ Render the form on a Page
|
|
108
|
+
|
|
109
|
+
```tsx
|
|
110
|
+
// app/subscibe/page.tsx
|
|
111
|
+
|
|
112
|
+
import { SubscribeForm } from "./SubscribeForm";
|
|
113
|
+
|
|
114
|
+
function SuccessMessage() {}
|
|
115
|
+
|
|
116
|
+
export function Page() {
|
|
117
|
+
return <SubscribeForm />;
|
|
118
|
+
}
|
|
119
|
+
```
|
|
120
|
+
|
|
37
121
|
## Usage
|
|
38
122
|
|
|
39
123
|
### `formAction` builder
|
|
40
124
|
|
|
125
|
+
The [`zod-form-data`](https://www.npmjs.com/package/zod-form-data) powered action builder.
|
|
126
|
+
|
|
41
127
|
```ts
|
|
42
128
|
// app/actions/auth.ts
|
|
43
129
|
"use server";
|
|
@@ -105,7 +191,9 @@ export const signUp = authAction
|
|
|
105
191
|
});
|
|
106
192
|
```
|
|
107
193
|
|
|
108
|
-
###
|
|
194
|
+
### Action Creator
|
|
195
|
+
|
|
196
|
+
Low-level action creator, which provides the `success`, `failure` and `invalid` envelope constructors. With the `createFormAction` you must handle the native `FormData` by yourself.
|
|
109
197
|
|
|
110
198
|
```ts
|
|
111
199
|
"use server";
|
|
@@ -170,61 +258,21 @@ export const updateUser = createFormAction<
|
|
|
170
258
|
);
|
|
171
259
|
```
|
|
172
260
|
|
|
173
|
-
###
|
|
261
|
+
### Action Context
|
|
174
262
|
|
|
175
|
-
|
|
176
|
-
"use client";
|
|
177
|
-
// Form connects the formAction to the formState & provides the meta state props via render prop
|
|
178
|
-
import { Form } from "react-form-action/client";
|
|
179
|
-
|
|
180
|
-
import { updateUser } from "@/actions";
|
|
181
|
-
|
|
182
|
-
export function UpdateUserForm() {
|
|
183
|
-
return (
|
|
184
|
-
<Form action={updateUser} initialData="foobar">
|
|
185
|
-
{({
|
|
186
|
-
error,
|
|
187
|
-
data,
|
|
188
|
-
validationError,
|
|
189
|
-
isPending,
|
|
190
|
-
isFailure,
|
|
191
|
-
isInvalid,
|
|
192
|
-
isSuccess,
|
|
193
|
-
isInitial,
|
|
194
|
-
}) => (
|
|
195
|
-
<>
|
|
196
|
-
{/* safely access the data or error by checking the mutually exclusive boolean flags: */}
|
|
197
|
-
{isSuccess && <p className="success-message">{data}</p>}
|
|
198
|
-
{isFailure && <p className="error-message">{error.message}</p>}
|
|
199
|
-
<input type="text" name="name" disabled={isPending} />
|
|
200
|
-
{isInvalid && (
|
|
201
|
-
<span className="input-error">{validationError.name}</span>
|
|
202
|
-
)}
|
|
203
|
-
|
|
204
|
-
<button disabled={isPending}>
|
|
205
|
-
{isPending ? "Submitting..." : "Submit"}
|
|
206
|
-
</button>
|
|
207
|
-
</>
|
|
208
|
-
)}
|
|
209
|
-
</Form>
|
|
210
|
-
);
|
|
211
|
-
}
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
### Context Form
|
|
263
|
+
The `<Action>` components enables you to access your `action`'s state with the `useActionContext()` hook:
|
|
215
264
|
|
|
216
265
|
```tsx
|
|
217
|
-
//
|
|
266
|
+
// 👉 Define standalone client form component (e.g. /app/auth/signup/SignUpForm.tsx)
|
|
218
267
|
"use client";
|
|
219
268
|
|
|
220
|
-
import {
|
|
221
|
-
import {
|
|
269
|
+
import { Action, Form, useActionContext } from "react-form-action/client";
|
|
270
|
+
import type { PropsWithChildren } from "react";
|
|
222
271
|
|
|
223
|
-
|
|
224
|
-
const { Form } = createForm(signupAction);
|
|
272
|
+
import { signupAction } from "./action";
|
|
225
273
|
|
|
226
|
-
function
|
|
227
|
-
// read any state from the
|
|
274
|
+
function Pending({ children }: PropsWithChildren) {
|
|
275
|
+
// read any state from the ActionContext:
|
|
228
276
|
const {
|
|
229
277
|
error,
|
|
230
278
|
data,
|
|
@@ -233,36 +281,64 @@ function Error() {
|
|
|
233
281
|
isFailure,
|
|
234
282
|
isInvalid,
|
|
235
283
|
isSuccess,
|
|
236
|
-
isInitial
|
|
237
|
-
} =
|
|
284
|
+
isInitial,
|
|
285
|
+
} = useActionContext();
|
|
238
286
|
|
|
239
|
-
return
|
|
287
|
+
return isPending && children;
|
|
240
288
|
}
|
|
241
289
|
|
|
242
|
-
// render this form on your RSC page (/app/auth/signup/page.tsx)
|
|
290
|
+
// 💡 render this form on your RSC page (/app/auth/signup/page.tsx)
|
|
243
291
|
export function SignupForm() {
|
|
244
|
-
// This Form does not use render props.
|
|
245
292
|
return (
|
|
246
|
-
<
|
|
247
|
-
<
|
|
248
|
-
|
|
293
|
+
<Action action={signupAction}>
|
|
294
|
+
<Form>
|
|
295
|
+
<input name="email" />
|
|
296
|
+
<input name="password" />
|
|
297
|
+
</Form>
|
|
298
|
+
{/* 🎆 Read the pending state outside the <Form> */}
|
|
249
299
|
<Pending>
|
|
250
|
-
{/* This renders only when the action is pending. */}
|
|
300
|
+
{/* This renders only when the action is pending. 😎 */}
|
|
251
301
|
<p>Please wait...</p>
|
|
252
302
|
</Pending>
|
|
253
|
-
</
|
|
303
|
+
</Action>
|
|
254
304
|
);
|
|
255
305
|
}
|
|
256
306
|
```
|
|
257
307
|
|
|
258
|
-
### `<
|
|
308
|
+
### `<Form>` Component
|
|
259
309
|
|
|
260
|
-
|
|
261
|
-
|
|
310
|
+
The `<form>` submits the action in `onSubmit` handler to [prevent automatic form reset](https://github.com/facebook/react/issues/29034).
|
|
311
|
+
Pass `autoReset` prop to use the `action` prop instead and keep the default reset.
|
|
262
312
|
|
|
263
313
|
```tsx
|
|
264
314
|
"use client";
|
|
265
|
-
|
|
315
|
+
|
|
316
|
+
import { Action, Form } from "react-form-action/client";
|
|
317
|
+
|
|
318
|
+
import { updateUser } from "./action";
|
|
319
|
+
|
|
320
|
+
export function UpdateUserForm() {
|
|
321
|
+
return (
|
|
322
|
+
<Action action={updateUser}>
|
|
323
|
+
<Form autoReset>{/* ... */}</Form>
|
|
324
|
+
</Action>
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
### Context Bound Components `createComponents()`
|
|
330
|
+
|
|
331
|
+
Use the `createComponents(action)` helper to create components which use the ActionContext and have types bound to the action type.
|
|
332
|
+
|
|
333
|
+
### `<FielError>` Component
|
|
334
|
+
|
|
335
|
+
```tsx
|
|
336
|
+
"use client";
|
|
337
|
+
|
|
338
|
+
// ⚠️ createComponents is usable only in "use client" components
|
|
339
|
+
import { Form, createComponents } from "react-form-action/client";
|
|
340
|
+
|
|
341
|
+
import { authAction } from "./actions";
|
|
266
342
|
|
|
267
343
|
export const signUp = authAction
|
|
268
344
|
.input(
|
|
@@ -280,51 +356,47 @@ export const signUp = authAction
|
|
|
280
356
|
})
|
|
281
357
|
)
|
|
282
358
|
.run(async ({ ctx, input }) => {
|
|
283
|
-
|
|
359
|
+
return null;
|
|
284
360
|
});
|
|
285
361
|
|
|
362
|
+
// 🌟 The FieldError is now bound do the signUp input schema which allows autocompletion for its "name" prop
|
|
363
|
+
// ⚠️ Usable only with actions created with the formAction builder
|
|
364
|
+
const { FieldError } = createComponents(signUp);
|
|
365
|
+
|
|
286
366
|
export function SignUpForm() {
|
|
287
367
|
return (
|
|
288
|
-
<
|
|
289
|
-
|
|
290
|
-
{/*
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
<ZodFieldError errors={validationError} name="password" />
|
|
300
|
-
{/* Access nested fields by dot access notation: */}
|
|
301
|
-
<ZodFieldError errors={validationError} name="user.email" />
|
|
302
|
-
</>
|
|
303
|
-
)
|
|
304
|
-
}
|
|
305
|
-
</Form>
|
|
368
|
+
<Action action={signUp} initialData={null}>
|
|
369
|
+
<Form>
|
|
370
|
+
{/* 1️⃣ When the "name" prop is ommited, the top-level error will be rendered e.g.:
|
|
371
|
+
"Passwords don't match" */}
|
|
372
|
+
<FieldError />
|
|
373
|
+
{/* 2️⃣ Access fields by their name: */}
|
|
374
|
+
<FieldError name="password" />
|
|
375
|
+
{/* 3️⃣ Access nested fields by dot access notation: */}
|
|
376
|
+
<FieldError name="user.email" />
|
|
377
|
+
</Form>
|
|
378
|
+
</Action>
|
|
306
379
|
);
|
|
307
380
|
}
|
|
308
381
|
```
|
|
309
382
|
|
|
310
|
-
###
|
|
383
|
+
### `<Pending>`
|
|
311
384
|
|
|
312
|
-
|
|
385
|
+
Render children when the action is pending:
|
|
313
386
|
|
|
314
387
|
```tsx
|
|
315
|
-
import {
|
|
388
|
+
import { Action, Pending } from "react-form-action/client";
|
|
316
389
|
|
|
317
|
-
|
|
390
|
+
import { Spinner } from "./components";
|
|
318
391
|
|
|
319
392
|
return function MyForm() {
|
|
320
393
|
return (
|
|
321
|
-
<
|
|
322
|
-
<
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
</form>
|
|
394
|
+
<Action action={action}>
|
|
395
|
+
{/* 👉 Unlike the React.useFormStatus() hook, we don't need here the <form> element at all. */}
|
|
396
|
+
<Pending>
|
|
397
|
+
<Spinner />
|
|
398
|
+
</Pending>
|
|
399
|
+
</Action>
|
|
328
400
|
);
|
|
329
401
|
};
|
|
330
402
|
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function l(a){return{type:"initial",data:a,error:null,validationError:null}}function n(a){function t(r){return{type:"success",data:r,error:null,validationError:null}}function o(r){return{type:"failure",data:null,error:r,validationError:null}}function i(r){return{type:"invalid",data:null,error:null,validationError:r}}return(r,e)=>a({success:t,failure:o,invalid:i})(r,e)}export{l as a,n as b};
|
package/dist/client.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import React__default, { PropsWithChildren, FormHTMLAttributes } from 'react';
|
|
3
|
-
import { b as FormAction, A as ActionState, a as InitialState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-
|
|
3
|
+
import { b as FormAction, A as ActionState, a as InitialState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-277e6e21.js';
|
|
4
|
+
import { ZodFormattedError } from 'zod';
|
|
4
5
|
import { RenderProp } from 'react-render-prop-type';
|
|
5
6
|
import { useFormStatus } from 'react-dom';
|
|
6
|
-
import { ZodFormattedError } from 'zod';
|
|
7
7
|
|
|
8
8
|
type ActionProps<Data, Error, ValidationError> = PropsWithChildren<{
|
|
9
9
|
action: FormAction<Data, Error, ValidationError>;
|
|
@@ -23,10 +23,14 @@ type ActionContextState<T extends ActionState<unknown, unknown, unknown>> = T &
|
|
|
23
23
|
action: (payload: FormData) => void;
|
|
24
24
|
isPending: boolean;
|
|
25
25
|
};
|
|
26
|
+
/**
|
|
27
|
+
* NOTE: ActionContextState<ActionState<...>> would not allow discriminate the union inside of the ActionState.
|
|
28
|
+
*/
|
|
29
|
+
type SpreadActionContext<Data = unknown, Error = unknown, ValidationError = unknown> = ActionContextState<InitialState<Data>> | ActionContextState<InvalidState<ValidationError>> | ActionContextState<FailureState<Error>> | ActionContextState<SuccessState<Data>>;
|
|
26
30
|
/**
|
|
27
31
|
* A hook to consume the form action state from the context.
|
|
28
32
|
*/
|
|
29
|
-
declare
|
|
33
|
+
declare function useActionContext<Data, Error, ValidationError>(action?: FormAction<Data, Error, ValidationError>): SpreadActionContext<Data, Error, ValidationError>;
|
|
30
34
|
declare function Action<Data, Error, ValidationError>({ children, action: formAction, initialData, permalink, }: ActionProps<Data, Error, ValidationError>): React__default.JSX.Element;
|
|
31
35
|
|
|
32
36
|
type ActionFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, "action"> & {
|
|
@@ -40,57 +44,6 @@ type ActionFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, "action"> & {
|
|
|
40
44
|
};
|
|
41
45
|
declare function ActionForm({ autoReset, ...props }: ActionFormProps): React__default.JSX.Element;
|
|
42
46
|
|
|
43
|
-
/**
|
|
44
|
-
* Creates a Form component which state is accessible via context - useFormContext() hook.
|
|
45
|
-
*/
|
|
46
|
-
declare function createForm<Data, Error = Data, ValidationError = Record<string, never>>(action: FormAction<Data, Error, ValidationError>): {
|
|
47
|
-
Form: ({ children, ...props }: PropsWithChildren<{
|
|
48
|
-
initialData: Data;
|
|
49
|
-
}>) => React__default.JSX.Element;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
type FormStateProps<Data, Error, ValidationError, Payload> = {
|
|
53
|
-
action: (state: ActionState<Data, Error, ValidationError>, payload: Payload) => Promise<ActionState<Data, Error, ValidationError>>;
|
|
54
|
-
initialData: Data;
|
|
55
|
-
permalink?: string;
|
|
56
|
-
/**
|
|
57
|
-
* Opt-in into automatic form reset by using the form "action" prop.
|
|
58
|
-
* By default, the onSubmit with a custom transition which opts-out of the implicit form reset.
|
|
59
|
-
* See for more. https://github.com/facebook/react/issues/29034
|
|
60
|
-
* @default false
|
|
61
|
-
*/
|
|
62
|
-
autoReset?: boolean;
|
|
63
|
-
};
|
|
64
|
-
declare function initial<Data>(data: Data): InitialState<Data>;
|
|
65
|
-
type FormStatusFlags<T extends ActionState<unknown, unknown, unknown>["type"] | unknown = unknown> = {
|
|
66
|
-
isInitial: T extends "initial" ? true : false;
|
|
67
|
-
isInvalid: T extends "invalid" ? true : false;
|
|
68
|
-
isFailure: T extends "failure" ? true : false;
|
|
69
|
-
isSuccess: T extends "success" ? true : false;
|
|
70
|
-
};
|
|
71
|
-
type FormMetaState<T extends ActionState<unknown, unknown, unknown>> = T & FormStatusFlags<T["type"]> & {
|
|
72
|
-
isPending: boolean;
|
|
73
|
-
};
|
|
74
|
-
type FormProps<Data, Error, ValidationError> = Omit<FormHTMLAttributes<HTMLFormElement>, "action" | "children"> & FormStateProps<Data, Error, ValidationError, FormData> & RenderProp<FormMetaState<InitialState<Data>> | FormMetaState<InvalidState<ValidationError>> | FormMetaState<FailureState<Error>> | FormMetaState<SuccessState<Data>>>;
|
|
75
|
-
declare function Form<Data, Error, ValidationError>({ children, action, initialData, permalink, autoReset, ...props }: FormProps<Data, Error, ValidationError>): React__default.JSX.Element;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* A context exposing the form action state.
|
|
79
|
-
* @private You don't need to use this component directly. Use the `createForm` function instead.
|
|
80
|
-
*/
|
|
81
|
-
declare const FormContext: React.Context<FormMetaState<ActionState<unknown, unknown, unknown>> | null>;
|
|
82
|
-
/**
|
|
83
|
-
* A hook to consume the form action state from the context.
|
|
84
|
-
*/
|
|
85
|
-
declare const useFormContext: () => FormMetaState<ActionState<unknown, unknown, unknown>>;
|
|
86
|
-
|
|
87
|
-
declare function FormStatus({ children, }: RenderProp<ReturnType<typeof useFormStatus>>): React.ReactNode;
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Conditionally renders the children, when the form action is in the "pending" state.
|
|
91
|
-
*/
|
|
92
|
-
declare function Pending({ children }: PropsWithChildren): React.ReactNode;
|
|
93
|
-
|
|
94
47
|
declare const SEPARATOR: ".";
|
|
95
48
|
type Paths<Err> = {
|
|
96
49
|
[k in keyof Err]: k extends string ? Err[k] extends object ? `${k}${typeof SEPARATOR}${Paths<Err[k]>}` : k : never;
|
|
@@ -104,4 +57,20 @@ type ZodFieldErrorProps<Errors extends ZodFormattedError<any>> = {
|
|
|
104
57
|
}>>;
|
|
105
58
|
declare function ZodFieldError<Errors extends ZodFormattedError<any>>({ errors, name, children, }: ZodFieldErrorProps<Errors>): React__default.ReactNode;
|
|
106
59
|
|
|
107
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Creates a typed components for actions created with the formAction builder.
|
|
62
|
+
*/
|
|
63
|
+
declare function createComponents<Data, Error, ValidationError extends ZodFormattedError<any>>(action: FormAction<Data, Error, ValidationError>): {
|
|
64
|
+
FieldError: ({ name, }: React__default.PropsWithChildren<{
|
|
65
|
+
name?: "" | InferZodErrorPaths<ValidationError> | undefined;
|
|
66
|
+
}>) => false | React__default.JSX.Element;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
declare function FormStatus({ children, }: RenderProp<ReturnType<typeof useFormStatus>>): React.ReactNode;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Conditionally renders the children, when the form action is in the "pending" state.
|
|
73
|
+
*/
|
|
74
|
+
declare function Pending({ children }: PropsWithChildren): React.ReactNode;
|
|
75
|
+
|
|
76
|
+
export { Action, ActionContextState, ActionForm, ActionFormProps, ActionProps, FormStatus, InferZodErrorPaths, Paths, Pending, ZodFieldError, ZodFieldErrorProps, createComponents, useActionContext };
|
package/dist/client.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import
|
|
1
|
+
"use client";import{a as p}from"./chunk-HL47DG4L.mjs";import f from"react";import{createContext as E,use as x}from"react";import{useActionState as A}from"react";var c={isInitial:!1,isInvalid:!1,isFailure:!1,isSuccess:!1},m=E(null);function a(t){let o=x(m);if(!o)throw new Error("ActionContext must be initialized before use. Is your useActionContext hook wrapped with an <Action> Component?");return o}function v({children:t,action:o,initialData:e,permalink:n}){let[r,i,s]=A(o,p(e),n),d=r.type==="initial"?{...r,...c,action:i,isPending:s,isInitial:!0}:r.type==="invalid"?{...r,...c,action:i,isPending:s,isInvalid:!0}:r.type==="failure"?{...r,...c,action:i,isPending:s,isFailure:!0}:{...r,...c,action:i,isPending:s,isSuccess:!0};return f.createElement(m,{value:d},t)}import F,{startTransition as y}from"react";function b({autoReset:t=!1,...o}){let{action:e}=a();return F.createElement("form",{...t?{action:e}:{onSubmit:r=>{r.preventDefault();let i=r.currentTarget;y(()=>{e(new FormData(i))})}},...o})}import h from"react";import l from"react";var S=".",P=Object.freeze({_errors:Object.freeze([])});function u({errors:t,name:o="",children:e=({errors:n})=>l.createElement(l.Fragment,null,n[0])}){if(o.length===0)return e({errors:t._errors});let n=o.split(S),r=t;for(;n.length;){let i=n.shift();r=r[i]??P}return e({errors:r._errors})}function L(t){function o({name:e}){let{isInvalid:n,validationError:r}=a(t);return n&&h.createElement(u,{errors:r,name:e})}return{FieldError:o}}import{useFormStatus as C}from"react-dom";function $({children:t}){return t(C())}function G({children:t}){let{isPending:o}=a();return o&&t}export{v as Action,b as ActionForm,$ as FormStatus,G as Pending,u as ZodFieldError,L as createComponents,a as useActionContext};
|
|
@@ -24,10 +24,11 @@ type SuccessState<T> = {
|
|
|
24
24
|
};
|
|
25
25
|
type ActionState<Data, Error, ValidationError = Record<string, never>> = InitialState<Data> | InvalidState<ValidationError> | FailureState<Error> | SuccessState<Data>;
|
|
26
26
|
type FormAction<Data, Error = Data, ValidationError = Record<string, never>, Payload = FormData> = (state: ActionState<Data, Error, ValidationError>, payload: Payload) => Promise<ActionState<Data, Error, ValidationError>>;
|
|
27
|
+
declare function initial<Data>(data: Data): InitialState<Data>;
|
|
27
28
|
declare function createFormAction<Data, Error = Data, ValidationError = Record<string, never>, Payload = FormData>(formAction: (params: {
|
|
28
29
|
success: (data: Data) => SuccessState<Data>;
|
|
29
30
|
failure: (error: Error) => FailureState<Error>;
|
|
30
31
|
invalid: (validationError: ValidationError) => InvalidState<ValidationError>;
|
|
31
32
|
}) => FormAction<Data, Error, ValidationError, Payload>): (state: ActionState<Data, Error, ValidationError>, payload: Payload) => Promise<ActionState<Data, Error, ValidationError>>;
|
|
32
33
|
|
|
33
|
-
export { ActionState as A, FailureState as F, InvalidState as I, SuccessState as S, InitialState as a, FormAction as b, createFormAction as c };
|
|
34
|
+
export { ActionState as A, FailureState as F, InvalidState as I, SuccessState as S, InitialState as a, FormAction as b, createFormAction as c, initial as i };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { A as ActionState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-
|
|
2
|
-
export { b as FormAction, a as InitialState, c as createFormAction } from './createFormAction-
|
|
1
|
+
import { A as ActionState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-277e6e21.js';
|
|
2
|
+
export { b as FormAction, a as InitialState, c as createFormAction, i as initial } from './createFormAction-277e6e21.js';
|
|
3
3
|
import { ZodTypeAny, ZodType, ZodObject, ZodRawShape, objectUtil, z, ZodSchema, ZodEffects } from 'zod';
|
|
4
4
|
|
|
5
5
|
declare const emptyInput: unique symbol;
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
import{a as A,b as p}from"./chunk-HL47DG4L.mjs";import{ZodFirstPartyTypeKind as C}from"zod";import{zfd as w}from"zod-form-data";var x=Symbol();function i(n,o=[],r){async function f(t){let e={formData:t};for(let a of o){let s=await a({ctx:e});e={...e,...s}}return e}return{input(t){if(n===x)return i(t,o,r);if(n._def.effect)throw new Error("Previous input is not augmentable because it contains an effect.");if(t._def.effect)throw new Error("Your input contains effect which prevents merging it with the previous inputs.");if(n._def.typeName===C.ZodObject){let e=n.merge(t);return i(e,o)}else throw Error("Merging inputs works only for object schemas without effects.")},use(t){return i(n,[...o,t],r)},error(t){return i(n,o,t)},run:n===x?t=>p(({success:e,failure:a})=>async(s,m)=>{let d=await f(m);try{return e(await t({ctx:d}))}catch(c){if(r)return a(r({error:c,ctx:d}));throw c}}):n===x?void 0:t=>p(({success:e,failure:a,invalid:s})=>{let m=w.formData(n);return async(d,c)=>{let y=await f(c),u=m.safeParse(c);if(!u.success)return s(u.error.format());let h=u.data;try{return e(await t({input:h,ctx:y}))}catch(S){if(r)return a(r({error:S,ctx:y}));throw S}}})}}var N=i(x);export{p as createFormAction,N as formAction,A as initial};
|