modules/behavior/drag.js (159 lines of code) (raw):

import { dispatch as d3_dispatch } from 'd3-dispatch'; import { customEvent as d3_customEvent, event as d3_event, mouse as d3_mouse, select as d3_select, selection as d3_selection, touches as d3_touches } from 'd3-selection'; import { osmNote } from '../osm'; import { utilRebind } from '../util/rebind'; import { utilPrefixCSSProperty, utilPrefixDOMProperty } from '../util'; /* `behaviorDrag` is like `d3_behavior.drag`, with the following differences: * The `origin` function is expected to return an [x, y] tuple rather than an {x, y} object. * The events are `start`, `move`, and `end`. (https://github.com/mbostock/d3/issues/563) * The `start` event is not dispatched until the first cursor movement occurs. (https://github.com/mbostock/d3/pull/368) * The `move` event has a `point` and `delta` [x, y] tuple properties rather than `x`, `y`, `dx`, and `dy` properties. * The `end` event is not dispatched if no movement occurs. * An `off` function is available that unbinds the drag's internal event handlers. */ export function behaviorDrag() { var dispatch = d3_dispatch('start', 'move', 'end'); var _origin = null; var _selector = ''; var _event; var _target; var _surface; var d3_event_userSelectProperty = utilPrefixCSSProperty('UserSelect'); var d3_event_userSelectSuppress = function() { var selection = d3_selection(); var select = selection.style(d3_event_userSelectProperty); selection.style(d3_event_userSelectProperty, 'none'); return function() { selection.style(d3_event_userSelectProperty, select); }; }; function d3_eventCancel() { d3_event.stopPropagation(); d3_event.preventDefault(); } function eventOf(thiz, argumentz) { return function(e1) { e1.target = behavior; d3_customEvent(e1, dispatch.apply, dispatch, [e1.type, thiz, argumentz]); }; } function dragstart() { _target = this; _event = eventOf(_target, arguments); var eventTarget = d3_event.target; var touchId = d3_event.touches ? d3_event.changedTouches[0].identifier : null; var offset; var startOrigin = point(); var started = false; var selectEnable = d3_event_userSelectSuppress(touchId !== null ? 'drag-' + touchId : 'drag'); d3_select(window) .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', dragmove) .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', dragend, true); if (_origin) { offset = _origin.apply(_target, arguments); offset = [offset[0] - startOrigin[0], offset[1] - startOrigin[1]]; } else { offset = [0, 0]; } if (touchId === null) { d3_event.stopPropagation(); } function point() { var p = _surface || _target.parentNode; return touchId !== null ? d3_touches(p).filter(function(p) { return p.identifier === touchId; })[0] : d3_mouse(p); } function dragmove() { var p = point(); var dx = p[0] - startOrigin[0]; var dy = p[1] - startOrigin[1]; if (dx === 0 && dy === 0) return; startOrigin = p; d3_eventCancel(); if (!started) { started = true; _event({ type: 'start' }); } else { _event({ type: 'move', point: [p[0] + offset[0], p[1] + offset[1]], delta: [dx, dy] }); } } function dragend() { if (started) { _event({ type: 'end' }); d3_eventCancel(); if (d3_event.target === eventTarget) { d3_select(window) .on('click.drag', click, true); } } d3_select(window) .on(touchId !== null ? 'touchmove.drag-' + touchId : 'mousemove.drag', null) .on(touchId !== null ? 'touchend.drag-' + touchId : 'mouseup.drag', null); selectEnable(); } function click() { d3_eventCancel(); d3_select(window) .on('click.drag', null); } } function behavior(selection) { var matchesSelector = utilPrefixDOMProperty('matchesSelector'); var delegate = dragstart; if (_selector) { delegate = function() { var root = this; var target = d3_event.target; for (; target && target !== root; target = target.parentNode) { var datum = target.__data__; var entity = datum instanceof osmNote ? datum : datum && datum.properties && datum.properties.entity; if (entity && target[matchesSelector](_selector)) { return dragstart.call(target, entity); } } }; } selection .on('mousedown.drag' + _selector, delegate) .on('touchstart.drag' + _selector, delegate); } behavior.off = function(selection) { selection .on('mousedown.drag' + _selector, null) .on('touchstart.drag' + _selector, null); }; behavior.selector = function(_) { if (!arguments.length) return _selector; _selector = _; return behavior; }; behavior.origin = function(_) { if (!arguments.length) return _origin; _origin = _; return behavior; }; behavior.cancel = function() { d3_select(window) .on('mousemove.drag', null) .on('mouseup.drag', null); return behavior; }; behavior.target = function() { if (!arguments.length) return _target; _target = arguments[0]; _event = eventOf(_target, Array.prototype.slice.call(arguments, 1)); return behavior; }; behavior.surface = function() { if (!arguments.length) return _surface; _surface = arguments[0]; return behavior; }; return utilRebind(behavior, dispatch, 'on'); }