Types & Events
Event Types
Section titled “Event Types”Form-Level Events (FormEventType)
Section titled “Form-Level Events (FormEventType)”type FormEventType = | 'field:valid' // FieldEventDetail | 'field:invalid' // FieldEventDetail | 'field:change' // FieldEventDetail | 'field:added' // FieldEventDetail | 'field:removed' // FieldEventDetail | 'form:valid' // FormEventDetail | 'form:invalid' // FormEventDetail | 'form:submit' // FormEventDetail | 'form:loading' // FormEventDetail | 'form:reset'; // FormEventDetailRegistry Events (RegistryEventType)
Section titled “Registry Events (RegistryEventType)”type RegistryEventType = 'form:registered' | 'form:unregistered';Field-Level Events (FieldControllerEventType)
Section titled “Field-Level Events (FieldControllerEventType)”type FieldControllerEventType = 'change' | 'valid' | 'invalid';Event Detail Types
Section titled “Event Detail Types”interface FieldEventDetail { formId: string; fieldName: string; state: FieldState;}
interface FormEventDetail { formId: string; state: FormState;}
interface FormLoadingStateDetail { formId: string; isSubmitting: boolean; submitter: HTMLElement | null; formEl: HTMLFormElement; state: FormState;}
interface FormLoadingStateOptions { attribute?: string; submitSelector?: string;}FieldControllerOptions
Section titled “FieldControllerOptions”interface FieldErrorRenderContext { message: string; index: number; errors: string[];}
interface FieldControllerOptions { validate?( value: string, rules: ValidatorRule[], defaultValidate: () => FieldValidationResult ): FieldValidationResult; onServerErrors?(errors: string[], fieldName: string): string[]; errorsSelector?: string; findErrorsElement?(field: FieldController): HTMLElement | null; renderError?(ctx: FieldErrorRenderContext, field: FieldController): string; errorsSeparator?: string; renderErrors?(errors: string[], ctx: FieldController): void;}FormControllerOptions
Section titled “FormControllerOptions”interface FormControllerOptions { fieldSelector?: string; fieldOptions?: FieldControllerOptions; fieldsMap?: CustomFieldsMap; loadingState?: false | FormLoadingStateOptions; onLoadingStateChange?: (detail: FormLoadingStateDetail) => void; onFormInvalid?: (detail: FormEventDetail) => void;}FormField Interface
Section titled “FormField Interface”The contract implemented by FieldController and custom field types. Any class conforming to this interface can participate in a FormController.
interface FormField { readonly name: string; getState(): FieldState; validate(): FieldValidationResult; reset(): void; destroy(): void; setEnabled(enabled: boolean): void; setServerErrors(errors: string[]): void; connect(onChange: (state: FieldState) => void): void; focus(): void;}Custom Field Types
Section titled “Custom Field Types”type FormFieldClass = new (wrapper: HTMLElement, options?: Record<string, unknown>) => FormField;
type FormFieldFactory = | FormFieldClass | (() => Promise<{ default: FormFieldClass }>);
type CustomFieldsMap = Record<string, FormFieldFactory>;Handler Types
Section titled “Handler Types”type FieldEventHandler = (detail: FieldEventDetail) => void;type FormLevelEventHandler = (detail: FormEventDetail) => void;type FormEventHandler = FieldEventHandler | FormLevelEventHandler;type RegistryEventHandler = (detail: { formId: string }) => void;type FieldControllerEventHandler = (state: FieldState) => void;State Types
Section titled “State Types”interface FieldState { name: string; value: string; isValid: boolean; isDirty: boolean; isTouched: boolean; errors: string[];}
interface FieldValidationResult { isValid: boolean; errors: string[];}
interface FormState { id: string; isValid: boolean; isSubmitting: boolean; isDirty: boolean; fields: Record<string, FieldState>;}Plugin Types
Section titled “Plugin Types”Form Plugins
Section titled “Form Plugins”interface FormPlugin { init(formEl: HTMLFormElement, api: FormPluginHost): void | Promise<void>; destroy(): void;}
type FormPluginFactory = () => Promise<{ default: new () => FormPlugin }>;FormPluginHost
Section titled “FormPluginHost”Given to form plugins during init():
interface FormPluginHost { readonly id: string; getFieldValue(name: string): string | undefined; getFieldNames(): string[]; setFieldEnabled(name: string, enabled: boolean): void; on(event: 'field:*', handler: FieldEventHandler): void; on(event: 'form:*', handler: FormLevelEventHandler): void; off(event: 'field:*', handler: FieldEventHandler): void; off(event: 'form:*', handler: FormLevelEventHandler): void;}Custom Field Types
Section titled “Custom Field Types”Custom fields implement the FormField interface. Register globally with registerFieldType() or per-form via fieldsMap:
interface FormField { readonly name: string; getState(): FieldState; validate(): FieldValidationResult; reset(): void; destroy(): void; setEnabled(enabled: boolean): void; setServerErrors(errors: string[]): void; connect(onChange: (state: FieldState) => void): void; focus(): void;}
type FormFieldClass = new (wrapper: HTMLElement, options?: FieldOptions) => FormField;type FormFieldFactory = FormFieldClass | (() => Promise<{ default: FormFieldClass }>);type CustomFieldsMap = Record<string, FormFieldFactory>;Submit Types
Section titled “Submit Types”interface FormSubmitActions { fallbackToNative(): void; applyValidationErrors(errors: Record<string, string[]>): void; redirect(url: string): void; finish(html?: string): void;}
interface FormSubmitContext extends FormSubmitActions { formEl: HTMLFormElement; formData: FormData; submitter: HTMLElement | null; signal: AbortSignal;}
type FormSubmitFunction = (context: FormSubmitContext) => Promise<void>;TYPO3 Types
Section titled “TYPO3 Types”interface Typo3FormsOptions { disableDefaultValidators?: boolean; additionalValidators?: Validator[]; additionalFieldTypes?: CustomFieldsMap; additionalFormPlugins?: FormPluginFactory[]; fieldsMap?: CustomFieldsMap; onSubmit?: FormSubmitFunction; formSelector?: string; fieldSelector?: string; hooks?: Typo3FormsHooks;}
interface Typo3FormsHooks { onFormRegistered?(api: FormControllerApi): void; onBeforeSubmit?(context: FormSubmitContext): boolean | void; onAfterSubmit?(response: Typo3AjaxFormResponse, formEl: HTMLFormElement): void; onStepChange?(page: { current: number; total: number }, formEl: HTMLFormElement): void; onFormFinished?(response: Typo3AjaxFormResponse, formEl: HTMLFormElement): void; onValidationError?(errors: Record<string, string[]>, formEl: HTMLFormElement): void; onSubmitError?(error: Error, formEl: HTMLFormElement): void;}
interface Typo3AjaxFormResponse { valid: boolean; errors: Record<string, string[]>; page: { current: number; total: number }; finished: boolean; redirect: string | null; message: string | null; state: string; html?: string | null;}
type Typo3RemountFn = (oldFormEl: HTMLFormElement, html: string) => HTMLFormElement | null;type Typo3UnmountFn = (formEl: HTMLFormElement) => void;
interface Typo3FormsApi { registry: FormRegistry; destroy(): void;}Constants (internal)
Section titled “Constants (internal)”These are not exported from the barrel but can be imported by path:
const CSS_CLASSES = { errorClass: 'is-invalid', errorMsgClass: 'invalid-feedback', descriptionClass: 'form-text',} as const;
const SELECTORS = { formField: '[data-form-field]', input: 'input, select, textarea',} as const;
const DEBOUNCE_MS = 300;