-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathuseDraggingContainer.ts
More file actions
134 lines (106 loc) · 3.6 KB
/
useDraggingContainer.ts
File metadata and controls
134 lines (106 loc) · 3.6 KB
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { useRef, useState } from "react";
type DragDirectionType = "X" | "Y";
type EventMapperType = {
[key in DragDirectionType]: "pageX" | "pageY";
};
const eventMapper: EventMapperType = {
X: "pageX",
Y: "pageY",
};
const FIRST_TOUCH_EVENT_IDX = 0;
export default function useDraggingContainer(dragDirection: DragDirectionType) {
const containerRef = useRef<HTMLElement | null>(null);
const currentRef = useRef(0);
const standardRef = useRef(0);
const [isStartDragging, setIsStartDragging] = useState(false);
const [isArrivedEnd, setIsArrivedEnd] = useState(false);
const [draggedDistance, setDraggedDistance] = useState(0);
function handleMouseDown(event: React.MouseEvent<HTMLElement, MouseEvent>) {
setIsStartDragging(true);
const page = getPageByEventType(event);
currentRef.current = page;
initializeForDraggedDistance(page);
}
function handleTouchStart(event: React.TouchEvent<HTMLElement>) {
setIsStartDragging(true);
const page = getPageByEventType(event);
currentRef.current = page;
initializeForDraggedDistance(page);
}
function getPageByEventType(event: React.SyntheticEvent<HTMLElement>): number {
const eventType = eventMapper[dragDirection];
if (event.nativeEvent instanceof TouchEvent) {
return event.nativeEvent.touches[FIRST_TOUCH_EVENT_IDX][eventType];
}
if (event.nativeEvent instanceof MouseEvent) {
return event.nativeEvent[eventType];
}
return 0;
}
function initializeForDraggedDistance(standard: number) {
setDraggedDistance(0);
standardRef.current = standard;
}
function handleMouseMove(event: React.MouseEvent<HTMLElement, MouseEvent>) {
const container = containerRef.current;
if (!container) return;
if (!isStartDragging) return;
const page = getPageByEventType(event);
moveContainerByCurrent(container, page);
handleArrivedEnd(container);
setDraggedDistance(Math.abs(page - standardRef.current));
}
function handleTouchMove(event: React.TouchEvent<HTMLElement>) {
const container = containerRef.current;
if (!container) return;
if (!isStartDragging) return;
const page = getPageByEventType(event);
moveContainerByCurrent(container, page);
handleArrivedEnd(container);
setDraggedDistance(Math.abs(page - standardRef.current));
}
function moveContainerByCurrent(container: HTMLElement, movedTrigger: number) {
const delta = currentRef.current - movedTrigger;
if (dragDirection === "X") {
container.scrollLeft += delta;
}
if (dragDirection === "Y") {
container.scrollTop += delta;
}
currentRef.current = movedTrigger;
}
function handleArrivedEnd(container: HTMLElement) {
if (dragDirection === "X") {
setIsArrivedEnd(container.scrollWidth - container.scrollLeft === container.clientWidth);
}
if (dragDirection === "Y") {
setIsArrivedEnd(container.scrollHeight - container.scrollTop === container.clientHeight);
}
}
function handleMouseUpOrLeave() {
reset();
}
function handleTouchEndOrCancel() {
reset();
}
function reset() {
setIsStartDragging(false);
currentRef.current = 0;
standardRef.current = 0;
}
return {
scrollableContainerProps: {
ref: containerRef,
onMouseDown: handleMouseDown,
onMouseMove: handleMouseMove,
onMouseUp: handleMouseUpOrLeave,
onMouseLeave: handleMouseUpOrLeave,
onTouchStart: handleTouchStart,
onTouchMove: handleTouchMove,
onTouchEnd: handleTouchEndOrCancel,
onTouchCancel: handleTouchEndOrCancel,
},
isDragging: draggedDistance > 10,
isArrivedEnd,
};
}