@sv-use/core
Version:
A collection of Svelte 5 utilities.
110 lines (109 loc) • 3.74 kB
JavaScript
import { onDestroy } from 'svelte';
import { handleEventListener } from '../handle-event-listener/index.svelte.js';
import { noop, normalizeValue } from '../__internal__/utils.svelte.js';
/**
* Runs a callback when a long press occurs on a given element.
* @param target The element on which to attach the long press.
* @param handler The callback to execute.
* @param options Additional options to customize the behavior.
* @returns A cleanup function.
* @see https://svelte-librarian.github.io/sv-use/docs/core/on-long-press
*/
export function onLongPress(target, handler, options = {}) {
const { autoCleanup = true, delay = 500, distanceThreshold = 10, onMouseUp = noop } = options;
const modifiers = {
capture: false,
once: false,
preventDefault: false,
self: false,
stopPropagation: false,
...(options.modifiers ?? {})
};
let cleanups = [];
const listenerOptions = {
capture: modifiers.capture,
once: modifiers.once,
autoCleanup
};
let timeout;
let startPosition;
let startTimestamp;
let isLongPress = false;
const _target = $derived(normalizeValue(target));
$effect(() => {
if (_target) {
(cleanups = []).push(handleEventListener(_target, 'pointerdown', onDown, listenerOptions), handleEventListener(_target, 'pointermove', onMove, listenerOptions), handleEventListener(_target, ['pointerup', 'pointerleave'], onRelease, listenerOptions));
}
});
if (autoCleanup) {
onDestroy(() => cleanup());
}
function onDown(event) {
if (modifiers.self && event.target !== _target)
return;
reset();
if (modifiers.preventDefault)
event.preventDefault();
if (modifiers.stopPropagation)
event.stopPropagation();
startPosition = {
x: event.x,
y: event.y
};
startTimestamp = event.timeStamp;
timeout = setTimeout(() => {
isLongPress = true;
handler(event);
}, delay);
}
function onMove(event) {
if (modifiers.self && event.target !== _target)
return;
if (!startPosition || !distanceThreshold)
return;
if (modifiers.preventDefault)
event.preventDefault();
if (modifiers.stopPropagation)
event.stopPropagation();
const dx = event.x - startPosition.x;
const dy = event.y - startPosition.y;
const distance = Math.sqrt(dx * dx + dy * dy);
if (distance >= distanceThreshold) {
reset();
}
}
function onRelease(event) {
const [_startTimestamp, _startPosition, _hasLongPressed] = [
startTimestamp,
startPosition,
isLongPress
];
reset();
if (!onMouseUp || !_startPosition || !_startTimestamp)
return;
if (modifiers.self && event.target !== _target)
return;
if (modifiers.preventDefault)
event.preventDefault();
if (modifiers.stopPropagation)
event.stopPropagation();
const dx = event.x - _startPosition.x;
const dy = event.y - _startPosition.y;
const distance = Math.sqrt(dx * dx + dy * dy);
onMouseUp(event.timeStamp - _startTimestamp, distance, _hasLongPressed);
}
function reset() {
if (timeout) {
clearTimeout(timeout);
timeout = undefined;
}
startPosition = undefined;
startTimestamp = undefined;
isLongPress = false;
}
function cleanup() {
cleanups.forEach((fn) => fn());
reset();
}
return cleanup;
}