From 0d871fe60168760f3f5950be47f95e09f21de783 Mon Sep 17 00:00:00 2001 From: Philipp Date: Mon, 22 Jan 2024 10:35:43 +0100 Subject: [PATCH] chore: remove touch interaction Closes #796 --- assets/diagram-js.css | 11 +- lib/features/touch/TouchFix.js | 63 --- lib/features/touch/TouchInteractionEvents.js | 372 ------------------ lib/features/touch/index.js | 15 - lib/navigation/touch/index.js | 11 - .../touch/TouchInteractionEventsSpec.js | 50 --- .../navigation/touch/TouchInteractionSpec.js | 71 ---- 7 files changed, 1 insertion(+), 592 deletions(-) delete mode 100644 lib/features/touch/TouchFix.js delete mode 100644 lib/features/touch/TouchInteractionEvents.js delete mode 100644 lib/features/touch/index.js delete mode 100644 lib/navigation/touch/index.js delete mode 100644 test/spec/features/touch/TouchInteractionEventsSpec.js delete mode 100755 test/spec/navigation/touch/TouchInteractionSpec.js diff --git a/assets/diagram-js.css b/assets/diagram-js.css index ada938d74..40baed1bc 100644 --- a/assets/diagram-js.css +++ b/assets/diagram-js.css @@ -749,22 +749,13 @@ marker.djs-dragger tspan { } /** - * touch + * bendpoints */ - -.djs-shape, -.djs-connection { - touch-action: none; -} - .djs-segment-dragger, .djs-bendpoint { display: none; } -/** - * bendpoints - */ .djs-segment-dragger .djs-visual { display: none; diff --git a/lib/features/touch/TouchFix.js b/lib/features/touch/TouchFix.js deleted file mode 100644 index 0e45621fd..000000000 --- a/lib/features/touch/TouchFix.js +++ /dev/null @@ -1,63 +0,0 @@ -import { - append as svgAppend, - attr as svgAttr, - create as svgCreate -} from 'tiny-svg'; - -/** - * @typedef {import('../../core/EventBus').default} EventBus - */ - -/** - * @param {EventBus} eventBus - */ -export default function TouchFix(eventBus) { - - var self = this; - - eventBus.on('canvas.init', function(e) { - self.addBBoxMarker(e.svg); - }); -} - -TouchFix.$inject = [ 'eventBus' ]; - - -/** - * Safari mobile (iOS 7) does not fire touchstart event in element - * if there is no shape between 0,0 and viewport elements origin. - * - * So touchstart event is only fired when the element was hit. - * Putting an element over and below the 'viewport' fixes that behavior. - * - * @param {SVGElement} svg - */ -TouchFix.prototype.addBBoxMarker = function(svg) { - - var markerStyle = { - fill: 'none', - class: 'outer-bound-marker' - }; - - var rect1 = svgCreate('rect'); - svgAttr(rect1, { - x: -10000, - y: 10000, - width: 10, - height: 10 - }); - svgAttr(rect1, markerStyle); - - svgAppend(svg, rect1); - - var rect2 = svgCreate('rect'); - svgAttr(rect2, { - x: 10000, - y: 10000, - width: 10, - height: 10 - }); - svgAttr(rect2, markerStyle); - - svgAppend(svg, rect2); -}; diff --git a/lib/features/touch/TouchInteractionEvents.js b/lib/features/touch/TouchInteractionEvents.js deleted file mode 100644 index a9ba9cd79..000000000 --- a/lib/features/touch/TouchInteractionEvents.js +++ /dev/null @@ -1,372 +0,0 @@ -import { - forEach -} from 'min-dash'; - -import { - event as domEvent, - closest as domClosest -} from 'min-dom'; - -import Hammer from 'hammerjs'; - -import { - toPoint -} from '../../util/Event'; - -/** - * @typedef {import('didi').Injector} Injector - * - * @typedef {import('../../core/Canvas').default} Canvas - * @typedef {import('../../core/ElementRegistry').default} ElementRegistry - * @typedef {import('../../core/EventBus').default} EventBus - * @typedef {import('../interaction-events/InteractionEvents').default} InteractionEvents - */ - -var MIN_ZOOM = 0.2, - MAX_ZOOM = 4; - -var mouseEvents = [ - 'mousedown', - 'mouseup', - 'mouseover', - 'mouseout', - 'click', - 'dblclick' -]; - -function log() { - - // console.log.apply(console, arguments); -} - -function get(service, injector) { - return injector.get(service, false); -} - -function stopEvent(event) { - - event.preventDefault(); - - if (typeof event.stopPropagation === 'function') { - event.stopPropagation(); - } else if (event.srcEvent && typeof event.srcEvent.stopPropagation === 'function') { - - // iPhone & iPad - event.srcEvent.stopPropagation(); - } - - if (typeof event.stopImmediatePropagation === 'function') { - event.stopImmediatePropagation(); - } -} - - -function createTouchRecognizer(node) { - - function stopMouse(event) { - - forEach(mouseEvents, function(e) { - domEvent.bind(node, e, stopEvent, true); - }); - } - - function allowMouse(event) { - setTimeout(function() { - forEach(mouseEvents, function(e) { - domEvent.unbind(node, e, stopEvent, true); - }); - }, 500); - } - - domEvent.bind(node, 'touchstart', stopMouse, true); - domEvent.bind(node, 'touchend', allowMouse, true); - domEvent.bind(node, 'touchcancel', allowMouse, true); - - // A touch event recognizer that handles - // touch events only (we know, we can already handle - // mouse events out of the box) - - var recognizer = new Hammer.Manager(node, { - inputClass: Hammer.TouchInput, - recognizers: [], - domEvents: true - }); - - - var tap = new Hammer.Tap(); - var pan = new Hammer.Pan({ threshold: 10 }); - var press = new Hammer.Press(); - var pinch = new Hammer.Pinch(); - - var doubleTap = new Hammer.Tap({ event: 'doubletap', taps: 2 }); - - pinch.requireFailure(pan); - pinch.requireFailure(press); - - recognizer.add([ pan, press, pinch, doubleTap, tap ]); - - recognizer.reset = function(force) { - var recognizers = this.recognizers, - session = this.session; - - if (session.stopped) { - return; - } - - log('recognizer', 'stop'); - - recognizer.stop(force); - - setTimeout(function() { - var i, r; - - log('recognizer', 'reset'); - for (i = 0; (r = recognizers[i]); i++) { - r.reset(); - r.state = 8; // FAILED STATE - } - - session.curRecognizer = null; - }, 0); - }; - - recognizer.on('hammer.input', function(event) { - if (event.srcEvent.defaultPrevented) { - recognizer.reset(true); - } - }); - - return recognizer; -} - -/** - * A plugin that provides touch events for elements. - * - * @param {Injector} injector - * @param {Canvas} canvas - * @param {EventBus} eventBus - * @param {ElementRegistry} elementRegistry - * @param {InteractionEvents} interactionEvents - */ -export default function TouchInteractionEvents( - injector, canvas, eventBus, - elementRegistry, interactionEvents) { - - // optional integrations - var dragging = get('dragging', injector), - move = get('move', injector), - contextPad = get('contextPad', injector), - palette = get('palette', injector); - - // the touch recognizer - var recognizer; - - function handler(type, buttonType) { - - return function(event) { - log('element', type, event); - - var gfx = getGfx(event.target), - element = gfx && elementRegistry.get(gfx); - - // translate into an actual mouse click event - if (buttonType) { - event.srcEvent.button = buttonType; - } - - return interactionEvents.fire(type, event, element); - }; - } - - - function getGfx(target) { - var node = domClosest(target, 'svg, .djs-element', true); - return node; - } - - function initEvents(svg) { - - // touch recognizer - recognizer = createTouchRecognizer(svg); - - function startGrabCanvas(event) { - - log('canvas', 'grab start'); - - var lx = 0, ly = 0; - - function update(e) { - - var dx = e.deltaX - lx, - dy = e.deltaY - ly; - - canvas.scroll({ dx: dx, dy: dy }); - - lx = e.deltaX; - ly = e.deltaY; - } - - function end(e) { - recognizer.off('panmove', update); - recognizer.off('panend', end); - recognizer.off('pancancel', end); - - log('canvas', 'grab end'); - } - - recognizer.on('panmove', update); - recognizer.on('panend', end); - recognizer.on('pancancel', end); - } - - function startGrab(event) { - - var gfx = getGfx(event.target), - element = gfx && elementRegistry.get(gfx); - - // recognizer - if (move && canvas.getRootElement() !== element) { - log('element', 'move start', element, event, true); - return move.start(event, element, true); - } else { - startGrabCanvas(event); - } - } - - function startZoom(e) { - - log('canvas', 'zoom start'); - - var zoom = canvas.zoom(), - mid = e.center; - - function update(e) { - - var ratio = 1 - (1 - e.scale) / 1.50, - newZoom = Math.max(MIN_ZOOM, Math.min(MAX_ZOOM, ratio * zoom)); - - canvas.zoom(newZoom, mid); - - stopEvent(e); - } - - function end(e) { - recognizer.off('pinchmove', update); - recognizer.off('pinchend', end); - recognizer.off('pinchcancel', end); - - recognizer.reset(true); - - log('canvas', 'zoom end'); - } - - recognizer.on('pinchmove', update); - recognizer.on('pinchend', end); - recognizer.on('pinchcancel', end); - } - - recognizer.on('tap', handler('element.click')); - recognizer.on('doubletap', handler('element.dblclick', 1)); - - recognizer.on('panstart', startGrab); - recognizer.on('press', startGrab); - - recognizer.on('pinchstart', startZoom); - } - - if (dragging) { - - // simulate hover during dragging - eventBus.on('drag.move', function(event) { - - var originalEvent = event.originalEvent; - - if (!originalEvent || originalEvent instanceof MouseEvent) { - return; - } - - var position = toPoint(originalEvent); - - // this gets really expensive ... - var node = document.elementFromPoint(position.x, position.y), - gfx = getGfx(node), - element = gfx && elementRegistry.get(gfx); - - if (element !== event.hover) { - if (event.hover) { - dragging.out(event); - } - - if (element) { - dragging.hover({ element: element, gfx: gfx }); - - event.hover = element; - event.hoverGfx = gfx; - } - } - }); - } - - if (contextPad) { - - eventBus.on('contextPad.create', function(event) { - var node = event.pad.html; - - // touch recognizer - var padRecognizer = createTouchRecognizer(node); - - padRecognizer.on('panstart', function(event) { - log('context-pad', 'panstart', event); - contextPad.trigger('dragstart', event, true); - }); - - padRecognizer.on('press', function(event) { - log('context-pad', 'press', event); - contextPad.trigger('dragstart', event, true); - }); - - padRecognizer.on('tap', function(event) { - log('context-pad', 'tap', event); - contextPad.trigger('click', event); - }); - }); - } - - if (palette) { - eventBus.on('palette.create', function(event) { - var node = event.container; - - // touch recognizer - var padRecognizer = createTouchRecognizer(node); - - padRecognizer.on('panstart', function(event) { - log('palette', 'panstart', event); - palette.trigger('dragstart', event, true); - }); - - padRecognizer.on('press', function(event) { - log('palette', 'press', event); - palette.trigger('dragstart', event, true); - }); - - padRecognizer.on('tap', function(event) { - log('palette', 'tap', event); - palette.trigger('click', event); - }); - }); - } - - eventBus.on('canvas.init', function(event) { - initEvents(event.svg); - }); -} - - -TouchInteractionEvents.$inject = [ - 'injector', - 'canvas', - 'eventBus', - 'elementRegistry', - 'interactionEvents', - 'touchFix' -]; diff --git a/lib/features/touch/index.js b/lib/features/touch/index.js deleted file mode 100644 index 422fa7a16..000000000 --- a/lib/features/touch/index.js +++ /dev/null @@ -1,15 +0,0 @@ -import InteractionEventsModule from '../interaction-events'; - -import TouchInteractionEvents from './TouchInteractionEvents'; -import TouchFix from './TouchFix'; - - -/** - * @type { import('didi').ModuleDeclaration } - */ -export default { - __depends__: [ InteractionEventsModule ], - __init__: [ 'touchInteractionEvents' ], - touchInteractionEvents: [ 'type', TouchInteractionEvents ], - touchFix: [ 'type', TouchFix ] -}; \ No newline at end of file diff --git a/lib/navigation/touch/index.js b/lib/navigation/touch/index.js deleted file mode 100644 index a6646c9b4..000000000 --- a/lib/navigation/touch/index.js +++ /dev/null @@ -1,11 +0,0 @@ -import TouchModule from '../../features/touch'; - - -/** - * @type { import('didi').ModuleDeclaration } - */ -export default { - __depends__: [ - TouchModule - ] -}; \ No newline at end of file diff --git a/test/spec/features/touch/TouchInteractionEventsSpec.js b/test/spec/features/touch/TouchInteractionEventsSpec.js deleted file mode 100644 index f415c89b6..000000000 --- a/test/spec/features/touch/TouchInteractionEventsSpec.js +++ /dev/null @@ -1,50 +0,0 @@ -import { - bootstrapDiagram, - inject -} from 'test/TestHelper'; - -import { - forEach -} from 'min-dash'; - -import touchInteractionModule from 'lib/features/touch'; - - -describe('features/touch', function() { - - describe('bootstrap', function() { - - beforeEach(bootstrapDiagram({ modules: [ touchInteractionModule ] })); - - it('should bootstrap diagram with component', inject(function(eventBus, canvas) { - - // given - var touchEvents = [ - 'shape.tap', - 'shape.dbltap', - 'shape.click', - 'shape.dblclick', - 'connection.tap', - 'connection.dbltap', - 'connection.click', - 'connection.dblclick', - 'canvas.click', - 'canvas.tap' - ]; - - forEach(touchEvents, function(eventName) { - eventBus.on(eventName, function(e) { - console.log(eventName, e); - }); - }); - - canvas.addShape({ id: 's1', x: 100, y: 200, width: 50, height: 50 }); - canvas.addShape({ id: 's2', x: 300, y: 200, width: 50, height: 50 }); - - canvas.addConnection({ id: 'c1', waypoints: [ { x: 150, y: 225 }, { x: 300, y: 225 } ] }); - - })); - - }); - -}); diff --git a/test/spec/navigation/touch/TouchInteractionSpec.js b/test/spec/navigation/touch/TouchInteractionSpec.js deleted file mode 100755 index 139b89cd8..000000000 --- a/test/spec/navigation/touch/TouchInteractionSpec.js +++ /dev/null @@ -1,71 +0,0 @@ -import { - bootstrapDiagram, - inject -} from 'test/TestHelper'; - -import touchModule from 'lib/navigation/touch'; -import contextPadModule from 'lib/features/context-pad'; -import paletteModule from 'lib/features/palette'; - - -describe('navigation/touch', function() { - - describe('bootstrap', function() { - - beforeEach(bootstrapDiagram({ modules: [ touchModule ] })); - - it('should bootstrap', inject(function(canvas) { - - canvas.addShape({ - id: 'test', - width: 100, - height: 100, - x: 100, - y: 100 - }); - - })); - - }); - - - describe('integration', function() { - - describe('contextPad', function() { - - beforeEach(bootstrapDiagram({ modules: [ contextPadModule, touchModule ] })); - - it('should integrate with contextPad.create', inject(function(canvas, contextPad) { - - canvas.addShape({ - id: 'test', - width: 100, - height: 100, - x: 100, - y: 100 - }); - })); - - }); - - describe('palette', function() { - - beforeEach(bootstrapDiagram({ modules: [ paletteModule, touchModule ] })); - - it('should integrate with palette.create', inject(function(canvas, palette) { - - canvas.addShape({ - id: 'test', - width: 100, - height: 100, - x: 100, - y: 100 - }); - - })); - - }); - - }); - -});