Skip to content

Commit

Permalink
refactor: move multi selection outline into separate feature
Browse files Browse the repository at this point in the history
  • Loading branch information
misiekhardcore committed Oct 30, 2024
1 parent e662738 commit f0d5377
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 95 deletions.
97 changes: 97 additions & 0 deletions lib/features/outline/MultiSelectionOutline.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@

import {
append as svgAppend,
attr as svgAttr,
create as svgCreate,
classes as svgClasses,
clear as svgClear
} from 'tiny-svg';

import { assign } from 'min-dash';

import { getBBox } from 'lib/util/Elements';

var SELECTION_OUTLINE_PADDING = 6;

/**
* @typedef {import('../../model/Types').Element} Element
*
* @typedef {import('../../core/EventBus').default} EventBus
* @typedef {import('../selection/Selection').default} Selection
* @typedef {import('../../core/Canvas').default} Canvas
*/

/**
* @class
*
* A plugin that adds an outline to shapes and connections that may be activated and styled
* via CSS classes.
*
* @param {EventBus} eventBus
* @param {Canvas} canvas
* @param {Selection} selection
*/
export default function MultiSelectionOutline(eventBus, canvas, selection) {

this._canvas = canvas;

var self = this;



eventBus.on('element.changed', function(event) {
if (selection.isSelected(event.element)) {
self._updateMultiSelectionOutline(selection.get());
}
});

eventBus.on('selection.changed', function(event) {
var newSelection = event.newSelection;

self._updateMultiSelectionOutline(newSelection);
});
}



MultiSelectionOutline.prototype._updateMultiSelectionOutline = function(selection) {
var layer = this._canvas.getLayer('selectionOutline');

svgClear(layer);

var enabled = selection.length > 1;

var container = this._canvas.getContainer();

svgClasses(container)[enabled ? 'add' : 'remove']('djs-multi-select');

if (!enabled) {
return;
}

var bBox = addSelectionOutlinePadding(getBBox(selection));

var rect = svgCreate('rect');

svgAttr(rect, assign({
rx: 3
}, bBox));

svgClasses(rect).add('djs-selection-outline');

svgAppend(layer, rect);
};


MultiSelectionOutline.$inject = [ 'eventBus', 'canvas', 'selection' ];

// helpers //////////

function addSelectionOutlinePadding(bBox) {
return {
x: bBox.x - SELECTION_OUTLINE_PADDING,
y: bBox.y - SELECTION_OUTLINE_PADDING,
width: bBox.width + SELECTION_OUTLINE_PADDING * 2,
height: bBox.height + SELECTION_OUTLINE_PADDING * 2
};
}
7 changes: 4 additions & 3 deletions lib/features/outline/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import Outline from './Outline';

import MultiSelectionOutline from './MultiSelectionOutline';

/**
* @type { import('didi').ModuleDeclaration }
*/
export default {
__init__: [ 'outline' ],
outline: [ 'type', Outline ]
__init__: [ 'outline', 'multiSelectionOutline' ],
outline: [ 'type', Outline ],
multiSelectionOutline: [ 'type', MultiSelectionOutline ]
};
73 changes: 2 additions & 71 deletions lib/features/selection/SelectionVisuals.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,15 @@
import {
assign,
forEach
} from 'min-dash';

import {
append as svgAppend,
attr as svgAttr,
classes as svgClasses,
clear as svgClear,
create as svgCreate
} from 'tiny-svg';

import { getBBox } from '../../util/Elements';

/**
* @typedef {import('../../core/Canvas').default} Canvas
* @typedef {import('../../core/EventBus').default} EventBus
* @typedef {import('./Selection').default} Selection
*/

var MARKER_HOVER = 'hover',
MARKER_SELECTED = 'selected';

var SELECTION_OUTLINE_PADDING = 6;


