chore(navigation): move features to diagram-js
This commit moves the navigation features from bpmn-js to diagram-js so that they can be reused in other modeling / viewer tools. Related to #124 BREAKING CHANGE: Navigation features have been moved to diagram-js. If you use them in custom bundles, update their location accordingly: bpmn-js/lib/features/movecanvas -> diagram-js/lib/navigation/movecanvas bpmn-js/lib/features/zoomscroll -> diagram-js/lib/navigation/zoomscroll bpmn-js/lib/features/touch -> diagram-js/lib/navigation/touch
This commit is contained in:
parent
74900412d0
commit
5a2c5c425c
|
@ -56,16 +56,16 @@ Modeler.prototype.createModdle = function() {
|
||||||
Modeler.prototype._interactionModules = [
|
Modeler.prototype._interactionModules = [
|
||||||
// non-modeling components
|
// non-modeling components
|
||||||
require('./features/label-editing'),
|
require('./features/label-editing'),
|
||||||
require('./features/zoomscroll'),
|
require('diagram-js/lib/navigation/zoomscroll'),
|
||||||
require('./features/touch'),
|
require('diagram-js/lib/navigation/movecanvas'),
|
||||||
require('./features/movecanvas')
|
require('diagram-js/lib/navigation/touch')
|
||||||
];
|
];
|
||||||
|
|
||||||
Modeler.prototype._modelingModules = [
|
Modeler.prototype._modelingModules = [
|
||||||
// modeling components
|
// modeling components
|
||||||
require('bpmn-js-cli'),
|
require('bpmn-js-cli'),
|
||||||
require('./features/modeling'),
|
|
||||||
require('diagram-js/lib/features/move'),
|
require('diagram-js/lib/features/move'),
|
||||||
|
require('./features/modeling'),
|
||||||
require('./features/context-pad')
|
require('./features/context-pad')
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1,108 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var $ = require('jquery');
|
|
||||||
var _ = require('lodash');
|
|
||||||
|
|
||||||
function MoveCanvas(events, canvas) {
|
|
||||||
|
|
||||||
var THRESHOLD = 20;
|
|
||||||
|
|
||||||
function init(element) {
|
|
||||||
|
|
||||||
var context = {};
|
|
||||||
|
|
||||||
function getPosition(e) {
|
|
||||||
return { x: e.clientX, y: e.clientY };
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDelta(p1, p2) {
|
|
||||||
return {
|
|
||||||
x: p1.x - p2.x,
|
|
||||||
y: p1.y - p2.y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function isThresholdReached(delta) {
|
|
||||||
return Math.abs(delta.x) > THRESHOLD || Math.abs(delta.y) > THRESHOLD;
|
|
||||||
}
|
|
||||||
|
|
||||||
function cursor(value) {
|
|
||||||
var current = document.body.style.cursor || '';
|
|
||||||
|
|
||||||
if (value !== undefined) {
|
|
||||||
document.body.style.cursor = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleMove(event) {
|
|
||||||
|
|
||||||
var position = getPosition(event);
|
|
||||||
|
|
||||||
|
|
||||||
if (!context.dragging && isThresholdReached(getDelta(context.start, position))) {
|
|
||||||
context.dragging = true;
|
|
||||||
|
|
||||||
context.cursor = cursor('move');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (context.dragging) {
|
|
||||||
|
|
||||||
var lastPos = context.last || context.start;
|
|
||||||
|
|
||||||
var delta = getDelta(position, lastPos);
|
|
||||||
|
|
||||||
canvas.scroll({
|
|
||||||
dx: delta.x,
|
|
||||||
dy: delta.y
|
|
||||||
});
|
|
||||||
|
|
||||||
context.last = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent select
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function handleEnd(event) {
|
|
||||||
$(element).off('mousemove', handleMove);
|
|
||||||
$(document).off('mouseup', handleEnd);
|
|
||||||
|
|
||||||
if (context) {
|
|
||||||
cursor(context.cursor);
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent select
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleStart(event) {
|
|
||||||
|
|
||||||
var position = getPosition(event);
|
|
||||||
|
|
||||||
context = {
|
|
||||||
start: position
|
|
||||||
};
|
|
||||||
|
|
||||||
$(element).on('mousemove', handleMove);
|
|
||||||
$(document).on('mouseup', handleEnd);
|
|
||||||
|
|
||||||
// prevent select
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
$(element).on('mousedown', handleStart);
|
|
||||||
}
|
|
||||||
|
|
||||||
events.on('canvas.init', function(e) {
|
|
||||||
init(e.paper.node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
MoveCanvas.$inject = [ 'eventBus', 'canvas' ];
|
|
||||||
|
|
||||||
module.exports = MoveCanvas;
|
|
|
@ -1,4 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
__init__: [ 'moveCanvas' ],
|
|
||||||
moveCanvas: [ 'type', require('./MoveCanvas') ]
|
|
||||||
};
|
|
|
@ -1,198 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var $ = require('jquery');
|
|
||||||
var _ = require('lodash');
|
|
||||||
|
|
||||||
function TouchInteraction(eventBus, canvas) {
|
|
||||||
|
|
||||||
var context;
|
|
||||||
|
|
||||||
var RANGE = { min: 0.2, max: 4 };
|
|
||||||
var DOUBLE_TAP_THRESHOLD = 300; // ms
|
|
||||||
|
|
||||||
function handleTouchStart(event) {
|
|
||||||
|
|
||||||
var orgEvent = event.originalEvent ? event.originalEvent : event;
|
|
||||||
|
|
||||||
_.forEach(orgEvent.changedTouches, function (touch) {
|
|
||||||
context.start[touch.identifier] = touch;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (orgEvent.touches.length === 1) {
|
|
||||||
initMove(orgEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (orgEvent.touches.length === 2) {
|
|
||||||
initScale(orgEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Stop all interaction for more than 2 touch sources
|
|
||||||
if (orgEvent.touches.length > 2) {
|
|
||||||
contextInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTouchMove(event) {
|
|
||||||
var orgEvent = event.originalEvent;
|
|
||||||
var cX = 0;
|
|
||||||
var cY = 0;
|
|
||||||
var mX = 0;
|
|
||||||
var mY = 0;
|
|
||||||
|
|
||||||
if (context.move && !context.scale) {
|
|
||||||
|
|
||||||
_.forEach(orgEvent.changedTouches, function (touch) {
|
|
||||||
if (touch.identifier === context.moveId) {
|
|
||||||
cX = touch.clientX;
|
|
||||||
cY = touch.clientY;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!context.lasttouch.cX) {
|
|
||||||
context.lasttouch.cX = cX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!context.lasttouch.cY) {
|
|
||||||
context.lasttouch.cY = cY;
|
|
||||||
}
|
|
||||||
|
|
||||||
mX = cX - context.lasttouch.cX;
|
|
||||||
mY = cY - context.lasttouch.cY;
|
|
||||||
|
|
||||||
canvas.scroll({
|
|
||||||
dx: mX,
|
|
||||||
dy: mY
|
|
||||||
});
|
|
||||||
|
|
||||||
context.lasttouch.cX = cX;
|
|
||||||
context.lasttouch.cY = cY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (context.scale) {
|
|
||||||
var dist = euclideanDistance(orgEvent.touches[0], orgEvent.touches[1]);
|
|
||||||
var distRatio = dist / context.start.dist;
|
|
||||||
|
|
||||||
zoom(distRatio * context.start.zoom, context.start.mid);
|
|
||||||
context.lastDistance = dist;
|
|
||||||
}
|
|
||||||
|
|
||||||
// prevent select
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleTouchEnd(event) {
|
|
||||||
|
|
||||||
var orgEvent = event.originalEvent;
|
|
||||||
|
|
||||||
if (orgEvent.touches.length < 1) {
|
|
||||||
contextInit();
|
|
||||||
}
|
|
||||||
if (orgEvent.touches.length === 1) {
|
|
||||||
initMove(orgEvent);
|
|
||||||
}
|
|
||||||
if (orgEvent.touches.length === 2) {
|
|
||||||
initScale(orgEvent);
|
|
||||||
}
|
|
||||||
|
|
||||||
_.forEach(orgEvent.changedTouches, function (touch) {
|
|
||||||
delete context[touch.identifier];
|
|
||||||
});
|
|
||||||
|
|
||||||
// prevent select
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
function init(element) {
|
|
||||||
|
|
||||||
contextInit();
|
|
||||||
|
|
||||||
//--- general handlers
|
|
||||||
$(element).on('touchstart', handleTouchStart);
|
|
||||||
$(element).on('touchmove', handleTouchMove);
|
|
||||||
$(document).on('touchend', handleTouchEnd);
|
|
||||||
|
|
||||||
|
|
||||||
//--- snap handler
|
|
||||||
|
|
||||||
eventBus.on('shape.touchstart', function(event) {
|
|
||||||
// ---
|
|
||||||
// TODO must forwarded to touchstart???#
|
|
||||||
handleTouchStart(event);
|
|
||||||
});
|
|
||||||
|
|
||||||
eventBus.on('shape.touchend', function(event) {
|
|
||||||
|
|
||||||
if(event.touches.length !== 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if((Date.now() - context.lastTap.time) < DOUBLE_TAP_THRESHOLD) {
|
|
||||||
eventBus.fire('shape.dbltap', event);
|
|
||||||
}
|
|
||||||
|
|
||||||
context.lastTap.time = Date.now();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function cap(scale) {
|
|
||||||
return Math.max(RANGE.min, Math.min(RANGE.max, scale));
|
|
||||||
}
|
|
||||||
|
|
||||||
function zoom(pinchRatio, position) {
|
|
||||||
canvas.zoom(cap(pinchRatio), position);
|
|
||||||
}
|
|
||||||
|
|
||||||
function euclideanDistance(touch1, touch2) {
|
|
||||||
var dist =
|
|
||||||
Math.sqrt(
|
|
||||||
(touch1.clientX - touch2.clientX) *
|
|
||||||
(touch1.clientX - touch2.clientX) +
|
|
||||||
(touch1.clientY - touch2.clientY) *
|
|
||||||
(touch1.clientY - touch2.clientY)
|
|
||||||
);
|
|
||||||
|
|
||||||
return dist;
|
|
||||||
}
|
|
||||||
|
|
||||||
function initMove(orgEvent) {
|
|
||||||
context.move = true;
|
|
||||||
context.moveId = orgEvent.touches[0].identifier;
|
|
||||||
context.scale = false;
|
|
||||||
if (!context.lasttouch) {
|
|
||||||
context.lasttouch = {
|
|
||||||
cX: undefined,
|
|
||||||
cY: undefined,
|
|
||||||
mX: 0,
|
|
||||||
mY: 0
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function initScale(orgEvent) {
|
|
||||||
context.scale = true;
|
|
||||||
context.start.dist = euclideanDistance(orgEvent.touches[0], orgEvent.touches[1]);
|
|
||||||
context.lastDistance = context.start.dist;
|
|
||||||
context.start.zoom = canvas.zoom();
|
|
||||||
context.start.mid = {
|
|
||||||
x: (orgEvent.touches[0].clientX + orgEvent.touches[1].clientX) / 2,
|
|
||||||
y: (orgEvent.touches[0].clientY + orgEvent.touches[1].clientY) / 2
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function contextInit() {
|
|
||||||
context = {
|
|
||||||
start: {},
|
|
||||||
lastTap: {}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
eventBus.on('canvas.init', function(e) {
|
|
||||||
init(e.paper.node);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
TouchInteraction.$inject = [ 'eventBus', 'canvas', 'touchFix' ];
|
|
||||||
|
|
||||||
module.exports = TouchInteraction;
|
|
|
@ -1,7 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
__depends__: [
|
|
||||||
require('diagram-js/lib/features/touch')
|
|
||||||
],
|
|
||||||
__init__: [ 'touchInteraction' ],
|
|
||||||
touchInteraction: [ 'type', require('./TouchInteraction') ]
|
|
||||||
};
|
|
|
@ -1,90 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var $ = require('jquery');
|
|
||||||
|
|
||||||
var mousewheel = require('jquery-mousewheel');
|
|
||||||
if (mousewheel !== $ && !$.mousewheel) { mousewheel($); }
|
|
||||||
|
|
||||||
|
|
||||||
function ZoomScroll(events, canvas) {
|
|
||||||
|
|
||||||
var RANGE = { min: 0.2, max: 4 };
|
|
||||||
|
|
||||||
var ZOOM_OFFSET = 5;
|
|
||||||
var SCROLL_OFFSET = 50;
|
|
||||||
|
|
||||||
function cap(scale) {
|
|
||||||
return Math.max(RANGE.min, Math.min(RANGE.max, scale));
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
canvas.zoom('fit-viewport');
|
|
||||||
}
|
|
||||||
|
|
||||||
function zoom(direction, position) {
|
|
||||||
|
|
||||||
var currentZoom = canvas.zoom();
|
|
||||||
var factor = 1 + (direction / ZOOM_OFFSET);
|
|
||||||
|
|
||||||
canvas.zoom(cap(currentZoom * factor), position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function init(element) {
|
|
||||||
|
|
||||||
$(element).on('mousewheel', function(event) {
|
|
||||||
|
|
||||||
var shift = event.shiftKey,
|
|
||||||
ctrl = event.ctrlKey;
|
|
||||||
|
|
||||||
var x = event.deltaX,
|
|
||||||
y = event.deltaY;
|
|
||||||
|
|
||||||
if (shift || ctrl) {
|
|
||||||
var delta = {};
|
|
||||||
|
|
||||||
if (ctrl) {
|
|
||||||
delta.dx = SCROLL_OFFSET * x;
|
|
||||||
} else {
|
|
||||||
delta.dy = SCROLL_OFFSET * x;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.scroll(delta);
|
|
||||||
} else {
|
|
||||||
var offset = {};
|
|
||||||
|
|
||||||
// Gecko Browser should use _offsetX
|
|
||||||
if(!event.originalEvent.offsetX) {
|
|
||||||
offset = {
|
|
||||||
x: event.originalEvent.layerX,
|
|
||||||
y: event.originalEvent.layerY
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
offset = {
|
|
||||||
x: event.offsetX,
|
|
||||||
y: event.offsetY
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// zoom in relative to diagram {x,y} coordinates
|
|
||||||
zoom(y, offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
events.on('canvas.init', function(e) {
|
|
||||||
init(e.paper.node);
|
|
||||||
});
|
|
||||||
|
|
||||||
// API
|
|
||||||
this.zoom = zoom;
|
|
||||||
this.reset = reset;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
ZoomScroll.$inject = [ 'eventBus', 'canvas' ];
|
|
||||||
|
|
||||||
module.exports = ZoomScroll;
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
__init__: [ 'zoomScroll' ],
|
|
||||||
zoomScroll: [ 'type', require('./ZoomScroll') ]
|
|
||||||
};
|
|
|
@ -11,6 +11,7 @@
|
||||||
},
|
},
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"bpmn",
|
"bpmn",
|
||||||
|
"bpmn-js",
|
||||||
"toolkit",
|
"toolkit",
|
||||||
"web modeler",
|
"web modeler",
|
||||||
"modeler",
|
"modeler",
|
||||||
|
@ -56,7 +57,6 @@
|
||||||
"diagram-js-direct-editing": "^0.3.0",
|
"diagram-js-direct-editing": "^0.3.0",
|
||||||
"didi": "^0.0.4",
|
"didi": "^0.0.4",
|
||||||
"jquery": "^2.1.0",
|
"jquery": "^2.1.0",
|
||||||
"jquery-mousewheel": "^3.1.11",
|
|
||||||
"lodash": "^2.4.0",
|
"lodash": "^2.4.0",
|
||||||
"object-refs": "^0.1.0",
|
"object-refs": "^0.1.0",
|
||||||
"ids": "^0.0.3",
|
"ids": "^0.0.3",
|
||||||
|
|
|
@ -1,35 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var Matchers = require('../../../Matchers'),
|
|
||||||
TestHelper = require('../../../TestHelper');
|
|
||||||
|
|
||||||
/* global bootstrapViewer, inject */
|
|
||||||
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
|
|
||||||
var touchModule = require('../../../../lib/features/touch'),
|
|
||||||
bpmnModule = require('../../../../lib/draw');
|
|
||||||
|
|
||||||
|
|
||||||
describe('features - touch', function() {
|
|
||||||
|
|
||||||
beforeEach(Matchers.addDeepEquals);
|
|
||||||
|
|
||||||
|
|
||||||
var diagramXML = fs.readFileSync('test/fixtures/bpmn/complex.bpmn', 'utf-8');
|
|
||||||
|
|
||||||
var testModules = [ touchModule, bpmnModule ];
|
|
||||||
|
|
||||||
beforeEach(bootstrapViewer(diagramXML, { modules: testModules }));
|
|
||||||
|
|
||||||
|
|
||||||
describe('bootstrap', function() {
|
|
||||||
|
|
||||||
it('should bootstrap', inject(function(touchInteraction) {
|
|
||||||
expect(touchInteraction).not.toBe(null);
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -1,38 +0,0 @@
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var Matchers = require('../../../Matchers'),
|
|
||||||
TestHelper = require('../../../TestHelper');
|
|
||||||
|
|
||||||
/* global bootstrapViewer, inject */
|
|
||||||
|
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
var $ = require('jquery');
|
|
||||||
|
|
||||||
|
|
||||||
var zoomscrollModule = require('../../../../lib/features/zoomscroll'),
|
|
||||||
bpmnModule = require('../../../../lib/draw');
|
|
||||||
|
|
||||||
|
|
||||||
describe('features - zoomscroll', function() {
|
|
||||||
|
|
||||||
beforeEach(Matchers.addDeepEquals);
|
|
||||||
|
|
||||||
|
|
||||||
var diagramXML = fs.readFileSync('test/fixtures/bpmn/complex.bpmn', 'utf-8');
|
|
||||||
|
|
||||||
var testModules = [ zoomscrollModule, bpmnModule ];
|
|
||||||
|
|
||||||
beforeEach(bootstrapViewer(diagramXML, { modules: testModules }));
|
|
||||||
|
|
||||||
|
|
||||||
describe('bootstrap', function() {
|
|
||||||
|
|
||||||
it('should bootstrap', inject(function(zoomScroll) {
|
|
||||||
expect(zoomScroll).not.toBe(null);
|
|
||||||
}));
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
Loading…
Reference in New Issue