From edc4b0b6b5b4d4b4e24f70685c90b8cc9ed03b7c Mon Sep 17 00:00:00 2001 From: Niklas Kiefer Date: Tue, 30 Oct 2018 12:51:34 +0100 Subject: [PATCH] feat(selection): add marker to related elements --- assets/diagram-js.css | 19 ++- lib/features/selection/HighlightRelated.js | 79 ++++++++++ lib/features/selection/index.js | 6 +- .../selection/HighlightRelatedSpec.js | 140 ++++++++++++++++++ 4 files changed, 241 insertions(+), 3 deletions(-) create mode 100644 lib/features/selection/HighlightRelated.js create mode 100644 test/spec/features/selection/HighlightRelatedSpec.js diff --git a/assets/diagram-js.css b/assets/diagram-js.css index 7c6f320dd..abcc57f12 100644 --- a/assets/diagram-js.css +++ b/assets/diagram-js.css @@ -8,7 +8,9 @@ } .djs-element.hover .djs-outline, -.djs-element.selected .djs-outline { +.djs-element.selected .djs-outline, +.djs-element.related-selected .djs-outline, +.djs-element.related-hover .djs-outline { visibility: visible; shape-rendering: crispEdges; stroke-dasharray: 3,3; @@ -16,14 +18,29 @@ .djs-element.selected .djs-outline { stroke: #8888FF; + opacity: 1; stroke-width: 1px; } .djs-element.hover .djs-outline { stroke: #FF8888; + opacity: 1; + stroke-width: 1px; +} + +.djs-element.related-selected .djs-outline { + stroke: #8888FF; + opacity: 0.5; + stroke-width: 1px; +} + +.djs-element.related-hover .djs-outline { + stroke: #FF8888; + opacity: 0.5; stroke-width: 1px; } + .djs-shape.connect-ok .djs-visual > :nth-child(1) { fill: #DCFECC /* light-green */ !important; } diff --git a/lib/features/selection/HighlightRelated.js b/lib/features/selection/HighlightRelated.js new file mode 100644 index 000000000..347dfe8f9 --- /dev/null +++ b/lib/features/selection/HighlightRelated.js @@ -0,0 +1,79 @@ +import { + forEach +} from 'min-dash'; + +import { + getType +} from '../../util/Elements'; + +var MARKER_RELATED_SELECTED = 'related-selected', + MARKER_RELATED_HOVER = 'related-hover'; + + +/** + * A plugin that adds a visible selection UI to related elements after an element + * was selected by appending the related-selected and + * related-hover classes to them. + * + * @class + * + * Creates outline on related elements after selecting an element + * + * @param {EventBus} events + * @param {Canvas} canvas + */ +export default function HighlightRelated(events, canvas) { + + function applyToRelatedElements(e, cls, fn) { + + // shape, connection -> mark related labels + if (getType(e) === 'shape' || getType(e) === 'connection') { + forEach(e.labels, function(label) { + fn(label, cls); + }); + } + + // label -> mark related shape, connection + if (e.labelTarget) { + fn(e.labelTarget, cls); + } + } + + function addMarkerToRelated(e, cls) { + applyToRelatedElements(e, cls, canvas.addMarker.bind(canvas)); + } + + function removeMarkerFromRelated(e, cls) { + applyToRelatedElements(e, cls, canvas.removeMarker.bind(canvas)); + } + + events.on('element.hover', function(event) { + addMarkerToRelated(event.element, MARKER_RELATED_HOVER); + }); + + events.on('element.out', function(event) { + removeMarkerFromRelated(event.element, MARKER_RELATED_HOVER); + }); + + events.on('selection.changed', function(event) { + var oldSelection = event.oldSelection, + newSelection = event.newSelection; + + forEach(oldSelection, function(e) { + if (newSelection.indexOf(e) === -1) { + removeMarkerFromRelated(e, MARKER_RELATED_SELECTED); + } + }); + + forEach(newSelection, function(e) { + if (oldSelection.indexOf(e) === -1) { + addMarkerToRelated(e, MARKER_RELATED_SELECTED); + } + }); + }); +} + +HighlightRelated.$inject = [ + 'eventBus', + 'canvas', +]; \ No newline at end of file diff --git a/lib/features/selection/index.js b/lib/features/selection/index.js index 0ce1223e6..37071349d 100644 --- a/lib/features/selection/index.js +++ b/lib/features/selection/index.js @@ -4,15 +4,17 @@ import OutlineModule from '../outline'; import Selection from './Selection'; import SelectionVisuals from './SelectionVisuals'; import SelectionBehavior from './SelectionBehavior'; +import HighlightRelated from './HighlightRelated'; export default { - __init__: [ 'selectionVisuals', 'selectionBehavior' ], + __init__: [ 'selectionVisuals', 'selectionBehavior', 'highlightRelated' ], __depends__: [ InteractionEventsModule, OutlineModule ], selection: [ 'type', Selection ], selectionVisuals: [ 'type', SelectionVisuals ], - selectionBehavior: [ 'type', SelectionBehavior ] + selectionBehavior: [ 'type', SelectionBehavior ], + highlightRelated: [ 'type', HighlightRelated ] }; diff --git a/test/spec/features/selection/HighlightRelatedSpec.js b/test/spec/features/selection/HighlightRelatedSpec.js new file mode 100644 index 000000000..311826f4a --- /dev/null +++ b/test/spec/features/selection/HighlightRelatedSpec.js @@ -0,0 +1,140 @@ +import { + bootstrapDiagram, + inject +} from 'test/TestHelper'; + +import selectionModule from 'lib/features/selection'; + +import { + matches +} from 'min-dom'; + + +describe('features/selection/HighlightRelated', function() { + + beforeEach(bootstrapDiagram({ modules: [ selectionModule ] })); + + describe('bootstrap', function() { + + beforeEach(bootstrapDiagram({ modules: [ selectionModule ] })); + + it('should bootstrap diagram with component', inject(function() { + + })); + + }); + + describe('selection box on related elements', function() { + + var shape, shape2, connection, label, label2; + + beforeEach(inject(function(elementFactory, canvas) { + + // given + shape = elementFactory.createShape({ + id: 'child', + x: 100, y: 100, width: 100, height: 100 + }); + + canvas.addShape(shape); + + shape2 = elementFactory.createShape({ + id: 'child2', + x: 300, y: 100, width: 100, height: 100 + }); + + canvas.addShape(shape2); + + connection = elementFactory.createConnection({ + id: 'connection', + waypoints: [ { x: 150, y: 150 }, { x: 150, y: 200 }, { x: 350, y: 150 } ], + source: shape, + target: shape2 + }); + + canvas.addConnection(connection); + + label = elementFactory.createLabel({ + id: 'label', + x: 100, y: 200, width: 20, height: 20, + labelTarget: shape + }); + + canvas.addShape(label); + + label2 = elementFactory.createLabel({ + id: 'label2', + x: 200, y: 200, width: 20, height: 20, + labelTarget: connection + }); + + canvas.addShape(label2); + })); + + describe('shapes', function() { + + + it('should show box on related label on select', + inject(function(selection, canvas) { + + // when + selection.select(shape); + + // then + var gfx = canvas.getGraphics(label), + hasOutline = matches(gfx, '.related-selected'); + + expect(hasOutline).to.be.true; + })); + + + it('should show box on shape on selecting label', + inject(function(selection, canvas) { + + // when + selection.select(label); + + // then + var gfx = canvas.getGraphics(shape), + hasOutline = matches(gfx, '.related-selected'); + + expect(hasOutline).to.be.true; + })); + }); + + + describe('connection', function() { + + + it('should show box on related label on select', + inject(function(selection, canvas) { + + // when + selection.select(connection); + + // then + var gfx = canvas.getGraphics(label2), + hasOutline = matches(gfx, '.related-selected'); + + expect(hasOutline).to.be.true; + })); + + + it('should paler box on connection on selecting label', + inject(function(selection, canvas) { + + // when + selection.select(label2); + + // then + var gfx = canvas.getGraphics(connection), + hasOutline = matches(gfx, '.related-selected'); + + expect(hasOutline).to.be.true; + })); + + }); + + }); + +});