Skip to content

Commit

Permalink
feat(movable): support touch devices
Browse files Browse the repository at this point in the history
  • Loading branch information
vnphanquang committed Nov 9, 2023
1 parent 6b48eab commit 11a42e2
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 28 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-mangos-glow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@svelte-put/movable': minor
---

Support touch devices by switching `MouseEvent` to `PointerEvent` and forcing `touch-action: none` on `handle` (prevent touch events from being registered as scroll) (#242)
59 changes: 33 additions & 26 deletions packages/actions/movable/src/movable.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { tick } from 'svelte';

/**
* Trigger node displacement on mousedown (via position.left & position.top)
* Trigger node displacement on pointerdown (via position.left & position.top)
* @public
*
* @example
Expand Down Expand Up @@ -61,13 +61,13 @@ import { tick } from 'svelte';
*
* Things that will happen in the above example:
*
* 1. on `mousedown` of the handle (`button` element), a `movablestart` {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent | CustomEvent } is dispatched,
* 1. on `pointerdown` of the handle (`button` element), a `movablestart` {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent | CustomEvent } is dispatched,
*
* 2. any `mousemove` event will tell `div` to move accordingly;
* 2. any `pointermove` event will tell `div` to move accordingly;
*
* 3. movement will be limited to the border of the `containerNode`, ±20% of the width & height of the `div` that the action is being used on,
*
* 4. `mouseup` event will stop the movement; a `movableend` {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent | CustomEvent } is dispatched.
* 4. `pointerup` event will stop the movement; a `movableend` {@link https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent | CustomEvent } is dispatched.
*
* @remarks
*
Expand All @@ -87,11 +87,11 @@ import { tick } from 'svelte';
*
* Be aware of side effects:
*
* - element.style.position is set to `relative` if not already 'absolute', 'relative', or 'fixed during the first time mousedown is triggered
* - element.style.position is set to `relative` if not already 'absolute', 'relative', or 'fixed during the first time pointerdown is triggered
*
* - document.body.userSelect is set to `none` after `mousedown` and restored on `mouseup`
* - document.body.userSelect is set to `none` after `pointerdown` and restored on `pointerup`
*
* - document.body.cursor is set to `move` after `mousedown` and restored on `mouseup`
* - document.body.cursor is set to `move` after `pointerdown` and restored on `pointerup`
*
* @param {HTMLElement} node - HTMLElement to be moved
* @param {import('./public').MovableParameter} param - svelte action parameters
Expand All @@ -114,7 +114,7 @@ export function movable(node, param = { enabled: true }) {
}

/**
* @param {MouseEvent} event
* @param {PointerEvent} event
*/
function updateLastMousePosition(event) {
lastMousePosition.x = event.clientX;
Expand All @@ -130,9 +130,9 @@ export function movable(node, param = { enabled: true }) {
}

/**
* @param {MouseEvent} event
* @param {PointerEvent} event
*/
function onMouseMove(event) {
function move(event) {
const Δx = event.clientX - lastMousePosition.x;
const Δy = event.clientY - lastMousePosition.y;
updateLastMousePosition(event);
Expand Down Expand Up @@ -230,18 +230,22 @@ export function movable(node, param = { enabled: true }) {
}
handle.style.cursor = 'grab';
}
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', end);
window.removeEventListener('pointermove', move);
window.removeEventListener('pointerup', end);
window.removeEventListener('pointercancel', end);

/** @type {import('./public').MovableEventDetail} */
const detail = { node, position: lastNodePosition };
node.dispatchEvent(new CustomEvent('movableend', { detail }));
}

/**
* @param {MouseEvent} event
* @param {PointerEvent} event
*/
function onMouseDown(event) {
function start(event) {
// if (event.target?.hasPointerCapture(event.pointerId)) {
// event.target?.releasePointerCapture(event.pointerId);
// }
const ignoredElements = getIgnoredElements();
if (
ignoredElements.some((node) => node.isSameNode(/** @type {HTMLElement} */ (event.target)))
Expand Down Expand Up @@ -274,11 +278,13 @@ export function movable(node, param = { enabled: true }) {
document.body.style.cursor = 'grabbing';
handle.style.cursor = 'grabbing';
}
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', end);
window.addEventListener('pointermove', move);
window.addEventListener('pointerup', end);
window.addEventListener('pointercancel', end);
}

function addCursor() {
function addStyles() {
handle.style.touchAction = 'none';
if (cursor) {
handle.style.cursor = 'grab';
const ignoredElements = getIgnoredElements();
Expand All @@ -289,7 +295,8 @@ export function movable(node, param = { enabled: true }) {
}
}
}
function removeCursor() {
function removeStyles() {
handle.style.removeProperty('touch-action');
if (cursor) {
if (handle?.style.cursor === 'grab') {
handle.style.removeProperty('cursor');
Expand All @@ -304,27 +311,27 @@ export function movable(node, param = { enabled: true }) {
}

if (enabled) {
handle.addEventListener('mousedown', onMouseDown, true);
handle.addEventListener('pointerdown', start, true);
tick().then(() => {
addCursor();
addStyles();
});
}
return {
update(update) {
removeCursor();
handle.removeEventListener('mousedown', onMouseDown, true);
removeStyles();
handle.removeEventListener('pointerdown', start, true);
({ parent, normalizedDelta, handle, enabled, ignore, cursor } = input(node, update));

if (enabled) {
handle.addEventListener('mousedown', onMouseDown, true);
handle.addEventListener('pointerdown', start, true);
tick().then(() => {
addCursor();
addStyles();
});
}
},
destroy() {
handle.removeEventListener('mousedown', onMouseDown, true);
removeCursor();
handle.removeEventListener('pointerdown', start, true);
removeStyles();
},
};
}
Expand Down
2 changes: 0 additions & 2 deletions sites/docs/src/routes/(main)/docs/(pkg)/movable/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
<script lang="ts">
import typescript from 'svelte-highlight/languages/typescript';
import Code from '$client/components/Code/Code.svelte';
import Installation from '$client/components/Installation/Installation.svelte';
import ResourceLink from '$client/components/ResourceLink/ResourceLink.svelte';
Expand Down

0 comments on commit 11a42e2

Please sign in to comment.