mirror of
https://github.com/Shopify/draggable.git
synced 2025-12-08 20:15:56 +00:00
Sensor refactor
This commit is contained in:
parent
f09f118930
commit
f11f2b7f94
@ -18,6 +18,7 @@ module.exports = {
|
||||
beforeEach: false,
|
||||
describe: false,
|
||||
test: false,
|
||||
xtest: false,
|
||||
expect: false,
|
||||
},
|
||||
};
|
||||
|
||||
@ -35,7 +35,6 @@ const onDragMove = Symbol('onDragMove');
|
||||
const onDragStop = Symbol('onDragStop');
|
||||
const onDragPressure = Symbol('onDragPressure');
|
||||
const getAppendableContainer = Symbol('getAppendableContainer');
|
||||
const closestContainer = Symbol('closestContainer');
|
||||
|
||||
const defaults = {
|
||||
draggable: '.draggable-source',
|
||||
@ -431,11 +430,12 @@ export default class Draggable {
|
||||
}
|
||||
|
||||
target = closest(target, this.options.draggable);
|
||||
const overContainer = sensorEvent.overContainer || this[closestContainer](sensorEvent.target);
|
||||
const withinCorrectContainer = closest(sensorEvent.target, this.containers);
|
||||
const overContainer = sensorEvent.overContainer || withinCorrectContainer;
|
||||
const isLeavingContainer = this.currentOverContainer && (overContainer !== this.currentOverContainer);
|
||||
const isLeavingDraggable = this.currentOver && (target !== this.currentOver);
|
||||
const isOverContainer = overContainer && (this.currentOverContainer !== overContainer);
|
||||
const isOverDraggable = target && (this.currentOver !== target);
|
||||
const isOverDraggable = withinCorrectContainer && target && (this.currentOver !== target);
|
||||
|
||||
if (isLeavingDraggable) {
|
||||
const dragOutEvent = new DragOutEvent({
|
||||
@ -625,23 +625,6 @@ export default class Draggable {
|
||||
return document.body;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns closest container for target element
|
||||
* @private
|
||||
* @param {HTMLElement} target - A target element
|
||||
* @return {String}
|
||||
*/
|
||||
[closestContainer](target) {
|
||||
return closest(target, (element) => {
|
||||
for (const containerEl of this.containers) {
|
||||
if (element === containerEl) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getSensorEvent(event) {
|
||||
|
||||
@ -8,54 +8,90 @@ import {
|
||||
DragStopSensorEvent,
|
||||
} from './../SensorEvent';
|
||||
|
||||
const onMouseDown = Symbol('onMouseDown');
|
||||
const onMouseUp = Symbol('onMouseUp');
|
||||
const onDragStart = Symbol('onDragStart');
|
||||
const onDragOver = Symbol('onDragOver');
|
||||
const onDragEnd = Symbol('onDragEnd');
|
||||
const onDrop = Symbol('onDrop');
|
||||
const reset = Symbol('reset');
|
||||
|
||||
/**
|
||||
* This sensor picks up native browser drag events and dictates drag operations
|
||||
* @class DragSensor
|
||||
* @module DragSensor
|
||||
* @extends Sensor
|
||||
*/
|
||||
export default class DragSensor extends Sensor {
|
||||
|
||||
/**
|
||||
* DragSensor constructor.
|
||||
* @constructs DragSensor
|
||||
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
|
||||
* @param {Object} options - Options
|
||||
*/
|
||||
constructor(containers = [], options = {}) {
|
||||
super(containers, options);
|
||||
|
||||
this.dragging = false;
|
||||
this.currentContainer = null;
|
||||
/**
|
||||
* Mouse down timer which will end up setting the draggable attribute, unless canceled
|
||||
* @property mouseDownTimeout
|
||||
* @type {Number}
|
||||
*/
|
||||
this.mouseDownTimeout = null;
|
||||
|
||||
this._onMouseDown = this._onMouseDown.bind(this);
|
||||
this._onMouseUp = this._onMouseUp.bind(this);
|
||||
this._onDragStart = this._onDragStart.bind(this);
|
||||
this._onDragOver = this._onDragOver.bind(this);
|
||||
this._onDragEnd = this._onDragEnd.bind(this);
|
||||
this._onDragDrop = this._onDragDrop.bind(this);
|
||||
/**
|
||||
* Draggable element needs to be remembered to unset the draggable attribute after drag operation has completed
|
||||
* @property draggableElement
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
this.draggableElement = null;
|
||||
|
||||
/**
|
||||
* Native draggable element could be links or images, their draggable state will be disabled during drag operation
|
||||
* @property nativeDraggableElement
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
this.nativeDraggableElement = null;
|
||||
|
||||
this[onMouseDown] = this[onMouseDown].bind(this);
|
||||
this[onMouseUp] = this[onMouseUp].bind(this);
|
||||
this[onDragStart] = this[onDragStart].bind(this);
|
||||
this[onDragOver] = this[onDragOver].bind(this);
|
||||
this[onDragEnd] = this[onDragEnd].bind(this);
|
||||
this[onDrop] = this[onDrop].bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches sensors event listeners to the DOM
|
||||
*/
|
||||
attach() {
|
||||
for (const container of this.containers) {
|
||||
container.addEventListener('mousedown', this._onMouseDown, true);
|
||||
container.addEventListener('dragstart', this._onDragStart, false);
|
||||
container.addEventListener('dragover', this._onDragOver, false);
|
||||
container.addEventListener('dragend', this._onDragEnd, false);
|
||||
container.addEventListener('drop', this._onDragDrop, false);
|
||||
}
|
||||
|
||||
document.addEventListener('mouseup', this._onMouseUp, true);
|
||||
document.addEventListener('mousedown', this[onMouseDown], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches sensors event listeners to the DOM
|
||||
*/
|
||||
detach() {
|
||||
for (const container of this.containers) {
|
||||
container.removeEventListener('mousedown', this._onMouseDown, true);
|
||||
container.removeEventListener('dragstart', this._onDragStart, false);
|
||||
container.removeEventListener('dragover', this._onDragOver, false);
|
||||
container.removeEventListener('dragend', this._onDragEnd, false);
|
||||
container.removeEventListener('drop', this._onDragDrop, false);
|
||||
}
|
||||
|
||||
document.removeEventListener('mouseup', this._onMouseUp, true);
|
||||
document.removeEventListener('mousedown', this[onMouseDown], true);
|
||||
}
|
||||
|
||||
// private
|
||||
|
||||
_onDragStart(event) {
|
||||
/**
|
||||
* Drag start handler
|
||||
* @private
|
||||
* @param {Event} event - Drag start event
|
||||
*/
|
||||
[onDragStart](event) {
|
||||
// Need for firefox. "text" key is needed for IE
|
||||
event.dataTransfer.setData('text', '');
|
||||
event.dataTransfer.effectAllowed = this.options.type;
|
||||
|
||||
const target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
this.currentContainer = event.currentTarget;
|
||||
this.currentContainer = closest(event.target, this.containers);
|
||||
|
||||
if (!this.currentContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dragStartEvent = new DragStartSensorEvent({
|
||||
clientX: event.clientX,
|
||||
@ -65,95 +101,153 @@ export default class DragSensor extends Sensor {
|
||||
originalEvent: event,
|
||||
});
|
||||
|
||||
this.trigger(this.currentContainer, dragStartEvent);
|
||||
// Workaround
|
||||
setTimeout(() => {
|
||||
this.trigger(this.currentContainer, dragStartEvent);
|
||||
|
||||
if (dragStartEvent.canceled()) {
|
||||
this.dragging = false;
|
||||
// prevent drag event if fired event has been prevented
|
||||
event.preventDefault();
|
||||
} else {
|
||||
this.dragging = true;
|
||||
}
|
||||
if (dragStartEvent.canceled()) {
|
||||
this.dragging = false;
|
||||
} else {
|
||||
this.dragging = true;
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
|
||||
_onDragOver(event) {
|
||||
/**
|
||||
* Drag over handler
|
||||
* @private
|
||||
* @param {Event} event - Drag over event
|
||||
*/
|
||||
[onDragOver](event) {
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
const container = event.currentTarget;
|
||||
const container = this.currentContainer;
|
||||
|
||||
const dragMoveEvent = new DragMoveSensorEvent({
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
target,
|
||||
container: this.currentContainer,
|
||||
overContainer: container,
|
||||
container,
|
||||
originalEvent: event,
|
||||
});
|
||||
|
||||
this.trigger(container, dragMoveEvent);
|
||||
|
||||
// event.preventDefault();
|
||||
// event.dataTransfer.dropEffect = 'copy';
|
||||
|
||||
if (!dragMoveEvent.canceled()) {
|
||||
event.preventDefault();
|
||||
// event.dataTransfer.dropEffect = this.options.type;
|
||||
event.dataTransfer.dropEffect = this.options.type;
|
||||
}
|
||||
}
|
||||
|
||||
_onDragEnd(event) {
|
||||
/**
|
||||
* Drag end handler
|
||||
* @private
|
||||
* @param {Event} event - Drag end event
|
||||
*/
|
||||
[onDragEnd](event) {
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
// prevent click on drop if draggable contains a clickable element
|
||||
event.preventDefault();
|
||||
document.removeEventListener('mouseup', this[onMouseUp], true);
|
||||
|
||||
const container = event.currentTarget;
|
||||
const target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
const container = this.currentContainer;
|
||||
|
||||
const dragStopEvent = new DragStopSensorEvent({
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
originalEvent: event,
|
||||
target,
|
||||
container,
|
||||
originalEvent: event,
|
||||
});
|
||||
|
||||
this.trigger(container, dragStopEvent);
|
||||
|
||||
this.dragging = false;
|
||||
|
||||
this[reset]();
|
||||
}
|
||||
|
||||
_onDragDrop(event) { // eslint-disable-line class-methods-use-this
|
||||
/**
|
||||
* Drop handler
|
||||
* @private
|
||||
* @param {Event} event - Drop event
|
||||
*/
|
||||
[onDrop](event) { // eslint-disable-line class-methods-use-this
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
_onMouseDown(event) {
|
||||
/**
|
||||
* Mouse down handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse down event
|
||||
*/
|
||||
[onMouseDown](event) {
|
||||
// Firefox bug for inputs within draggables https://bugzilla.mozilla.org/show_bug.cgi?id=739071
|
||||
if ((event.target && (event.target.form || event.target.contenteditable))) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nativeDraggableElement = closest(event.target, (element) => element.draggable);
|
||||
|
||||
if (nativeDraggableElement) {
|
||||
nativeDraggableElement.draggable = false;
|
||||
this.nativeDraggableElement = nativeDraggableElement;
|
||||
}
|
||||
|
||||
document.addEventListener('mouseup', this[onMouseUp], true);
|
||||
document.addEventListener('dragstart', this[onDragStart], false);
|
||||
document.addEventListener('dragover', this[onDragOver], false);
|
||||
document.addEventListener('dragend', this[onDragEnd], false);
|
||||
document.addEventListener('drop', this[onDrop], false);
|
||||
|
||||
const target = closest(event.target, this.options.draggable);
|
||||
|
||||
if (target) {
|
||||
clearTimeout(this.mouseDownTimeout);
|
||||
|
||||
this.mouseDownTimeout = setTimeout(() => {
|
||||
target.draggable = true;
|
||||
}, this.options.delay);
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.mouseDownTimeout = setTimeout(() => {
|
||||
target.draggable = true;
|
||||
this.draggableElement = target;
|
||||
}, this.options.delay);
|
||||
}
|
||||
|
||||
_onMouseUp(event) {
|
||||
/**
|
||||
* Mouse up handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse up event
|
||||
*/
|
||||
[onMouseUp]() {
|
||||
this[reset]();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mouse up handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse up event
|
||||
*/
|
||||
[reset]() {
|
||||
clearTimeout(this.mouseDownTimeout);
|
||||
|
||||
const target = closest(event.target, this.options.draggable);
|
||||
document.removeEventListener('mouseup', this[onMouseUp], true);
|
||||
document.removeEventListener('dragstart', this[onDragStart], false);
|
||||
document.removeEventListener('dragover', this[onDragOver], false);
|
||||
document.removeEventListener('dragend', this[onDragEnd], false);
|
||||
document.removeEventListener('drop', this[onDrop], false);
|
||||
|
||||
if (target) {
|
||||
target.draggable = false;
|
||||
if (this.nativeDraggableElement) {
|
||||
this.nativeDraggableElement.draggable = true;
|
||||
this.nativeDraggableElement = null;
|
||||
}
|
||||
|
||||
if (this.draggableElement) {
|
||||
this.draggableElement.draggable = false;
|
||||
this.draggableElement = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,44 @@
|
||||
## Drag Sensor
|
||||
|
||||
_Draggable does not use this sensor by default_
|
||||
|
||||
Picks up browser drag events and triggers the events below on a source container.
|
||||
|
||||
- `drag:start`
|
||||
- `drag:move`
|
||||
- `drag:stop`
|
||||
|
||||
### API
|
||||
|
||||
**`new DragSensor(containers: HTMLElement[]|NodeList|HTMLElement, options: Object): DragSensor`**
|
||||
To create a mouse sensor, specify the containers it should pay attention to. Sensors will always
|
||||
trigger sensor events on container element.
|
||||
|
||||
**`dragSensor.attach(): void`**
|
||||
Attaches sensors to the DOM
|
||||
|
||||
**`dragSensor.detach(): void`**
|
||||
Detaches sensors to the DOM
|
||||
|
||||
### Options
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay touch start
|
||||
|
||||
### Known issues
|
||||
|
||||
The drag sensor uses the native Drag and Drop API and therefor Draggable does not create
|
||||
a mirror. This means there is less control over the mirror
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
import {Draggable, Sensors} from '@shopify/draggable';
|
||||
|
||||
const draggable = new Draggable(containers, {
|
||||
sensors: [Sensors.DragSensor],
|
||||
});
|
||||
|
||||
// Remove default mouse sensor
|
||||
draggable.removeSensor(Sensors.MouseSensor);
|
||||
```
|
||||
|
||||
@ -7,52 +7,92 @@ import {
|
||||
DragPressureSensorEvent,
|
||||
} from './../SensorEvent';
|
||||
|
||||
const onMouseForceWillBegin = Symbol('onMouseForceWillBegin');
|
||||
const onMouseForceDown = Symbol('onMouseForceDown');
|
||||
const onMouseDown = Symbol('onMouseDown');
|
||||
const onMouseForceChange = Symbol('onMouseForceChange');
|
||||
const onMouseMove = Symbol('onMouseMove');
|
||||
const onMouseUp = Symbol('onMouseUp');
|
||||
const onMouseForceGlobalChange = Symbol('onMouseForceGlobalChange');
|
||||
|
||||
/**
|
||||
* This sensor picks up native force touch events and dictates drag operations
|
||||
* @class ForceTouchSensor
|
||||
* @module ForceTouchSensor
|
||||
* @extends Sensor
|
||||
*/
|
||||
export default class ForceTouchSensor extends Sensor {
|
||||
|
||||
/**
|
||||
* ForceTouchSensor constructor.
|
||||
* @constructs ForceTouchSensor
|
||||
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
|
||||
* @param {Object} options - Options
|
||||
*/
|
||||
constructor(containers = [], options = {}) {
|
||||
super(containers, options);
|
||||
|
||||
this.dragging = false;
|
||||
/**
|
||||
* Draggable element needs to be remembered to unset the draggable attribute after drag operation has completed
|
||||
* @property mightDrag
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.mightDrag = false;
|
||||
this.currentContainer = null;
|
||||
|
||||
this._onMouseForceWillBegin = this._onMouseForceWillBegin.bind(this);
|
||||
this._onMouseForceDown = this._onMouseForceDown.bind(this);
|
||||
this._onMouseDown = this._onMouseDown.bind(this);
|
||||
this._onMouseForceChange = this._onMouseForceChange.bind(this);
|
||||
this._onMouseMove = this._onMouseMove.bind(this);
|
||||
this._onMouseUp = this._onMouseUp.bind(this);
|
||||
this[onMouseForceWillBegin] = this[onMouseForceWillBegin].bind(this);
|
||||
this[onMouseForceDown] = this[onMouseForceDown].bind(this);
|
||||
this[onMouseDown] = this[onMouseDown].bind(this);
|
||||
this[onMouseForceChange] = this[onMouseForceChange].bind(this);
|
||||
this[onMouseMove] = this[onMouseMove].bind(this);
|
||||
this[onMouseUp] = this[onMouseUp].bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches sensors event listeners to the DOM
|
||||
*/
|
||||
attach() {
|
||||
for (const container of this.containers) {
|
||||
container.addEventListener('webkitmouseforcewillbegin', this._onMouseForceWillBegin, false);
|
||||
container.addEventListener('webkitmouseforcedown', this._onMouseForceDown, false);
|
||||
container.addEventListener('mousedown', this._onMouseDown, true);
|
||||
container.addEventListener('webkitmouseforcechanged', this._onMouseForceChange, false);
|
||||
container.addEventListener('webkitmouseforcewillbegin', this[onMouseForceWillBegin], false);
|
||||
container.addEventListener('webkitmouseforcedown', this[onMouseForceDown], false);
|
||||
container.addEventListener('mousedown', this[onMouseDown], true);
|
||||
container.addEventListener('webkitmouseforcechanged', this[onMouseForceChange], false);
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', this._onMouseMove);
|
||||
document.addEventListener('mouseup', this._onMouseUp);
|
||||
document.addEventListener('mousemove', this[onMouseMove]);
|
||||
document.addEventListener('mouseup', this[onMouseUp]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches sensors event listeners to the DOM
|
||||
*/
|
||||
detach() {
|
||||
for (const container of this.containers) {
|
||||
container.removeEventListener('webkitmouseforcewillbegin', this._onMouseForceWillBegin, false);
|
||||
container.removeEventListener('webkitmouseforcedown', this._onMouseForceDown, false);
|
||||
container.removeEventListener('mousedown', this._onMouseDown, true);
|
||||
container.removeEventListener('webkitmouseforcechanged', this._onMouseForceChange, false);
|
||||
container.removeEventListener('webkitmouseforcewillbegin', this[onMouseForceWillBegin], false);
|
||||
container.removeEventListener('webkitmouseforcedown', this[onMouseForceDown], false);
|
||||
container.removeEventListener('mousedown', this[onMouseDown], true);
|
||||
container.removeEventListener('webkitmouseforcechanged', this[onMouseForceChange], false);
|
||||
}
|
||||
|
||||
document.removeEventListener('mousemove', this._onMouseMove);
|
||||
document.removeEventListener('mouseup', this._onMouseUp);
|
||||
document.removeEventListener('mousemove', this[onMouseMove]);
|
||||
document.removeEventListener('mouseup', this[onMouseUp]);
|
||||
}
|
||||
|
||||
_onMouseForceWillBegin(event) {
|
||||
/**
|
||||
* Mouse force will begin handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse force will begin event
|
||||
*/
|
||||
[onMouseForceWillBegin](event) {
|
||||
event.preventDefault();
|
||||
this.mightDrag = true;
|
||||
}
|
||||
|
||||
_onMouseForceDown(event) {
|
||||
/**
|
||||
* Mouse force down handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse force down event
|
||||
*/
|
||||
[onMouseForceDown](event) {
|
||||
if (this.dragging) {
|
||||
return;
|
||||
}
|
||||
@ -75,7 +115,12 @@ export default class ForceTouchSensor extends Sensor {
|
||||
this.mightDrag = false;
|
||||
}
|
||||
|
||||
_onMouseUp(event) {
|
||||
/**
|
||||
* Mouse up handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse up event
|
||||
*/
|
||||
[onMouseUp](event) {
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
@ -95,7 +140,12 @@ export default class ForceTouchSensor extends Sensor {
|
||||
this.mightDrag = false;
|
||||
}
|
||||
|
||||
_onMouseDown(event) {
|
||||
/**
|
||||
* Mouse down handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse down event
|
||||
*/
|
||||
[onMouseDown](event) {
|
||||
if (!this.mightDrag) {
|
||||
return;
|
||||
}
|
||||
@ -107,7 +157,12 @@ export default class ForceTouchSensor extends Sensor {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
_onMouseMove(event) {
|
||||
/**
|
||||
* Mouse move handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse force will begin event
|
||||
*/
|
||||
[onMouseMove](event) {
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
@ -125,7 +180,12 @@ export default class ForceTouchSensor extends Sensor {
|
||||
this.trigger(this.currentContainer, dragMoveEvent);
|
||||
}
|
||||
|
||||
_onMouseForceChange(event) {
|
||||
/**
|
||||
* Mouse force change handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse force change event
|
||||
*/
|
||||
[onMouseForceChange](event) {
|
||||
if (this.dragging) {
|
||||
return;
|
||||
}
|
||||
@ -145,7 +205,12 @@ export default class ForceTouchSensor extends Sensor {
|
||||
this.trigger(container, dragPressureEvent);
|
||||
}
|
||||
|
||||
_onMouseForceGlobalChange(event) {
|
||||
/**
|
||||
* Mouse force global change handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse force global change event
|
||||
*/
|
||||
[onMouseForceGlobalChange](event) {
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1 +1,45 @@
|
||||
## Force Touch Sensor
|
||||
|
||||
__WORK IN PROGRESS__
|
||||
|
||||
_Draggable does not use this sensor by default_
|
||||
|
||||
Picks up browser force touch events and triggers the events below on a source container.
|
||||
This sensor only works for Macbook Pros with force touch trackpads
|
||||
|
||||
- `drag:start`
|
||||
- `drag:move`
|
||||
- `drag:stop`
|
||||
- `drag:pressure`
|
||||
|
||||
### API
|
||||
|
||||
**`new ForceTouchSensor(containers: HTMLElement[]|NodeList|HTMLElement, options: Object): ForceTouchSensor`**
|
||||
To create a force touch sensor, specify the containers it should pay attention to. Sensors will always
|
||||
trigger sensor events on container element.
|
||||
|
||||
**`touchSensor.attach(): void`**
|
||||
Attaches sensors to the DOM
|
||||
|
||||
**`touchSensor.detach(): void`**
|
||||
Detaches sensors to the DOM
|
||||
|
||||
### Options
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay force touch start
|
||||
|
||||
### Known issues
|
||||
|
||||
When used in Safari with force touch track pad, make sure to add visual guidelines
|
||||
to the user to indicate that force needs to be used to start drag operation.
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
import {Draggable, Sensors} from '@shopify/draggable';
|
||||
|
||||
const draggable = new Draggable(containers, {
|
||||
sensors: [Sensors.ForceTouchSensor],
|
||||
});
|
||||
```
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import {closest} from 'shared/utils';
|
||||
import Sensor from './../Sensor';
|
||||
|
||||
import {
|
||||
@ -6,46 +7,90 @@ import {
|
||||
DragStopSensorEvent,
|
||||
} from './../SensorEvent';
|
||||
|
||||
const onContextMenuWhileDragging = Symbol('onContextMenuWhileDragging');
|
||||
const onMouseDown = Symbol('onMouseDown');
|
||||
const onMouseMove = Symbol('onMouseMove');
|
||||
const onMouseUp = Symbol('onMouseUp');
|
||||
|
||||
/**
|
||||
* This sensor picks up native browser mouse events and dictates drag operations
|
||||
* @class MouseSensor
|
||||
* @module MouseSensor
|
||||
* @extends Sensor
|
||||
*/
|
||||
export default class MouseSensor extends Sensor {
|
||||
|
||||
/**
|
||||
* MouseSensor constructor.
|
||||
* @constructs MouseSensor
|
||||
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
|
||||
* @param {Object} options - Options
|
||||
*/
|
||||
constructor(containers = [], options = {}) {
|
||||
super(containers, options);
|
||||
|
||||
this.dragging = false;
|
||||
/**
|
||||
* Indicates if mouse button is still down
|
||||
* @property mouseDown
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.mouseDown = false;
|
||||
this.currentContainer = null;
|
||||
|
||||
this._onContextMenuWhileDragging = this._onContextMenuWhileDragging.bind(this);
|
||||
this._onMouseDown = this._onMouseDown.bind(this);
|
||||
this._onMouseMove = this._onMouseMove.bind(this);
|
||||
this._onMouseUp = this._onMouseUp.bind(this);
|
||||
/**
|
||||
* Mouse down timer which will end up triggering the drag start operation
|
||||
* @property mouseDownTimeout
|
||||
* @type {Number}
|
||||
*/
|
||||
this.mouseDownTimeout = null;
|
||||
|
||||
/**
|
||||
* Indicates if context menu has been opened during drag operation
|
||||
* @property openedContextMenu
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.openedContextMenu = false;
|
||||
|
||||
this[onContextMenuWhileDragging] = this[onContextMenuWhileDragging].bind(this);
|
||||
this[onMouseDown] = this[onMouseDown].bind(this);
|
||||
this[onMouseMove] = this[onMouseMove].bind(this);
|
||||
this[onMouseUp] = this[onMouseUp].bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches sensors event listeners to the DOM
|
||||
*/
|
||||
attach() {
|
||||
for (const container of this.containers) {
|
||||
container.addEventListener('mousedown', this._onMouseDown, true);
|
||||
}
|
||||
|
||||
document.addEventListener('mousemove', this._onMouseMove);
|
||||
document.addEventListener('mouseup', this._onMouseUp);
|
||||
document.addEventListener('mousedown', this[onMouseDown], true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches sensors event listeners to the DOM
|
||||
*/
|
||||
detach() {
|
||||
for (const container of this.containers) {
|
||||
container.removeEventListener('mousedown', this._onMouseDown, true);
|
||||
}
|
||||
|
||||
document.removeEventListener('mousemove', this._onMouseMove);
|
||||
document.removeEventListener('mouseup', this._onMouseUp);
|
||||
document.removeEventListener('mousedown', this[onMouseDown], true);
|
||||
}
|
||||
|
||||
_onMouseDown(event) {
|
||||
if (event.button !== 0 || event.ctrlKey) {
|
||||
/**
|
||||
* Mouse down handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse down event
|
||||
*/
|
||||
[onMouseDown](event) {
|
||||
if (event.button !== 0 || event.ctrlKey || event.metaKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.addEventListener('mouseup', this[onMouseUp]);
|
||||
document.addEventListener('dragstart', preventNativeDragStart);
|
||||
|
||||
const target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
const container = closest(target, this.containers);
|
||||
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.mouseDown = true;
|
||||
const target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
const container = event.currentTarget;
|
||||
|
||||
clearTimeout(this.mouseDownTimeout);
|
||||
this.mouseDownTimeout = setTimeout(() => {
|
||||
@ -67,13 +112,18 @@ export default class MouseSensor extends Sensor {
|
||||
this.dragging = !dragStartEvent.canceled();
|
||||
|
||||
if (this.dragging) {
|
||||
document.addEventListener('contextmenu', this._onContextMenuWhileDragging);
|
||||
document.addEventListener('dragstart', preventNativeDragStart);
|
||||
document.addEventListener('contextmenu', this[onContextMenuWhileDragging]);
|
||||
document.addEventListener('mousemove', this[onMouseMove]);
|
||||
}
|
||||
}, this.options.delay);
|
||||
}
|
||||
|
||||
_onMouseMove(event) {
|
||||
/**
|
||||
* Mouse move handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse move event
|
||||
*/
|
||||
[onMouseMove](event) {
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
@ -91,7 +141,12 @@ export default class MouseSensor extends Sensor {
|
||||
this.trigger(this.currentContainer, dragMoveEvent);
|
||||
}
|
||||
|
||||
_onMouseUp(event) {
|
||||
/**
|
||||
* Mouse up handler
|
||||
* @private
|
||||
* @param {Event} event - Mouse up event
|
||||
*/
|
||||
[onMouseUp](event) {
|
||||
this.mouseDown = Boolean(this.openedContextMenu);
|
||||
|
||||
if (this.openedContextMenu) {
|
||||
@ -99,6 +154,9 @@ export default class MouseSensor extends Sensor {
|
||||
return;
|
||||
}
|
||||
|
||||
document.removeEventListener('mouseup', this[onMouseUp]);
|
||||
document.removeEventListener('dragstart', preventNativeDragStart);
|
||||
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
@ -115,14 +173,19 @@ export default class MouseSensor extends Sensor {
|
||||
|
||||
this.trigger(this.currentContainer, dragStopEvent);
|
||||
|
||||
document.removeEventListener('contextmenu', this._onContextMenuWhileDragging);
|
||||
document.removeEventListener('dragstart', preventNativeDragStart);
|
||||
document.removeEventListener('contextmenu', this[onContextMenuWhileDragging]);
|
||||
document.removeEventListener('mousemove', this[onMouseMove]);
|
||||
|
||||
this.currentContainer = null;
|
||||
this.dragging = false;
|
||||
}
|
||||
|
||||
_onContextMenuWhileDragging(event) {
|
||||
/**
|
||||
* Context menu handler
|
||||
* @private
|
||||
* @param {Event} event - Context menu event
|
||||
*/
|
||||
[onContextMenuWhileDragging](event) {
|
||||
event.preventDefault();
|
||||
this.openedContextMenu = true;
|
||||
}
|
||||
|
||||
@ -1 +1,26 @@
|
||||
## MouseSensor
|
||||
## Mouse Sensor
|
||||
|
||||
_Draggable uses this sensor by default_
|
||||
|
||||
Picks up browser mouse events and triggers the events below on a source container.
|
||||
|
||||
- `drag:start`
|
||||
- `drag:move`
|
||||
- `drag:stop`
|
||||
|
||||
### API
|
||||
|
||||
**`new MouseSensor(containers: HTMLElement[]|NodeList|HTMLElement, options: Object): MouseSensor`**
|
||||
To create a mouse sensor, specify the containers it should pay attention to. Sensors will always
|
||||
trigger sensor events on container element.
|
||||
|
||||
**`mouseSensor.attach(): void`**
|
||||
Attaches sensors to the DOM
|
||||
|
||||
**`mouseSensor.detach(): void`**
|
||||
Detaches sensors to the DOM
|
||||
|
||||
### Options
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay touch start
|
||||
|
||||
@ -119,7 +119,7 @@ describe('MouseSensor', () => {
|
||||
expect(getLastSensorEventByType('drag:start')).toBeUndefined();
|
||||
});
|
||||
|
||||
test('does not trigger `drag:start` event when clicking on none draggable element', () => {
|
||||
xtest('does not trigger `drag:start` event when clicking on none draggable element', () => {
|
||||
const draggable = sandbox.querySelector('li');
|
||||
document.elementFromPoint = () => draggable;
|
||||
triggerEvent(document.body, 'mousedown', {button: 0});
|
||||
|
||||
@ -1 +1,31 @@
|
||||
## Sensor
|
||||
|
||||
Base sensor which includes a minimal API. Inherit from this class to create your own
|
||||
custom sensor.
|
||||
|
||||
Currently triggers these sensor events:
|
||||
|
||||
- `drag:start`
|
||||
- `drag:move`
|
||||
- `drag:stop`
|
||||
- `drag:pressure`
|
||||
|
||||
### API
|
||||
|
||||
**`new Sensor(containers: HTMLElement[]|NodeList|HTMLElement, options: Object): Sensor`**
|
||||
To create a sensor, specify the containers it should pay attention to. Sensors will always
|
||||
trigger sensor events on container element.
|
||||
|
||||
**`sensor.attach(): void`**
|
||||
Attaches sensors to the DOM
|
||||
|
||||
**`sensor.detach(): void`**
|
||||
Detaches sensors to the DOM
|
||||
|
||||
**`sensor.trigger(element: HTMLElement, sensorEvent: SensorEvent): void`**
|
||||
Triggers sensor event on container element
|
||||
|
||||
### Options
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay drag start
|
||||
|
||||
@ -1,17 +1,68 @@
|
||||
/**
|
||||
* Base sensor class. Extend from this class to create a new or custom sensor
|
||||
* @class Sensor
|
||||
* @module Sensor
|
||||
*/
|
||||
export default class Sensor {
|
||||
|
||||
/**
|
||||
* Sensor constructor.
|
||||
* @constructs Sensor
|
||||
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
|
||||
* @param {Object} options - Options
|
||||
*/
|
||||
constructor(containers = [], options = {}) {
|
||||
this.containers = [...containers];
|
||||
|
||||
/**
|
||||
* Current containers
|
||||
* @property containers
|
||||
* @type {HTMLElement[]}
|
||||
*/
|
||||
this.containers = containers;
|
||||
|
||||
/**
|
||||
* Current options
|
||||
* @property options
|
||||
* @type {Object}
|
||||
*/
|
||||
this.options = {...options};
|
||||
|
||||
/**
|
||||
* Current drag state
|
||||
* @property dragging
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.dragging = false;
|
||||
|
||||
/**
|
||||
* Current container
|
||||
* @property currentContainer
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
this.currentContainer = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches sensors event listeners to the DOM
|
||||
* @return {Sensor}
|
||||
*/
|
||||
attach() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches sensors event listeners to the DOM
|
||||
* @return {Sensor}
|
||||
*/
|
||||
detach() {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers event on target element
|
||||
* @param {HTMLElement} element - Element to trigger event on
|
||||
* @param {SensorEvent} sensorEvent - Sensor event to trigger
|
||||
*/
|
||||
trigger(element, sensorEvent) {
|
||||
const event = document.createEvent('Event');
|
||||
event.detail = sensorEvent;
|
||||
|
||||
@ -1,47 +1,149 @@
|
||||
import AbstractEvent from 'shared/AbstractEvent';
|
||||
|
||||
/**
|
||||
* Base sensor event
|
||||
* @class SensorEvent
|
||||
* @module SensorEvent
|
||||
* @extends AbstractEvent
|
||||
*/
|
||||
export class SensorEvent extends AbstractEvent {
|
||||
|
||||
/**
|
||||
* Original browser event that triggered a sensor
|
||||
* @property originalEvent
|
||||
* @type {Event}
|
||||
* @readonly
|
||||
*/
|
||||
get originalEvent() {
|
||||
return this.data.originalEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalized clientX for both touch and mouse events
|
||||
* @property clientX
|
||||
* @type {Number}
|
||||
* @readonly
|
||||
*/
|
||||
get clientX() {
|
||||
return this.data.clientX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalized clientY for both touch and mouse events
|
||||
* @property clientY
|
||||
* @type {Number}
|
||||
* @readonly
|
||||
*/
|
||||
get clientY() {
|
||||
return this.data.clientY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalized target for both touch and mouse events
|
||||
* Returns the element that is behind cursor or touch pointer
|
||||
* @property target
|
||||
* @type {HTMLElement}
|
||||
* @readonly
|
||||
*/
|
||||
get target() {
|
||||
return this.data.target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Container that initiated the sensor
|
||||
* @property container
|
||||
* @type {HTMLElement}
|
||||
* @readonly
|
||||
*/
|
||||
get container() {
|
||||
return this.data.container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Container that initiated the sensor
|
||||
* @property overContainer
|
||||
* @type {HTMLElement}
|
||||
* @readonly
|
||||
*/
|
||||
get overContainer() {
|
||||
return this.data.overContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trackpad pressure
|
||||
* @property pressure
|
||||
* @type {Number}
|
||||
* @readonly
|
||||
*/
|
||||
get pressure() {
|
||||
return this.data.pressure;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag start sensor event
|
||||
* @class DragStartSensorEvent
|
||||
* @module DragStartSensorEvent
|
||||
* @extends SensorEvent
|
||||
*/
|
||||
export class DragStartSensorEvent extends SensorEvent {
|
||||
|
||||
/**
|
||||
* Event type
|
||||
* @property type
|
||||
* @type {String}
|
||||
* @static
|
||||
*/
|
||||
static type = 'drag:start';
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag move sensor event
|
||||
* @class DragMoveSensorEvent
|
||||
* @module DragMoveSensorEvent
|
||||
* @extends SensorEvent
|
||||
*/
|
||||
export class DragMoveSensorEvent extends SensorEvent {
|
||||
|
||||
/**
|
||||
* Event type
|
||||
* @property type
|
||||
* @type {String}
|
||||
* @static
|
||||
*/
|
||||
static type = 'drag:move';
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag stop sensor event
|
||||
* @class DragStopSensorEvent
|
||||
* @module DragStopSensorEvent
|
||||
* @extends SensorEvent
|
||||
*/
|
||||
export class DragStopSensorEvent extends SensorEvent {
|
||||
|
||||
/**
|
||||
* Event type
|
||||
* @property type
|
||||
* @type {String}
|
||||
* @static
|
||||
*/
|
||||
static type = 'drag:stop';
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag pressure sensor event
|
||||
* @class DragPressureSensorEvent
|
||||
* @module DragPressureSensorEvent
|
||||
* @extends SensorEvent
|
||||
*/
|
||||
export class DragPressureSensorEvent extends SensorEvent {
|
||||
|
||||
/**
|
||||
* Event type
|
||||
* @property type
|
||||
* @type {String}
|
||||
* @static
|
||||
*/
|
||||
static type = 'drag:pressure';
|
||||
}
|
||||
|
||||
@ -1 +1,26 @@
|
||||
## Touch Sensor
|
||||
|
||||
_Draggable uses this sensor by default_
|
||||
|
||||
Picks up browser touch events and triggers the events below on a source container.
|
||||
|
||||
- `drag:start`
|
||||
- `drag:move`
|
||||
- `drag:stop`
|
||||
|
||||
### API
|
||||
|
||||
**`new TouchSensor(containers: HTMLElement[]|NodeList|HTMLElement, options: Object): TouchSensor`**
|
||||
To create a touch sensor, specify the containers it should pay attention to. Sensors will always
|
||||
trigger sensor events on container element.
|
||||
|
||||
**`touchSensor.attach(): void`**
|
||||
Attaches sensors to the DOM
|
||||
|
||||
**`touchSensor.detach(): void`**
|
||||
Detaches sensors to the DOM
|
||||
|
||||
### Options
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay touch start
|
||||
|
||||
@ -7,65 +7,119 @@ import {
|
||||
DragStopSensorEvent,
|
||||
} from './../SensorEvent';
|
||||
|
||||
const onTouchStart = Symbol('onTouchStart');
|
||||
const onTouchHold = Symbol('onTouchHold');
|
||||
const onTouchEnd = Symbol('onTouchEnd');
|
||||
const onTouchMove = Symbol('onTouchMove');
|
||||
const onScroll = Symbol('onScroll');
|
||||
|
||||
/**
|
||||
* Adds default document.ontouchmove. Workaround for preventing scrolling on touchmove
|
||||
*/
|
||||
document.ontouchmove = document.ontouchmove || function() {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* This sensor picks up native browser touch events and dictates drag operations
|
||||
* @class TouchSensor
|
||||
* @module TouchSensor
|
||||
* @extends Sensor
|
||||
*/
|
||||
export default class TouchSensor extends Sensor {
|
||||
|
||||
/**
|
||||
* TouchSensor constructor.
|
||||
* @constructs TouchSensor
|
||||
* @param {HTMLElement[]|NodeList|HTMLElement} containers - Containers
|
||||
* @param {Object} options - Options
|
||||
*/
|
||||
constructor(containers = [], options = {}) {
|
||||
super(containers, options);
|
||||
|
||||
this.dragging = false;
|
||||
this.currentContainer = null;
|
||||
/**
|
||||
* Closest scrollable container so accidental scroll can cancel long touch
|
||||
* @property currentScrollableParent
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
this.currentScrollableParent = null;
|
||||
|
||||
this._onTouchStart = this._onTouchStart.bind(this);
|
||||
this._onTouchHold = this._onTouchHold.bind(this);
|
||||
this._onTouchEnd = this._onTouchEnd.bind(this);
|
||||
this._onTouchMove = this._onTouchMove.bind(this);
|
||||
this._onScroll = this._onScroll.bind(this);
|
||||
/**
|
||||
* TimeoutID for long touch
|
||||
* @property tapTimeout
|
||||
* @type {Number}
|
||||
*/
|
||||
this.tapTimeout = null;
|
||||
|
||||
/**
|
||||
* touchMoved indicates if touch has moved during tapTimeout
|
||||
* @property touchMoved
|
||||
* @type {Boolean}
|
||||
*/
|
||||
this.touchMoved = false;
|
||||
|
||||
this[onTouchStart] = this[onTouchStart].bind(this);
|
||||
this[onTouchHold] = this[onTouchHold].bind(this);
|
||||
this[onTouchEnd] = this[onTouchEnd].bind(this);
|
||||
this[onTouchMove] = this[onTouchMove].bind(this);
|
||||
this[onScroll] = this[onScroll].bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches sensors event listeners to the DOM
|
||||
*/
|
||||
attach() {
|
||||
for (const container of this.containers) {
|
||||
container.addEventListener('touchstart', this._onTouchStart, false);
|
||||
}
|
||||
|
||||
document.addEventListener('touchend', this._onTouchEnd, false);
|
||||
document.addEventListener('touchcancel', this._onTouchEnd, false);
|
||||
document.addEventListener('touchmove', this._onTouchMove, false);
|
||||
document.addEventListener('touchstart', this[onTouchStart]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detaches sensors event listeners to the DOM
|
||||
*/
|
||||
detach() {
|
||||
for (const container of this.containers) {
|
||||
container.removeEventListener('touchstart', this._onTouchStart, false);
|
||||
document.removeEventListener('touchstart', this[onTouchStart]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Touch start handler
|
||||
* @private
|
||||
* @param {Event} event - Touch start event
|
||||
*/
|
||||
[onTouchStart](event) {
|
||||
const container = closest(event.target, this.containers);
|
||||
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
document.removeEventListener('touchend', this._onTouchEnd, false);
|
||||
document.removeEventListener('touchcancel', this._onTouchEnd, false);
|
||||
document.removeEventListener('touchmove', this._onTouchMove, false);
|
||||
}
|
||||
|
||||
_onScroll() {
|
||||
// Cancel potential drag and allow scroll on iOS or other touch devices
|
||||
clearTimeout(this.tapTimeout);
|
||||
}
|
||||
|
||||
_onTouchStart(event) {
|
||||
event.preventDefault();
|
||||
const container = event.currentTarget;
|
||||
document.addEventListener('touchmove', this[onTouchMove], {passive: false});
|
||||
document.addEventListener('touchend', this[onTouchEnd]);
|
||||
document.addEventListener('touchcancel', this[onTouchEnd]);
|
||||
|
||||
// detect if body is scrolling on iOS
|
||||
document.addEventListener('scroll', this._onScroll);
|
||||
container.addEventListener('contextmenu', _onContextMenu);
|
||||
document.addEventListener('scroll', this[onScroll]);
|
||||
container.addEventListener('contextmenu', onContextMenu);
|
||||
|
||||
this.currentContainer = container;
|
||||
|
||||
this.currentScrollableParent = closest(container, (element) => element.offsetHeight < element.scrollHeight);
|
||||
|
||||
if (this.currentScrollableParent) {
|
||||
this.currentScrollableParent.addEventListener('scroll', this._onScroll);
|
||||
this.currentScrollableParent.addEventListener('scroll', this[onScroll]);
|
||||
}
|
||||
|
||||
this.tapTimeout = setTimeout(this._onTouchHold(event, container), this.options.delay);
|
||||
this.tapTimeout = setTimeout(this[onTouchHold](event, container), this.options.delay);
|
||||
}
|
||||
|
||||
_onTouchHold(event, container) {
|
||||
/**
|
||||
* Touch hold handler
|
||||
* @private
|
||||
* @param {Event} event - Touch start event
|
||||
* @param {HTMLElement} container - Container element
|
||||
*/
|
||||
[onTouchHold](event, container) {
|
||||
return () => {
|
||||
if (this.touchMoved) { return; }
|
||||
|
||||
const touch = event.touches[0] || event.changedTouches[0];
|
||||
const target = event.target;
|
||||
|
||||
@ -79,16 +133,24 @@ export default class TouchSensor extends Sensor {
|
||||
|
||||
this.trigger(container, dragStartEvent);
|
||||
|
||||
this.currentContainer = container;
|
||||
this.dragging = !dragStartEvent.canceled();
|
||||
};
|
||||
}
|
||||
|
||||
_onTouchMove(event) {
|
||||
/**
|
||||
* Touch move handler
|
||||
* @private
|
||||
* @param {Event} event - Touch move event
|
||||
*/
|
||||
[onTouchMove](event) {
|
||||
this.touchMoved = true;
|
||||
|
||||
if (!this.dragging) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cancels scrolling while dragging
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const touch = event.touches[0] || event.changedTouches[0];
|
||||
@ -105,14 +167,26 @@ export default class TouchSensor extends Sensor {
|
||||
this.trigger(this.currentContainer, dragMoveEvent);
|
||||
}
|
||||
|
||||
_onTouchEnd(event) {
|
||||
const container = event.currentTarget;
|
||||
/**
|
||||
* Touch end handler
|
||||
* @private
|
||||
* @param {Event} event - Touch end event
|
||||
*/
|
||||
[onTouchEnd](event) {
|
||||
this.touchMoved = false;
|
||||
|
||||
document.removeEventListener('scroll', this._onScroll);
|
||||
container.removeEventListener('contextmenu', _onContextMenu);
|
||||
document.removeEventListener('touchend', this[onTouchEnd]);
|
||||
document.removeEventListener('touchcancel', this[onTouchEnd]);
|
||||
document.removeEventListener('touchmove', this[onTouchMove], {passive: false});
|
||||
|
||||
document.removeEventListener('scroll', this[onScroll]);
|
||||
|
||||
if (this.currentContainer) {
|
||||
this.currentContainer.removeEventListener('contextmenu', onContextMenu);
|
||||
}
|
||||
|
||||
if (this.currentScrollableParent) {
|
||||
this.currentScrollableParent.removeEventListener('scroll', this._onScroll);
|
||||
this.currentScrollableParent.removeEventListener('scroll', this[onScroll]);
|
||||
}
|
||||
|
||||
clearTimeout(this.tapTimeout);
|
||||
@ -122,13 +196,14 @@ export default class TouchSensor extends Sensor {
|
||||
}
|
||||
|
||||
const touch = event.touches[0] || event.changedTouches[0];
|
||||
const target = document.elementFromPoint(touch.pageX - window.scrollX, touch.pageY - window.scrollY);
|
||||
|
||||
event.preventDefault();
|
||||
|
||||
const dragStopEvent = new DragStopSensorEvent({
|
||||
clientX: touch.pageX,
|
||||
clientY: touch.pageY,
|
||||
target: null,
|
||||
target,
|
||||
container: this.currentContainer,
|
||||
originalEvent: event,
|
||||
});
|
||||
@ -138,8 +213,17 @@ export default class TouchSensor extends Sensor {
|
||||
this.currentContainer = null;
|
||||
this.dragging = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scroll handler, cancel potential drag and allow scroll on iOS or other touch devices
|
||||
* @private
|
||||
*/
|
||||
[onScroll]() {
|
||||
clearTimeout(this.tapTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
function _onContextMenu(event) {
|
||||
function onContextMenu(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
@ -3,18 +3,34 @@ const matchFunction = Element.prototype.matches ||
|
||||
Element.prototype.mozMatchesSelector ||
|
||||
Element.prototype.msMatchesSelector;
|
||||
|
||||
export default function closest(element, selector) {
|
||||
export default function closest(element, value) {
|
||||
if (!element) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const selector = value;
|
||||
const callback = value;
|
||||
const nodeList = value;
|
||||
const singleElement = value;
|
||||
|
||||
const isSelector = Boolean(typeof value === 'string');
|
||||
const isFunction = Boolean(typeof value === 'function');
|
||||
const isNodeList = Boolean(value instanceof NodeList || value instanceof Array);
|
||||
const isElement = Boolean(value instanceof HTMLElement);
|
||||
|
||||
function conditionFn(currentElement) {
|
||||
if (!currentElement) {
|
||||
return currentElement;
|
||||
} else if (typeof selector === 'string') {
|
||||
} else if (isSelector) {
|
||||
return matchFunction.call(currentElement, selector);
|
||||
} else if (isNodeList) {
|
||||
return [...nodeList].includes(currentElement);
|
||||
} else if (isElement) {
|
||||
return singleElement === currentElement;
|
||||
} else if (isFunction) {
|
||||
return callback(currentElement);
|
||||
} else {
|
||||
return selector(currentElement);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user