UNPKG

@kform/react

Version:

React integration for KForm.

265 lines (264 loc) 12.3 kB
import { AbsolutePath, CancellablePromise, DisplayStatus, FormManager, Path, Schema, SealedFormManagerEvent, SealedLocatedValidationIssue, SealedValidationIssue, SealedValueEvent, StateEvent, ValidationStatus } from "@kform/core"; /** Options available to the {@link useController} hook. */ export interface ControllerOptions<T = unknown, TState extends ControllerState<T> = ControllerState<T>> { /** * Required if no form context is in scope. * * If a form context is in scope and this value is also provided, then the * provided form manager will be used, in which case the current path of the * form context is ignored. */ formManager?: FormManager; /** * Whether to enable the controller. * * @default true */ enabled?: boolean; /** * Default extra state. * * @internal */ _defaultState?: Partial<TState>; /** * Function called once the controller has been initialised. * * @param info Controller's info. */ onInitialized?: (state: TState & InitializedControllerState<T>) => void | PromiseLike<void>; /** Function called whenever the controller is uninitialised. */ onUninitialized?: (state: TState & UninitializedControllerState<T>) => void; /** * Function called whenever an event matching the controller's path is * emitted. * * @param event Form manager event. */ onFormManagerEvent?: (event: SealedFormManagerEvent, state: TState & InitializedControllerState<T>) => void | PromiseLike<void>; onValueChange?: (event: SealedValueEvent, state: TState & InitializedControllerState<T>) => void | PromiseLike<void>; onValidationStatusChange?: (event: StateEvent.ValidationChange, state: TState & InitializedControllerState<T>) => void | PromiseLike<void>; onDisplayStatusChange?: (event: StateEvent.DisplayChange, state: TState & InitializedControllerState<T>) => void | PromiseLike<void>; onDirtyStatusChange?: (event: StateEvent.DirtyChange, state: TState & InitializedControllerState<T>) => void | PromiseLike<void>; onTouchedStatusChange?: (event: StateEvent.TouchedChange, state: TState & InitializedControllerState<T>) => void | PromiseLike<void>; } /** Controller for a value of the form. */ export interface Controller<T = unknown, TState extends ControllerState<T> = ControllerState<T>> { /** Returns the current state of the controller. */ readonly getState: () => TState; /** * Sets the (non-private) state of the controller. * * @param state State to set. * @internal */ readonly _setState: (state: Partial<TState> | ((state: TState) => Partial<TState>)) => void; /** * Subscribes to changes in the controller's state. * * @param selector Selector used to select which part of the controller's * state to observe. * @param listener Function called whenever the selected state changes. * @param options Subscription options. * @returns Function which should be called to unsubscribe. */ readonly subscribe: <TSelected = unknown>(selector: (state: TState) => TSelected, listener: (selectedState: TSelected, prevSelectedState: TSelected | undefined) => void, options?: ControllerSubscriptionOptions<TSelected>) => () => void; /** * Hook used to select part of the controller's state. * * A component using this hook is re-rendered whenever said part of the state * changes. * * @param selector Selector used to select part of the controller's state. */ readonly useState: (() => TState) & (<TResult = unknown>(selector: (state: TState) => TResult, options?: ControllerUseStateOptions<TResult>) => TResult); /** Hook which returns the form manager being used by the controller. */ readonly useFormManager: () => FormManager; /** Hook which returns the schema of the value being controlled. */ readonly useSchema: () => Schema<T>; /** * Hook which returns the path of the value being controlled by this * controller. * * This path will not contain any provided recursive wildcards. */ readonly usePath: () => AbsolutePath; /** Hook which returns the schema path of the value being controlled. */ readonly useSchemaPath: () => AbsolutePath; /** * Hook which returns whether the controller is currently observing * descendants. */ readonly useObservingDescendants: () => boolean; /** Hook which returns whether the controller has been initialised. */ readonly useInitialized: () => boolean; /** * Hook which returns whether a value exists at the path being controlled. * * Returns `undefined` when the controller is not initialised. */ readonly useExists: () => boolean | undefined; /** * Hook which returns the form value being controlled. * * Note that, when observing descendants, this hook will cause a component to * rerender when a descendant is changed, even if the identity of the value * hasn't changed. * * Returns `undefined` when the controller is not initialised. */ readonly useValue: () => T | undefined; /** * Hook which returns whether the value being controlled is dirty. * * Returns `undefined` when the controller is not initialised. */ readonly useDirty: () => boolean | undefined; /** * Hook which returns whether the value being controlled is touched. * * Returns `undefined` when the controller is not initialised. */ readonly useTouched: () => boolean | undefined; /** * Hook which returns the issues of the value being controlled. * * Returns `undefined` when the controller is not initialised. */ readonly useIssues: () => SealedValidationIssue[] | undefined; /** * Hook which returns the validation status of the value being controlled. * * Returns `undefined` when the controller is not initialised. */ readonly useValidationStatus: () => ValidationStatus | undefined; /** * Hook which returns the display status of the value being controlled. * * Returns `undefined` when the controller is not initialised. */ readonly useDisplayStatus: () => DisplayStatus | undefined; readonly get: (<TValue = unknown, TResult = unknown>(path: Path | string, valueHandler: (value: TValue) => TResult | PromiseLike<TResult>) => CancellablePromise<TResult>) & (<TResult = unknown>(valueHandler: (value: T) => TResult | PromiseLike<TResult>) => CancellablePromise<TResult>); readonly getClone: (() => CancellablePromise<T>) & (<TValue = unknown>(path?: Path | string) => CancellablePromise<TValue>); readonly set: ((path: Path | string, toSet: unknown) => CancellablePromise<void>) & ((toSet: T) => CancellablePromise<void>); readonly reset: (path?: Path | string) => CancellablePromise<void>; readonly remove: (path?: Path | string) => CancellablePromise<void>; readonly validate: (path?: Path | string) => CancellablePromise<SealedLocatedValidationIssue[]>; readonly setDirty: (path?: Path | string) => CancellablePromise<void>; readonly setPristine: (path?: Path | string) => CancellablePromise<void>; readonly setTouched: (path?: Path | string) => CancellablePromise<void>; readonly setUntouched: (path?: Path | string) => CancellablePromise<void>; } /** Options available to the controller's `useState` hook. */ export interface ControllerUseStateOptions<T = unknown> { /** * Function used to specify when two selections are considered equal to each * other. * * @param v1 First selection. * @param v2 Second selection. * @returns Whether the two selections are considered equal. */ equalityFn?: (v1: T, v2: T) => boolean; } /** Options available when subscribing to the state of the controller. */ export interface ControllerSubscriptionOptions<T = unknown> { /** * Function used to specify when two selections are considered equal to each * other. * * @param v1 First selection. * @param v2 Second selection. * @returns Whether the two selections are considered equal. */ equalityFn?: (v1: T, v2: T) => boolean; /** Whether the subscription's listener should be invoked immediately. */ fireImmediately?: boolean; } /** Controller's state. */ export type ControllerState<T = unknown> = UninitializedControllerState<T> | InitializedControllerState<T>; /** Base controller's state. */ export interface BaseControllerState<T = unknown> { /** Form manager being used by the controller. */ readonly formManager: FormManager; /** Schema of the form value being controlled. */ readonly schema: Schema<T>; /** Path of the form value being controlled. */ readonly path: AbsolutePath; /** Schema path of the form value being controller. */ readonly schemaPath: AbsolutePath; /** * Whether the controller is observing descendants. * * This will be `true` when the path provided to the controller ends in a * recursive wildcard. */ readonly observingDescendants: boolean; /** Whether the controller has been initialised. */ readonly initialized: boolean; /** Whether the form value being controlled exists. */ readonly exists: boolean | undefined; /** Form value. */ readonly value: T | undefined; /** Whether the form value being controlled is dirty. */ readonly dirty: boolean | undefined; /** Whether the form value being controlled has been touched. */ readonly touched: boolean | undefined; /** Validation issues associated with the form value being controlled. */ readonly issues: SealedValidationIssue[] | undefined; /** Validation status of the form value being controlled. */ readonly validationStatus: ValidationStatus | undefined; /** Display status of the form value being controlled. */ readonly displayStatus: DisplayStatus | undefined; } /** Uninitialised controller's state. */ export interface UninitializedControllerState<T = unknown> extends BaseControllerState<T> { readonly initialized: false; readonly exists: undefined; readonly value: undefined; readonly dirty: undefined; readonly touched: undefined; readonly issues: undefined; readonly validationStatus: undefined; readonly displayStatus: undefined; } /** Initialized controller's state. */ export type InitializedControllerState<T = unknown> = NonexistentValueControllerState<T> | ExistingValueControllerState<T>; /** Controller state of a nonexistent form value. */ export interface NonexistentValueControllerState<T = unknown> extends BaseControllerState<T> { readonly initialized: true; readonly exists: false; readonly value: undefined; readonly dirty: undefined; readonly touched: undefined; readonly issues: undefined; readonly validationStatus: undefined; readonly displayStatus: undefined; } /** Controller state of an existing form value. */ export interface ExistingValueControllerState<T = unknown> extends BaseControllerState<T> { readonly initialized: true; readonly exists: true; readonly value: T; readonly dirty: boolean; readonly touched: boolean; readonly issues: SealedValidationIssue[]; readonly validationStatus: ValidationStatus; readonly displayStatus: DisplayStatus; } /** * Hook providing access to a controller used to read and control a value of the * form. * * @param path Path of the form value to control, relative to the current path. * * The path must consist of only identifiers, except for the last fragment, * which may be a recursive wildcard to indicate that descendants should also * be observed. * @param options Available options. * @returns A controller used to read and control the form value. * @throws {Error} When {@link path} is invalid or contains fragments other than * ids. */ export declare function useController<T = unknown>(path?: Path | string, options?: undefined): Controller<T>; export declare function useController<T = unknown, TState extends ControllerState<T> = ControllerState<T>>(path: Path | string | undefined, options: ControllerOptions<T, TState>): Controller<T, TState>;