react-form-action 1.5.0 → 2.0.1
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 +174 -104
- package/dist/chunk-HL47DG4L.mjs +1 -0
- package/dist/client.d.mts +6 -41
- 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,93 @@ 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/subscribe/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/subscribe/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/subscribe/page.tsx
|
|
111
|
+
|
|
112
|
+
import { SubscribeForm } from "./SubscribeForm";
|
|
113
|
+
|
|
114
|
+
export function Page() {
|
|
115
|
+
return <SubscribeForm />;
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
37
119
|
## Usage
|
|
38
120
|
|
|
39
121
|
### `formAction` builder
|
|
40
122
|
|
|
123
|
+
The [`zod-form-data`](https://www.npmjs.com/package/zod-form-data) powered action builder.
|
|
124
|
+
|
|
41
125
|
```ts
|
|
42
126
|
// app/actions/auth.ts
|
|
43
127
|
"use server";
|
|
@@ -105,7 +189,9 @@ export const signUp = authAction
|
|
|
105
189
|
});
|
|
106
190
|
```
|
|
107
191
|
|
|
108
|
-
###
|
|
192
|
+
### Action Creator
|
|
193
|
+
|
|
194
|
+
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
195
|
|
|
110
196
|
```ts
|
|
111
197
|
"use server";
|
|
@@ -170,61 +256,21 @@ export const updateUser = createFormAction<
|
|
|
170
256
|
);
|
|
171
257
|
```
|
|
172
258
|
|
|
173
|
-
###
|
|
174
|
-
|
|
175
|
-
```tsx
|
|
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";
|
|
259
|
+
### Action Context
|
|
181
260
|
|
|
182
|
-
|
|
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
|
|
261
|
+
The `<Action>` components enables you to access your `action`'s state with the `useActionContext()` hook:
|
|
215
262
|
|
|
216
263
|
```tsx
|
|
217
|
-
//
|
|
264
|
+
// 👉 Define standalone client form component (e.g. /app/auth/signup/SignUpForm.tsx)
|
|
218
265
|
"use client";
|
|
219
266
|
|
|
220
|
-
import {
|
|
221
|
-
import {
|
|
267
|
+
import { Action, Form, useActionContext } from "react-form-action/client";
|
|
268
|
+
import type { PropsWithChildren } from "react";
|
|
222
269
|
|
|
223
|
-
|
|
224
|
-
const { Form } = createForm(signupAction);
|
|
270
|
+
import { signupAction } from "./action";
|
|
225
271
|
|
|
226
|
-
function
|
|
227
|
-
// read any state from the
|
|
272
|
+
function Pending({ children }: PropsWithChildren) {
|
|
273
|
+
// read any state from the ActionContext:
|
|
228
274
|
const {
|
|
229
275
|
error,
|
|
230
276
|
data,
|
|
@@ -233,36 +279,64 @@ function Error() {
|
|
|
233
279
|
isFailure,
|
|
234
280
|
isInvalid,
|
|
235
281
|
isSuccess,
|
|
236
|
-
isInitial
|
|
237
|
-
} =
|
|
282
|
+
isInitial,
|
|
283
|
+
} = useActionContext();
|
|
238
284
|
|
|
239
|
-
return
|
|
285
|
+
return isPending && children;
|
|
240
286
|
}
|
|
241
287
|
|
|
242
|
-
// render this form on your RSC page (/app/auth/signup/page.tsx)
|
|
288
|
+
// 💡 render this form on your RSC page (/app/auth/signup/page.tsx)
|
|
243
289
|
export function SignupForm() {
|
|
244
|
-
// This Form does not use render props.
|
|
245
290
|
return (
|
|
246
|
-
<
|
|
247
|
-
<
|
|
248
|
-
|
|
291
|
+
<Action action={signupAction}>
|
|
292
|
+
<Form>
|
|
293
|
+
<input name="email" />
|
|
294
|
+
<input name="password" />
|
|
295
|
+
</Form>
|
|
296
|
+
{/* 🎆 Read the pending state outside the <Form> */}
|
|
249
297
|
<Pending>
|
|
250
|
-
{/* This renders only when the action is pending. */}
|
|
298
|
+
{/* This renders only when the action is pending. 😎 */}
|
|
251
299
|
<p>Please wait...</p>
|
|
252
300
|
</Pending>
|
|
253
|
-
</
|
|
301
|
+
</Action>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
### `<Form>` Component
|
|
307
|
+
|
|
308
|
+
The `<form>` submits the action in `onSubmit` handler to [prevent automatic form reset](https://github.com/facebook/react/issues/29034).
|
|
309
|
+
Pass `autoReset` prop to use the `action` prop instead and keep the default reset.
|
|
310
|
+
|
|
311
|
+
```tsx
|
|
312
|
+
"use client";
|
|
313
|
+
|
|
314
|
+
import { Action, Form } from "react-form-action/client";
|
|
315
|
+
|
|
316
|
+
import { updateUser } from "./action";
|
|
317
|
+
|
|
318
|
+
export function UpdateUserForm() {
|
|
319
|
+
return (
|
|
320
|
+
<Action action={updateUser}>
|
|
321
|
+
<Form autoReset>{/* ... */}</Form>
|
|
322
|
+
</Action>
|
|
254
323
|
);
|
|
255
324
|
}
|
|
256
325
|
```
|
|
257
326
|
|
|
258
|
-
###
|
|
327
|
+
### Context Bound Components `createComponents()`
|
|
328
|
+
|
|
329
|
+
Use the `createComponents(action)` helper to create components which use the ActionContext and have types bound to the action type.
|
|
259
330
|
|
|
260
|
-
|
|
261
|
-
To easily access the nested error, use the helper `<ZodFieldError>` component:
|
|
331
|
+
### `<FielError>` Component
|
|
262
332
|
|
|
263
333
|
```tsx
|
|
264
334
|
"use client";
|
|
265
|
-
|
|
335
|
+
|
|
336
|
+
// ⚠️ createComponents is usable only in "use client" components
|
|
337
|
+
import { Form, createComponents } from "react-form-action/client";
|
|
338
|
+
|
|
339
|
+
import { authAction } from "./actions";
|
|
266
340
|
|
|
267
341
|
export const signUp = authAction
|
|
268
342
|
.input(
|
|
@@ -280,51 +354,47 @@ export const signUp = authAction
|
|
|
280
354
|
})
|
|
281
355
|
)
|
|
282
356
|
.run(async ({ ctx, input }) => {
|
|
283
|
-
|
|
357
|
+
return null;
|
|
284
358
|
});
|
|
285
359
|
|
|
360
|
+
// 🌟 The FieldError is now bound do the signUp input schema which allows autocompletion for its "name" prop
|
|
361
|
+
// ⚠️ Usable only with actions created with the formAction builder
|
|
362
|
+
const { FieldError } = createComponents(signUp);
|
|
363
|
+
|
|
286
364
|
export function SignUpForm() {
|
|
287
365
|
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>
|
|
366
|
+
<Action action={signUp} initialData={null}>
|
|
367
|
+
<Form>
|
|
368
|
+
{/* 1️⃣ When the "name" prop is ommited, the top-level error will be rendered e.g.:
|
|
369
|
+
"Passwords don't match" */}
|
|
370
|
+
<FieldError />
|
|
371
|
+
{/* 2️⃣ Access fields by their name: */}
|
|
372
|
+
<FieldError name="password" />
|
|
373
|
+
{/* 3️⃣ Access nested fields by dot access notation: */}
|
|
374
|
+
<FieldError name="user.email" />
|
|
375
|
+
</Form>
|
|
376
|
+
</Action>
|
|
306
377
|
);
|
|
307
378
|
}
|
|
308
379
|
```
|
|
309
380
|
|
|
310
|
-
###
|
|
381
|
+
### `<Pending>`
|
|
311
382
|
|
|
312
|
-
|
|
383
|
+
Render children when the action is pending:
|
|
313
384
|
|
|
314
385
|
```tsx
|
|
315
|
-
import {
|
|
386
|
+
import { Action, Pending } from "react-form-action/client";
|
|
316
387
|
|
|
317
|
-
|
|
388
|
+
import { Spinner } from "./components";
|
|
318
389
|
|
|
319
390
|
return function MyForm() {
|
|
320
391
|
return (
|
|
321
|
-
<
|
|
322
|
-
<
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
</form>
|
|
392
|
+
<Action action={action}>
|
|
393
|
+
{/* 👉 Unlike the React.useFormStatus() hook, we don't need here the <form> element at all. */}
|
|
394
|
+
<Pending>
|
|
395
|
+
<Spinner />
|
|
396
|
+
</Pending>
|
|
397
|
+
</Action>
|
|
328
398
|
);
|
|
329
399
|
};
|
|
330
400
|
```
|
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import React__default, { PropsWithChildren,
|
|
3
|
-
import { b as FormAction, A as ActionState, a as InitialState, I as InvalidState, F as FailureState, S as SuccessState } from './createFormAction-
|
|
2
|
+
import React__default, { PropsWithChildren, JSX } from 'react';
|
|
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
4
|
import { ZodFormattedError } from 'zod';
|
|
5
5
|
import { RenderProp } from 'react-render-prop-type';
|
|
6
6
|
import { useFormStatus } from 'react-dom';
|
|
@@ -30,10 +30,10 @@ type SpreadActionContext<Data = unknown, Error = unknown, ValidationError = unkn
|
|
|
30
30
|
/**
|
|
31
31
|
* A hook to consume the form action state from the context.
|
|
32
32
|
*/
|
|
33
|
-
declare function useActionContext<Data, Error, ValidationError>(): SpreadActionContext<Data, Error, ValidationError>;
|
|
33
|
+
declare function useActionContext<Data, Error, ValidationError>(action?: FormAction<Data, Error, ValidationError>): SpreadActionContext<Data, Error, ValidationError>;
|
|
34
34
|
declare function Action<Data, Error, ValidationError>({ children, action: formAction, initialData, permalink, }: ActionProps<Data, Error, ValidationError>): React__default.JSX.Element;
|
|
35
35
|
|
|
36
|
-
type
|
|
36
|
+
type FormProps = Omit<JSX.IntrinsicElements["form"], "action"> & {
|
|
37
37
|
/**
|
|
38
38
|
* Opt-in into automatic form reset by using the form "action" prop.
|
|
39
39
|
* By default, the onSubmit with a custom transition which opts-out of the implicit form reset.
|
|
@@ -42,7 +42,7 @@ type ActionFormProps = Omit<FormHTMLAttributes<HTMLFormElement>, "action"> & {
|
|
|
42
42
|
*/
|
|
43
43
|
autoReset?: boolean;
|
|
44
44
|
};
|
|
45
|
-
declare function
|
|
45
|
+
declare function Form({ autoReset, ...props }: FormProps): JSX.Element;
|
|
46
46
|
|
|
47
47
|
declare const SEPARATOR: ".";
|
|
48
48
|
type Paths<Err> = {
|
|
@@ -66,41 +66,6 @@ declare function createComponents<Data, Error, ValidationError extends ZodFormat
|
|
|
66
66
|
}>) => false | React__default.JSX.Element;
|
|
67
67
|
};
|
|
68
68
|
|
|
69
|
-
type FormStateProps<Data, Error, ValidationError, Payload> = {
|
|
70
|
-
action: (state: ActionState<Data, Error, ValidationError>, payload: Payload) => Promise<ActionState<Data, Error, ValidationError>>;
|
|
71
|
-
initialData: Data;
|
|
72
|
-
permalink?: string;
|
|
73
|
-
/**
|
|
74
|
-
* Opt-in into automatic form reset by using the form "action" prop.
|
|
75
|
-
* By default, the onSubmit with a custom transition which opts-out of the implicit form reset.
|
|
76
|
-
* See for more. https://github.com/facebook/react/issues/29034
|
|
77
|
-
* @default false
|
|
78
|
-
*/
|
|
79
|
-
autoReset?: boolean;
|
|
80
|
-
};
|
|
81
|
-
declare function initial<Data>(data: Data): InitialState<Data>;
|
|
82
|
-
type FormStatusFlags<T extends ActionState<unknown, unknown, unknown>["type"] | unknown = unknown> = {
|
|
83
|
-
isInitial: T extends "initial" ? true : false;
|
|
84
|
-
isInvalid: T extends "invalid" ? true : false;
|
|
85
|
-
isFailure: T extends "failure" ? true : false;
|
|
86
|
-
isSuccess: T extends "success" ? true : false;
|
|
87
|
-
};
|
|
88
|
-
type FormMetaState<T extends ActionState<unknown, unknown, unknown>> = T & FormStatusFlags<T["type"]> & {
|
|
89
|
-
isPending: boolean;
|
|
90
|
-
};
|
|
91
|
-
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>>>;
|
|
92
|
-
declare function Form<Data, Error, ValidationError>({ children, action, initialData, permalink, autoReset, ...props }: FormProps<Data, Error, ValidationError>): React__default.JSX.Element;
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* A context exposing the form action state.
|
|
96
|
-
* @private You don't need to use this component directly. Use the `createForm` function instead.
|
|
97
|
-
*/
|
|
98
|
-
declare const FormContext: React.Context<FormMetaState<ActionState<unknown, unknown, unknown>> | null>;
|
|
99
|
-
/**
|
|
100
|
-
* A hook to consume the form action state from the context.
|
|
101
|
-
*/
|
|
102
|
-
declare const useFormContext: () => FormMetaState<ActionState<unknown, unknown, unknown>>;
|
|
103
|
-
|
|
104
69
|
declare function FormStatus({ children, }: RenderProp<ReturnType<typeof useFormStatus>>): React.ReactNode;
|
|
105
70
|
|
|
106
71
|
/**
|
|
@@ -108,4 +73,4 @@ declare function FormStatus({ children, }: RenderProp<ReturnType<typeof useFormS
|
|
|
108
73
|
*/
|
|
109
74
|
declare function Pending({ children }: PropsWithChildren): React.ReactNode;
|
|
110
75
|
|
|
111
|
-
export { Action, ActionContextState,
|
|
76
|
+
export { Action, ActionContextState, ActionProps, Form, FormProps, 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 c}from"./chunk-HL47DG4L.mjs";import f from"react";import{createContext as E,use as x}from"react";import{useActionState as S}from"react";var p={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]=S(o,c(e),n),d=r.type==="initial"?{...r,...p,action:i,isPending:s,isInitial:!0}:r.type==="invalid"?{...r,...p,action:i,isPending:s,isInvalid:!0}:r.type==="failure"?{...r,...p,action:i,isPending:s,isFailure:!0}:{...r,...p,action:i,isPending:s,isSuccess:!0};return f.createElement(m,{value:d},t)}import y,{startTransition as A}from"react";function V({autoReset:t=!1,...o}){let{action:e}=a();return y.createElement("form",{...t?{action:e}:{onSubmit:r=>{r.preventDefault();let i=r.currentTarget;A(()=>{e(new FormData(i))})}},...o})}import h from"react";import l from"react";var F=".",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(F),r=t;for(;n.length;){let i=n.shift();r=r[i]??P}return e({errors:r._errors})}function $(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 X({children:t}){return t(C())}function q({children:t}){let{isPending:o}=a();return o&&t}export{v as Action,V as Form,X as FormStatus,q as Pending,u as ZodFieldError,$ 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};
|