@conform-to/dom
Version:
A set of opinionated helpers built on top of the Constraint Validation API
161 lines (160 loc) • 6.6 kB
TypeScript
import { getFormAction, getFormEncType, getFormMethod } from './dom';
import { type Intent, type Submission, type SubmissionResult } from './submission';
type BaseCombine<T, K extends PropertyKey = T extends unknown ? keyof T : never> = T extends unknown ? T & Partial<Record<Exclude<K, keyof T>, never>> : never;
export type Combine<T> = {
[K in keyof BaseCombine<T>]: BaseCombine<T>[K];
};
export type DefaultValue<Schema> = Schema extends string | number | boolean | Date | bigint | null | undefined ? Schema | string | null | undefined : Schema extends File ? null | undefined : Schema extends Array<infer Item> ? Array<DefaultValue<Item>> | null | undefined : Schema extends Record<string, any> ? {
[Key in keyof Schema]?: DefaultValue<Schema[Key]>;
} | null | undefined : string | null | undefined;
export type FormValue<Schema> = Schema extends string | number | boolean | Date | bigint | null | undefined ? string | undefined : Schema extends File ? File | undefined : Schema extends File[] ? File | Array<File> | undefined : Schema extends Array<infer Item> ? string | Array<FormValue<Item>> | undefined : Schema extends Record<string, any> ? {
[Key in keyof Schema]?: FormValue<Schema[Key]>;
} | undefined : unknown;
declare const error: unique symbol;
declare const field: unique symbol;
declare const form: unique symbol;
export type FormId<Schema extends Record<string, unknown> = Record<string, unknown>, Error = string[]> = string & {
[error]?: Error;
[form]?: Schema;
};
export type FieldName<FieldSchema, FormSchema extends Record<string, unknown> = Record<string, unknown>, Error = string[]> = string & {
[field]?: FieldSchema;
[error]?: Error;
[form]?: FormSchema;
};
export type Constraint = {
required?: boolean;
minLength?: number;
maxLength?: number;
min?: string | number;
max?: string | number;
step?: string | number;
multiple?: boolean;
pattern?: string;
};
export type FormMeta<FormError> = {
formId: string;
isValueUpdated: boolean;
pendingIntents: Intent[];
submissionStatus?: 'error' | 'success';
defaultValue: Record<string, unknown>;
initialValue: Record<string, unknown>;
value: Record<string, unknown>;
error: Record<string, FormError>;
constraint: Record<string, Constraint>;
key: Record<string, string | undefined>;
validated: Record<string, boolean>;
};
export type FormState<FormError> = Omit<FormMeta<FormError>, 'formId' | 'isValueUpdated'> & {
valid: Record<string, boolean>;
dirty: Record<string, boolean>;
};
export type FormOptions<Schema, FormError = string[], FormValue = Schema> = {
/**
* The id of the form.
*/
formId: string;
/**
* An object representing the initial value of the form.
*/
defaultValue?: DefaultValue<Schema>;
/**
* An object describing the constraint of each field
*/
constraint?: Record<string, Constraint>;
/**
* An object describing the result of the last submission
*/
lastResult?: SubmissionResult<FormError> | null | undefined;
/**
* Define when conform should start validation.
* Support "onSubmit", "onInput", "onBlur".
*
* @default "onSubmit"
*/
shouldValidate?: 'onSubmit' | 'onBlur' | 'onInput';
/**
* Define when conform should revalidate again.
* Support "onSubmit", "onInput", "onBlur".
*
* @default Same as shouldValidate, or "onSubmit" if shouldValidate is not provided.
*/
shouldRevalidate?: 'onSubmit' | 'onBlur' | 'onInput';
/**
* Define if conform should consider the field for dirty state.
* e.g. Excluding form fields that are not managed by Conform, such as CSRF token
*/
shouldDirtyConsider?: (name: string) => boolean;
/**
* A function to be called when the form should be (re)validated.
*/
onValidate?: (context: {
form: HTMLFormElement;
submitter: HTMLInputElement | HTMLButtonElement | null;
formData: FormData;
}) => Submission<Schema, FormError, FormValue>;
};
export type SubscriptionSubject = {
[key in 'error' | 'initialValue' | 'value' | 'key' | 'valid' | 'dirty']?: SubscriptionScope;
} & {
formId?: boolean;
status?: boolean;
pendingIntents?: boolean;
};
export type SubscriptionScope = {
prefix?: string[];
name?: string[];
};
export type ControlButtonProps = {
name: string;
value: string;
form: string;
formNoValidate: boolean;
};
export type FormContext<Schema extends Record<string, any> = any, FormError = string[], FormValue = Schema> = {
getFormId(): string;
submit(event: SubmitEvent): {
formData: FormData;
action: ReturnType<typeof getFormAction>;
encType: ReturnType<typeof getFormEncType>;
method: ReturnType<typeof getFormMethod>;
submission?: Submission<Schema, FormError, FormValue>;
};
onReset(event: Event): void;
onInput(event: Event): void;
onBlur(event: Event): void;
onUpdate(options: Partial<FormOptions<Schema, FormError, FormValue>>): void;
observe(): () => void;
runSideEffect(intents: Intent[]): void;
subscribe(callback: () => void, getSubject?: () => SubscriptionSubject | undefined): () => void;
getState(): FormState<FormError>;
getSerializedState(): string;
} & {
[Type in Intent['type']]: {} extends Extract<Intent, {
type: Type;
}>['payload'] ? (<FieldSchema = Schema>(payload?: Extract<Intent<FieldSchema>, {
type: Type;
}>['payload']) => void) & {
getButtonProps<FieldSchema = Schema>(payload?: Extract<Intent<FieldSchema>, {
type: Type;
}>['payload']): ControlButtonProps;
} : (<FieldSchema = Schema>(payload: Extract<Intent<FieldSchema>, {
type: Type;
}>['payload']) => void) & {
getButtonProps<FieldSchema = Schema>(payload: Extract<Intent<FieldSchema>, {
type: Type;
}>['payload']): ControlButtonProps;
};
};
export declare function createFormContext<Schema extends Record<string, any>, FormError = string[], FormValue = Schema>(options: FormOptions<Schema, FormError, FormValue>): FormContext<Schema, FormError, FormValue>;
/**
* Updates the DOM element with the provided value.
*
* @param element The form element to update
* @param options The options to update the form element
*/
export declare function updateFieldValue(element: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement, options: {
value?: string | string[];
defaultValue?: string | string[];
}): void;
export {};