diff --git a/lib/features/lasso-tool/LassoTool.js b/lib/features/lasso-tool/LassoTool.js index 9776d8086..a106aff36 100644 --- a/lib/features/lasso-tool/LassoTool.js +++ b/lib/features/lasso-tool/LassoTool.js @@ -21,6 +21,8 @@ import { * @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'; @@ -109,7 +111,9 @@ export default function LassoTool( // lasso interaction implementation - eventBus.on('lasso.end', function(event) { + eventBus.on('lasso.end', 0, function(event) { + + var context = event.context; var bbox = toBBox(event); @@ -117,7 +121,9 @@ export default function LassoTool( return element; }); - self.select(elements, bbox); + var add = hasSecondaryModifier(event); + + self.select(elements, bbox, add ? context.selection : []); }); eventBus.on('lasso.start', function(event) { @@ -126,6 +132,8 @@ export default function LassoTool( context.bbox = toBBox(event); visuals.create(context); + + context.selection = selection.get(); }); eventBus.on('lasso.move', function(event) { @@ -169,7 +177,12 @@ LassoTool.$inject = [ 'mouse' ]; - +/** + * Activate lasso. + * + * @param {MouseEvent} event + * @param {boolean} [autoActivate=false] + */ LassoTool.prototype.activateLasso = function(event, autoActivate) { this._dragging.init(event, 'lasso', { @@ -181,6 +194,12 @@ LassoTool.prototype.activateLasso = function(event, autoActivate) { }); }; +/** + * Activate selection. + * + * @param {MouseEvent} event + * @param {boolean} [autoActivate=false] + */ LassoTool.prototype.activateSelection = function(event, autoActivate) { this._dragging.init(event, 'lasso.selection', { @@ -189,16 +208,30 @@ LassoTool.prototype.activateSelection = function(event, autoActivate) { cursor: LASSO_TOOL_CURSOR, data: { context: {} - } + }, + keepSelection: true }); }; -LassoTool.prototype.select = function(elements, bbox) { +/** + * 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(values(selectedElements)); + this._selection.select([ + ...previousSelection, + ...values(selectedElements) + ]); }; +/** + * Toggle the lasso tool. + */ LassoTool.prototype.toggle = function() { if (this.isActive()) { return this._dragging.cancel(); @@ -209,6 +242,11 @@ LassoTool.prototype.toggle = function() { this.activateSelection(mouseEvent, !!mouseEvent); }; +/** + * Check if the lasso tool is active. + * + * @returns {boolean} + */ LassoTool.prototype.isActive = function() { var context = this._dragging.context(); @@ -216,7 +254,6 @@ LassoTool.prototype.isActive = function() { }; - function toBBox(event) { var start = { diff --git a/test/spec/features/lasso-tool/LassoToolSpec.js b/test/spec/features/lasso-tool/LassoToolSpec.js index 2881523a0..184991d4e 100755 --- a/test/spec/features/lasso-tool/LassoToolSpec.js +++ b/test/spec/features/lasso-tool/LassoToolSpec.js @@ -19,7 +19,6 @@ import draggingModule from 'lib/features/dragging'; describe('features/lasso-tool', function() { - beforeEach(bootstrapDiagram({ modules: [ modelingModule, @@ -102,7 +101,6 @@ describe('features/lasso-tool', function() { height: 220 }; - // when lassoTool.select(elements, bbox); @@ -113,36 +111,73 @@ describe('features/lasso-tool', function() { expect(selectedElements[0]).to.equal(childShape2); expect(selectedElements[1]).to.equal(childShape3); })); + + + it('shoud select elements in bbox and previously selected elements', inject(function(lassoTool, selection) { + + // given + selection.select([ childShape ]); + + var elements = [ childShape2, childShape3 ]; + + var bbox = { + x: 175, + y: 0, + width: 120, + height: 220 + }; + + // when + lassoTool.select(elements, bbox, selection.get()); + + // then + var selectedElements = selection.get(); + + expect(selectedElements.length).to.equal(3); + expect(selectedElements[0]).to.equal(childShape); + expect(selectedElements[1]).to.equal(childShape2); + expect(selectedElements[2]).to.equal(childShape3); + })); + }); - describe('visuals', function() { + describe('#activateLasso', function() { beforeEach(inject(function(dragging) { dragging.setOptions({ manual: true }); })); - it('should show lasso box', inject(function(lassoTool, canvas, dragging) { + it('should select after lasso', inject(function(lassoTool, dragging, selection, elementRegistry) { // when lassoTool.activateLasso(canvasEvent({ x: 100, y: 100 })); dragging.move(canvasEvent({ x: 200, y: 300 })); + dragging.end(); // then - expect(canvas._svg.querySelector('.djs-lasso-overlay')).to.exist; + expect(selection.get()).to.eql([ elementRegistry.get('child') ]); })); + }); - it('should select after lasso', inject(function(lassoTool, dragging, selection, elementRegistry) { + + describe('visuals', function() { + + beforeEach(inject(function(dragging) { + dragging.setOptions({ manual: true }); + })); + + + it('should show lasso box', inject(function(lassoTool, canvas, dragging) { // when lassoTool.activateLasso(canvasEvent({ x: 100, y: 100 })); dragging.move(canvasEvent({ x: 200, y: 300 })); - dragging.end(); // then - expect(selection.get()).to.eql([ elementRegistry.get('child') ]); + expect(canvas._svg.querySelector('.djs-lasso-overlay')).to.exist; })); });