Skip to content

Commit

Permalink
WIP: experiment with native browser focus handling
Browse files Browse the repository at this point in the history
Related to #281
  • Loading branch information
nikku committed Oct 4, 2018
1 parent 30262ba commit 30ecd19
Show file tree
Hide file tree
Showing 8 changed files with 196 additions and 40 deletions.
16 changes: 14 additions & 2 deletions assets/diagram-js.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
*/

.djs-outline {
fill: none;
fill: #CCCCCC66;
visibility: hidden;
}

.djs-element.hover .djs-outline,
.djs-element.selected .djs-outline {
.djs-element.selected .djs-outline,
.djs-element.focussed .djs-outline {
visibility: visible;
shape-rendering: crispEdges;
stroke-dasharray: 3,3;
Expand All @@ -24,6 +25,11 @@
stroke-width: 1px;
}

.djs-element.focussed .djs-outline {
stroke: #8888FF;
stroke-width: 2px;
}

.djs-shape.connect-ok .djs-visual > :nth-child(1) {
fill: #DCFECC /* light-green */ !important;
}
Expand Down Expand Up @@ -170,10 +176,12 @@ svg.new-parent {
*/
.djs-shape .djs-hit {
pointer-events: all;
outline: none;
}

.djs-connection .djs-hit {
pointer-events: stroke;
outline: none;
}

/**
Expand Down Expand Up @@ -358,6 +366,10 @@ svg.new-parent {
background-color: #FEFEFE;
box-shadow: 0 0 2px 1px #FEFEFE;
pointer-events: all;

border: none;
outline: none;
padding: 0;
}

.djs-context-pad .entry:before {
Expand Down
18 changes: 12 additions & 6 deletions lib/features/context-pad/ContextPad.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,22 @@ var entrySelector = '.entry';
*
* @param {EventBus} eventBus
* @param {Overlays} overlays
* @param {Canvas} canvas
*/
export default function ContextPad(eventBus, overlays) {
export default function ContextPad(eventBus, overlays, canvas) {

this._providers = [];

this._eventBus = eventBus;
this._overlays = overlays;
this._canvas = canvas;

this._current = null;

this._init();
}

ContextPad.$inject = [ 'eventBus', 'overlays' ];
ContextPad.$inject = [ 'eventBus', 'overlays', 'canvas' ];


/**
Expand All @@ -44,15 +46,19 @@ ContextPad.$inject = [ 'eventBus', 'overlays' ];
ContextPad.prototype._init = function() {

var eventBus = this._eventBus;
var canvas = this._canvas;

var self = this;

eventBus.on('selection.changed', function(e) {

var selection = e.newSelection;
var selection = e.newSelection,
focussed = e.focussed;

if (selection.length === 1) {
self.open(selection[0]);
var target = selection.length === 1 && (focussed || selection[0]);

if (target && canvas.getRootElement() !== target) {
self.open(target);
} else {
self.close();
}
Expand Down Expand Up @@ -179,7 +185,7 @@ ContextPad.prototype._updateAndOpen = function(element) {

forEach(entries, function(entry, id) {
var grouping = entry.group || 'default',
control = domify(entry.html || '<div class="entry" draggable="true"></div>'),
control = domify(entry.html || '<button class="entry" draggable="true"></button>'),
container;

domAttr(control, 'data-action', id);
Expand Down
50 changes: 48 additions & 2 deletions lib/features/interaction-events/InteractionEvents.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ export default function InteractionEvents(eventBus, elementRegistry, styles) {

var HIT_STYLE = styles.cls('djs-hit', [ 'no-fill', 'no-border' ], {
stroke: 'white',
strokeWidth: 15
strokeWidth: 15,
tabindex: 0
});

/**
Expand Down Expand Up @@ -117,7 +118,9 @@ export default function InteractionEvents(eventBus, elementRegistry, styles) {
dblclick: 'element.dblclick',
mousedown: 'element.mousedown',
mouseup: 'element.mouseup',
contextmenu: 'element.contextmenu'
contextmenu: 'element.contextmenu',
focus: 'element.focus',
blur: 'element.blur'
};

var ignoredFilters = {
Expand Down Expand Up @@ -219,6 +222,27 @@ export default function InteractionEvents(eventBus, elementRegistry, styles) {
svgAppend(gfx, hit);
});

eventBus.on('selection.changed', 500, function(event) {

var newSelection = event.newSelection;

var element = newSelection[0];

if (!element) {
return;
}

var gfx = elementRegistry.getGraphics(element);

var hit = domQuery('.djs-hit', gfx);

if (newSelection.length) {
if (document.activeElement !== hit) {
// hit.focus();
}
}
});

// Update djs-hit on change.
// A low priortity is necessary, because djs-hit of labels has to be updated
// after the label bounds have been updated in the renderer.
Expand Down Expand Up @@ -340,4 +364,26 @@ InteractionEvents.$inject = [
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/

/**
* An event indicating that the element received user focus.
*
* @event element.focus
*
* @type {Object}
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/

/**
* An event indicating that the element lost user focus.
*
* @event element.blur
*
* @type {Object}
* @property {djs.model.Base} element
* @property {SVGElement} gfx
* @property {Event} originalEvent
*/
6 changes: 3 additions & 3 deletions lib/features/outline/Outline.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { getBBox } from '../../util/Elements';
var LOW_PRIORITY = 500;

import {
append as svgAppend,
prepend as svgPrepend,
attr as svgAttr,
create as svgCreate
} from 'tiny-svg';
Expand Down Expand Up @@ -31,7 +31,7 @@ export default function Outline(eventBus, styles, elementRegistry) {

this.offset = 6;

var OUTLINE_STYLE = styles.cls('djs-outline', [ 'no-fill' ]);
var OUTLINE_STYLE = styles.cls('djs-outline');

var self = this;

Expand All @@ -45,7 +45,7 @@ export default function Outline(eventBus, styles, elementRegistry) {
height: 100
}, OUTLINE_STYLE));

svgAppend(gfx, outline);
svgPrepend(gfx, outline);

return outline;
}
Expand Down
43 changes: 37 additions & 6 deletions lib/features/selection/Selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Selection.$inject = [ 'eventBus' ];
Selection.prototype.deselect = function(element) {
var selectedElements = this._selectedElements;

console.log(`deselect #${element.id}`);

var idx = selectedElements.indexOf(element);

if (idx !== -1) {
Expand Down Expand Up @@ -69,8 +71,9 @@ Selection.prototype.isSelected = function(element) {
* @param {boolean} [add] whether the element(s) should be appended to the current selection, defaults to false
*/
Selection.prototype.select = function(elements, add) {
var selectedElements = this._selectedElements,
oldSelection = selectedElements.slice();

var oldSelection = this._selectedElements,
newSelection = oldSelection;

if (!isArray(elements)) {
elements = elements ? [ elements ] : [];
Expand All @@ -80,16 +83,44 @@ Selection.prototype.select = function(elements, add) {
// to the method
if (add) {
forEach(elements, function(element) {
if (selectedElements.indexOf(element) !== -1) {
if (newSelection.indexOf(element) !== -1) {
// already selected
return;
} else {
selectedElements.push(element);
newSelection = newSelection.concat(element);
}
});
} else {
this._selectedElements = selectedElements = elements.slice();
newSelection = elements.slice();
}

this._eventBus.fire('selection.changed', { oldSelection: oldSelection, newSelection: selectedElements });
// only emit selection changed on actual changes
if (newSelection !== oldSelection) {

console.log(`select #${elements.map(e => e.id).join(', ')} (add=${add})`);

this._selectedElements = newSelection;

this._eventBus.fire('selection.changed', {
oldSelection: oldSelection,
newSelection: newSelection,
selection: newSelection,
focussed: this._focussedElement
});
}
};


Selection.prototype.setFocussed = function(element) {

const oldFocussed = this._focussedElement;

this._focussedElement = element;

this._eventBus.fire('selection.focusChanged', {
oldFocussed: oldFocussed,
newFocussed: element,
focussed: element,
selection: this._selectedElements
});
};
68 changes: 53 additions & 15 deletions lib/features/selection/SelectionBehavior.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
hasPrimaryModifier
hasPrimaryModifier,
hasSecondaryModifier
} from '../../util/Mouse';

import {
Expand Down Expand Up @@ -50,32 +51,69 @@ export default function SelectionBehavior(

var element = event.element;

// do not select the root element
// or connections
// don't select root element
if (element === canvas.getRootElement()) {
element = null;
}

var isSelected = selection.isSelected(element),
isMultiSelect = selection.get().length > 1;
console.log(`click #${element && element.id}`);

// mouse-event: SELECTION_KEY
var add = hasPrimaryModifier(event);
// SHIFT + CLICK : add element to selection
var forceAdd = hasSecondaryModifier(event);

// select OR deselect element in multi selection
if (isSelected && isMultiSelect) {
if (add) {
if (forceAdd) {
return selection.select(element, true);
}

var isSelected = selection.isSelected(element);

// CTRL + CLICK : toggle element selection
var toggleAdd = hasPrimaryModifier(event);

if (toggleAdd) {
if (isSelected) {
return selection.deselect(element);
} else {
return selection.select(element);
return selection.select(element, true);
}
} else
}

// CLICK : select element
if (!isSelected) {
selection.select(element, add);
} else {
selection.deselect(element);
selection.select(element);
}
});

eventBus.on('element.focus', function(event) {
var element = event.element;

console.log(`focus #${element.id}`);

selection.setFocussed(element);

const timeout = setTimeout(function() {

console.log('FOCUS WITHOUT CLICK');

if (selection.isSelected(element)) {
return;
}

selection.select(element, false);
}, 100);

eventBus.on('element.click', function() {
clearTimeout(timeout);
});
});

eventBus.on('element.blur', function(event) {
var element = event.element;

console.log(`blur #${element.id}`);

selection.setFocussed(false);
});
}

SelectionBehavior.$inject = [
Expand Down
Loading

0 comments on commit 30ecd19

Please sign in to comment.