UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

164 lines (163 loc) 5.96 kB
import { TComponentState } from "../../../lib/Component"; import { DragContext, DragDiff } from "../../../services/drag"; import { BlockState } from "../../../store/block/Block"; import { GroupState, TGroup, TGroupId } from "../../../store/group/Group"; import { TMeasureTextOptions } from "../../../utils/functions/text"; import { TRect } from "../../../utils/types/shapes"; import { GraphComponent } from "../GraphComponent"; import { TGraphLayerContext } from "../layers/graphLayer/GraphLayer"; import { BlockGroups } from "./BlockGroups"; export type TGroupStyle = { background: string; border: string; borderWidth: number; selectedBackground: string; selectedBorder: string; /** Background color when group is highlighted (block is being dragged over it) */ highlightedBackground: string; /** Border color when group is highlighted */ highlightedBorder: string; }; export type TGroupGeometry = { padding: [number, number, number, number]; }; export type TGroupProps = { id: TGroupId; onDragUpdate: (groupId: string, diff: { deltaX: number; deltaY: number; }) => void; style?: Partial<TGroupStyle>; geometry?: Partial<TGroupGeometry>; draggable?: boolean; }; type TGroupState<T extends TGroup = TGroup> = TComponentState & T & { rect: TRect; }; export declare class Group<T extends TGroup = TGroup> extends GraphComponent<TGroupProps, TGroupState<T>, TGraphLayerContext> { static define(config: { style?: Partial<TGroupStyle>; geometry?: Partial<TGroupGeometry>; }): typeof Group; get zIndex(): number; context: TGraphLayerContext; protected blocks: BlockState[]; protected groupState: GroupState<T>; cursor: string; protected style: TGroupStyle; protected geometry: TGroupGeometry; /** Whether the group is highlighted (block is being dragged over it) */ protected highlighted: boolean; /** Whether the group is currently being dragged */ protected isDragging: boolean; protected dragStartRect: { x: number; y: number; } | null; protected lastSnappedPos: { x: number; y: number; } | null; constructor(props: TGroupProps, parent: BlockGroups); protected handleClick: (event: MouseEvent) => void; /** * Set the highlighted state of the group */ setHighlighted(highlighted: boolean): void; /** * Check if the group is currently highlighted (block is being dragged over it). * * This method is useful for custom group components that override the `render()` method * and need to apply different styling when the group is highlighted during transfer mode. * * @returns `true` if the group is highlighted, `false` otherwise * * @example * ```typescript * class CustomGroup extends Group { * protected override render() { * const ctx = this.context.ctx; * const rect = this.getRect(); * * // Apply different styles based on state * if (this.isHighlighted()) { * ctx.strokeStyle = 'rgba(100, 200, 100, 1)'; * ctx.lineWidth = 3; * } else if (this.state.selected) { * ctx.strokeStyle = 'rgba(100, 100, 100, 1)'; * ctx.lineWidth = 2; * } else { * ctx.strokeStyle = 'rgba(100, 100, 100, 0.4)'; * ctx.lineWidth = 1; * } * * ctx.beginPath(); * ctx.roundRect(rect.x, rect.y, rect.width, rect.height, 8); * ctx.stroke(); * } * } * ``` */ isHighlighted(): boolean; getEntityId(): string; /** * Check if group can be dragged based on props.draggable and canDrag setting */ isDraggable(): boolean; /** * Override to apply snapping or other position transforms during drag. * Called with the raw (pre-snap) target position computed from the drag start + cumulative diff. * The default implementation returns the position unchanged (no snapping). * * @example * ```typescript * protected override snapPosition(x: number, y: number) { * const grid = 16; * return { x: Math.round(x / grid) * grid, y: Math.round(y / grid) * grid }; * } * ``` */ protected snapPosition(x: number, y: number): { x: number; y: number; }; /** * Handle drag start - stores the initial rect position and sets isDragging flag. * Subclasses that override this method should call super.handleDragStart() to preserve this behavior. */ handleDragStart(_context: DragContext): void; /** * Handle drag update - moves the group rect and notifies via onDragUpdate. * Uses the cumulative diff from drag start so that snapPosition always operates * on the absolute target position (avoids error accumulation from per-frame deltas). * onDragUpdate is called only when the position actually changes. */ handleDrag(diff: DragDiff, _context: DragContext): void; /** * Handle drag end - clears isDragging flag and drag tracking state. * Subclasses that override this method should call super.handleDragEnd() to preserve this behavior. */ handleDragEnd(_context: DragContext): void; protected getRect(rect?: TRect): { x: number; y: number; width: number; height: number; }; protected subscribeToGroup(): void; protected unmount(): void; protected updateHitBox(rect: TRect): void; protected layoutText(text: string, textParams?: TMeasureTextOptions): { measures: import("../../../utils/functions/text").TWrapText; lines: any[]; lineHeight: number; }; protected renderBody(ctx: CanvasRenderingContext2D, rect?: { x: number; y: number; width: number; height: number; }): void; protected render(): void; } export {};