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..6ee99cf4b
--- /dev/null
+++ b/test/spec/features/selection/HighlightRelatedSpec.js
@@ -0,0 +1,138 @@
+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;
+ }));
+
+ });
+
+ });
+
+});