@gravity-ui/graph
Version:
Modern graph editor component
174 lines (173 loc) • 9.1 kB
TypeScript
import { Graph } from "../graph";
import { TGraphColors, TGraphConstants } from "../graphConfig";
import { GraphEventsDefinitions } from "../graphEvents";
import { CoreComponent } from "../lib";
import { Component, TComponentState } from "../lib/Component";
import { ICamera, TCameraState } from "./camera/CameraService";
import "./Layer.css";
export type LayerPropsElementProps = {
zIndex: number;
classNames?: string[];
root?: HTMLElement;
transformByCameraPosition?: boolean;
};
export type LayerProps = {
canvas?: LayerPropsElementProps & {
respectPixelRatio?: boolean;
};
html?: LayerPropsElementProps;
root?: HTMLElement;
camera: ICamera;
graph: Graph;
};
export type LayerContext = {
graph: Graph;
camera: ICamera;
constants: TGraphConstants;
colors: TGraphColors;
graphCanvas: HTMLCanvasElement;
ctx: CanvasRenderingContext2D;
layer: Layer;
};
/**
* Utility type to extract public props for a Layer constructor.
* Excludes internal props that are provided by the graph instance:
* - root: managed by the layers service
* - camera: provided by the graph's camera service
* - graph: provided by the graph instance
*
* The root prop is made optional as it can be overridden by the user.
*
* @template T - Layer constructor type
*/
export type LayerPublicProps<T extends Constructor<Layer>> = T extends Constructor<Layer<infer Props>> ? Omit<Props, "root" | "camera" | "graph"> & {
root?: Props["root"];
} : never;
export type LayerConstructor<T extends Constructor<Layer>> = T extends Constructor<Layer> ? T : never;
export declare class Layer<Props extends LayerProps = LayerProps, Context extends LayerContext = LayerContext, State extends TComponentState = TComponentState> extends Component<Props, State, Context> {
static id?: string;
protected canvas: HTMLCanvasElement;
protected html: HTMLElement;
protected root?: HTMLDivElement;
protected attached: boolean;
/**
* AbortController used to manage event listeners.
* All event listeners (both graph.on and DOM addEventListener) are registered with this controller's signal.
* When the layer is unmounted, the controller is aborted, which automatically removes all event listeners.
*/
protected eventAbortController: AbortController;
/**
* A wrapper for this.props.graph.on that automatically includes the AbortController signal.
* The method is named onGraphEvent to indicate it's specifically for graph events.
* This simplifies event subscription and ensures proper cleanup when the layer is unmounted.
*
* IMPORTANT: Always use this method in the afterInit() method, NOT in the constructor.
* This ensures that event subscriptions are properly set up when the layer is reattached.
* When a layer is unmounted, the AbortController is aborted and a new one is created.
* When the layer is reattached, afterInit() is called again, which sets up new subscriptions
* with the new AbortController.
*
* @param eventName - The name of the event to subscribe to
* @param handler - The event handler function
* @param options - Additional options (optional)
* @returns The result of graph.on call (an unsubscribe function)
*/
protected onGraphEvent<EventName extends keyof GraphEventsDefinitions, Cb extends GraphEventsDefinitions[EventName]>(eventName: EventName, handler: Cb, options?: Omit<AddEventListenerOptions, "signal">): () => void;
hide(): void;
isHidden(): boolean;
show(): void;
/**
* A wrapper for HTMLElement.addEventListener that automatically includes the AbortController signal.
* This method is for adding event listeners to the HTML element of the layer.
* It simplifies event subscription and ensures proper cleanup when the layer is unmounted.
*
* IMPORTANT: Always use this method in the afterInit() method, NOT in the constructor.
* This ensures that event subscriptions are properly set up when the layer is reattached.
* When a layer is unmounted, the AbortController is aborted and a new one is created.
* When the layer is reattached, afterInit() is called again, which sets up new subscriptions
* with the new AbortController.
*
* @param eventName - The name of the DOM event to subscribe to
* @param handler - The event handler function
* @param options - Additional options (optional)
*/
protected onHtmlEvent<K extends keyof HTMLElementEventMap>(eventName: K, handler: ((this: HTMLElement, ev: HTMLElementEventMap[K]) => void) | EventListenerObject, options?: Omit<AddEventListenerOptions, "signal">): void;
/**
* A wrapper for HTMLCanvasElement.addEventListener that automatically includes the AbortController signal.
* This method is for adding event listeners to the canvas element of the layer.
* It simplifies event subscription and ensures proper cleanup when the layer is unmounted.
*
* IMPORTANT: Always use this method in the afterInit() method, NOT in the constructor.
* This ensures that event subscriptions are properly set up when the layer is reattached.
* When a layer is unmounted, the AbortController is aborted and a new one is created.
* When the layer is reattached, afterInit() is called again, which sets up new subscriptions
* with the new AbortController.
*
* @param eventName - The name of the DOM event to subscribe to
* @param handler - The event handler function
* @param options - Additional options (optional)
*/
protected onCanvasEvent<K extends keyof HTMLElementEventMap>(eventName: K, handler: ((this: HTMLCanvasElement, ev: HTMLElementEventMap[K]) => void) | EventListenerObject, options?: Omit<AddEventListenerOptions, "signal">): void;
/**
* A wrapper for HTMLElement.addEventListener that automatically includes the AbortController signal.
* This method is for adding event listeners to the root element of the layer.
* It simplifies event subscription and ensures proper cleanup when the layer is unmounted.
*
* IMPORTANT: Always use this method in the afterInit() method, NOT in the constructor.
* This ensures that event subscriptions are properly set up when the layer is reattached.
* When a layer is unmounted, the AbortController is aborted and a new one is created.
* When the layer is reattached, afterInit() is called again, which sets up new subscriptions
* with the new AbortController.
*
* @param eventName - The name of the DOM event to subscribe to
* @param handler - The event handler function
* @param options - Additional options (optional)
*/
protected onRootEvent<K extends keyof HTMLElementEventMap>(eventName: K, handler: ((this: HTMLElement, ev: HTMLElementEventMap[K]) => void) | EventListenerObject, options?: Omit<AddEventListenerOptions, "signal">): void;
/**
* Subscribes to a signal (with .subscribe) and automatically unsubscribes when the layer's AbortController is aborted.
*
* Usage:
* this.onSignal(signal, handler)
*
* @template S - Signal type (must have .subscribe method)
* @template T - Value type of the signal
* @param signal - Signal with .subscribe method (returns unsubscribe function)
* @param handler - Handler function to call on signal change
* @returns The unsubscribe function (called automatically on abort)
*/
protected onSignal<S extends {
subscribe: (handler: (value: T) => void) => () => void;
}, T = S extends {
subscribe: (handler: (value: infer U) => void) => () => void;
} ? U : unknown>(signal: S, handler: (value: T) => void): () => void;
constructor(props: Props, parent?: CoreComponent);
protected sizeTouched: boolean;
updateSize: () => void;
/**
* Called after initialization and when the layer is reattached.
* This is the proper place to set up event subscriptions using onGraphEvent().
*
* When a layer is unmounted, the AbortController is aborted and a new one is created.
* When the layer is reattached, this method is called again, which sets up new subscriptions
* with the new AbortController.
*
* All derived Layer classes should call super.afterInit() at the end of their afterInit method.
*/
protected afterInit(): void;
protected onCameraChange(camera: TCameraState): void;
protected init(): void;
protected unmountLayer(): void;
protected unmount(): void;
getCanvas(): HTMLCanvasElement;
getHTML(): HTMLElement;
attachLayer(root: HTMLElement): void;
detachLayer(): void;
protected createCanvas(params: LayerProps["canvas"]): HTMLCanvasElement;
protected createHTML(params: LayerProps["html"]): HTMLDivElement;
getDRP(): number;
protected applyTransform(x: number, y: number, scale: number, respectPixelRatio?: boolean): void;
protected updateCanvasSize(): void;
resetTransform(): void;
protected render(): void;
}