diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..6fc92ba --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "baseUrl": "./src/" + } +} diff --git a/src/Draggable/Draggable.js b/src/Draggable/Draggable.js index 2ff3c5c..2f471fb 100644 --- a/src/Draggable/Draggable.js +++ b/src/Draggable/Draggable.js @@ -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')); diff --git a/src/Draggable/Sensors/DragSensor/DragSensor.js b/src/Draggable/Sensors/DragSensor/DragSensor.js index 1ba420c..74e9dec 100644 --- a/src/Draggable/Sensors/DragSensor/DragSensor.js +++ b/src/Draggable/Sensors/DragSensor/DragSensor.js @@ -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); } diff --git a/src/Draggable/Sensors/DragSensor/README.md b/src/Draggable/Sensors/DragSensor/README.md index 4e0e531..c259665 100644 --- a/src/Draggable/Sensors/DragSensor/README.md +++ b/src/Draggable/Sensors/DragSensor/README.md @@ -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 diff --git a/src/Draggable/Sensors/DragSensor/tests/DragSensor.test.js b/src/Draggable/Sensors/DragSensor/tests/DragSensor.test.js index 68ac7e4..0b8336a 100644 --- a/src/Draggable/Sensors/DragSensor/tests/DragSensor.test.js +++ b/src/Draggable/Sensors/DragSensor/tests/DragSensor.test.js @@ -13,8 +13,9 @@ import DragSensor from '..'; const sampleMarkup = ` `; @@ -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'); + }); }); }); diff --git a/src/Draggable/Sensors/ForceTouchSensor/ForceTouchSensor.js b/src/Draggable/Sensors/ForceTouchSensor/ForceTouchSensor.js index fa5ebd6..fe6c757 100644 --- a/src/Draggable/Sensors/ForceTouchSensor/ForceTouchSensor.js +++ b/src/Draggable/Sensors/ForceTouchSensor/ForceTouchSensor.js @@ -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, }); diff --git a/src/Draggable/Sensors/ForceTouchSensor/README.md b/src/Draggable/Sensors/ForceTouchSensor/README.md index 1f6aa20..1925219 100644 --- a/src/Draggable/Sensors/ForceTouchSensor/README.md +++ b/src/Draggable/Sensors/ForceTouchSensor/README.md @@ -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. \ No newline at end of file +- 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. diff --git a/src/Draggable/Sensors/MouseSensor/MouseSensor.js b/src/Draggable/Sensors/MouseSensor/MouseSensor.js index c2a04ee..2097408 100644 --- a/src/Draggable/Sensors/MouseSensor/MouseSensor.js +++ b/src/Draggable/Sensors/MouseSensor/MouseSensor.js @@ -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, }); diff --git a/src/Draggable/Sensors/MouseSensor/README.md b/src/Draggable/Sensors/MouseSensor/README.md index b5bb930..c45cfaf 100644 --- a/src/Draggable/Sensors/MouseSensor/README.md +++ b/src/Draggable/Sensors/MouseSensor/README.md @@ -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. diff --git a/src/Draggable/Sensors/MouseSensor/tests/MouseSensor.test.js b/src/Draggable/Sensors/MouseSensor/tests/MouseSensor.test.js index dd5b35d..4edab90 100644 --- a/src/Draggable/Sensors/MouseSensor/tests/MouseSensor.test.js +++ b/src/Draggable/Sensors/MouseSensor/tests/MouseSensor.test.js @@ -3,8 +3,18 @@ import MouseSensor from '..'; const sampleMarkup = ` `; @@ -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); diff --git a/src/Draggable/Sensors/Sensor/README.md b/src/Draggable/Sensors/Sensor/README.md index 823601f..49e5e53 100644 --- a/src/Draggable/Sensors/Sensor/README.md +++ b/src/Draggable/Sensors/Sensor/README.md @@ -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. diff --git a/src/Draggable/Sensors/Sensor/Sensor.js b/src/Draggable/Sensors/Sensor/Sensor.js index ddbf787..8ac04fe 100644 --- a/src/Draggable/Sensors/Sensor/Sensor.js +++ b/src/Draggable/Sensors/Sensor/Sensor.js @@ -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 diff --git a/src/Draggable/Sensors/SensorEvent/README.md b/src/Draggable/Sensors/SensorEvent/README.md index a0224cb..5787e0b 100644 --- a/src/Draggable/Sensors/SensorEvent/README.md +++ b/src/Draggable/Sensors/SensorEvent/README.md @@ -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` | diff --git a/src/Draggable/Sensors/SensorEvent/SensorEvent.js b/src/Draggable/Sensors/SensorEvent/SensorEvent.js index d5aa455..e3705f0 100644 --- a/src/Draggable/Sensors/SensorEvent/SensorEvent.js +++ b/src/Draggable/Sensors/SensorEvent/SensorEvent.js @@ -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 diff --git a/src/Draggable/Sensors/TouchSensor/README.md b/src/Draggable/Sensors/TouchSensor/README.md index 8fc7776..fb77698 100644 --- a/src/Draggable/Sensors/TouchSensor/README.md +++ b/src/Draggable/Sensors/TouchSensor/README.md @@ -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. diff --git a/src/Draggable/Sensors/TouchSensor/TouchSensor.js b/src/Draggable/Sensors/TouchSensor/TouchSensor.js index 89f4183..fc5a5bb 100644 --- a/src/Draggable/Sensors/TouchSensor/TouchSensor.js +++ b/src/Draggable/Sensors/TouchSensor/TouchSensor.js @@ -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, }); diff --git a/src/Draggable/Sensors/TouchSensor/tests/TouchSensor.test.js b/src/Draggable/Sensors/TouchSensor/tests/TouchSensor.test.js index a6f4da8..38642a8 100644 --- a/src/Draggable/Sensors/TouchSensor/tests/TouchSensor.test.js +++ b/src/Draggable/Sensors/TouchSensor/tests/TouchSensor.test.js @@ -4,8 +4,9 @@ import TouchSensor from '..'; const sampleMarkup = ` `; @@ -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);