UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

214 lines (213 loc) 7.3 kB
import type { DragState } from "../../../services/drag/types"; import { TBlock } from "../blocks/Block"; import { BlockGroups, BlockGroupsProps } from "./BlockGroups"; import { Group } from "./Group"; /** * Callback called when block transfer starts (Shift pressed during drag) * @param blockIds - IDs of blocks being transferred * @param sourceGroupIds - Set of source group IDs (groups blocks came from) */ export type OnTransferStart = (blockIds: TBlock["id"][], sourceGroupIds: Set<string>) => void; /** * Callback called when block transfer ends (mouse released or Shift released) * @param blockIds - IDs of blocks that were transferred * @param targetGroupId - Target group ID (null if removed from group) */ export type OnTransferEnd = (blockIds: TBlock["id"][], targetGroupId: string | null) => void; /** * Object representing a change in block's group membership */ export type TBlockGroupsTransferGroupChange = { blockId: TBlock["id"]; sourceGroup?: string | null; targetGroup?: string | null; }; /** * Callback called when blocks' groups change * @param changes - Array of changes to apply */ export type OnBlockGroupChange = (changes: TBlockGroupsTransferGroupChange[]) => void; export type BlockGroupsTransferLayerProps = BlockGroupsProps & { /** * Enable/disable block transfer between groups with Shift+drag. * Default: true */ transferEnabled?: boolean; /** * Called when block transfer starts (Shift pressed during drag) */ onTransferStart?: OnTransferStart; /** * Called when block transfer ends (mouse released or Shift released) */ onTransferEnd?: OnTransferEnd; /** * Called when a block's group changes */ onBlockGroupChange?: OnBlockGroupChange; /** * If true, blocks will move when the group is dragged */ updateBlocksOnDrag?: boolean; }; type TransferState = { isTransferring: boolean; /** All blocks being transferred */ blocks: TBlock[]; /** Source group IDs for each block (null if block was not in a group) */ sourceGroupIds: Set<string>; /** Current target group ID */ targetGroupId: string | null; /** Currently highlighted group ID */ highlightedGroupId: string | null; }; /** * BlockGroups layer with block-to-group transfer functionality. * * ## Features * - Hold Shift during drag to activate transfer mode * - Release Shift to deactivate transfer mode and return to normal drag * - Groups highlight when blocks are dragged over them * - Multi-block transfer: all selected blocks are transferred together * - Source groups lock their size during transfer * - Callbacks for state synchronization with external stores (Redux, MobX, etc.) * * ## Basic Usage * ```typescript * const layer = graph.addLayer(BlockGroupsTransferLayer, { * transferEnabled: true, * draggable: true, * }); * ``` * * ## With Automatic Grouping * ```typescript * const GroupsLayer = BlockGroupsTransferLayer.withBlockGrouping({ * groupingFn: (blocks) => groupBy(blocks, (b) => b.$state.value.group), * mapToGroups: (groupId, { rect }) => ({ id: groupId, rect }), * }); * * graph.addLayer(GroupsLayer, { * transferEnabled: true, * updateBlocksOnDrag: true, // Blocks move with the group * }); * ``` * * ## With Redux Integration * ```typescript * graph.addLayer(GroupsLayer, { * onTransferStart: (blockIds, sourceGroupIds) => { * console.log('Transfer started:', blockIds); * }, * onBlockGroupChange: (changes) => { * // Sync with Redux * changes.forEach(({ blockId, targetGroup }) => { * store.dispatch(updateBlockGroup({ blockId, groupId: targetGroup })); * }); * }, * onTransferEnd: (blockIds, targetGroupId) => { * console.log('Transfer completed:', blockIds, 'to group:', targetGroupId); * }, * }); * ``` * * Uses DragService.$state.currentEvent.shiftKey to track Shift state in real-time. */ export declare class BlockGroupsTransferLayer<P extends BlockGroupsTransferLayerProps = BlockGroupsTransferLayerProps> extends BlockGroups<P> { /** Current transfer state */ protected transferState: TransferState; /** Cleanup function for the drag state subscription */ protected disposeSubscription: (() => void) | null; protected get isTransferEnabled(): boolean; protected afterInit(): void; /** * Subscribe to DragService state changes */ protected subscribeToDragState(): void; /** * Handle drag state changes - react to Shift key in real-time */ protected handleDragStateChange(dragState: DragState, isShiftPressed: boolean): void; /** * Activate transfer mode for currently dragged blocks */ protected activateTransfer(dragState: DragState): void; /** * Deactivate transfer mode - apply transfer and unlock groups * Called when Shift is released during drag */ protected deactivateTransfer(): void; /** * Lock all groups' sizes */ protected lockAllGroups(): void; /** * Unlock all groups' sizes */ protected unlockAllGroups(): void; protected createIdleState(): TransferState; /** * Update highlighting based on cursor position */ protected updateHighlight(point: [number, number]): void; /** * End transfer on drag end (mouseup) - apply transfer if in transfer mode */ protected endTransfer(): void; /** * Cancel the transfer operation without applying changes. * * This method can be called to abort an ongoing transfer without moving blocks to a new group. * It will unhighlight groups, unlock sizes, and reset the transfer state. * * @example * ```typescript * // Cancel transfer on Escape key * document.addEventListener('keydown', (e) => { * if (e.key === 'Escape' && layer.isTransferring()) { * layer.cancelTransfer(); * } * }); * ``` */ cancelTransfer(): void; /** * Find a group at the given point */ protected findGroupAtPoint(point: [number, number]): Group | null; /** * Set highlight state for a group directly on the component */ protected setGroupHighlight(groupId: string, highlighted: boolean): void; /** * Apply the group change to the block */ protected applyGroupChange(changes: TBlockGroupsTransferGroupChange[]): void; /** * Check if a block transfer is currently in progress. * * @returns `true` if transfer mode is active (Shift is pressed during drag), `false` otherwise * * @example * ```typescript * if (layer.isTransferring()) { * console.log('Transferring', layer.getTransferringBlocksCount(), 'blocks'); * } * ``` */ isTransferring(): boolean; /** * Get the number of blocks being transferred in the current operation. * * @returns Number of blocks currently being transferred, or 0 if no transfer is in progress * * @example * ```typescript * const count = layer.getTransferringBlocksCount(); * console.log(`Transferring ${count} block${count !== 1 ? 's' : ''}`); * ``` */ getTransferringBlocksCount(): number; protected unmountLayer(): void; } export {};