/
dragscroll.action.ts
116 lines (105 loc) · 3.2 KB
/
dragscroll.action.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type { Action, ActionReturn } from 'svelte/action';
import type { DragScrollParameters } from './dragscroll.types';
/**
* @internal
*/
function resolveParameters(parameters: Partial<DragScrollParameters> = {}) {
const { cursor = true, enabled = true, axis = 'x', event = 'pointer' } = parameters;
return {
enabled,
axes: {
x: axis === 'x' || axis === 'both',
y: axis === 'y' || axis === 'both',
},
events: {
down: event === 'pointer' ? ('pointerdown' as const) : ('mousedown' as const),
up: event === 'pointer' ? ('pointerup' as const) : ('mouseup' as const),
move: event === 'pointer' ? ('pointermove' as const) : ('mousemove' as const),
leave: event === 'pointer' ? ('pointerleave' as const) : ('mouseleave' as const),
},
cursor,
};
}
/**
* svelte action `use:dragscroll` for adding 'drag-to-scroll' behavior
* @public
*
* @param node - node to apply the action
* @param parameters - instructions for customizing action behavior
* @returns svelte {@link ActionReturn}
*/
export const dragscroll: Action<HTMLElement, Partial<DragScrollParameters>> = function (
node,
parameters = {},
) {
let isDown = false;
let startX: number;
let startY: number;
let scrollLeft: number;
let scrollTop: number;
let { enabled, axes, events, cursor } = resolveParameters(parameters);
function handlePointerDown(e: PointerEvent | MouseEvent) {
changeCursor(true);
isDown = true;
startX = e.pageX - node.offsetLeft;
scrollLeft = node.scrollLeft;
startY = e.pageY - node.offsetTop;
scrollTop = node.scrollTop;
}
function handlePointerUpAndLeave() {
changeCursor();
isDown = false;
}
function handlePointerMove(e: PointerEvent | MouseEvent) {
if (!isDown) return;
e.preventDefault();
if (axes.x) {
const x = e.pageX - node.offsetLeft;
const walkX = x - startX;
node.scrollLeft = scrollLeft - walkX;
}
if (axes.y) {
const y = e.pageY - node.offsetTop;
const walkY = y - startY;
node.scrollTop = scrollTop - walkY;
}
}
function addEvents() {
if (!node) return;
node.addEventListener(events.down, handlePointerDown);
node.addEventListener(events.leave, handlePointerUpAndLeave);
node.addEventListener(events.up, handlePointerUpAndLeave);
node.addEventListener(events.move, handlePointerMove);
}
function removeEvents() {
if (!node) return;
node.removeEventListener(events.down, handlePointerDown);
node.removeEventListener(events.leave, handlePointerUpAndLeave);
node.removeEventListener(events.up, handlePointerUpAndLeave);
node.removeEventListener(events.move, handlePointerMove);
}
function changeCursor(active = false) {
if (!node) return;
if (cursor) {
node.style.cursor = active ? 'grabbing' : 'grab';
} else {
node.style.removeProperty('cursor');
}
}
if (enabled) {
changeCursor();
addEvents();
}
return {
update(update = {}) {
removeEvents();
({ enabled, axes, events, cursor } = resolveParameters(update));
changeCursor();
addEvents();
},
destroy() {
removeEvents();
},
};
};