mirror of
https://github.com/Shopify/draggable.git
synced 2025-12-08 20:15:56 +00:00
fix(sensor): return early when the target isn't in handle or draggable
elements
This commit is contained in:
parent
1590e5de8b
commit
9bcab66e7a
5
jsconfig.json
Normal file
5
jsconfig.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./src/"
|
||||
}
|
||||
}
|
||||
@ -387,7 +387,7 @@ export default class Draggable {
|
||||
*/
|
||||
[onDragStart](event) {
|
||||
const sensorEvent = getSensorEvent(event);
|
||||
const {target, container} = sensorEvent;
|
||||
const {target, container, originalSource} = sensorEvent;
|
||||
|
||||
if (!this.containers.includes(container)) {
|
||||
return;
|
||||
@ -398,15 +398,9 @@ export default class Draggable {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find draggable source element
|
||||
this.originalSource = closest(target, this.options.draggable);
|
||||
this.originalSource = originalSource;
|
||||
this.sourceContainer = container;
|
||||
|
||||
if (!this.originalSource) {
|
||||
sensorEvent.cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.lastPlacedSource && this.lastPlacedContainer) {
|
||||
clearTimeout(this.placedTimeoutID);
|
||||
this.lastPlacedSource.classList.remove(...this.getClassNamesFor('source:placed'));
|
||||
|
||||
@ -80,9 +80,9 @@ export default class DragSensor extends Sensor {
|
||||
event.dataTransfer.effectAllowed = this.options.type;
|
||||
|
||||
const target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
this.currentContainer = closest(event.target, this.containers);
|
||||
const originalSource = this.draggableElement;
|
||||
|
||||
if (!this.currentContainer) {
|
||||
if (!originalSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -90,6 +90,7 @@ export default class DragSensor extends Sensor {
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
target,
|
||||
originalSource,
|
||||
container: this.currentContainer,
|
||||
originalEvent: event,
|
||||
});
|
||||
@ -187,6 +188,23 @@ export default class DragSensor extends Sensor {
|
||||
return;
|
||||
}
|
||||
|
||||
const target = event.target;
|
||||
this.currentContainer = closest(target, this.containers);
|
||||
|
||||
if (!this.currentContainer) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.handle && target && !closest(target, this.options.handle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalSource = closest(target, this.options.draggable);
|
||||
|
||||
if (!originalSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nativeDraggableElement = closest(event.target, (element) => element.draggable);
|
||||
|
||||
if (nativeDraggableElement) {
|
||||
@ -200,17 +218,11 @@ export default class DragSensor extends Sensor {
|
||||
document.addEventListener('dragend', this[onDragEnd], false);
|
||||
document.addEventListener('drop', this[onDrop], false);
|
||||
|
||||
const target = closest(event.target, this.options.draggable);
|
||||
|
||||
if (!target) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.startEvent = event;
|
||||
|
||||
this.mouseDownTimeout = setTimeout(() => {
|
||||
target.draggable = true;
|
||||
this.draggableElement = target;
|
||||
originalSource.draggable = true;
|
||||
this.draggableElement = originalSource;
|
||||
}, this.delay.drag);
|
||||
}
|
||||
|
||||
|
||||
@ -22,6 +22,9 @@ Detaches sensors to the DOM
|
||||
|
||||
### Options
|
||||
|
||||
**`draggable {String}`**
|
||||
A css selector for draggable elements within the `containers` specified.
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay touch start
|
||||
|
||||
|
||||
@ -13,8 +13,9 @@ import DragSensor from '..';
|
||||
|
||||
const sampleMarkup = `
|
||||
<ul>
|
||||
<li>First item</li>
|
||||
<li>Second item</li>
|
||||
<li class="draggable">First item</li>
|
||||
<li class="draggable">Second item</li>
|
||||
<li class="non-draggable">Non draggable item</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
@ -22,127 +23,209 @@ describe('DragSensor', () => {
|
||||
let sandbox;
|
||||
let dragSensor;
|
||||
let draggableElement;
|
||||
let nonDraggableElement;
|
||||
|
||||
function setup(optionsParam = {}) {
|
||||
const options = {
|
||||
draggable: '.draggable',
|
||||
delay: 0,
|
||||
distance: 0,
|
||||
...optionsParam,
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
sandbox = createSandbox(sampleMarkup);
|
||||
const containers = sandbox.querySelectorAll('ul');
|
||||
draggableElement = sandbox.querySelector('li');
|
||||
dragSensor = new DragSensor(containers, {
|
||||
draggable: 'li',
|
||||
delay: DRAG_DELAY,
|
||||
});
|
||||
draggableElement = sandbox.querySelector('.draggable');
|
||||
nonDraggableElement = sandbox.querySelector('.non-draggable');
|
||||
dragSensor = new DragSensor(containers, options);
|
||||
dragSensor.attach();
|
||||
});
|
||||
}
|
||||
|
||||
afterEach(() => {
|
||||
function teardown() {
|
||||
dragSensor.detach();
|
||||
sandbox.parentNode.removeChild(sandbox);
|
||||
});
|
||||
}
|
||||
|
||||
it('mousedown handler adds draggable attribute', () => {
|
||||
expect(draggableElement.draggable).toBeUndefined();
|
||||
describe('common', () => {
|
||||
beforeEach(setup);
|
||||
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
afterEach(teardown);
|
||||
|
||||
expect(draggableElement.draggable).toBe(true);
|
||||
it('mousedown handler adds draggable attribute', () => {
|
||||
expect(draggableElement.draggable).toBeUndefined();
|
||||
|
||||
releaseMouse(draggableElement);
|
||||
|
||||
expect(draggableElement.draggable).toBe(false);
|
||||
});
|
||||
|
||||
it('triggers `drag:start` sensor event on dragstart', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:start');
|
||||
});
|
||||
expect(draggableElement.draggable).toBe(true);
|
||||
|
||||
it('cancels `drag:start` event when canceling sensor event', () => {
|
||||
sandbox.addEventListener('drag:start', (event) => {
|
||||
event.detail.cancel();
|
||||
releaseMouse(draggableElement);
|
||||
|
||||
expect(draggableElement.draggable).toBe(false);
|
||||
});
|
||||
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
it('triggers `drag:start` sensor event on dragstart', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(dragFlow).toHaveCanceledSensorEvent('drag:start');
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:start');
|
||||
});
|
||||
|
||||
it('cancels `drag:start` event when canceling sensor event', () => {
|
||||
sandbox.addEventListener('drag:start', (event) => {
|
||||
event.detail.cancel();
|
||||
});
|
||||
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(dragFlow).toHaveCanceledSensorEvent('drag:start');
|
||||
});
|
||||
|
||||
it('does not trigger `drag:start` event releasing mouse before timeout', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
function hastyDragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
dragStart(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(hastyDragFlow).not.toHaveTriggeredSensorEvent('drag:start');
|
||||
|
||||
expect(hastyDragFlow).not.toHaveTriggeredSensorEvent('drag:stop');
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:start');
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:stop');
|
||||
});
|
||||
|
||||
it('triggers `drag:move` event while moving the mouse', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragOver(document.body);
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:move');
|
||||
});
|
||||
|
||||
it('triggers `drag:stop` event when releasing mouse', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragOver(document.body);
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:stop');
|
||||
});
|
||||
|
||||
it('does not trigger `drag:start` event when clicking on none draggable element', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(document.body);
|
||||
waitForDragDelay();
|
||||
dragStart(document.body);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
|
||||
clickMouse(nonDraggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(nonDraggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(dragFlow).not.toHaveTriggeredSensorEvent('drag:start');
|
||||
});
|
||||
});
|
||||
|
||||
it('does not trigger `drag:start` event releasing mouse before timeout', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
describe('using delay', () => {
|
||||
beforeEach(() => {
|
||||
setup({delay: DRAG_DELAY});
|
||||
});
|
||||
|
||||
function hastyDragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
dragStart(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
afterEach(teardown);
|
||||
|
||||
expect(hastyDragFlow).not.toHaveTriggeredSensorEvent('drag:start');
|
||||
it('triggers `drag:start` sensor event after delay', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(hastyDragFlow).not.toHaveTriggeredSensorEvent('drag:stop');
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:start');
|
||||
});
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:start');
|
||||
it('does not trigger `drag:start` event releasing mouse before delay', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:stop');
|
||||
});
|
||||
function hastyDragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
dragStart(draggableElement);
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
it('triggers `drag:move` event while moving the mouse', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragOver(document.body);
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
expect(hastyDragFlow).not.toHaveTriggeredSensorEvent('drag:start');
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:move');
|
||||
});
|
||||
expect(hastyDragFlow).not.toHaveTriggeredSensorEvent('drag:stop');
|
||||
|
||||
it('triggers `drag:stop` event when releasing mouse', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragOver(document.body);
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:start');
|
||||
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:stop');
|
||||
});
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:stop');
|
||||
});
|
||||
|
||||
it('does not trigger `drag:start` event when clicking on none draggable element', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(document.body);
|
||||
waitForDragDelay();
|
||||
dragStart(document.body);
|
||||
waitForDragDelay();
|
||||
}
|
||||
it('triggers `drag:move` event while moving the mouse after delay', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragStart(draggableElement);
|
||||
waitForDragDelay();
|
||||
dragOver(document.body);
|
||||
dragStop(draggableElement);
|
||||
releaseMouse(document.body);
|
||||
}
|
||||
|
||||
expect(dragFlow).not.toHaveTriggeredSensorEvent('drag:start');
|
||||
expect(dragFlow).toHaveTriggeredSensorEvent('drag:move');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import {closest} from 'shared/utils';
|
||||
import Sensor from '../Sensor';
|
||||
import {DragStartSensorEvent, DragMoveSensorEvent, DragStopSensorEvent, DragPressureSensorEvent} from '../SensorEvent';
|
||||
|
||||
@ -93,11 +94,22 @@ export default class ForceTouchSensor extends Sensor {
|
||||
const target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
const container = event.currentTarget;
|
||||
|
||||
if (this.options.handle && target && !closest(target, this.options.handle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalSource = closest(target, this.options.draggable);
|
||||
|
||||
if (!originalSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const dragStartEvent = new DragStartSensorEvent({
|
||||
clientX: event.clientX,
|
||||
clientY: event.clientY,
|
||||
target,
|
||||
container,
|
||||
originalSource,
|
||||
originalEvent: event,
|
||||
});
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
## Force Touch Sensor
|
||||
|
||||
__WORK IN PROGRESS__
|
||||
**WORK IN PROGRESS**
|
||||
|
||||
_Draggable does not use this sensor by default_
|
||||
|
||||
@ -26,9 +26,15 @@ Detaches sensors to the DOM
|
||||
|
||||
### Options
|
||||
|
||||
**`draggable {String}`**
|
||||
A css selector for draggable elements within the `containers` specified.
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay force touch start
|
||||
|
||||
**`handle {String}`**
|
||||
Specify a css selector for a handle element if you don't want to allow drag action on the entire element.
|
||||
|
||||
### Example
|
||||
|
||||
```js
|
||||
@ -41,4 +47,4 @@ const draggable = new Draggable(containers, {
|
||||
|
||||
### Caveats
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
@ -83,6 +83,16 @@ export default class MouseSensor extends Sensor {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.handle && event.target && !closest(event.target, this.options.handle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalSource = closest(event.target, this.options.draggable);
|
||||
|
||||
if (!originalSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {delay} = this;
|
||||
const {pageX, pageY} = event;
|
||||
|
||||
@ -91,6 +101,7 @@ export default class MouseSensor extends Sensor {
|
||||
this.startEvent = event;
|
||||
|
||||
this.currentContainer = container;
|
||||
this.originalSource = originalSource;
|
||||
document.addEventListener('mouseup', this[onMouseUp]);
|
||||
document.addEventListener('dragstart', preventNativeDragStart);
|
||||
document.addEventListener('mousemove', this[onDistanceChange]);
|
||||
@ -107,12 +118,14 @@ export default class MouseSensor extends Sensor {
|
||||
[startDrag]() {
|
||||
const startEvent = this.startEvent;
|
||||
const container = this.currentContainer;
|
||||
const originalSource = this.originalSource;
|
||||
|
||||
const dragStartEvent = new DragStartSensorEvent({
|
||||
clientX: startEvent.clientX,
|
||||
clientY: startEvent.clientY,
|
||||
target: startEvent.target,
|
||||
container,
|
||||
originalSource,
|
||||
originalEvent: startEvent,
|
||||
});
|
||||
|
||||
|
||||
@ -22,5 +22,14 @@ Detaches sensors to the DOM
|
||||
|
||||
### Options
|
||||
|
||||
**`draggable {String}`**
|
||||
A css selector for draggable elements within the `containers` specified.
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay touch start
|
||||
This value will delay touch start.
|
||||
|
||||
**`distance {Number}`**
|
||||
The distance you want the pointer to have moved before drag starts.
|
||||
|
||||
**`handle {String}`**
|
||||
Specify a css selector for a handle element if you don't want to allow drag action on the entire element.
|
||||
|
||||
@ -3,8 +3,18 @@ import MouseSensor from '..';
|
||||
|
||||
const sampleMarkup = `
|
||||
<ul>
|
||||
<li>First item</li>
|
||||
<li>Second item</li>
|
||||
<li class="draggable">
|
||||
<div class="handle">First handle</div>
|
||||
First item
|
||||
</li>
|
||||
<li class="draggable">
|
||||
<div class="handle">Second handle</div>
|
||||
Second item
|
||||
</li>
|
||||
<li class="non-draggable">
|
||||
<div class="handle">Non draggable handle</div>
|
||||
Non draggable item
|
||||
</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
@ -12,11 +22,20 @@ describe('MouseSensor', () => {
|
||||
let sandbox;
|
||||
let mouseSensor;
|
||||
let draggableElement;
|
||||
let nonDraggableElement;
|
||||
|
||||
function setup(optionsParam = {}) {
|
||||
const options = {
|
||||
draggable: '.draggable',
|
||||
delay: 0,
|
||||
distance: 0,
|
||||
...optionsParam,
|
||||
};
|
||||
|
||||
function setup(options = {delay: 0, distance: 0}) {
|
||||
sandbox = createSandbox(sampleMarkup);
|
||||
const containers = sandbox.querySelectorAll('ul');
|
||||
draggableElement = sandbox.querySelector('li');
|
||||
draggableElement = sandbox.querySelector('.draggable');
|
||||
nonDraggableElement = sandbox.querySelector('.non-draggable');
|
||||
mouseSensor = new MouseSensor(containers, options);
|
||||
mouseSensor.attach();
|
||||
}
|
||||
@ -35,6 +54,8 @@ describe('MouseSensor', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(document.body);
|
||||
waitForDragDelay();
|
||||
clickMouse(nonDraggableElement);
|
||||
waitForDragDelay();
|
||||
}
|
||||
|
||||
expect(dragFlow).not.toHaveTriggeredSensorEvent('drag:start');
|
||||
@ -77,6 +98,16 @@ describe('MouseSensor', () => {
|
||||
releaseMouse(document.body);
|
||||
});
|
||||
|
||||
it('does not prevent `dragstart` event when attempting to drag non draggable element', () => {
|
||||
clickMouse(nonDraggableElement);
|
||||
moveMouse(document, {pageX: 1, pageY: 1});
|
||||
const nativeDragEvent = triggerEvent(nonDraggableElement, 'dragstart');
|
||||
|
||||
expect(nativeDragEvent).not.toHaveDefaultPrevented();
|
||||
|
||||
releaseMouse(document.body);
|
||||
});
|
||||
|
||||
it('triggers `drag:stop` event when releasing mouse while dragging', () => {
|
||||
function dragFlow() {
|
||||
clickMouse(draggableElement);
|
||||
@ -126,9 +157,52 @@ describe('MouseSensor', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('using handle', () => {
|
||||
let handleInDraggableElement;
|
||||
let handleInNonDraggableElement;
|
||||
|
||||
beforeEach(() => {
|
||||
setup({handle: '.handle'});
|
||||
handleInDraggableElement = sandbox.querySelector('.draggable .handle');
|
||||
handleInNonDraggableElement = sandbox.querySelector('.non-draggable .handle');
|
||||
});
|
||||
|
||||
afterEach(teardown);
|
||||
|
||||
it('does not prevent `dragstart` event when attempting to drag handle in non draggable element', () => {
|
||||
clickMouse(handleInNonDraggableElement);
|
||||
moveMouse(document, {pageX: 1, pageY: 1});
|
||||
const nativeDragEvent = triggerEvent(handleInNonDraggableElement, 'dragstart');
|
||||
|
||||
expect(nativeDragEvent).not.toHaveDefaultPrevented();
|
||||
|
||||
releaseMouse(document.body);
|
||||
});
|
||||
|
||||
it('prevent `dragstart` event when attempting to drag handle in draggable element', () => {
|
||||
clickMouse(handleInDraggableElement);
|
||||
moveMouse(document, {pageX: 1, pageY: 1});
|
||||
const nativeDragEvent = triggerEvent(handleInDraggableElement, 'dragstart');
|
||||
|
||||
expect(nativeDragEvent).toHaveDefaultPrevented();
|
||||
|
||||
releaseMouse(document.body);
|
||||
});
|
||||
|
||||
it('does not prevent `dragstart` event when attempting to drag outside of handle inside of draggable', () => {
|
||||
clickMouse(draggableElement);
|
||||
moveMouse(document, {pageX: 1, pageY: 1});
|
||||
const nativeDragEvent = triggerEvent(draggableElement, 'dragstart');
|
||||
|
||||
expect(nativeDragEvent).not.toHaveDefaultPrevented();
|
||||
|
||||
releaseMouse(document.body);
|
||||
});
|
||||
});
|
||||
|
||||
describe('using distance', () => {
|
||||
beforeEach(() => {
|
||||
setup({delay: 0, distance: 1});
|
||||
setup({distance: 1});
|
||||
});
|
||||
|
||||
afterEach(teardown);
|
||||
@ -178,7 +252,7 @@ describe('MouseSensor', () => {
|
||||
|
||||
describe('using delay', () => {
|
||||
beforeEach(() => {
|
||||
setup({delay: DRAG_DELAY, distance: 0});
|
||||
setup({delay: DRAG_DELAY});
|
||||
});
|
||||
|
||||
afterEach(teardown);
|
||||
|
||||
@ -27,5 +27,14 @@ Triggers sensor event on container element
|
||||
|
||||
### Options
|
||||
|
||||
**`draggable {String}`**
|
||||
A css selector for draggable elements within the `containers` specified.
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay drag start
|
||||
This value will delay drag start.
|
||||
|
||||
**`distance {Number}`**
|
||||
The distance you want the pointer to have moved before drag starts.
|
||||
|
||||
**`handle {String}`**
|
||||
Specify a css selector for a handle element if you don't want to allow drag action on the entire element.
|
||||
|
||||
@ -45,6 +45,13 @@ export default class Sensor {
|
||||
*/
|
||||
this.currentContainer = null;
|
||||
|
||||
/**
|
||||
* Draggables original source element
|
||||
* @property originalSource
|
||||
* @type {HTMLElement}
|
||||
*/
|
||||
this.originalSource = null;
|
||||
|
||||
/**
|
||||
* The event of the initial sensor down
|
||||
* @property startEvent
|
||||
|
||||
@ -2,12 +2,12 @@
|
||||
|
||||
The base sensor event for all Sensor events that sensors emits.
|
||||
|
||||
| | |
|
||||
| --------------------- | ---------------------------------------------------------- |
|
||||
| **Interface** | `SensorEvent` |
|
||||
| **Cancelable** | false |
|
||||
| **Cancel action** | - |
|
||||
| **type** | `sensor` |
|
||||
| | |
|
||||
| ----------------- | ------------- |
|
||||
| **Interface** | `SensorEvent` |
|
||||
| **Cancelable** | false |
|
||||
| **Cancel action** | - |
|
||||
| **type** | `sensor` |
|
||||
|
||||
### API
|
||||
|
||||
@ -25,55 +25,58 @@ Read-only property for the normalized target for both touch and mouse events.
|
||||
Returns the element that is behind cursor or touch pointer.
|
||||
|
||||
**`sensorEvent.container: Number`**
|
||||
Read-only property for the container that fired the sensor event
|
||||
Read-only property for the container that fired the sensor event.
|
||||
|
||||
**`sensorEvent.originalSource: String`**
|
||||
Read-only property for the original source element that was picked up.
|
||||
|
||||
**`sensorEvent.pressure: Number`**
|
||||
Read-only property for the pressure applied
|
||||
Read-only property for the pressure applied.
|
||||
|
||||
## DragStartSensorEvent
|
||||
|
||||
`DragStartSensorEvent` gets triggered by sensors for drag start.
|
||||
|
||||
| | |
|
||||
| --------------------- | ---------------------------------------------------------- |
|
||||
| **Specification** | `SensorEvent` |
|
||||
| **Interface** | `DragStartSensorEvent` |
|
||||
| **Cancelable** | true |
|
||||
| **Cancel action** | Prevents drag start |
|
||||
| **type** | `drag:start` |
|
||||
| | |
|
||||
| ----------------- | ---------------------- |
|
||||
| **Specification** | `SensorEvent` |
|
||||
| **Interface** | `DragStartSensorEvent` |
|
||||
| **Cancelable** | true |
|
||||
| **Cancel action** | Prevents drag start |
|
||||
| **type** | `drag:start` |
|
||||
|
||||
## DragMoveSensorEvent
|
||||
|
||||
`DragMoveSensorEvent` gets triggered by sensors for drag move.
|
||||
|
||||
| | |
|
||||
| --------------------- | ---------------------------------------------------------- |
|
||||
| **Specification** | `SensorEvent` |
|
||||
| **Interface** | `DragMoveSensorEvent` |
|
||||
| **Cancelable** | false |
|
||||
| **Cancel action** | - |
|
||||
| **type** | `drag:move` |
|
||||
| | |
|
||||
| ----------------- | --------------------- |
|
||||
| **Specification** | `SensorEvent` |
|
||||
| **Interface** | `DragMoveSensorEvent` |
|
||||
| **Cancelable** | false |
|
||||
| **Cancel action** | - |
|
||||
| **type** | `drag:move` |
|
||||
|
||||
## DragStopSensorEvent
|
||||
|
||||
`DragStopSensorEvent` gets triggered by sensors for drag stop.
|
||||
|
||||
| | |
|
||||
| --------------------- | ---------------------------------------------------------- |
|
||||
| **Specification** | `SensorEvent` |
|
||||
| **Interface** | `DragStopSensorEvent` |
|
||||
| **Cancelable** | false |
|
||||
| **Cancel action** | - |
|
||||
| **type** | `drag:stop` |
|
||||
| | |
|
||||
| ----------------- | --------------------- |
|
||||
| **Specification** | `SensorEvent` |
|
||||
| **Interface** | `DragStopSensorEvent` |
|
||||
| **Cancelable** | false |
|
||||
| **Cancel action** | - |
|
||||
| **type** | `drag:stop` |
|
||||
|
||||
## DragPressureSensorEvent
|
||||
|
||||
`DragPressureSensorEvent` gets triggered by sensors for drag pressure.
|
||||
|
||||
| | |
|
||||
| --------------------- | ---------------------------------------------------------- |
|
||||
| **Specification** | `SensorEvent` |
|
||||
| **Interface** | `DragPressureSensorEvent` |
|
||||
| **Cancelable** | false |
|
||||
| **Cancel action** | - |
|
||||
| **type** | `drag:pressure` |
|
||||
| | |
|
||||
| ----------------- | ------------------------- |
|
||||
| **Specification** | `SensorEvent` |
|
||||
| **Interface** | `DragPressureSensorEvent` |
|
||||
| **Cancelable** | false |
|
||||
| **Cancel action** | - |
|
||||
| **type** | `drag:pressure` |
|
||||
|
||||
@ -58,6 +58,16 @@ export class SensorEvent extends AbstractEvent {
|
||||
return this.data.container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Draggables original source element
|
||||
* @property originalSource
|
||||
* @type {HTMLElement}
|
||||
* @readonly
|
||||
*/
|
||||
get originalSource() {
|
||||
return this.data.originalSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trackpad pressure
|
||||
* @property pressure
|
||||
|
||||
@ -22,5 +22,14 @@ Detaches sensors to the DOM
|
||||
|
||||
### Options
|
||||
|
||||
**`draggable {String}`**
|
||||
A css selector for draggable elements within the `containers` specified.
|
||||
|
||||
**`delay {Number}`**
|
||||
This value will delay touch start
|
||||
This value will delay touch start.
|
||||
|
||||
**`distance {Number}`**
|
||||
The distance you want the pointer to have moved before drag starts.
|
||||
|
||||
**`handle {String}`**
|
||||
Specify a css selector for a handle element if you don't want to allow drag action on the entire element.
|
||||
|
||||
@ -111,6 +111,17 @@ export default class TouchSensor extends Sensor {
|
||||
if (!container) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.options.handle && event.target && !closest(event.target, this.options.handle)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const originalSource = closest(event.target, this.options.draggable);
|
||||
|
||||
if (!originalSource) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {distance = 0} = this.options;
|
||||
const {delay} = this;
|
||||
const {pageX, pageY} = touchCoords(event);
|
||||
@ -119,6 +130,7 @@ export default class TouchSensor extends Sensor {
|
||||
this.onTouchStartAt = Date.now();
|
||||
this.startEvent = event;
|
||||
this.currentContainer = container;
|
||||
this.originalSource = originalSource;
|
||||
|
||||
document.addEventListener('touchend', this[onTouchEnd]);
|
||||
document.addEventListener('touchcancel', this[onTouchEnd]);
|
||||
@ -142,12 +154,14 @@ export default class TouchSensor extends Sensor {
|
||||
const startEvent = this.startEvent;
|
||||
const container = this.currentContainer;
|
||||
const touch = touchCoords(startEvent);
|
||||
const originalSource = this.originalSource;
|
||||
|
||||
const dragStartEvent = new DragStartSensorEvent({
|
||||
clientX: touch.pageX,
|
||||
clientY: touch.pageY,
|
||||
target: startEvent.target,
|
||||
container,
|
||||
originalSource,
|
||||
originalEvent: startEvent,
|
||||
});
|
||||
|
||||
|
||||
@ -4,8 +4,9 @@ import TouchSensor from '..';
|
||||
|
||||
const sampleMarkup = `
|
||||
<ul>
|
||||
<li>First item</li>
|
||||
<li>Second item</li>
|
||||
<li class="draggable">First item</li>
|
||||
<li class="draggable">Second item</li>
|
||||
<li class="non-draggable">Non draggable item</li>
|
||||
</ul>
|
||||
`;
|
||||
|
||||
@ -13,11 +14,20 @@ describe('TouchSensor', () => {
|
||||
let sandbox;
|
||||
let touchSensor;
|
||||
let draggableElement;
|
||||
let nonDraggableElement;
|
||||
|
||||
function setup(optionsParam = {}) {
|
||||
const options = {
|
||||
draggable: '.draggable',
|
||||
delay: 0,
|
||||
distance: 0,
|
||||
...optionsParam,
|
||||
};
|
||||
|
||||
function setup(options = {delay: 0, distance: 0}) {
|
||||
sandbox = createSandbox(sampleMarkup);
|
||||
const containers = sandbox.querySelectorAll('ul');
|
||||
draggableElement = sandbox.querySelector('li');
|
||||
draggableElement = sandbox.querySelector('.draggable');
|
||||
nonDraggableElement = sandbox.querySelector('.non-draggable');
|
||||
touchSensor = new TouchSensor(containers, options);
|
||||
touchSensor.attach();
|
||||
}
|
||||
@ -50,6 +60,8 @@ describe('TouchSensor', () => {
|
||||
function dragFlow() {
|
||||
touchStart(document.body);
|
||||
waitForDragDelay();
|
||||
touchStart(nonDraggableElement);
|
||||
waitForDragDelay();
|
||||
}
|
||||
|
||||
expect(dragFlow).not.toHaveTriggeredSensorEvent('drag:start');
|
||||
@ -101,7 +113,7 @@ describe('TouchSensor', () => {
|
||||
|
||||
describe('using distance', () => {
|
||||
beforeEach(() => {
|
||||
setup({delay: 0, distance: 1});
|
||||
setup({distance: 1});
|
||||
});
|
||||
|
||||
afterEach(teardown);
|
||||
@ -157,7 +169,7 @@ describe('TouchSensor', () => {
|
||||
|
||||
describe('using delay', () => {
|
||||
beforeEach(() => {
|
||||
setup({delay: DRAG_DELAY, distance: 0});
|
||||
setup({delay: DRAG_DELAY});
|
||||
});
|
||||
|
||||
afterEach(teardown);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user