UNPKG

@sv-use/core

Version:

A collection of Svelte 5 utilities.

110 lines (109 loc) 3.74 kB
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; }