UNPKG

diagram-js

Version:

A toolbox for displaying and modifying diagrams on the web

319 lines (250 loc) 6.52 kB
import { values } from 'min-dash'; import { getEnclosedElements } from '../../util/Elements'; import { hasSecondaryModifier } from '../../util/Mouse'; import { append as svgAppend, attr as svgAttr, create as svgCreate, remove as svgRemove } from 'tiny-svg'; /** * @typedef {import('../../core/Canvas').default} Canvas * @typedef {import('../dragging/Dragging').default} Dragging * @typedef {import('../../core/ElementRegistry').default} ElementRegistry * @typedef {import('../../core/EventBus').default} EventBus * @typedef {import('../mouse/Mouse').default} Mouse * @typedef {import('../selection/Selection').default} Selection * @typedef {import('../tool-manager/ToolManager').default} ToolManager * * @typedef {import('../../util/Types').Rect} Rect */ var LASSO_TOOL_CURSOR = 'crosshair'; /** * @param {EventBus} eventBus * @param {Canvas} canvas * @param {Dragging} dragging * @param {ElementRegistry} elementRegistry * @param {Selection} selection * @param {ToolManager} toolManager * @param {Mouse} mouse */ export default function LassoTool( eventBus, canvas, dragging, elementRegistry, selection, toolManager, mouse) { this._selection = selection; this._dragging = dragging; this._mouse = mouse; var self = this; // lasso visuals implementation /** * A helper that realizes the selection box visual */ var visuals = { create: function(context) { var container = canvas.getActiveLayer(), frame; frame = context.frame = svgCreate('rect'); svgAttr(frame, { class: 'djs-lasso-overlay', width: 1, height: 1, x: 0, y: 0 }); svgAppend(container, frame); }, update: function(context) { var frame = context.frame, bbox = context.bbox; svgAttr(frame, { x: bbox.x, y: bbox.y, width: bbox.width, height: bbox.height }); }, remove: function(context) { if (context.frame) { svgRemove(context.frame); } } }; toolManager.registerTool('lasso', { tool: 'lasso.selection', dragging: 'lasso' }); eventBus.on('lasso.selection.end', function(event) { var target = event.originalEvent.target; // only reactive on diagram click // on some occasions, event.hover is not set and we have to check if the target is an svg if (!event.hover && !(target instanceof SVGElement)) { return; } eventBus.once('lasso.selection.ended', function() { self.activateLasso(event.originalEvent, true); }); }); // lasso interaction implementation eventBus.on('lasso.end', 0, function(event) { var context = event.context; var bbox = toBBox(event); var elements = elementRegistry.filter(function(element) { return element; }); var add = hasSecondaryModifier(event); self.select(elements, bbox, add ? context.selection : []); }); eventBus.on('lasso.start', function(event) { var context = event.context; context.bbox = toBBox(event); visuals.create(context); context.selection = selection.get(); }); eventBus.on('lasso.move', function(event) { var context = event.context; context.bbox = toBBox(event); visuals.update(context); }); eventBus.on('lasso.cleanup', function(event) { var context = event.context; visuals.remove(context); }); // event integration eventBus.on('element.mousedown', 1500, function(event) { if (!hasSecondaryModifier(event)) { return; } self.activateLasso(event.originalEvent); // we've handled the event return true; }); } LassoTool.$inject = [ 'eventBus', 'canvas', 'dragging', 'elementRegistry', 'selection', 'toolManager', 'mouse' ]; /** * Activate lasso. * * @param {MouseEvent} event * @param {boolean} [autoActivate=false] */ LassoTool.prototype.activateLasso = function(event, autoActivate) { this._dragging.init(event, 'lasso', { autoActivate: autoActivate, cursor: LASSO_TOOL_CURSOR, data: { context: {} } }); }; /** * Activate selection. * * @param {MouseEvent} event * @param {boolean} [autoActivate=false] */ LassoTool.prototype.activateSelection = function(event, autoActivate) { this._dragging.init(event, 'lasso.selection', { trapClick: false, autoActivate: autoActivate, cursor: LASSO_TOOL_CURSOR, data: { context: {} }, keepSelection: true }); }; /** * Select elements within the given bounds. * * @param {Element[]} elements * @param {Rect} bbox * @param {Element[]} [previousSelection] */ LassoTool.prototype.select = function(elements, bbox, previousSelection = []) { var selectedElements = getEnclosedElements(elements, bbox); this._selection.select([ ...previousSelection, ...values(selectedElements) ]); }; /** * Toggle the lasso tool. */ LassoTool.prototype.toggle = function() { if (this.isActive()) { return this._dragging.cancel(); } var mouseEvent = this._mouse.getLastMoveEvent(); this.activateSelection(mouseEvent, !!mouseEvent); }; /** * Check if the lasso tool is active. * * @returns {boolean} */ LassoTool.prototype.isActive = function() { var context = this._dragging.context(); return context && /^lasso/.test(context.prefix); }; function toBBox(event) { var start = { x: event.x - event.dx, y: event.y - event.dy }; var end = { x: event.x, y: event.y }; var bbox; if ((start.x <= end.x && start.y < end.y) || (start.x < end.x && start.y <= end.y)) { bbox = { x: start.x, y: start.y, width: end.x - start.x, height: end.y - start.y }; } else if ((start.x >= end.x && start.y < end.y) || (start.x > end.x && start.y <= end.y)) { bbox = { x: end.x, y: start.y, width: start.x - end.x, height: end.y - start.y }; } else if ((start.x <= end.x && start.y > end.y) || (start.x < end.x && start.y >= end.y)) { bbox = { x: start.x, y: end.y, width: end.x - start.x, height: start.y - end.y }; } else if ((start.x >= end.x && start.y > end.y) || (start.x > end.x && start.y >= end.y)) { bbox = { x: end.x, y: end.y, width: start.x - end.x, height: start.y - end.y }; } else { bbox = { x: end.x, y: end.y, width: 0, height: 0 }; } return bbox; }