@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
TypeScript
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;
}