UNPKG

@canard/schema-form

Version:

React-based component library that renders forms based on JSON Schema with plugin system support for validators and UI components

261 lines (260 loc) 13 kB
import type { Fn, Nullish } from '../../../@aileron/declare'; import type { AllowedValue, JsonSchemaType, JsonSchemaWithVirtual, JsonSchemaError as ValidationError } from '../../../types'; import { type ChildNode, type NodeEventOptions, type NodeEventPayload, NodeEventType, type NodeListener, type NodeStateFlags, type SchemaNode, type SchemaNodeConstructorProps, type UnionSetValueOption } from '../type'; export declare abstract class AbstractNode<Schema extends JsonSchemaWithVirtual = JsonSchemaWithVirtual, Value extends AllowedValue = any> { #private; /** [readonly] Node's group, `branch` or `terminal` */ readonly group: 'branch' | 'terminal'; /** [readonly] Node's type, `array`, `number`, `object`, `string`, `boolean`, `virtual`, `null` */ abstract readonly type: Exclude<JsonSchemaType, 'integer'>; /** [readonly] Schema's type, `array`, `number`, `integer`, `object`, `string`, `boolean`, `virtual`, `null` */ readonly schemaType: JsonSchemaType; /** [readonly] Node's depth */ readonly depth: number; /** [readonly] Whether this is the root node */ readonly isRoot: boolean; /** [readonly] Root node */ readonly rootNode: SchemaNode; /** [readonly] Node's parent node */ readonly parentNode: SchemaNode | null; /** [readonly] Node's JSON Schema */ readonly jsonSchema: Schema; /** [readonly] Node's scope */ readonly scope: string | undefined; /** [readonly] Node's variant */ readonly variant: number | undefined; /** [readonly] Whether the node value is required */ readonly required: boolean; /** [readonly] Whether the node value is nullable */ readonly nullable: boolean; /** * [readonly] Node's name * @note Basically it is readonly, but can be changed with `setName` by the parent node. * */ get name(): string; /** * [readonly] Node's escaped name, it can be same as `name` * @note Basically it is readonly, but can be changed with `setName` by the parent node. * */ get escapedName(): string; /** * Sets the node's name. Only the parent can change the name. * @param name - The name to set * @param actor - The node setting the name */ setName(this: AbstractNode, name: string, actor: SchemaNode): void; /** * [readonly] Node's data path. * @note Basically it is readonly, but can be changed with `updatePath` by the parent node. * */ get path(): string; /** [readonly] Node's schema path * @note Basically it is readonly, but can be changed with `updatePath` by the parent node. */ get schemaPath(): string; /** [readonly] Unique identifier combining schemaPath and data path */ get key(): string; /** * Updates the node's path. Updates its own path by referencing the parent node's path. * @returns Whether the path was changed * @returns {boolean} Whether the path was changed */ updatePath(this: AbstractNode): boolean; /** * [readonly] Context node for accessing form-wide shared data * @note Root nodes return their own context, child nodes delegate to rootNode.context * @internal Internal implementation method. Do not call directly. */ get context(): SchemaNode | null; /** * Node's default value * - set: `setDefaultValue`, can only be updated by inherited nodes * - get: `defaultValue`, can be read in all situations */ get defaultValue(): Nullish | Value; /** * Changes the node's default value, can only be performed by inherited nodes * For use in `constructor` * @param value input value for updating defaultValue */ protected setDefaultValue(this: AbstractNode, value: Value | Nullish): void; /** * Changes the node's default value and publishes a Refresh event. Can only be performed by inherited nodes * For use outside of `constructor` * @param value input value for updating defaultValue * @returns {Promise<void>} A promise that resolves when the refresh is complete */ protected refresh(this: AbstractNode, value: Value | Nullish): void; /** * Gets the node's value * @returns The node's value */ abstract get value(): Value | Nullish; /** * Sets the node's value * @param input - The value to set */ abstract set value(input: Value | Nullish); /** * Sets the node's value, can be redefined by inherited nodes * @param input The value to set or a function that returns a value * @param options Set options * - replace(boolean): Overwrite existing value, (default: false, merge with existing value) */ protected abstract applyValue(this: AbstractNode, input: Value | Nullish, option: UnionSetValueOption): void; /** * Sets the node's value. Performs preprocessing on the input before reflecting the actual data with applyValue. * @param input - The value to set or a function that returns a value * @param option - Set options, can be combined using bitwise operators * - `Overwrite`(default): `Replace` | `Propagate` | `Refresh` * - `Merge`: `Propagate` | `Refresh` * - `Replace`: Replace the current value * - `Propagate`: Propagate the update to child nodes * - `Refresh`: Trigger a refresh to update the FormTypeInput * - `Normal`: Only update the value */ setValue(this: AbstractNode, input: Value | Nullish | Fn<[prev: Value | Nullish], Value | Nullish>, option?: UnionSetValueOption): void; /** * Function called when the node's value changes. * @param input - The changed value * @param batch - Optional flag indicating whether the change should be batched */ protected onChange(this: AbstractNode, input: Value | Nullish, batch?: boolean): void; /** List of child nodes, nodes without child nodes return an `null` */ get children(): ChildNode[] | null; /** List of subnodes, nodes without subnodes return an `null` */ get subnodes(): ChildNode[] | null; constructor({ name, scope, variant, jsonSchema, schemaType, nullable, defaultValue, onChange, parentNode, validationMode, validatorFactory, required, context, }: SchemaNodeConstructorProps<Schema, Value>); /** * Finds the node corresponding to the given pointer in the node tree. * @param pointer - JSON Pointer of the node to find (e.g., '/foo/0/bar'), returns itself if not provided * @returns {SchemaNode|null} The found node, null if not found */ find(this: AbstractNode, pointer?: string): SchemaNode | null; /** * Saves an event unsubscribe function. * @param unsubscribe - The unsubscribe function to save */ protected saveUnsubscribe(this: AbstractNode, unsubscribe: Fn): void; /** * Initializes the node's event listener/subscription list. Initialization must be called by itself or by the parent node. * @param actor - The node requesting initialization * @internal Internal implementation method. Do not call directly. */ cleanUp(this: AbstractNode, actor?: SchemaNode): void; /** * Registers a node event listener * @param listener Event listener * @returns Event listener removal function */ subscribe(this: AbstractNode, listener: NodeListener): () => void; /** * Publishes an event to the node's listeners * @param type - Event type (see NodeEventType) * @param payload - Data for the event (see NodeEventPayload) * @param options - Options for the event (see NodeEventOptions) * @param immediate - If true, executes listeners synchronously; if false, batches event via EventCascade */ publish<Type extends NodeEventType>(this: AbstractNode, type: Type, payload?: NodeEventPayload[Type], options?: NodeEventOptions[Type], immediate?: boolean): void; /** [readonly] Whether the node is initialized */ get initialized(): boolean; /** * Initializes the node. Initialization must be called by itself or by the parent node. * @param actor - The node requesting initialization * @returns {boolean} Whether initialization occurred * @internal Internal implementation method. Do not call directly. */ initialize(this: AbstractNode, actor?: SchemaNode): boolean; /** [readonly] Whether the node has computed properties */ get computeEnabled(): boolean; /** [readonly] Whether the node can assign values and update state (controlled by computed.active) */ get active(): boolean; /** [readonly] Whether the node should be displayed in UI (controlled by computed.visible) */ get visible(): boolean; /** [readonly] Whether the node is active, visible, and within scope (ready for rendering) */ get enabled(): boolean; /** [readonly] Whether the node value cannot be modified by user (controlled by computed.readOnly) */ get readOnly(): boolean; /** [readonly] Whether the node is disabled for user interaction (controlled by computed.disabled) */ get disabled(): boolean; /** [readonly] Currently active oneOf branch index (-1 if none active) */ get oneOfIndex(): number; /** [readonly] Currently active anyOf branch indices (empty array if none active) */ get anyOfIndices(): number[]; /** [readonly] Computed values from dependencies, used for triggering component re-renders */ get watchValues(): readonly any[]; /** * Updates the node's computed properties. * @internal Internal implementation method. Do not call directly. */ protected updateComputedProperties(this: AbstractNode): void; /** * Resets the current node to its initial value. Uses the current node's initial value, or the provided value if one is given. * @param updateScoped - Whether to update the scoped property * @param preferLatestValue - Whether to use the latest value, uses the latest value if available * @param preferInitialValue - Whether to use the initial value, uses the initial value if available * @param inputValue - The value to set, uses the provided value if given * @internal Internal implementation method. Do not call directly. */ reset(this: AbstractNode, updateScoped: boolean, preferLatestValue?: boolean, preferInitialValue?: boolean, inputValue?: Value | Nullish): void; /** * [readonly] Node's state flags * @note use `setState` method to set the state * */ get state(): NodeStateFlags; /** * Sets the node's state. Maintains existing state unless explicitly passing undefined. * @param input - The state to set or a function that computes new state based on previous state */ setState(this: AbstractNode, input?: ((prev: NodeStateFlags) => NodeStateFlags) | NodeStateFlags): void; /** * Returns the merged result of errors that occurred inside the form and externally received errors. * @returns All of the errors that occurred inside the form and externally received errors */ get globalErrors(): ValidationError[]; /** * Returns the merged result of own errors and externally received errors. * @returns Local errors and externally received errors */ get errors(): ValidationError[]; /** * Updates own errors and then merges them with externally received errors. * @param errors - List of errors to set */ setErrors(this: AbstractNode, errors: ValidationError[]): void; /** * Clears own errors. * @note Does not clear externally received errors. */ clearErrors(this: AbstractNode): void; /** * Merges externally received errors with local errors. For rootNode, also merges internal errors. * @param errors - List of received errors */ setExternalErrors(this: AbstractNode, errors?: ValidationError[]): void; /** * Clears externally received errors. * @note Does not clear localErrors / internalErrors. */ clearExternalErrors(this: AbstractNode): void; /** * Finds and removes specific errors from the externally received errors. * @param errors - List of errors to remove */ removeFromExternalErrors(this: AbstractNode, errors: ValidationError[]): void; /** * Adds or updates a value in the enhancer for validation purposes * @param pointer - JSON Pointer path to the value location * @param value - Value to set in enhancer (typically from virtual/computed fields) * @internal Internal implementation method. Do not call directly. * */ adjustEnhancer(this: AbstractNode, pointer: string, value: any): void; /** * Performs validation based on the current value. * @returns {Promise<ValidationError[]>} List of errors that occurred inside the form * @note If `ValidationMode.None` is set, an empty array is returned. */ validate(this: AbstractNode): Promise<ValidationError[]>; /** [readonly] Whether validation is enabled for this form */ get validation(): boolean; }