form-atoms
Version:
Form primitives for Jotai
1,005 lines (1,001 loc) • 39.6 kB
TypeScript
import * as React from 'react';
import { Atom, WritableAtom, Getter, Setter, ExtractAtomArgs, ExtractAtomValue, createStore } from 'jotai';
export { Provider } from 'jotai';
import { RESET } from 'jotai/utils';
export { RESET } from 'jotai/utils';
/**
* A React component that renders form atoms and their fields in an isolated
* scope using a Jotai Provider.
*
* @param {FormProps<Fields>} props - Component props
*/
declare function Form<Fields extends FormFields>(props: FormProps<Fields>): React.JSX.Element;
/**
* A React component that renders field atoms with initial values. This is
* most useful for fields that are rendered as native HTML elements because
* the props can unpack directly into the underlying component.
*
* @param {FieldProps<Value>} props - Component props
*/
declare function InputField<Type extends React.HTMLInputTypeAttribute, Value extends InputFieldValueForType<Type> = InputFieldValueForType<Type>>(props: InputFieldProps<Type, Value>): JSX.Element;
/**
* A React component that renders field atoms with initial values. This is
* most useful for fields that are rendered as native HTML elements because
* the props can unpack directly into the underlying component.
*
* @param {FieldProps<Value>} props - Component props
*/
declare function SelectField<Value extends string = string, Multiple extends Readonly<boolean> = false>(props: SelectFieldProps<Value, Multiple>): JSX.Element;
/**
* A React component that renders field atoms with initial values. This is
* most useful for fields that are rendered as native HTML elements because
* the props can unpack directly into the underlying component.
*
* @param {FieldProps<Value>} props - Component props
*/
declare function TextareaField<Value extends string>(props: TextareaFieldProps<Value>): JSX.Element;
/**
* A React component that renders field atoms with initial values. This is
* most useful for fields that aren't rendered as native HTML elements.
*
* @param {FieldProps<Value>} props - Component props
*/
declare function Field<Value>(props: FieldProps<Value>): JSX.Element;
/**
* An atom that derives its state fields atoms and allows you to submit,
* validate, and reset your form.
*
* @param {FormFields} fields - An object containing field atoms to
* be included in the form. Field atoms can be deeply nested in
* objects and arrays.
* @returns The `formAtom` function returns a Jotai `Atom`
* comprised of other atoms for managing the state of the form.
*/
declare function formAtom<Fields extends FormFields>(fields: Fields): FormAtom<Fields>;
/**
* A hook that returns an object that contains the `fieldAtoms` and actions to
* validate, submit, and reset the form.
*
* @param {FormAtom<FormFields>} formAtom - The atom that stores the form state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns A set of functions that can be used to interact
* with the form.
*/
declare function useForm<Fields extends FormFields>(formAtom: FormAtom<Fields>, options?: UseAtomOptions): UseForm<Fields>;
/**
* A hook that returns the primary state of the form atom including values, errors,
* submit and validation status, as well as the `fieldAtoms`. Note that this
* hook will cuase its parent component to re-render any time those states
* change, so it can be useful to use more targeted state hooks like
* `useFormStatus`.
*
* @param {FormAtom<FormFields>} formAtom - The atom that stores the form state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
*/
declare function useFormState<Fields extends FormFields>(formAtom: FormAtom<Fields>, options?: UseAtomOptions): UseFormState<Fields>;
/**
* A hook that returns a set of actions that can be used to update the state
* of the form atom. This includes updating fields, submitting, resetting,
* and validating the form.
*
* @param {FormAtom<FormFields>} formAtom - The atom that stores the form state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
*/
declare function useFormActions<Fields extends FormFields>(formAtom: FormAtom<Fields>, options?: UseAtomOptions): UseFormActions<Fields>;
/**
* A hook that returns the errors of the form atom.
*
* @param {FormAtom<FormFields>} formAtom - The atom that stores the form data.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns The errors of the form.
*/
declare function useFormErrors<Fields extends FormFields>(formAtom: FormAtom<Fields>, options?: UseAtomOptions): UseFormErrors<Fields>;
/**
* A hook that returns the values of the form atom
*
* @param {FormAtom<FormFields>} formAtom - The atom that stores the form state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns The values of the form.
*/
declare function useFormValues<Fields extends FormFields>(formAtom: FormAtom<Fields>, options?: UseAtomOptions): UseFormValues<Fields>;
/**
* A hook that returns the `submitStatus` and `validateStatus` of
* the form atom.
*
* @param {FormAtom<FormFields>} formAtom - The atom that stores the form state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns An object containing the `submitStatus` and
* `validateStatus` of the form
*/
declare function useFormStatus<Fields extends FormFields>(formAtom: FormAtom<Fields>, options?: UseAtomOptions): UseFormStatus;
/**
* A hook that returns a callback for handling form submission.
*
* @param {FormAtom<FormFields>} formAtom - The atom that stores the form state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns A callback for handling form submission. The callback
* takes the form values as an argument and returs an additional callback
* that invokes `event.preventDefault()` if it receives an event as its argument.
*/
declare function useFormSubmit<Fields extends FormFields>(formAtom: FormAtom<Fields>, options?: UseAtomOptions): UseFormSubmit<Fields>;
/**
* An atom that represents a field in a form. It manages state for the field,
* including the name, value, errors, dirty, validation, and touched state.
*
* @param {FieldAtomConfig<Value>} config - The initial state and configuration of the field.
* @returns A FieldAtom.
*/
declare function fieldAtom<Value>(config: FieldAtomConfig<Value>): FieldAtom<Value>;
/**
* A hook that returns a set of actions that can be used to interact with the
* field atom state.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns A set of actions that can be used to interact with the field atom.
*/
declare function useFieldActions<Value>(fieldAtom: FieldAtom<Value>, options?: UseAtomOptions): UseFieldActions<Value>;
/**
* A hook that returns a set of props that can be destructured
* directly into an `<input>` element.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseInputFieldPropsOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns A set of props that can be destructured directly into an `<input>`.
*/
declare function useInputFieldProps<Type extends React.HTMLInputTypeAttribute, Value extends InputFieldValueForType<Type> = InputFieldValueForType<Type>>(fieldAtom: FieldAtom<Value>, options?: UseInputFieldPropsOptions<Type>): UseInputFieldProps<Type>;
declare const numberTypes: Set<"number" | "range">;
declare const dateTypes: Set<"time" | "date" | "datetime-local" | "month" | "week">;
declare const fileTypes: Set<"file">;
/**
* A hook that returns a set of props that can be destructured
* directly into an `<textarea>` element.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseTextareaFieldPropsOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns A set of props that can be destructured directly into an `<textarea>`.
*/
declare function useTextareaFieldProps<Value extends string>(fieldAtom: FieldAtom<Value>, options?: UseTextareaFieldPropsOptions): UseTextareaFieldProps<Value>;
/**
* A hook that returns a set of props that can be destructured
* directly into an `<select>` element.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseSelectFieldPropsOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns A set of props that can be destructured directly into an `<select>`.
*/
declare function useSelectFieldProps<Value extends string = string, Multiple extends Readonly<boolean> = false>(fieldAtom: FieldAtom<Multiple extends true ? Value[] : Value>, options?: UseSelectFieldPropsOptions<Multiple>): UseSelectFieldProps<Value, Multiple>;
/**
* A hook that returns the state of a field atom. This includes the field's
* value, whether it has been touched, whether it is dirty, the validation status,
* and any errors.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns The state of the field atom.
*/
declare function useFieldState<Value>(fieldAtom: FieldAtom<Value>, options?: UseAtomOptions): UseFieldState<Value>;
/**
* A hook that returns the value of a field atom.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns The value of the field atom.
*/
declare function useFieldValue<Value>(fieldAtom: FieldAtom<Value>, options?: UseAtomOptions): UseFieldValue<Value>;
/**
* A hook that returns the errors of a field atom.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
* @returns The errors of the field atom.
*/
declare function useFieldErrors<Value>(fieldAtom: FieldAtom<Value>, options?: UseAtomOptions): UseFieldErrors<Value>;
/**
* Sets the initial value of a field atom.
*
* @param {FieldAtom<any>} fieldAtom - The atom that you want to use to store the value.
* @param {Value} initialValue - The initial value of the field or `RESET` to reset the initial value.
* @param {UseAtomOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
*/
declare function useFieldInitialValue<Value>(fieldAtom: FieldAtom<Value>, initialValue?: Value | typeof RESET, options?: UseFieldInitialValueOptions<Value>): UseFieldInitialValue;
/**
* A hook that returns `state` and `actions` of a field atom from
* `useFieldState` and `useFieldActions`.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseFieldOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
*/
declare function useField<Value>(fieldAtom: FieldAtom<Value>, options?: UseFieldOptions<Value>): UseField<Value>;
/**
* A hook that returns `props`, `state`, and `actions` of a field atom from
* `useInputFieldProps`, `useFieldState`, and `useFieldActions`.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseInputFieldOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
*/
declare function useInputField<Type extends React.HTMLInputTypeAttribute, Value extends InputFieldValueForType<Type> = InputFieldValueForType<Type>>(fieldAtom: FieldAtom<Value>, options?: UseInputFieldOptions<Type, Value>): UseInputField<Type, Value>;
/**
* A hook that returns `props`, `state`, and `actions` of a field atom from
* `useInputFieldProps`, `useFieldState`, and `useFieldActions`.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseInputFieldOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
*/
declare function useSelectField<Multiple extends Readonly<boolean> = false, Value extends string = string>(fieldAtom: FieldAtom<Multiple extends true ? Value[] : Value>, options?: UseSelectFieldOptions<Value, Multiple>): UseSelectField<Value, Multiple>;
/**
* A hook that returns `props`, `state`, and `actions` of a field atom from
* `useTextareaFieldProps`, `useFieldState`, and `useFieldActions`.
*
* @param {FieldAtom<any>} fieldAtom - The atom that stores the field's state.
* @param {UseTextareaFieldOptions} options - Options to pass to the underlying `useAtomValue`
* and `useSetAtom` hooks.
*/
declare function useTextareaField<Value extends string>(fieldAtom: FieldAtom<Value>, options?: UseTextareaFieldOptions<Value>): UseTextareaField<Value>;
/**
* A function that walks through an object containing nested field atoms
* and calls a visitor function for each atom it finds.
*
* @param {FormFields} fields - An object containing nested field atoms
* @param visitor - A function that will be called for each field atom. You can
* exit early by returning `false` from the function.
* @param path - The base path of the field atom.
*/
declare function walkFields<Fields extends FormFields>(fields: Fields, visitor: (field: FieldAtom<any> | null, path: string[]) => void | false, options: {
includeEmptyArrays: true;
}, path?: string[]): void;
declare function walkFields<Fields extends FormFields>(fields: Fields, visitor: (field: FieldAtom<any>, path: string[]) => void | false, options: {
includeEmptyArrays: false;
}, path?: string[]): void;
declare function walkFields<Fields extends FormFields>(fields: Fields, visitor: (field: FieldAtom<any>, path: string[]) => void | false, options?: {
includeEmptyArrays?: boolean;
}, path?: string[]): void;
type InputFieldProps<Type extends React.HTMLInputTypeAttribute, Value extends InputFieldValueForType<Type> = InputFieldValueForType<Type>> = (UseInputFieldOptions<Type, Value> & {
/**
* A field atom
*/
atom: FieldAtom<Value>;
/**
* The initial value of the field
*/
initialValue?: Value;
}) & ({
/**
* A render prop
*
* @param props - Props that can be directly unpacked into a native HTML `<input>` element
* @param state - The state of the field atom
* @param actions - The actions of the field atom
*/
render(props: UseInputFieldProps<Type>, state: UseFieldState<Value>, actions: UseFieldActions<Value>): JSX.Element;
} | {
/**
* A React component
*/
component: "input" | React.ComponentType<UseInputFieldProps<Type>>;
});
type SelectFieldProps<Value extends string, Multiple extends Readonly<boolean> = false> = (UseSelectFieldOptions<Value, Multiple> & {
/**
* A field atom
*/
atom: FieldAtom<Multiple extends true ? Value[] : Value>;
/**
* The initial value of the field
*/
initialValue?: Multiple extends true ? Value[] : Value;
}) & ({
/**
* A render prop
*
* @param props - Props that can be directly unpacked into a native HTML `<select>` element
* @param state - The state of the field atom
* @param actions - The actions of the field atom
*/
render(props: UseSelectFieldProps<Value, Multiple>, state: UseFieldState<Multiple extends true ? Value[] : Value>, actions: UseFieldActions<Multiple extends true ? Value[] : Value>): JSX.Element;
} | {
/**
* A React component
*/
component: "select" | React.ComponentType<UseSelectFieldProps<Value, Multiple>>;
});
type TextareaFieldProps<Value extends string> = (UseTextareaFieldOptions<Value> & {
/**
* A field atom
*/
atom: FieldAtom<Value>;
/**
* The initial value of the field
*/
initialValue?: Value;
}) & ({
/**
* A render prop
*
* @param props - Props that can be directly unpacked into a native HTML `<textarea>` element
* @param state - The state of the field atom
* @param actions - The actions of the field atom
*/
render(props: UseTextareaFieldProps<Value>, state: UseFieldState<Value>, actions: UseFieldActions<Value>): JSX.Element;
} | {
/**
* A React component
*/
component: "textarea" | React.ComponentType<UseTextareaFieldProps<Value>>;
});
type FieldProps<Value> = (UseAtomOptions & {
/**
* A field atom
*/
atom: FieldAtom<Value>;
/**
* The initial value of the field
*/
initialValue?: Value;
}) & ({
/**
* A render prop
*
* @param state - The state of the field atom
* @param actions - The actions of the field atom
*/
render(state: UseFieldState<Value>, actions: UseFieldActions<Value>): JSX.Element;
} | {
/**
* A React component
*/
component: React.ComponentType<{
state: UseFieldState<Value>;
actions: UseFieldActions<Value>;
}>;
});
type FormProps<Fields extends FormFields> = {
/**
* A form atom
*/
atom: FormAtom<Fields>;
/**
* When using atoms with a scope, the provider with the same scope will be used.
* The recommendation for the scope value is a unique symbol. The primary use case
* of scope is for library usage.
*/
store?: AtomStore;
} & ({
/**
* A render prop
*
* @param props - Props returned from a `useForm` hook
*/
render(props: UseForm<Fields>): JSX.Element;
} | {
/**
* A React component.
*/
component: React.ComponentType<UseForm<Fields>>;
});
/**
* A form submission status
*/
type SubmitStatus = "idle" | "submitting" | "submitted";
/**
* A form and field validation status
*/
type ValidateStatus = "validating" | "valid" | "invalid";
/**
* Event types that a field atom may validate against
*/
type ValidateOn = "user" | "blur" | "change" | "touch" | "submit";
type FieldAtom<Value> = Atom<{
/**
* An atom containing the field's name
*/
name: WritableAtom<string | undefined, [
string | undefined | typeof RESET
], void>;
/**
* An atom containing the field's value
*/
value: WritableAtom<Value, [
Value | typeof RESET | ((prev: Value) => Value)
], void>;
/**
* An atom containing the field's touched status
*/
touched: WritableAtom<boolean, [
boolean | typeof RESET | ((prev: boolean) => boolean)
], void>;
/**
* An atom containing the field's dirty status
*/
dirty: Atom<boolean>;
/**
* A write-only atom for validating the field's value
*/
validate: WritableAtom<null, [] | [ValidateOn], void>;
/**
* An atom containing the field's validation status
*/
validateStatus: WritableAtom<ValidateStatus, [ValidateStatus], void>;
/**
* An atom containing the field's validation errors
*/
errors: WritableAtom<string[], [
string[] | ((value: string[]) => string[])
], void>;
/**
* A write-only atom for resetting the field atoms to their
* initial states.
*/
reset: WritableAtom<null, [], void>;
/**
* An atom containing a reference to the `HTMLElement` the field
* is bound to.
*/
ref: WritableAtom<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null, [
HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null | ((value: HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null) => HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | null)
], void>;
/**
* An atom containing the field's initial value
*/
_initialValue: WritableAtom<Value | undefined, [
Value | undefined | typeof RESET | ((prev: Value | undefined) => Value | undefined)
], void>;
_validateCount: WritableAtom<number, [
number | ((current: number) => number)
], void>;
_validateCallback?: Validate<Value>;
}>;
type FormAtom<Fields extends FormFields> = Atom<{
/**
* An atom containing an object of nested field atoms
*/
fields: WritableAtom<Fields, [
Fields | typeof RESET | ((prev: Fields) => Fields)
], void>;
/**
* An read-only atom that derives the form's values from
* its nested field atoms.
*/
values: Atom<FormFieldValues<Fields>>;
/**
* An read-only atom that derives the form's errors from
* its nested field atoms.
*/
errors: Atom<FormFieldErrors<Fields>>;
/**
* A read-only atom that returns `true` if any of the fields in
* the form are dirty.
*/
dirty: Atom<boolean>;
/**
* A read-only atom derives the touched state of its nested field atoms.
*/
touchedFields: Atom<TouchedFields<Fields>>;
/**
* A write-only atom that resets the form's nested field atoms
*/
reset: WritableAtom<null, [], void>;
/**
* A write-only atom that validates the form's nested field atoms
*/
validate: WritableAtom<null, [] | [ValidateOn], void>;
/**
* A read-only atom that derives the form's validation status
*/
validateStatus: Atom<ValidateStatus>;
/**
* A write-only atom for submitting the form
*/
submit: WritableAtom<null, [
(value: FormFieldValues<Fields>) => void | Promise<void>
], void>;
/**
* A read-only atom that reads the number of times the form has
* been submitted
*/
submitCount: Atom<number>;
/**
* An atom that contains the form's submission status
*/
submitStatus: WritableAtom<SubmitStatus, [SubmitStatus], void>;
_validateFields: (get: Getter, set: Setter, event: ValidateOn) => Promise<void>;
}>;
/**
* An object containing nested field atoms
*/
type FormFields = {
[key: string | number]: FieldAtom<any> | FormFields | FormFields[] | FieldAtom<any>[];
};
/**
* A utility type for inferring the value types of a form's nested field atoms.
*
* @example
* ```ts
* const nameForm = formAtom({
* name: fieldAtom({ value: '' }),
* })
*
* type NameFormValues = FormValues<typeof nameForm>
* ```
*/
type FormValues<Form extends FormAtom<any>> = Form extends FormAtom<infer Fields> ? FormFieldValues<Fields> : never;
/**
* A utility type for inferring the error types of a form's nested field atoms.
*
* @example
* ```ts
* const nameForm = formAtom({
* name: fieldAtom({ value: '' }),
* })
*
* type NameFormErrors = FormErrors<typeof nameForm>
* ```
*/
type FormErrors<Form extends FormAtom<any>> = Form extends FormAtom<infer Fields> ? FormFieldErrors<Fields> : never;
/**
* An object containing the values of a form's nested field atoms
*/
type FormFieldValues<Fields extends FormFields> = Flatten<{
[Key in keyof Fields]: Fields[Key] extends FieldAtom<infer Value> ? Value : Fields[Key] extends FormFields ? FormFieldValues<Fields[Key]> : Fields[Key] extends Array<infer Item> ? Item extends FieldAtom<infer Value> ? Value[] : Item extends FormFields ? FormFieldValues<Item>[] : never : never;
}>;
/**
* An object containing the errors of a form's nested field atoms
*/
type FormFieldErrors<Fields extends FormFields> = Flatten<{
[Key in keyof Fields]: Fields[Key] extends FieldAtom<any> ? string[] : Fields[Key] extends FormFields ? FormFieldErrors<Fields[Key]> : Fields[Key] extends Array<infer Item> ? Item extends FieldAtom<any> ? string[][] : Item extends FormFields ? FormFieldErrors<Item>[] : never : never;
}>;
/**
* An object containing the errors of a form's touched fields
*/
type TouchedFields<Fields extends FormFields> = Flatten<{
[Key in keyof Fields]: Fields[Key] extends FieldAtom<any> ? boolean : Fields[Key] extends FormFields ? FormFieldValues<Fields[Key]> : Fields[Key] extends Array<infer Item> ? Item extends FieldAtom<any> ? boolean[] : Item extends FormFields ? TouchedFields<Item>[] : never : never;
}>;
type UseForm<Fields extends FormFields> = {
/**
* An object containing the values of a form's nested field atoms
*/
fieldAtoms: Fields;
/**
* A function for handling form submissions.
*
* @param handleSubmit - A function that is called with the form's values
* when the form is submitted
*/
submit(handleSubmit: (values: FormFieldValues<Fields>) => void | Promise<void>): (event?: React.FormEvent<HTMLFormElement>) => void;
/**
* A function that validates the form's nested field atoms with a
* `"user"` validation event.
*/
validate(): void;
/**
* A function that resets the form's nested field atoms to their
* initial states.
*/
reset(event?: React.FormEvent<HTMLFormElement>): void;
};
type UseFormStatus = {
/**
* The validation status of the form
*/
validateStatus: ValidateStatus;
/**
* The submission status of the form
*/
submitStatus: SubmitStatus;
};
type UseFormSubmit<Fields extends FormFields> = {
(values: (value: FormFieldValues<Fields>) => void | Promise<void>): (event?: React.FormEvent<HTMLFormElement>) => void;
};
type UseFormState<Fields extends FormFields> = {
/**
* An object containing the form's nested field atoms
*/
fieldAtoms: Fields;
/**
* An object containing the values of a form's nested field atoms
*/
values: FormFieldValues<Fields>;
/**
* An object containing the errors of a form's nested field atoms
*/
errors: FormFieldErrors<Fields>;
/**
* `true` if any of the fields in the form are dirty.
*/
dirty: boolean;
/**
* An object containing the touched state of the form's nested field atoms.
*/
touchedFields: TouchedFields<Fields>;
/**
* The number of times a form has been submitted
*/
submitCount: number;
/**
* The validation status of the form
*/
validateStatus: ValidateStatus;
/**
* The submission status of the form
*/
submitStatus: SubmitStatus;
};
type UseFormActions<Fields extends FormFields> = {
/**
* A function for adding/removing fields from the form.
*
* @param fields - An object containing the form's nested field atoms or
* a callback that receives the current fields and returns the next
* fields.
*/
updateFields(fields: ExtractAtomArgs<ExtractAtomValue<FormAtom<Fields>>["fields"]>[0]): void;
/**
* A function for handling form submissions.
*
* @param handleSubmit - A function that is called with the form's values
* when the form is submitted
*/
submit(handleSubmit: (values: FormFieldValues<Fields>) => void | Promise<void>): (event?: React.FormEvent<HTMLFormElement>) => void;
/**
* A function that validates the form's nested field atoms with a
* `"user"` validation event.
*/
validate(): void;
/**
* A function that resets the form's nested field atoms to their
* initial states.
*/
reset(): void;
};
type UseFormErrors<Fields extends FormFields> = FormFieldErrors<Fields>;
type UseFormValues<Fields extends FormFields> = FormFieldValues<Fields>;
type UseField<Value> = {
/**
* Actions for managing the state of the field
*/
actions: UseFieldActions<Value>;
/**
* The current state of the field
*/
state: UseFieldState<Value>;
};
type UseInputField<Type extends React.HTMLInputTypeAttribute, Value extends InputFieldValueForType<Type> = InputFieldValueForType<Type>> = {
/**
* `<input>` props for the field
*/
props: UseInputFieldProps<Type>;
/**
* Actions for managing the state of the field
*/
actions: UseFieldActions<Value>;
/**
* The current state of the field
*/
state: UseFieldState<Value>;
};
type UseInputFieldProps<Type extends React.HTMLInputTypeAttribute> = {
/**
* The name of the field if there is one
*/
name: string | undefined;
/**
* The value of the field
*/
value: Type extends DateType ? string : Type extends NumberType ? number | string : Type extends FileType ? undefined : string;
/**
* The type of the field
*
* @default "text"
*/
type: Type;
/**
* A WAI-ARIA property that tells a screen reader whether the
* field is invalid
*/
"aria-invalid": boolean;
/**
* A React callback ref that is used to bind the field atom to
* an `<input>` element so that it can be read and focused.
*/
ref: React.RefCallback<HTMLInputElement>;
onBlur(event: React.FormEvent<HTMLInputElement>): void;
onChange(event: React.ChangeEvent<HTMLInputElement>): void;
};
type UseInputFieldPropsOptions<Type extends React.HTMLInputTypeAttribute> = UseAtomOptions & {
/**
* The type of the `<input>` element
*
* @default "text"
* @see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#%3Cinput%3E_types
*/
type?: Type;
};
type DateType = typeof dateTypes extends Set<infer T> ? T : never;
type NumberType = typeof numberTypes extends Set<infer T> ? T : never;
type FileType = typeof fileTypes extends Set<infer T> ? T : never;
/**
* A utility type that maps input types to their corresponding
* value types.
*/
type InputFieldValueForType<Type extends React.HTMLInputTypeAttribute> = Type extends NumberType ? number | null : Type extends DateType ? Date | null : Type extends FileType ? FileList | null : string;
type UseSelectField<Value extends string, Multiple extends Readonly<boolean> = false> = {
/**
* `<input>` props for the field
*/
props: UseSelectFieldProps<Value, Multiple>;
/**
* Actions for managing the state of the field
*/
actions: UseFieldActions<Multiple extends true ? Value[] : Value>;
/**
* The current state of the field
*/
state: UseFieldState<Multiple extends true ? Value[] : Value>;
};
type UseSelectFieldProps<Value extends string, Multiple extends Readonly<boolean> = false> = {
/**
* The name of the field if there is one
*/
name: string | undefined;
/**
* The value of the field
*/
value: Multiple extends true ? Value[] : Value;
/**
* Whether the field is a multiple select
*/
multiple?: Multiple;
/**
* A WAI-ARIA property that tells a screen reader whether the
* field is invalid
*/
"aria-invalid": boolean;
/**
* A React callback ref that is used to bind the field atom to
* an `<input>` element so that it can be read and focused.
*/
ref: React.RefCallback<HTMLSelectElement>;
onBlur(event: React.FormEvent<HTMLSelectElement>): void;
onChange(event: React.ChangeEvent<HTMLSelectElement>): void;
};
type UseSelectFieldPropsOptions<Multiple extends Readonly<boolean>> = UseAtomOptions & {
/**
* Whether the field is a multiple select
*/
multiple?: Multiple;
};
type UseTextareaField<Value extends string> = {
/**
* `<input>` props for the field
*/
props: UseTextareaFieldProps<Value>;
/**
* Actions for managing the state of the field
*/
actions: UseFieldActions<Value>;
/**
* The current state of the field
*/
state: UseFieldState<Value>;
};
type UseTextareaFieldProps<Value extends string> = {
/**
* The name of the field if there is one
*/
name: string | undefined;
/**
* The value of the field
*/
value: Value;
/**
* A WAI-ARIA property that tells a screen reader whether the
* field is invalid
*/
"aria-invalid": boolean;
/**
* A React callback ref that is used to bind the field atom to
* an `<input>` element so that it can be read and focused.
*/
ref: React.RefCallback<HTMLTextAreaElement>;
onBlur(event: React.FormEvent<HTMLTextAreaElement>): void;
onChange(event: React.ChangeEvent<HTMLTextAreaElement>): void;
};
type UseTextareaFieldPropsOptions = UseAtomOptions;
type UseFieldActions<Value> = {
/**
* A function that validates the field's value with a `"user"` validation
* event.
*/
validate(): void;
/**
* A function for changing the value of a field. This will trigger a `"change"`
* validation event.
*
* @param {Value} value - The new value of the field
*/
setValue(value: ExtractAtomArgs<ExtractAtomValue<FieldAtom<Value>>["value"]>[0]): void;
/**
* A function for changing the touched state of a field. This will trigger a
* `"touch"` validation event.
*
* @param {boolean} touched - The new touched state of the field
*/
setTouched(touched: ExtractAtomArgs<ExtractAtomValue<FieldAtom<Value>>["touched"]>[0]): void;
/**
* A function for changing the error state of a field
*
* @param {string[]} errors - The new error state of the field
*/
setErrors(errors: ExtractAtomArgs<ExtractAtomValue<FieldAtom<Value>>["errors"]>[0]): void;
/**
* Focuses the field atom's `<input>` element if there is one bound to it.
*/
focus(): void;
/**
* Resets the field atom to its initial state.
*/
reset(): void;
};
type UseFieldState<Value> = {
/**
* The value of the field
*/
value: ExtractAtomValue<ExtractAtomValue<FieldAtom<Value>>["value"]>;
/**
* The touched state of the field
*/
touched: ExtractAtomValue<ExtractAtomValue<FieldAtom<Value>>["touched"]>;
/**
* The dirty state of the field. A field is "dirty" if it's value has
* been changed.
*/
dirty: ExtractAtomValue<ExtractAtomValue<FieldAtom<Value>>["dirty"]>;
/**
* The validation status of the field
*/
validateStatus: ExtractAtomValue<ExtractAtomValue<FieldAtom<Value>>["validateStatus"]>;
/**
* The error state of the field
*/
errors: ExtractAtomValue<ExtractAtomValue<FieldAtom<Value>>["errors"]>;
};
type UseFieldValue<Value> = Value;
type UseFieldErrors<Value> = UseFieldState<Value>["errors"];
type UseFieldInitialValue = void;
type UseFieldOptions<Value> = UseAtomOptions & {
initialValue?: Value;
};
type UseInputFieldOptions<Type extends React.HTMLInputTypeAttribute, Value extends InputFieldValueForType<Type> = InputFieldValueForType<Type>> = UseInputFieldPropsOptions<Type> & {
/**
* The initial value of the field
*/
initialValue?: Value;
};
type UseSelectFieldOptions<Value extends string = string, Multiple extends Readonly<boolean> = false> = UseSelectFieldPropsOptions<Multiple> & {
/**
* The initial value of the field
*/
initialValue?: Multiple extends true ? Value[] : Value;
};
type UseTextareaFieldOptions<Value extends string> = UseTextareaFieldPropsOptions & {
/**
* The initial value of the field
*/
initialValue?: Value;
};
type FieldAtomConfig<Value> = {
/**
* Optionally provide a name for the field that will be added
* to any attached `<input>` elements
*/
name?: string;
/**
* The initial value of the field
*/
value: Value;
/**
* The initial touched state of the field
*/
touched?: boolean;
/**
* Transform the value of the field each time `setValue` is
* called and before validation
*/
preprocess?: (value: Value) => Value;
/**
* A function that validates the value of the field any time
* one of its atoms changes. It must either return an array of
* string error messages or undefined. If it returns undefined,
* the validation is "skipped" and the current errors in state
* are retained.
*/
validate?: (state: {
/**
* A Jotai getter that can read other atoms
*/
get: Getter;
/**
* A Jotai setter that can write to atoms
*/
set: Setter;
/**
* The current value of the field
*/
value: Value;
/**
* The dirty state of the field
*/
dirty: boolean;
/**
* The touched state of the field
*/
touched: boolean;
/**
* The event that caused the validation. Either:
*
* - `"change"` - The value of the field has changed
* - `"touch"` - The field has been touched
* - `"blur"` - The field has been blurred
* - `"submit"` - The form has been submitted
* - `"user"` - A user/developer has triggered the validation
*/
event: ValidateOn;
}) => void | string[] | Promise<void | string[]>;
};
/**
* A utility type for easily typing validate functions
*/
type Validate<Value> = FieldAtomConfig<Value>["validate"];
/**
* A utility type for easily typing validate function configurations
*/
type ValidateConfig<Value> = Parameters<Exclude<FieldAtomConfig<Value>["validate"], undefined>>[0];
/**
* A Jotai store
*
* @see https://jotai.org/docs/api/core#createstore
*/
type AtomStore = ReturnType<typeof createStore>;
/**
* Options that are forwarded to the `useAtom`, `useAtomValue`,
* and `useSetAtom` hooks
*/
type UseAtomOptions = {
delay?: number;
/**
* Optionally provide a Jotai store to use for the atom.
*/
store?: AtomStore;
};
type UseFieldInitialValueOptions<Value> = UseAtomOptions & {
/**
* When fields are not dirty and their current value is not equal to
* their initial value, the initial value will be set as the current
* value.
*
* @default Object.is
*/
areEqual?: (a: Value | typeof RESET, b: Value | typeof RESET | undefined) => boolean;
};
type Flatten<T> = Identity<{
[K in keyof T]: T[K];
}>;
type Identity<T> = T;
export { type AtomStore, type DateType, Field, type FieldAtom, type FieldAtomConfig, type FieldProps, type FileType, Form, type FormAtom, type FormErrors, type FormFieldErrors, type FormFieldValues, type FormFields, type FormProps, type FormValues, InputField, type InputFieldProps, type InputFieldValueForType, type NumberType, SelectField, type SelectFieldProps, type SubmitStatus, TextareaField, type TextareaFieldProps, type TouchedFields, type UseAtomOptions, type UseField, type UseFieldActions, type UseFieldErrors, type UseFieldInitialValue, type UseFieldInitialValueOptions, type UseFieldOptions, type UseFieldState, type UseFieldValue, type UseForm, type UseFormActions, type UseFormState, type UseFormStatus, type UseFormSubmit, type UseInputField, type UseInputFieldOptions, type UseInputFieldProps, type UseInputFieldPropsOptions, type UseSelectField, type UseSelectFieldOptions, type UseSelectFieldProps, type UseSelectFieldPropsOptions, type UseTextareaField, type UseTextareaFieldOptions, type UseTextareaFieldProps, type UseTextareaFieldPropsOptions, type Validate, type ValidateConfig, type ValidateOn, type ValidateStatus, fieldAtom, formAtom, useField, useFieldActions, useFieldErrors, useFieldInitialValue, useFieldState, useFieldValue, useForm, useFormActions, useFormErrors, useFormState, useFormStatus, useFormSubmit, useFormValues, useInputField, useInputFieldProps, useSelectField, useSelectFieldProps, useTextareaField, useTextareaFieldProps, walkFields };