UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

186 lines (185 loc) 6.5 kB
import { GraphMouseEvent } from "../../../../graphEvents"; import { Layer, LayerContext, LayerProps } from "../../../../services/Layer"; import { TCameraState } from "../../../../services/camera/CameraService"; import { TBlockId } from "../../../../store/block/Block"; import { PortState } from "../../../../store/connection/port/Port"; import type { Emitter } from "../../../../utils/Emitter"; import { Point, TPoint } from "../../../../utils/types/shapes"; export type TPortSnapConditionContext = { sourcePort: PortState; targetPort: PortState; cursorPosition: TPoint; distance: number; }; export type TPortSnapCondition = (context: TPortSnapConditionContext) => boolean; /** * Port metadata structure for PortConnectionLayer * This structure is stored under PortConnectionLayer.PortMetaKey in port.meta */ export interface IPortConnectionMeta { /** Enable snapping for this port. If false or undefined, port will not participate in snapping */ snappable?: boolean; /** Custom condition for snapping - validates if connection is allowed */ snapCondition?: TPortSnapCondition; } type TIcon = { path: string; fill?: string; stroke?: string; width: number; height: number; viewWidth: number; viewHeight: number; }; type LineStyle = { color: string; width?: number; dash: number[]; }; type DrawLineFunction = (start: TPoint, end: TPoint) => { path: Path2D; style: LineStyle; }; type PortConnectionLayerProps = LayerProps & { createIcon?: TIcon; point?: TIcon; drawLine?: DrawLineFunction; searchRadius?: number; lineWidth?: number; lineDash?: number[]; }; declare module "../../../../graphEvents" { interface GraphEventsDefinitions { /** * Port-based event fired when a user initiates a connection from a port. * Extends connection-create-start with direct port reference. */ "port-connection-create-start": (event: CustomEvent<{ blockId: TBlockId; anchorId: string | undefined; sourcePort: PortState; }>) => void; /** * Port-based event fired when the connection hovers over a potential target port. * Extends connection-create-hover with direct port references. */ "port-connection-create-hover": (event: CustomEvent<{ sourceBlockId: TBlockId; sourceAnchorId: string | undefined; targetBlockId: TBlockId | undefined; targetAnchorId: string | undefined; sourcePort: PortState; targetPort?: PortState; }>) => void; /** * Port-based event fired when a connection is successfully created between two ports. * Extends connection-created with direct port references. */ "port-connection-created": (event: CustomEvent<{ sourceBlockId: TBlockId; sourceAnchorId?: string; targetBlockId: TBlockId; targetAnchorId?: string; sourcePort: PortState; targetPort: PortState; }>) => void; "port-connection-cancel": (event: CustomEvent<{ sourcePort: PortState; targetPort: PortState; }>) => void; /** * Port-based event fired when the user releases the mouse button. * Extends connection-create-drop with direct port references. */ "port-connection-create-drop": (event: CustomEvent<{ sourceBlockId: TBlockId; sourceAnchorId: string; targetBlockId?: TBlockId; targetAnchorId?: string; point: Point; sourcePort: PortState; targetPort?: PortState; }>) => void; } } /** * PortConnectionLayer - new layer for creating connections, working only with ports * * Key differences from ConnectionLayer: * - Works only with ports, (use Block and Anchor components only to pass more info about ports in Event) * - Uses findPortAtPoint to detect ports under cursor * - Metadata is stored under unique PortConnectionLayer.PortMetaKey key * - More efficient search through spatial index * - Events extended with sourcePort and targetPort parameters * * @example * ```typescript * // Configure port for snapping * port.updatePort({ * meta: { * [PortConnectionLayer.PortMetaKey]: { * snappable: true, * snapCondition: (ctx) => { * // Custom validation logic * return true; * } * } * } * }); * ``` */ export declare class PortConnectionLayer extends Layer<PortConnectionLayerProps, LayerContext & { canvas: HTMLCanvasElement; ctx: CanvasRenderingContext2D; }> { /** * Unique key for port metadata * Using a symbol prevents conflicts with other layers */ static readonly PortMetaKey: unique symbol; private startState; private endState; private sourcePort?; private targetPort?; private snappingPortsTree; private isSnappingTreeOutdated; private portsUnsubscribe?; protected enabled: boolean; constructor(props: PortConnectionLayerProps); protected afterInit(): void; protected onCameraChange(_camera: TCameraState): void; enable: () => void; disable: () => void; protected currentListener: Emitter | null; private isSnappablePort; protected handleMouseDown: (nativeEvent: GraphMouseEvent) => void; protected renderEndpoint(ctx: CanvasRenderingContext2D): void; protected render(): void; private onStartConnection; private onMoveNewConnection; protected selectPort(port: PortState, select: boolean): void; protected cancelNewConnection(): void; private onEndNewConnection; private findNearestSnappingPort; /** * Rebuild the RBush spatial index for snapping ports * Optimization: Only includes ports from components visible in viewport + padding */ private rebuildSnappingTree; /** * Determine the port type (IN or OUT) * @param port Port to check * @returns EAnchorType.IN, EAnchorType.OUT, or null if the port is a block point (no specific direction) */ private getPortType; /** * Get full event parameters from a port * Includes both legacy parameters (blockId, anchorId) and new port reference */ protected getEventParams(port?: PortState): { blockId?: TBlockId; anchorId?: string; }; unmount(): void; } export {};