UNPKG

@gravity-ui/graph

Version:

Modern graph editor component

145 lines (144 loc) 4.99 kB
import { ESelectionStrategy } from "../../../services/selection/types"; import { isAllowDrag, isMetaKeyEvent } from "../../../utils/functions"; import { layoutText } from "../../../utils/renderers/text"; import { GraphComponent } from "../GraphComponent"; const defaultStyle = { background: "rgba(100, 100, 100, 0.1)", border: "rgba(100, 100, 100, 0.3)", borderWidth: 2, selectedBackground: "rgba(100, 100, 100, 1)", selectedBorder: "rgba(100, 100, 100, 1)", }; const defaultGeometry = { padding: [20, 20, 20, 20], }; export class Group extends GraphComponent { static define(config) { return class SpecificGroup extends Group { constructor(props, parent) { super({ ...props, style: { ...defaultStyle, ...config.style, ...props.style, }, geometry: { ...defaultGeometry, ...config.geometry, ...props.geometry, }, }, parent); } }; } get zIndex() { return 0; } constructor(props, parent) { super(props, parent); this.blocks = []; this.cursor = "pointer"; this.style = { ...defaultStyle, ...props.style, }; this.geometry = { ...defaultGeometry, ...props.geometry, }; this.subscribeToGroup(); this.addEventListener("click", (event) => { event.stopPropagation(); this.groupState.setSelection(true, !isMetaKeyEvent(event) ? ESelectionStrategy.REPLACE : ESelectionStrategy.APPEND); }); } getEntityId() { return this.props.id; } /** * Check if group can be dragged based on props.draggable and canDrag setting */ isDraggable() { const canDrag = this.context.graph.rootStore.settings.$canDrag.value; return Boolean(this.props.draggable) && isAllowDrag(canDrag, this.state.selected); } /** * Handle drag start - nothing special needed, DragService handles autopanning and cursor */ handleDragStart(_context) { // DragService handles autopanning and cursor locking } /** * Handle drag update - update group rect and notify via callback */ handleDrag(diff, _context) { const rect = { x: this.state.rect.x + diff.deltaX, y: this.state.rect.y + diff.deltaY, width: this.state.rect.width, height: this.state.rect.height, }; this.setState({ rect, }); this.updateHitBox(rect); this.props.onDragUpdate(this.props.id, { deltaX: diff.deltaX, deltaY: diff.deltaY }); } /** * Handle drag end - nothing special needed, DragService handles cleanup */ handleDragEnd(_context) { // DragService handles autopanning disable and cursor unlock } getRect(rect = this.state.rect) { const [paddingTop, paddingRight, paddingBottom, paddingLeft] = this.geometry.padding; return { x: rect.x - paddingLeft, y: rect.y - paddingTop, width: rect.width + paddingLeft + paddingRight, height: rect.height + paddingTop + paddingBottom, }; } subscribeToGroup() { this.groupState = this.context.graph.rootStore.groupsList.getGroupState(this.props.id); this.subscribeSignal(this.groupState.$selected, (selected) => { this.setState({ selected, }); }); return this.subscribeSignal(this.groupState.$state, (group) => { if (group) { this.setState({ ...this.state, ...group, }); this.updateHitBox(this.getRect(group.rect)); } }); } updateHitBox(rect) { this.setHitBox(rect.x, rect.y, rect.x + rect.width, rect.y + rect.height); } layoutText(text, textParams) { const currentRect = this.getRect(); return layoutText(text, this.context.ctx, currentRect, { maxWidth: currentRect.width, maxHeight: currentRect.height, ...textParams, }); } renderBody(ctx, rect = this.getRect()) { ctx.strokeStyle = this.state.selected ? this.style.selectedBorder : this.style.border; ctx.fillStyle = this.state.selected ? this.style.selectedBackground : this.style.background; ctx.lineWidth = this.style.borderWidth; // Рисуем прямоугольник группы ctx.beginPath(); ctx.roundRect(rect.x, rect.y, rect.width, rect.height, 8); ctx.fill(); ctx.stroke(); } render() { this.renderBody(this.context.ctx, this.getRect()); } }