UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

214 lines (213 loc) 8.71 kB
import { DragContext, DragDiff } from "../../../services/drag"; import { BlockState } from "../../../store/block/Block"; import { TGroup, TGroupId } from "../../../store/group/Group"; import { TRect } from "../../../utils/types/shapes"; import { Group, TGroupGeometry, TGroupStyle } from "./Group"; declare module "../../../graphEvents" { interface GraphEventsDefinitions { "group-collapse-change": (event: CustomEvent<{ groupId: TGroupId; collapsed: boolean; currentRect: TRect; nextRect: TRect; }>) => void; } } /** A single collapse direction axis value. */ export type TCollapseDirection = "start" | "center" | "end"; export interface TCollapsibleGroup extends TGroup { /** Whether this group is currently collapsed */ collapsed?: boolean; /** * The visual rect used when collapsed. When set, the group renders and * responds to hit-tests using this rect instead of the normal `rect`. * * `rect` itself continues to be managed by `withBlockGrouping` (or * manually) and always reflects the real block bounding box. */ collapsedRect?: TRect; /** * Where the collapsed header appears relative to the full group rect. * * - `x`: `"start"` → left edge | `"center"` → centered | `"end"` → right edge * - `y`: `"start"` → top edge | `"center"` → centered | `"end"` → bottom edge * * Defaults to `{ x: "start", y: "start" }` (top-left corner). * * Only used by the default collapse rect computation. Ignored when * {@link getCollapseRect} is provided. */ collapseDirection?: { x?: TCollapseDirection; y?: TCollapseDirection; }; /** * User-defined function to compute the collapsed rect from the expanded rect. * * When not provided, a default implementation computes a * {@link DEFAULT_COLLAPSED_WIDTH}×{@link DEFAULT_COLLAPSED_HEIGHT} rect * pinned at the position determined by {@link collapseDirection}. * * @example * ```typescript * const group: TCollapsibleGroup = { * id: "my-group", * rect: { x: 0, y: 0, width: 400, height: 300 }, * component: CollapsibleGroup, * getCollapseRect: (_group, rect) => ({ * x: rect.x + rect.width / 2 - 100, * y: rect.y, * width: 200, * height: 48, * }), * }; * ``` */ getCollapseRect?: (group: TCollapsibleGroup, expandedRect: TRect) => TRect; } /** * Default collapse rect computation. Produces a rect of the given size * pinned at the position determined by `direction`. * * Exported so users can call it from their custom `getCollapseRect` and * extend or modify the default behavior. * * @param expandedRect - The full group rect before collapsing. * @param direction - Where the header is pinned (defaults to top-left). * @param collapsedWidth - Header width (defaults to 200). * @param collapsedHeight - Header height (defaults to 48). */ export declare function computeDefaultCollapseRect(expandedRect: TRect, direction?: { x?: TCollapseDirection; y?: TCollapseDirection; }, collapsedWidth?: number, collapsedHeight?: number): TRect; /** * Cached result of {@link CollapsibleGroup.getPortDelegationCounts}; invalidated * when delegation membership changes (delegate/undelegate), not when rects move. */ type TPortDelegationCounts = { left: number; right: number; }; export declare class CollapsibleGroup<T extends TCollapsibleGroup = TCollapsibleGroup> extends Group<T> { /** Snapshot of `collapsedRect` at drag start; see {@link handleDrag}. */ private dragStartCollapsedRect; /** * Lazily computed delegation counts for {@link renderCollapsedView}; cleared when * delegation changes (delegate/undelegate ports). */ private portDelegationCountsCache; static define(config: { style?: Partial<TGroupStyle>; geometry?: Partial<TGroupGeometry>; }): typeof CollapsibleGroup; /** * Extend base subscription to also react to collapsed state on init. * subscribeSignal fires immediately with the current value, so a group * that starts with collapsed: true will hide its blocks on mount. * * Also handles external collapse state changes: if setGroups() is called * with collapsed: false while the group is currently collapsed, it expands. */ protected subscribeToGroup(): ReturnType<Group["subscribeToGroup"]>; /** * Returns the visual rect. When collapsed, returns `collapsedRect` * (with padding) so the group renders as a compact header. */ protected getRect(rect?: TRect): TRect; /** * Sets the hitbox to the collapsed rect when collapsed, or the expanded * rect otherwise. Passes the raw inner rect to super so that base Group's * updateHitBox can apply padding exactly once. */ protected updateHitBox(rect: TRect): void; /** * Remember inner rect and collapsed rect so {@link handleDrag} can translate * `collapsedRect` by the same snapped delta as `rect` (grid snapping in Group). */ handleDragStart(context: DragContext): void; handleDragEnd(context: DragContext): void; /** * When dragging a collapsed group, move `collapsedRect` in lockstep with the snapped * `rect` movement computed by {@link Group.handleDrag} (not per-frame mouse deltas). */ handleDrag(diff: DragDiff, context: DragContext): void; /** Whether this group is currently in the collapsed state. */ isCollapsed(): boolean; protected getGroupBlocks(): BlockState[]; /** * Returns the number of ports currently delegated to the left and right * group edge ports. Only meaningful when the group is collapsed. * * Cost is O(blocks × anchors) per call when the cache is cold — typical group * sizes keep this cheap. The result is cached until delegation changes * ({@link delegatePorts} / {@link undelegatePorts}); override {@link renderCollapsedView} * and call a custom counter if you need different invalidation rules. */ protected getPortDelegationCounts(): TPortDelegationCounts; private invalidatePortDelegationCountsCache; /** * Compute the collapsed rect for a given full rect. * * Uses the user-provided `getCollapseRect` if available, otherwise falls * back to the direction-based default. */ private computeCollapsedRect; /** * Collapse the group: set collapsedRect, hide member blocks, * and redirect their ports to the group edges. * * Emits a cancelable `group-collapse-change` event before applying changes. * If a listener calls `event.preventDefault()`, the collapse is cancelled. */ collapse(): void; /** * Expand the group: remove collapsedRect, show member blocks, and let * them resume managing their own ports. * * Emits a cancelable `group-collapse-change` event before applying changes. * If a listener calls `event.preventDefault()`, the expand is cancelled. */ expand(): void; protected unmount(): void; private applyBlockVisibility; /** * Get (or create) the group's left-edge port used as a delegation target. * Input ports and IN anchors delegate to this port when collapsed. */ private getLeftEdgePort; /** * Get (or create) the group's right-edge port used as a delegation target. * Output ports and OUT anchors delegate to this port when collapsed. */ private getRightEdgePort; /** * Update the group's edge port positions to match the given rect. */ private updateGroupPortPositions; /** * Delegate all ports of group blocks to the group's edge ports. * * - Input port → left-edge port * - Output port → right-edge port * - IN anchors → left-edge port * - OUT anchors → right-edge port * * While delegated, block ports mirror the group edge positions. * When the group is dragged, only the group edge ports need to be * updated — all delegated ports follow automatically. */ private delegatePorts; /** * Remove delegation from all ports of group blocks, restoring their * original positions (saved automatically by the delegation mechanism). */ private undelegatePorts; protected render(): void; /** * Render the compact header shown when the group is collapsed. * Override this method to customise the collapsed appearance. */ protected renderCollapsedView(ctx: CanvasRenderingContext2D): void; } export {};