/**
* A plugin that adds a visible selection UI to shapes and connections
* by appending the <code>hover</code> and <code>selected</code> classes to them.
Expand All @@ -35,15 +20,10 @@ var SELECTION_OUTLINE_PADDING = 6;
*
* @param {Canvas} canvas
* @param {EventBus} eventBus
* @param {Selection} selection
*/
export default function SelectionVisuals(canvas, eventBus, selection) {
export default function SelectionVisuals(canvas, eventBus) {
this._canvas = canvas;

var self = this;

this._multiSelectionBox = null;

function addMarker(e, cls) {
canvas.addMarker(e, cls);
}
Expand Down Expand Up @@ -84,59 +64,10 @@ export default function SelectionVisuals(canvas, eventBus, selection) {
select(e);
}
});

self._updateSelectionOutline(newSelection);
});


eventBus.on('element.changed', function(event) {
if (selection.isSelected(event.element)) {
self._updateSelectionOutline(selection.get());
}
});
}

SelectionVisuals.$inject = [
'canvas',
'eventBus',
'selection'
'eventBus'
];

SelectionVisuals.prototype._updateSelectionOutline = function(selection) {
var layer = this._canvas.getLayer('selectionOutline');

svgClear(layer);

var enabled = selection.length > 1;

var container = this._canvas.getContainer();

svgClasses(container)[enabled ? 'add' : 'remove']('djs-multi-select');

if (!enabled) {
return;
}

var bBox = addSelectionOutlinePadding(getBBox(selection));

var rect = svgCreate('rect');

svgAttr(rect, assign({
rx: 3
}, bBox));

svgClasses(rect).add('djs-selection-outline');

svgAppend(layer, rect);
};

// helpers //////////

function addSelectionOutlinePadding(bBox) {
return {
x: bBox.x - SELECTION_OUTLINE_PADDING,
y: bBox.y - SELECTION_OUTLINE_PADDING,
width: bBox.width + SELECTION_OUTLINE_PADDING * 2,
height: bBox.height + SELECTION_OUTLINE_PADDING * 2
};
}
74 changes: 74 additions & 0 deletions test/spec/features/outline/MultiSelectionOutlineSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

import {
bootstrapDiagram,
inject,
} from 'test/TestHelper';

import selectionModule from 'lib/features/selection';

import {
classes as domClasses,
query as domQuery
} from 'min-dom';

describe('features/outline/MultiSelectionOutline', function() {

beforeEach(bootstrapDiagram({ modules: [ selectionModule ] }));

describe('bootstrap', function() {

beforeEach(bootstrapDiagram({ modules: [ selectionModule ] }));

it('should bootstrap diagram with component', inject(function() {

}));

});


describe('multi selection outline', function() {

var shape1, shape2, outline;

beforeEach(inject(function(elementFactory, canvas, selection) {
shape1 = elementFactory.createShape({
id: 'shape1',
x: 100, y: 100, width: 100, height: 100
});

shape2 = elementFactory.createShape({
id: 'shape2',
x: 300, y: 100, width: 100, height: 100
});

canvas.addShape(shape1);
canvas.addShape(shape2);

selection.select([ shape1, shape2 ]);

outline = domQuery('.djs-selection-outline');

}));


it('should show box', inject(function() {
expect(outline).to.exist;
}));


it('should add djs-multi-select marker', inject(function(selection, canvas) {

// when
var element = canvas.getContainer();

// then
expect(domClasses(element).has('djs-multi-select')).to.be.true;

// but when
selection.select(null);

// then
expect(domClasses(element).has('djs-multi-select')).to.be.false;
}));
});
});
21 changes: 0 additions & 21 deletions test/spec/features/selection/SelectionVisualsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,27 +121,6 @@ describe('features/selection/SelectionVisuals', function() {
}));


it('should show box', inject(function() {
expect(outline).to.exist;
}));


it('should add djs-multi-select marker', inject(function(selection, canvas) {

// when
var element = canvas.getContainer();

// then
expect(domClasses(element).has('djs-multi-select')).to.be.true;

// but when
selection.select(null);

// then
expect(domClasses(element).has('djs-multi-select')).to.be.false;
}));


it('selection box should contain all selected elements', inject(function() {

// then
Expand Down

0 comments on commit f0d5377

Please sign in to comment.