forked from gridstack/gridstack.js
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathdd-touch.ts
200 lines (168 loc) · 6.07 KB
/
dd-touch.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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
/**
* touch.ts 7.1.0-dev
* Copyright (c) 2021 Alain Dumesny - see GridStack root license
*/
import { DDManager } from './dd-manager';
/**
* Detect touch support - Windows Surface devices and other touch devices
* should we use this instead ? (what we had for always showing resize handles)
* /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)
*/
export const isTouch: boolean = typeof window !== 'undefined' && typeof document !== 'undefined' &&
( 'ontouchstart' in document
|| 'ontouchstart' in window
// || !!window.TouchEvent // true on Windows 10 Chrome desktop so don't use this
|| ((window as any).DocumentTouch && document instanceof (window as any).DocumentTouch)
|| navigator.maxTouchPoints > 0
|| (navigator as any).msMaxTouchPoints > 0
);
// interface TouchCoord {x: number, y: number};
class DDTouch {
public static touchHandled: boolean;
public static pointerLeaveTimeout: number;
}
/**
* Get the x,y position of a touch event
*/
// function getTouchCoords(e: TouchEvent): TouchCoord {
// return {
// x: e.changedTouches[0].pageX,
// y: e.changedTouches[0].pageY
// };
// }
/**
* Simulate a mouse event based on a corresponding touch event
* @param {Object} e A touch event
* @param {String} simulatedType The corresponding mouse event
*/
function simulateMouseEvent(e: TouchEvent, simulatedType: string) {
// Ignore multi-touch events
if (e.touches.length > 1) return;
// Prevent "Ignored attempt to cancel a touchmove event with cancelable=false" errors
if (e.cancelable) e.preventDefault();
const touch = e.changedTouches[0], simulatedEvent = document.createEvent('MouseEvents');
// Initialize the simulated mouse event using the touch event's coordinates
simulatedEvent.initMouseEvent(
simulatedType, // type
true, // bubbles
true, // cancelable
window, // view
1, // detail
touch.screenX, // screenX
touch.screenY, // screenY
touch.clientX, // clientX
touch.clientY, // clientY
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
0, // button
null // relatedTarget
);
// Dispatch the simulated event to the target element
e.target.dispatchEvent(simulatedEvent);
}
/**
* Simulate a mouse event based on a corresponding Pointer event
* @param {Object} e A pointer event
* @param {String} simulatedType The corresponding mouse event
*/
function simulatePointerMouseEvent(e: PointerEvent, simulatedType: string) {
// Prevent "Ignored attempt to cancel a touchmove event with cancelable=false" errors
if (e.cancelable) e.preventDefault();
const simulatedEvent = document.createEvent('MouseEvents');
// Initialize the simulated mouse event using the touch event's coordinates
simulatedEvent.initMouseEvent(
simulatedType, // type
true, // bubbles
true, // cancelable
window, // view
1, // detail
e.screenX, // screenX
e.screenY, // screenY
e.clientX, // clientX
e.clientY, // clientY
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
0, // button
null // relatedTarget
);
// Dispatch the simulated event to the target element
e.target.dispatchEvent(simulatedEvent);
}
/**
* Handle the touchstart events
* @param {Object} e The widget element's touchstart event
*/
export function touchstart(e: TouchEvent) {
// Ignore the event if another widget is already being handled
if (DDTouch.touchHandled) return; DDTouch.touchHandled = true;
// Simulate the mouse events
// simulateMouseEvent(e, 'mouseover');
// simulateMouseEvent(e, 'mousemove');
simulateMouseEvent(e, 'mousedown');
}
/**
* Handle the touchmove events
* @param {Object} e The document's touchmove event
*/
export function touchmove(e: TouchEvent) {
// Ignore event if not handled by us
if (!DDTouch.touchHandled) return;
simulateMouseEvent(e, 'mousemove');
}
/**
* Handle the touchend events
* @param {Object} e The document's touchend event
*/
export function touchend(e: TouchEvent) {
// Ignore event if not handled
if (!DDTouch.touchHandled) return;
// cancel delayed leave event when we release on ourself which happens BEFORE we get this!
if (DDTouch.pointerLeaveTimeout) {
window.clearTimeout(DDTouch.pointerLeaveTimeout);
delete DDTouch.pointerLeaveTimeout;
}
const wasDragging = !!DDManager.dragElement;
// Simulate the mouseup event
simulateMouseEvent(e, 'mouseup');
// simulateMouseEvent(event, 'mouseout');
// If the touch interaction did not move, it should trigger a click
if (!wasDragging) {
simulateMouseEvent(e, 'click');
}
// Unset the flag to allow other widgets to inherit the touch event
DDTouch.touchHandled = false;
}
/**
* Note we don't get touchenter/touchleave (which are deprecated)
* see https://stackoverflow.com/questions/27908339/js-touch-equivalent-for-mouseenter
* so instead of PointerEvent to still get enter/leave and send the matching mouse event.
*/
export function pointerdown(e: PointerEvent) {
(e.target as HTMLElement).releasePointerCapture(e.pointerId) // <- Important!
}
export function pointerenter(e: PointerEvent) {
// ignore the initial one we get on pointerdown on ourself
if (!DDManager.dragElement) {
// console.log('pointerenter ignored');
return;
}
// console.log('pointerenter');
simulatePointerMouseEvent(e, 'mouseenter');
}
export function pointerleave(e: PointerEvent) {
// ignore the leave on ourself we get before releasing the mouse over ourself
// by delaying sending the event and having the up event cancel us
if (!DDManager.dragElement) {
// console.log('pointerleave ignored');
return;
}
DDTouch.pointerLeaveTimeout = window.setTimeout(() => {
delete DDTouch.pointerLeaveTimeout;
// console.log('pointerleave delayed');
simulatePointerMouseEvent(e, 'mouseleave');
}, 10);
}