remix-forms
Advanced tools
Comparing version 0.19.0 to 1.0.0
import * as React from 'react'; | ||
import { Fetcher, FormMethod, FormProps as FormProps$1 } from '@remix-run/react'; | ||
import { z, SomeZodObject, ZodTypeAny } from 'zod'; | ||
import { UseFormRegisterReturn, ValidationMode, UseFormReturn } from 'react-hook-form'; | ||
import { DomainFunction } from 'domain-functions'; | ||
import { Transition } from '@remix-run/react/dist/transition'; | ||
@@ -11,2 +9,4 @@ declare type FormSchema<T extends z.ZodTypeAny = z.SomeZodObject | z.ZodEffects<any>> = z.ZodEffects<T> | z.SomeZodObject; | ||
declare type RedirectFunction = (url: string, init?: number | ResponseInit) => Response; | ||
declare type JsonFunction = <Data>(data: Data, init?: number | ResponseInit) => Response; | ||
declare type FormActionFailure<SchemaType> = { | ||
@@ -37,3 +37,6 @@ errors: FormErrors<SchemaType>; | ||
declare function performMutation<Schema extends FormSchema, D extends unknown>({ request, schema, mutation, environment, }: PerformMutationProps<Schema, D>): Promise<PerformMutation<z.infer<Schema>, D>>; | ||
declare function formAction<Schema extends FormSchema, D extends unknown>({ request, schema, mutation, environment, beforeAction, beforeSuccess, successPath, }: FormActionProps<Schema, D>): Promise<Response>; | ||
declare function createFormAction({ redirect, json, }: { | ||
redirect: RedirectFunction; | ||
json: JsonFunction; | ||
}): <Schema extends FormSchema<z.SomeZodObject | z.ZodEffects<any, any, any>>, D extends unknown>({ request, schema, mutation, environment, beforeAction, beforeSuccess, successPath, }: FormActionProps<Schema, D>) => Promise<Response>; | ||
@@ -73,2 +76,10 @@ declare type SmartInputProps = { | ||
declare type FormMethod = 'get' | 'post' | 'put' | 'patch' | 'delete'; | ||
declare type BaseFormProps = { | ||
method?: FormMethod; | ||
onSubmit?: React.FormEventHandler<HTMLFormElement>; | ||
children: React.ReactNode; | ||
}; | ||
declare type BaseFormPropsWithHTMLAttributes = React.FormHTMLAttributes<HTMLFormElement> & BaseFormProps; | ||
declare type FormComponent = React.ForwardRefExoticComponent<BaseFormProps>; | ||
declare type Field<SchemaType> = { | ||
@@ -97,3 +108,2 @@ shape: ZodTypeAny; | ||
declare type Options<SchemaType> = Partial<Record<keyof SchemaType, Option[]>>; | ||
declare type AllRemixFormProps = FormProps$1 & React.RefAttributes<HTMLFormElement>; | ||
declare type Children<Schema extends SomeZodObject> = (helpers: { | ||
@@ -104,19 +114,18 @@ Field: FieldComponent<Schema>; | ||
Button: React.ComponentType<JSX.IntrinsicElements['button']> | string; | ||
transition: FormTransition; | ||
} & UseFormReturn<z.infer<Schema>, any>) => React.ReactNode; | ||
declare type FormTransition = (Fetcher<any> & { | ||
Form: any; | ||
submit: any; | ||
load: (href: string) => void; | ||
}) | Transition; | ||
declare type OnTransition<Schema extends SomeZodObject> = (helpers: { | ||
transition: FormTransition; | ||
} & UseFormReturn<z.infer<Schema>, any>) => void; | ||
declare type Transition = { | ||
state: 'idle' | 'loading' | 'submitting'; | ||
}; | ||
declare type OnTransition<Schema extends SomeZodObject> = (helpers: UseFormReturn<z.infer<Schema>, any>) => void; | ||
declare type SubmitFunction = ({ target }: { | ||
target: any; | ||
}) => void; | ||
declare type FetcherWithComponents = Transition & { | ||
data: any; | ||
Form: FormComponent; | ||
submit: SubmitFunction; | ||
}; | ||
declare type FormProps<Schema extends FormSchema> = { | ||
component?: React.ForwardRefExoticComponent<AllRemixFormProps>; | ||
fetcher?: Fetcher<any> & { | ||
Form: ReturnType<any>; | ||
submit: ReturnType<any>; | ||
load: (href: string) => void; | ||
}; | ||
component?: FormComponent; | ||
fetcher?: FetcherWithComponents; | ||
mode?: keyof ValidationMode; | ||
@@ -137,3 +146,2 @@ renderField?: RenderField<ObjectFromSchema<Schema>>; | ||
pendingButtonLabel?: string; | ||
method?: FormMethod; | ||
schema: Schema; | ||
@@ -151,5 +159,10 @@ errors?: FormErrors<z.infer<Schema>>; | ||
children?: Children<ObjectFromSchema<Schema>>; | ||
} & Omit<AllRemixFormProps, 'method' | 'children'>; | ||
declare function Form<Schema extends FormSchema>({ component, fetcher, mode, renderField, fieldComponent, globalErrorsComponent: Errors, errorComponent: Error, fieldErrorsComponent, labelComponent, inputComponent, multilineComponent, selectComponent, checkboxComponent, checkboxWrapperComponent, buttonComponent: Button, buttonLabel: rawButtonLabel, pendingButtonLabel, method, schema, beforeChildren, onTransition, parseActionData, children: childrenFn, labels, placeholders, options, hiddenFields, multiline, errors: errorsProp, values: valuesProp, ...props }: FormProps<Schema>): JSX.Element; | ||
} & Omit<BaseFormPropsWithHTMLAttributes, 'children'>; | ||
declare function createForm({ component: DefaultComponent, useNavigation, useSubmit, useActionData, }: { | ||
component: FormComponent; | ||
useNavigation: () => Transition; | ||
useSubmit: () => SubmitFunction; | ||
useActionData: () => unknown; | ||
}): <Schema extends FormSchema<SomeZodObject | z.ZodEffects<any, any, any>>>({ component, fetcher, mode, renderField, fieldComponent, globalErrorsComponent: Errors, errorComponent: Error, fieldErrorsComponent, labelComponent, inputComponent, multilineComponent, selectComponent, checkboxComponent, checkboxWrapperComponent, buttonComponent: Button, buttonLabel: rawButtonLabel, pendingButtonLabel, method, schema, beforeChildren, onTransition, parseActionData, children: childrenFn, labels, placeholders, options, hiddenFields, multiline, errors: errorsProp, values: valuesProp, ...props }: FormProps<Schema>) => JSX.Element; | ||
export { Callback, Form, FormActionProps, FormProps, FormSchema, PerformMutation, RenderField, RenderFieldProps, formAction, performMutation }; | ||
export { Callback, FormActionProps, FormProps, FormSchema, PerformMutation, RenderField, RenderFieldProps, createForm, createFormAction, performMutation }; |
@@ -20,3 +20,6 @@ "use strict"; | ||
}; | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); | ||
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
mod | ||
)); | ||
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
@@ -27,4 +30,4 @@ | ||
__export(src_exports, { | ||
Form: () => Form, | ||
formAction: () => formAction, | ||
createForm: () => createForm, | ||
createFormAction: () => createFormAction, | ||
performMutation: () => performMutation | ||
@@ -34,5 +37,4 @@ }); | ||
// src/Form.tsx | ||
var React5 = __toESM(require("react")); | ||
var import_react = require("@remix-run/react"); | ||
// src/createForm.tsx | ||
var React3 = __toESM(require("react")); | ||
@@ -44,3 +46,3 @@ // src/prelude.ts | ||
// src/Form.tsx | ||
// src/createForm.tsx | ||
var import_react_hook_form = require("react-hook-form"); | ||
@@ -50,3 +52,3 @@ var import_zod = require("@hookform/resolvers/zod"); | ||
// src/createField.tsx | ||
var React3 = __toESM(require("react")); | ||
var React2 = __toESM(require("react")); | ||
@@ -60,6 +62,8 @@ // src/mapChildren.ts | ||
if (child.props.children && typeof child.props.children !== "function") { | ||
return fn(React.cloneElement(child, { | ||
...child.props, | ||
children: mapChildren(child.props.children, fn) | ||
})); | ||
return fn( | ||
React.cloneElement(child, { | ||
...child.props, | ||
children: mapChildren(child.props.children, fn) | ||
}) | ||
); | ||
} | ||
@@ -77,12 +81,36 @@ return fn(child); | ||
if (typeName === "ZodEffects") { | ||
return shapeInfo(shape._def.schema, optional, nullable, getDefaultValue, enumValues); | ||
return shapeInfo( | ||
shape._def.schema, | ||
optional, | ||
nullable, | ||
getDefaultValue, | ||
enumValues | ||
); | ||
} | ||
if (typeName === "ZodOptional") { | ||
return shapeInfo(shape._def.innerType, true, nullable, getDefaultValue, enumValues); | ||
return shapeInfo( | ||
shape._def.innerType, | ||
true, | ||
nullable, | ||
getDefaultValue, | ||
enumValues | ||
); | ||
} | ||
if (typeName === "ZodNullable") { | ||
return shapeInfo(shape._def.innerType, optional, true, getDefaultValue, enumValues); | ||
return shapeInfo( | ||
shape._def.innerType, | ||
optional, | ||
true, | ||
getDefaultValue, | ||
enumValues | ||
); | ||
} | ||
if (typeName === "ZodDefault") { | ||
return shapeInfo(shape._def.innerType, optional, nullable, shape._def.defaultValue, enumValues); | ||
return shapeInfo( | ||
shape._def.innerType, | ||
optional, | ||
nullable, | ||
shape._def.defaultValue, | ||
enumValues | ||
); | ||
} | ||
@@ -144,3 +172,3 @@ if (typeName === "ZodEnum") { | ||
// src/createSmartInput.tsx | ||
var React2 = __toESM(require("react")); | ||
var import_jsx_runtime = require("react/jsx-runtime"); | ||
function createSmartInput({ | ||
@@ -168,3 +196,3 @@ inputComponent: Input = "input", | ||
if (fieldType === "boolean") { | ||
return /* @__PURE__ */ React2.createElement(Checkbox, { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Checkbox, { | ||
id: name, | ||
@@ -181,3 +209,3 @@ type, | ||
if (selectChildren) { | ||
return /* @__PURE__ */ React2.createElement(Select, { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Select, { | ||
id: name, | ||
@@ -188,7 +216,8 @@ ...registerProps, | ||
...a11yProps, | ||
...props | ||
}, selectChildren); | ||
...props, | ||
children: selectChildren | ||
}); | ||
} | ||
if (multiline) { | ||
return /* @__PURE__ */ React2.createElement(Multiline, { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Multiline, { | ||
id: name, | ||
@@ -203,3 +232,3 @@ ...registerProps, | ||
} | ||
return /* @__PURE__ */ React2.createElement(Input, { | ||
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Input, { | ||
id: name, | ||
@@ -218,2 +247,3 @@ type, | ||
// src/createField.tsx | ||
var import_jsx_runtime2 = require("react/jsx-runtime"); | ||
var types = { | ||
@@ -244,199 +274,222 @@ boolean: "checkbox", | ||
}) { | ||
return React3.forwardRef(({ | ||
fieldType = "string", | ||
shape, | ||
name, | ||
label, | ||
options, | ||
errors, | ||
type: typeProp, | ||
required = false, | ||
autoFocus = false, | ||
value: rawValue, | ||
multiline = false, | ||
placeholder, | ||
hidden = false, | ||
children: childrenFn, | ||
...props | ||
}, ref) => { | ||
const value = fieldType === "date" ? parseDate(rawValue) : rawValue; | ||
const selectChildren = options ? options.map(({ name: name2, value: value2 }) => /* @__PURE__ */ React3.createElement("option", { | ||
key: String(value2), | ||
value: value2 | ||
}, name2)) : void 0; | ||
const errorsChildren = (errors == null ? void 0 : errors.length) ? errors.map((error) => /* @__PURE__ */ React3.createElement(Error2, { | ||
key: error | ||
}, error)) : void 0; | ||
const style = hidden ? { display: "none" } : void 0; | ||
const type = typeProp || types[fieldType]; | ||
const registerProps = register(String(name), { | ||
setValueAs: (value2) => coerceValue(value2, shape) | ||
}); | ||
const labelId = `label-for-${name.toString()}`; | ||
const errorsId = `errors-for-${name.toString()}`; | ||
const a11yProps = { | ||
"aria-labelledby": labelId, | ||
"aria-invalid": Boolean(errors), | ||
"aria-describedby": errors ? errorsId : void 0, | ||
"aria-required": required | ||
}; | ||
const SmartInput = React3.useMemo(() => createSmartInput({ | ||
inputComponent: Input, | ||
multilineComponent: Multiline, | ||
selectComponent: Select, | ||
checkboxComponent: Checkbox | ||
}), [Input, Multiline, Select, Checkbox]); | ||
if (childrenFn) { | ||
const children = childrenFn({ | ||
Label, | ||
SmartInput, | ||
Input, | ||
Multiline, | ||
Select, | ||
Checkbox, | ||
CheckboxWrapper, | ||
Errors, | ||
Error: Error2, | ||
ref, | ||
shape, | ||
fieldType, | ||
name, | ||
required, | ||
label, | ||
type, | ||
options, | ||
errors, | ||
autoFocus, | ||
value, | ||
hidden, | ||
multiline, | ||
placeholder | ||
return React2.forwardRef( | ||
({ | ||
fieldType = "string", | ||
shape, | ||
name, | ||
label, | ||
options, | ||
errors, | ||
type: typeProp, | ||
required = false, | ||
autoFocus = false, | ||
value: rawValue, | ||
multiline = false, | ||
placeholder, | ||
hidden = false, | ||
children: childrenFn, | ||
...props | ||
}, ref) => { | ||
const value = fieldType === "date" ? parseDate(rawValue) : rawValue; | ||
const selectChildren = options ? options.map(({ name: name2, value: value2 }) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { | ||
value: value2, | ||
children: name2 | ||
}, String(value2))) : void 0; | ||
const errorsChildren = (errors == null ? void 0 : errors.length) ? errors.map((error) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Error2, { | ||
children: error | ||
}, error)) : void 0; | ||
const style = hidden ? { display: "none" } : void 0; | ||
const type = typeProp || types[fieldType]; | ||
const registerProps = register(String(name), { | ||
setValueAs: (value2) => coerceValue(value2, shape) | ||
}); | ||
return /* @__PURE__ */ React3.createElement(Field, { | ||
const labelId = `label-for-${name.toString()}`; | ||
const errorsId = `errors-for-${name.toString()}`; | ||
const a11yProps = { | ||
"aria-labelledby": labelId, | ||
"aria-invalid": Boolean(errors), | ||
"aria-describedby": errors ? errorsId : void 0, | ||
"aria-required": required | ||
}; | ||
const SmartInput = React2.useMemo( | ||
() => createSmartInput({ | ||
inputComponent: Input, | ||
multilineComponent: Multiline, | ||
selectComponent: Select, | ||
checkboxComponent: Checkbox | ||
}), | ||
[Input, Multiline, Select, Checkbox] | ||
); | ||
if (childrenFn) { | ||
const children = childrenFn({ | ||
Label, | ||
SmartInput, | ||
Input, | ||
Multiline, | ||
Select, | ||
Checkbox, | ||
CheckboxWrapper, | ||
Errors, | ||
Error: Error2, | ||
ref, | ||
shape, | ||
fieldType, | ||
name, | ||
required, | ||
label, | ||
type, | ||
options, | ||
errors, | ||
autoFocus, | ||
value, | ||
hidden, | ||
multiline, | ||
placeholder | ||
}); | ||
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Field, { | ||
hidden, | ||
style, | ||
...props, | ||
children: mapChildren(children, (child) => { | ||
if (!React2.isValidElement(child)) | ||
return child; | ||
if (child.type === Label) { | ||
return React2.cloneElement(child, { | ||
id: labelId, | ||
htmlFor: String(name), | ||
children: label, | ||
...child.props | ||
}); | ||
} else if (child.type === SmartInput) { | ||
return React2.cloneElement(child, { | ||
fieldType, | ||
type, | ||
selectChildren, | ||
multiline, | ||
placeholder, | ||
registerProps, | ||
autoFocus, | ||
value, | ||
a11yProps, | ||
...child.props | ||
}); | ||
} else if (child.type === Input) { | ||
return React2.cloneElement(child, { | ||
id: String(name), | ||
type, | ||
...registerProps, | ||
...a11yProps, | ||
placeholder, | ||
autoFocus, | ||
defaultValue: value, | ||
...child.props | ||
}); | ||
} else if (child.type === Multiline) { | ||
return React2.cloneElement(child, { | ||
id: String(name), | ||
...registerProps, | ||
...a11yProps, | ||
placeholder, | ||
autoFocus, | ||
defaultValue: value, | ||
...child.props | ||
}); | ||
} else if (child.type === Select) { | ||
return React2.cloneElement(child, { | ||
id: String(name), | ||
...registerProps, | ||
...a11yProps, | ||
autoFocus, | ||
defaultValue: value, | ||
children: selectChildren, | ||
...child.props | ||
}); | ||
} else if (child.type === Checkbox) { | ||
return React2.cloneElement(child, { | ||
id: String(name), | ||
type, | ||
autoFocus, | ||
...registerProps, | ||
...a11yProps, | ||
placeholder, | ||
defaultChecked: Boolean(value), | ||
...child.props | ||
}); | ||
} else if (child.type === Errors) { | ||
if (!child.props.children && !(errors == null ? void 0 : errors.length)) | ||
return null; | ||
if (child.props.children || !(errors == null ? void 0 : errors.length)) { | ||
return React2.cloneElement(child, { | ||
id: errorsId, | ||
role: "alert", | ||
...child.props | ||
}); | ||
} | ||
return React2.cloneElement(child, { | ||
id: errorsId, | ||
role: "alert", | ||
children: errorsChildren, | ||
...child.props | ||
}); | ||
} else { | ||
return child; | ||
} | ||
}) | ||
}); | ||
} | ||
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Field, { | ||
hidden, | ||
style, | ||
...props | ||
}, mapChildren(children, (child) => { | ||
if (!React3.isValidElement(child)) | ||
return child; | ||
if (child.type === Label) { | ||
return React3.cloneElement(child, { | ||
id: labelId, | ||
htmlFor: String(name), | ||
children: label, | ||
...child.props | ||
}); | ||
} else if (child.type === SmartInput) { | ||
return React3.cloneElement(child, { | ||
fieldType, | ||
type, | ||
selectChildren, | ||
multiline, | ||
placeholder, | ||
registerProps, | ||
autoFocus, | ||
value, | ||
a11yProps, | ||
...child.props | ||
}); | ||
} else if (child.type === Input) { | ||
return React3.cloneElement(child, { | ||
id: String(name), | ||
type, | ||
...registerProps, | ||
...a11yProps, | ||
placeholder, | ||
autoFocus, | ||
defaultValue: value, | ||
...child.props | ||
}); | ||
} else if (child.type === Multiline) { | ||
return React3.cloneElement(child, { | ||
id: String(name), | ||
...registerProps, | ||
...a11yProps, | ||
placeholder, | ||
autoFocus, | ||
defaultValue: value, | ||
...child.props | ||
}); | ||
} else if (child.type === Select) { | ||
return React3.cloneElement(child, { | ||
id: String(name), | ||
...registerProps, | ||
...a11yProps, | ||
autoFocus, | ||
defaultValue: value, | ||
children: selectChildren, | ||
...child.props | ||
}); | ||
} else if (child.type === Checkbox) { | ||
return React3.cloneElement(child, { | ||
id: String(name), | ||
type, | ||
autoFocus, | ||
...registerProps, | ||
...a11yProps, | ||
placeholder, | ||
defaultChecked: Boolean(value), | ||
...child.props | ||
}); | ||
} else if (child.type === Errors) { | ||
if (!child.props.children && !(errors == null ? void 0 : errors.length)) | ||
return null; | ||
if (child.props.children || !(errors == null ? void 0 : errors.length)) { | ||
return React3.cloneElement(child, { | ||
id: errorsId, | ||
role: "alert", | ||
...child.props | ||
}); | ||
} | ||
return React3.cloneElement(child, { | ||
...props, | ||
children: [ | ||
fieldType === "boolean" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(CheckboxWrapper, { | ||
children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Checkbox, { | ||
id: String(name), | ||
type, | ||
...registerProps, | ||
...a11yProps, | ||
placeholder, | ||
autoFocus, | ||
defaultChecked: Boolean(value) | ||
}), | ||
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Label, { | ||
id: labelId, | ||
htmlFor: String(name), | ||
children: label | ||
}) | ||
] | ||
}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { | ||
children: [ | ||
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Label, { | ||
id: labelId, | ||
htmlFor: String(name), | ||
children: label | ||
}), | ||
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(SmartInput, { | ||
fieldType, | ||
type, | ||
selectChildren, | ||
multiline, | ||
placeholder, | ||
registerProps, | ||
autoFocus, | ||
value, | ||
a11yProps | ||
}) | ||
] | ||
}), | ||
Boolean(errorsChildren) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Errors, { | ||
role: "alert", | ||
id: errorsId, | ||
role: "alert", | ||
children: errorsChildren, | ||
...child.props | ||
}); | ||
} else { | ||
return child; | ||
} | ||
})); | ||
children: errorsChildren | ||
}) | ||
] | ||
}); | ||
} | ||
return /* @__PURE__ */ React3.createElement(Field, { | ||
hidden, | ||
style, | ||
...props | ||
}, fieldType === "boolean" ? /* @__PURE__ */ React3.createElement(CheckboxWrapper, null, /* @__PURE__ */ React3.createElement(Checkbox, { | ||
id: String(name), | ||
type, | ||
...registerProps, | ||
...a11yProps, | ||
placeholder, | ||
autoFocus, | ||
defaultChecked: Boolean(value) | ||
}), /* @__PURE__ */ React3.createElement(Label, { | ||
id: labelId, | ||
htmlFor: String(name) | ||
}, label)) : /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(Label, { | ||
id: labelId, | ||
htmlFor: String(name) | ||
}, label), /* @__PURE__ */ React3.createElement(SmartInput, { | ||
fieldType, | ||
type, | ||
selectChildren, | ||
multiline, | ||
placeholder, | ||
registerProps, | ||
autoFocus, | ||
value, | ||
a11yProps | ||
})), Boolean(errorsChildren) && /* @__PURE__ */ React3.createElement(Errors, { | ||
role: "alert", | ||
id: errorsId | ||
}, errorsChildren)); | ||
}); | ||
); | ||
} | ||
// src/defaultRenderField.tsx | ||
var React4 = __toESM(require("react")); | ||
var import_jsx_runtime3 = require("react/jsx-runtime"); | ||
function defaultRenderField({ | ||
@@ -447,7 +500,6 @@ Field, | ||
}) { | ||
return /* @__PURE__ */ React4.createElement(Field, { | ||
key: String(name), | ||
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Field, { | ||
name, | ||
...props | ||
}); | ||
}, String(name)); | ||
} | ||
@@ -457,3 +509,5 @@ | ||
function startCase(str) { | ||
const matches = str.match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) ?? [""]; | ||
const matches = str.match( | ||
/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g | ||
) ?? [""]; | ||
return matches.map((x) => x.charAt(0).toUpperCase() + x.slice(1)).join(" "); | ||
@@ -467,3 +521,4 @@ } | ||
// src/Form.tsx | ||
// src/createForm.tsx | ||
var import_jsx_runtime4 = require("react/jsx-runtime"); | ||
var fieldTypes = { | ||
@@ -476,72 +531,17 @@ ZodString: "string", | ||
}; | ||
function Form({ | ||
component = import_react.Form, | ||
fetcher, | ||
mode = "onSubmit", | ||
renderField = defaultRenderField, | ||
fieldComponent, | ||
globalErrorsComponent: Errors = "div", | ||
errorComponent: Error2 = "div", | ||
fieldErrorsComponent, | ||
labelComponent, | ||
inputComponent, | ||
multilineComponent, | ||
selectComponent, | ||
checkboxComponent, | ||
checkboxWrapperComponent, | ||
buttonComponent: Button = "button", | ||
buttonLabel: rawButtonLabel = "OK", | ||
pendingButtonLabel = "OK", | ||
method = "post", | ||
schema, | ||
beforeChildren, | ||
onTransition, | ||
parseActionData, | ||
children: childrenFn, | ||
labels, | ||
placeholders, | ||
options, | ||
hiddenFields, | ||
multiline, | ||
errors: errorsProp, | ||
values: valuesProp, | ||
...props | ||
function createForm({ | ||
component: DefaultComponent, | ||
useNavigation, | ||
useSubmit, | ||
useActionData | ||
}) { | ||
var _a; | ||
const Component = (fetcher == null ? void 0 : fetcher.Form) ?? component; | ||
const submit = (fetcher == null ? void 0 : fetcher.submit) ?? (0, import_react.useSubmit)(); | ||
const transition = fetcher ?? (0, import_react.useTransition)(); | ||
const unparsedActionData = (fetcher == null ? void 0 : fetcher.data) ?? (0, import_react.useActionData)(); | ||
const actionData = parseActionData && unparsedActionData ? parseActionData(unparsedActionData) : unparsedActionData; | ||
const actionErrors = actionData == null ? void 0 : actionData.errors; | ||
const actionValues = actionData == null ? void 0 : actionData.values; | ||
const errors = { ...errorsProp, ...actionErrors }; | ||
const values = { ...valuesProp, ...actionValues }; | ||
const form = (0, import_react_hook_form.useForm)({ resolver: (0, import_zod.zodResolver)(schema), mode }); | ||
const { formState } = form; | ||
const { errors: formErrors, isValid } = formState; | ||
const [disabled, setDisabled] = React5.useState(false); | ||
React5.useEffect(() => { | ||
const shouldDisable = mode === "onChange" || mode === "all" ? transition.state === "submitting" || !isValid : transition.state === "submitting"; | ||
setDisabled(shouldDisable); | ||
}, [transition.state, formState]); | ||
React5.useEffect(() => { | ||
onTransition && onTransition({ transition, ...form }); | ||
}, [transition.state]); | ||
const onSubmit = (event) => { | ||
form.handleSubmit(() => submit(event.target))(event); | ||
}; | ||
const Field = React5.useMemo(() => createField({ | ||
register: form.register, | ||
function Form({ | ||
component = DefaultComponent, | ||
fetcher, | ||
mode = "onSubmit", | ||
renderField = defaultRenderField, | ||
fieldComponent, | ||
labelComponent, | ||
inputComponent, | ||
multilineComponent, | ||
selectComponent, | ||
checkboxComponent, | ||
checkboxWrapperComponent, | ||
globalErrorsComponent: Errors = "div", | ||
errorComponent: Error2 = "div", | ||
fieldErrorsComponent, | ||
errorComponent: Error2 | ||
}), [ | ||
fieldComponent, | ||
labelComponent, | ||
@@ -553,139 +553,219 @@ inputComponent, | ||
checkboxWrapperComponent, | ||
fieldErrorsComponent, | ||
Error2 | ||
]); | ||
const schemaShape = objectFromSchema(schema).shape; | ||
React5.useEffect(() => { | ||
var _a2; | ||
buttonComponent: Button = "button", | ||
buttonLabel: rawButtonLabel = "OK", | ||
pendingButtonLabel = "OK", | ||
method = "post", | ||
schema, | ||
beforeChildren, | ||
onTransition, | ||
parseActionData, | ||
children: childrenFn, | ||
labels, | ||
placeholders, | ||
options, | ||
hiddenFields, | ||
multiline, | ||
errors: errorsProp, | ||
values: valuesProp, | ||
...props | ||
}) { | ||
var _a; | ||
const Component = (fetcher == null ? void 0 : fetcher.Form) ?? component; | ||
const submit = (fetcher == null ? void 0 : fetcher.submit) ?? useSubmit(); | ||
const transition = fetcher ?? useNavigation(); | ||
const unparsedActionData = (fetcher == null ? void 0 : fetcher.data) ?? useActionData(); | ||
const actionData = parseActionData && unparsedActionData ? parseActionData(unparsedActionData) : unparsedActionData; | ||
const actionErrors = actionData == null ? void 0 : actionData.errors; | ||
const actionValues = actionData == null ? void 0 : actionData.values; | ||
const errors = { ...errorsProp, ...actionErrors }; | ||
const values = { ...valuesProp, ...actionValues }; | ||
const form = (0, import_react_hook_form.useForm)({ resolver: (0, import_zod.zodResolver)(schema), mode }); | ||
const { formState } = form; | ||
const { errors: formErrors, isValid } = formState; | ||
const [disabled, setDisabled] = React3.useState(false); | ||
React3.useEffect(() => { | ||
const shouldDisable = mode === "onChange" || mode === "all" ? transition.state === "submitting" || !isValid : transition.state === "submitting"; | ||
setDisabled(shouldDisable); | ||
}, [transition.state, formState]); | ||
React3.useEffect(() => { | ||
onTransition && onTransition(form); | ||
}, [transition.state]); | ||
const onSubmit = (event) => { | ||
form.handleSubmit(() => submit(event.target))(event); | ||
}; | ||
const Field = React3.useMemo( | ||
() => createField({ | ||
register: form.register, | ||
fieldComponent, | ||
labelComponent, | ||
inputComponent, | ||
multilineComponent, | ||
selectComponent, | ||
checkboxComponent, | ||
checkboxWrapperComponent, | ||
fieldErrorsComponent, | ||
errorComponent: Error2 | ||
}), | ||
[ | ||
fieldComponent, | ||
labelComponent, | ||
inputComponent, | ||
multilineComponent, | ||
selectComponent, | ||
checkboxComponent, | ||
checkboxWrapperComponent, | ||
fieldErrorsComponent, | ||
Error2 | ||
] | ||
); | ||
const schemaShape = objectFromSchema(schema).shape; | ||
React3.useEffect(() => { | ||
var _a2; | ||
for (const stringKey in schemaShape) { | ||
const key = stringKey; | ||
if (errors && ((_a2 = errors[key]) == null ? void 0 : _a2.length)) { | ||
try { | ||
form.setFocus(key); | ||
} catch { | ||
} | ||
} | ||
} | ||
}, [errorsProp, unparsedActionData]); | ||
let autoFocused = false; | ||
let fields = []; | ||
for (const stringKey in schemaShape) { | ||
const key = stringKey; | ||
if (errors && ((_a2 = errors[key]) == null ? void 0 : _a2.length)) { | ||
try { | ||
form.setFocus(key); | ||
} catch { | ||
} | ||
} | ||
const message = (_a = formErrors[key]) == null ? void 0 : _a.message; | ||
const shape = schemaShape[stringKey]; | ||
const errorsArray = message && [message] || errors && errors[key]; | ||
const fieldErrors = errorsArray && errorsArray.length ? errorsArray : void 0; | ||
const autoFocus = Boolean(fieldErrors && !autoFocused); | ||
if (autoFocus) | ||
autoFocused = true; | ||
const { typeName, optional, nullable, getDefaultValue, enumValues } = shapeInfo(shape); | ||
const fieldType = typeName ? fieldTypes[typeName] : "string"; | ||
const required = !(optional || nullable); | ||
const propOptions = options && options[key]; | ||
const enumOptions = enumValues ? enumValues.map((value2) => ({ | ||
name: inferLabel(value2), | ||
value: value2 | ||
})) : void 0; | ||
const rawOptions = propOptions || enumOptions; | ||
const fieldOptions = rawOptions && !required ? [{ name: "", value: "" }, ...rawOptions ?? []] : rawOptions; | ||
const label = labels && labels[key] || inferLabel(String(stringKey)); | ||
const value = values && values[key]; | ||
fields.push({ | ||
shape, | ||
fieldType, | ||
name: stringKey, | ||
required, | ||
label, | ||
options: fieldOptions, | ||
errors: fieldErrors, | ||
autoFocus, | ||
value: value === void 0 ? getDefaultValue && getDefaultValue() : value, | ||
hidden: hiddenFields && Boolean(hiddenFields.find((item) => item === key)), | ||
multiline: multiline && Boolean(multiline.find((item) => item === key)), | ||
placeholder: placeholders && placeholders[key] | ||
}); | ||
} | ||
}, [errorsProp, unparsedActionData]); | ||
let autoFocused = false; | ||
let fields = []; | ||
for (const stringKey in schemaShape) { | ||
const key = stringKey; | ||
const message = (_a = formErrors[key]) == null ? void 0 : _a.message; | ||
const shape = schemaShape[stringKey]; | ||
const errorsArray = message && [message] || errors && errors[key]; | ||
const fieldErrors = errorsArray && errorsArray.length ? errorsArray : void 0; | ||
const autoFocus = Boolean(fieldErrors && !autoFocused); | ||
if (autoFocus) | ||
autoFocused = true; | ||
const { typeName, optional, nullable, getDefaultValue, enumValues } = shapeInfo(shape); | ||
const fieldType = typeName ? fieldTypes[typeName] : "string"; | ||
const required = !(optional || nullable); | ||
const propOptions = options && options[key]; | ||
const enumOptions = enumValues ? enumValues.map((value2) => ({ | ||
name: inferLabel(value2), | ||
value: value2 | ||
})) : void 0; | ||
const rawOptions = propOptions || enumOptions; | ||
const fieldOptions = rawOptions && !required ? [{ name: "", value: "" }, ...rawOptions ?? []] : rawOptions; | ||
const label = labels && labels[key] || inferLabel(String(stringKey)); | ||
const value = values && values[key]; | ||
fields.push({ | ||
shape, | ||
fieldType, | ||
name: stringKey, | ||
required, | ||
label, | ||
options: fieldOptions, | ||
errors: fieldErrors, | ||
autoFocus, | ||
value: value === void 0 ? getDefaultValue && getDefaultValue() : value, | ||
hidden: hiddenFields && Boolean(hiddenFields.find((item) => item === key)), | ||
multiline: multiline && Boolean(multiline.find((item) => item === key)), | ||
placeholder: placeholders && placeholders[key] | ||
}); | ||
} | ||
const globalErrors = errors == null ? void 0 : errors._global; | ||
const buttonLabel = transition.state === "submitting" ? pendingButtonLabel : rawButtonLabel; | ||
if (childrenFn) { | ||
const children = childrenFn({ | ||
Field, | ||
Errors, | ||
Error: Error2, | ||
Button, | ||
transition, | ||
...form | ||
}); | ||
return /* @__PURE__ */ React5.createElement(Component, { | ||
const globalErrors = errors == null ? void 0 : errors._global; | ||
const buttonLabel = transition.state === "submitting" ? pendingButtonLabel : rawButtonLabel; | ||
if (childrenFn) { | ||
const children = childrenFn({ | ||
Field, | ||
Errors, | ||
Error: Error2, | ||
Button, | ||
...form | ||
}); | ||
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Component, { | ||
method, | ||
onSubmit, | ||
...props, | ||
children: [ | ||
beforeChildren, | ||
mapChildren(children, (child) => { | ||
if (!React3.isValidElement(child)) | ||
return child; | ||
if (child.type === Field) { | ||
const { name } = child.props; | ||
const field = fields.find((field2) => field2.name === name); | ||
const autoFocus = autoFocused ? field == null ? void 0 : field.autoFocus : child.props.autoFocus; | ||
if (!child.props.children && field) { | ||
return renderField({ | ||
Field, | ||
...field, | ||
...child.props, | ||
autoFocus | ||
}); | ||
} | ||
return React3.cloneElement(child, { | ||
shape: field == null ? void 0 : field.shape, | ||
fieldType: field == null ? void 0 : field.fieldType, | ||
label: field == null ? void 0 : field.label, | ||
placeholder: field == null ? void 0 : field.placeholder, | ||
required: field == null ? void 0 : field.required, | ||
options: field == null ? void 0 : field.options, | ||
value: field == null ? void 0 : field.value, | ||
errors: field == null ? void 0 : field.errors, | ||
hidden: field == null ? void 0 : field.hidden, | ||
multiline: field == null ? void 0 : field.multiline, | ||
...child.props, | ||
autoFocus | ||
}); | ||
} else if (child.type === Errors) { | ||
if (!child.props.children && !(globalErrors == null ? void 0 : globalErrors.length)) | ||
return null; | ||
if (child.props.children || !(globalErrors == null ? void 0 : globalErrors.length)) { | ||
return React3.cloneElement(child, { | ||
role: "alert", | ||
...child.props | ||
}); | ||
} | ||
return React3.cloneElement(child, { | ||
role: "alert", | ||
children: globalErrors.map((error) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Error2, { | ||
children: error | ||
}, error)), | ||
...child.props | ||
}); | ||
} else if (child.type === Button) { | ||
return React3.cloneElement(child, { | ||
disabled, | ||
children: buttonLabel, | ||
...child.props | ||
}); | ||
} else { | ||
return child; | ||
} | ||
}) | ||
] | ||
}); | ||
} | ||
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(Component, { | ||
method, | ||
onSubmit, | ||
...props | ||
}, beforeChildren, mapChildren(children, (child) => { | ||
if (!React5.isValidElement(child)) | ||
return child; | ||
if (child.type === Field) { | ||
const { name } = child.props; | ||
const field = fields.find((field2) => field2.name === name); | ||
const autoFocus = autoFocused ? field == null ? void 0 : field.autoFocus : child.props.autoFocus; | ||
if (!child.props.children && field) { | ||
return renderField({ Field, ...field, ...child.props, autoFocus }); | ||
} | ||
return React5.cloneElement(child, { | ||
shape: field == null ? void 0 : field.shape, | ||
fieldType: field == null ? void 0 : field.fieldType, | ||
label: field == null ? void 0 : field.label, | ||
placeholder: field == null ? void 0 : field.placeholder, | ||
required: field == null ? void 0 : field.required, | ||
options: field == null ? void 0 : field.options, | ||
value: field == null ? void 0 : field.value, | ||
errors: field == null ? void 0 : field.errors, | ||
hidden: field == null ? void 0 : field.hidden, | ||
multiline: field == null ? void 0 : field.multiline, | ||
...child.props, | ||
autoFocus | ||
}); | ||
} else if (child.type === Errors) { | ||
if (!child.props.children && !(globalErrors == null ? void 0 : globalErrors.length)) | ||
return null; | ||
if (child.props.children || !(globalErrors == null ? void 0 : globalErrors.length)) { | ||
return React5.cloneElement(child, { | ||
role: "alert", | ||
...child.props | ||
}); | ||
} | ||
return React5.cloneElement(child, { | ||
...props, | ||
children: [ | ||
beforeChildren, | ||
fields.map((field) => renderField({ Field, ...field })), | ||
(globalErrors == null ? void 0 : globalErrors.length) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Errors, { | ||
role: "alert", | ||
children: globalErrors.map((error) => /* @__PURE__ */ React5.createElement(Error2, { | ||
key: error | ||
}, error)), | ||
...child.props | ||
}); | ||
} else if (child.type === Button) { | ||
return React5.cloneElement(child, { | ||
children: globalErrors.map((error) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Error2, { | ||
children: error | ||
}, error)) | ||
}), | ||
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Button, { | ||
disabled, | ||
children: buttonLabel, | ||
...child.props | ||
}); | ||
} else { | ||
return child; | ||
} | ||
})); | ||
children: buttonLabel | ||
}) | ||
] | ||
}); | ||
} | ||
return /* @__PURE__ */ React5.createElement(Component, { | ||
method, | ||
onSubmit, | ||
...props | ||
}, beforeChildren, fields.map((field) => renderField({ Field, ...field })), (globalErrors == null ? void 0 : globalErrors.length) && /* @__PURE__ */ React5.createElement(Errors, { | ||
role: "alert" | ||
}, globalErrors.map((error) => /* @__PURE__ */ React5.createElement(Error2, { | ||
key: error | ||
}, error))), /* @__PURE__ */ React5.createElement(Button, { | ||
disabled | ||
}, buttonLabel)); | ||
return Form; | ||
} | ||
// src/formAction.server.ts | ||
var import_server_runtime = require("@remix-run/server-runtime"); | ||
// src/mutations.ts | ||
var import_domain_functions = require("domain-functions"); | ||
var import_domain_functions2 = require("domain-functions"); | ||
async function getFormValues(request, schema) { | ||
@@ -715,4 +795,6 @@ const shape = objectFromSchema(schema).shape; | ||
errors: { | ||
...(0, import_domain_functions2.errorMessagesForSchema)(result.inputErrors, schema), | ||
_global: result.errors.length || result.environmentErrors.length ? [...result.errors, ...result.environmentErrors].map((error) => error.message) : void 0 | ||
...(0, import_domain_functions.errorMessagesForSchema)(result.inputErrors, schema), | ||
_global: result.errors.length || result.environmentErrors.length ? [...result.errors, ...result.environmentErrors].map( | ||
(error) => error.message | ||
) : void 0 | ||
}, | ||
@@ -723,39 +805,45 @@ values | ||
} | ||
async function formAction({ | ||
request, | ||
schema, | ||
mutation, | ||
environment, | ||
beforeAction, | ||
beforeSuccess, | ||
successPath | ||
function createFormAction({ | ||
redirect, | ||
json | ||
}) { | ||
if (beforeAction) { | ||
const beforeActionResponse = await beforeAction(request); | ||
if (beforeActionResponse) | ||
return beforeActionResponse; | ||
} | ||
const result = await performMutation({ | ||
async function formAction({ | ||
request, | ||
schema, | ||
mutation, | ||
environment | ||
}); | ||
if (result.success) { | ||
if (beforeSuccess) { | ||
const beforeSuccessResponse = await beforeSuccess(request); | ||
if (beforeSuccessResponse) | ||
return beforeSuccessResponse; | ||
environment, | ||
beforeAction, | ||
beforeSuccess, | ||
successPath | ||
}) { | ||
if (beforeAction) { | ||
const beforeActionResponse = await beforeAction(request); | ||
if (beforeActionResponse) | ||
return beforeActionResponse; | ||
} | ||
const path = typeof successPath === "function" ? successPath(result.data) : successPath; | ||
return path ? (0, import_server_runtime.redirect)(path) : (0, import_server_runtime.json)(result.data); | ||
} else { | ||
return (0, import_server_runtime.json)({ errors: result.errors, values: result.values }); | ||
const result = await performMutation({ | ||
request, | ||
schema, | ||
mutation, | ||
environment | ||
}); | ||
if (result.success) { | ||
if (beforeSuccess) { | ||
const beforeSuccessResponse = await beforeSuccess(request); | ||
if (beforeSuccessResponse) | ||
return beforeSuccessResponse; | ||
} | ||
const path = typeof successPath === "function" ? successPath(result.data) : successPath; | ||
return path ? redirect(path) : json(result.data); | ||
} else { | ||
return json({ errors: result.errors, values: result.values }); | ||
} | ||
} | ||
return formAction; | ||
} | ||
// Annotate the CommonJS export names for ESM import in node: | ||
0 && (module.exports = { | ||
Form, | ||
formAction, | ||
createForm, | ||
createFormAction, | ||
performMutation | ||
}); |
{ | ||
"name": "remix-forms", | ||
"version": "0.19.0", | ||
"description": "Magically create forms + actions in Remix!", | ||
"version": "1.0.0", | ||
"description": "The full-stack form library for Remix and React Router", | ||
"main": "./dist/index.js", | ||
@@ -9,3 +9,3 @@ "module": "./dist/index.mjs", | ||
"files": [ | ||
"*", | ||
"README.md", | ||
"./dist/*" | ||
@@ -35,26 +35,22 @@ ], | ||
"lint": "eslint *.ts*", | ||
"tsc": "tsc", | ||
"test": "vitest run" | ||
}, | ||
"peerDependencies": { | ||
"@remix-run/react": ">=1.2", | ||
"@remix-run/server-runtime": ">=1.2", | ||
"domain-functions": ">=1.0.0", | ||
"react": ">=16.8", | ||
"react-hook-form": ">=7.27", | ||
"domain-functions": ">=1.0.0", | ||
"zod": ">=3.12" | ||
}, | ||
"devDependencies": { | ||
"@remix-run/dev": "^1.4.1", | ||
"@remix-run/node": "^1.4.1", | ||
"@remix-run/react": "^1.4.1", | ||
"@types/react": "^17.0.37", | ||
"@types/react-dom": "^17.0.11", | ||
"@types/react": ">=16.8", | ||
"@types/react-dom": ">=16.8", | ||
"c8": "^7.11.2", | ||
"eslint": "^7.32.0", | ||
"eslint-config-custom": "*", | ||
"react": "^17.0.2", | ||
"react": ">=16.8", | ||
"tsconfig": "*", | ||
"tsup": "^6.1.3", | ||
"typescript": "^4.5.2", | ||
"vitest": "^0.10.0" | ||
"typescript": "~4.5.5", | ||
"vitest": "~0.10.0" | ||
}, | ||
@@ -61,0 +57,0 @@ "dependencies": { |
Sorry, the diff of this file is not supported yet
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
5
10
0
59452
5
1750