1 | import { invariant } from '@react-dnd/invariant';
|
2 | import { ListenerType } from './interfaces.js';
|
3 | import { OptionsReader } from './OptionsReader.js';
|
4 | import { distance, inAngleRanges } from './utils/math.js';
|
5 | import { getEventClientOffset, getNodeClientOffset } from './utils/offsets.js';
|
6 | import { eventShouldEndDrag, eventShouldStartDrag, isTouchEvent } from './utils/predicates.js';
|
7 | import { supportsPassive } from './utils/supportsPassive.js';
|
8 | const eventNames = {
|
9 | [ListenerType.mouse]: {
|
10 | start: 'mousedown',
|
11 | move: 'mousemove',
|
12 | end: 'mouseup',
|
13 | contextmenu: 'contextmenu'
|
14 | },
|
15 | [ListenerType.touch]: {
|
16 | start: 'touchstart',
|
17 | move: 'touchmove',
|
18 | end: 'touchend'
|
19 | },
|
20 | [ListenerType.keyboard]: {
|
21 | keydown: 'keydown'
|
22 | }
|
23 | };
|
24 | export class TouchBackendImpl {
|
25 | |
26 |
|
27 | profile() {
|
28 | var ref;
|
29 | return {
|
30 | sourceNodes: this.sourceNodes.size,
|
31 | sourcePreviewNodes: this.sourcePreviewNodes.size,
|
32 | sourcePreviewNodeOptions: this.sourcePreviewNodeOptions.size,
|
33 | targetNodes: this.targetNodes.size,
|
34 | dragOverTargetIds: ((ref = this.dragOverTargetIds) === null || ref === void 0 ? void 0 : ref.length) || 0
|
35 | };
|
36 | }
|
37 |
|
38 | get document() {
|
39 | return this.options.document;
|
40 | }
|
41 | setup() {
|
42 | const root = this.options.rootElement;
|
43 | if (!root) {
|
44 | return;
|
45 | }
|
46 | invariant(!TouchBackendImpl.isSetUp, 'Cannot have two Touch backends at the same time.');
|
47 | TouchBackendImpl.isSetUp = true;
|
48 | this.addEventListener(root, 'start', this.getTopMoveStartHandler());
|
49 | this.addEventListener(root, 'start', this.handleTopMoveStartCapture, true);
|
50 | this.addEventListener(root, 'move', this.handleTopMove);
|
51 | this.addEventListener(root, 'move', this.handleTopMoveCapture, true);
|
52 | this.addEventListener(root, 'end', this.handleTopMoveEndCapture, true);
|
53 | if (this.options.enableMouseEvents && !this.options.ignoreContextMenu) {
|
54 | this.addEventListener(root, 'contextmenu', this.handleTopMoveEndCapture);
|
55 | }
|
56 | if (this.options.enableKeyboardEvents) {
|
57 | this.addEventListener(root, 'keydown', this.handleCancelOnEscape, true);
|
58 | }
|
59 | }
|
60 | teardown() {
|
61 | const root = this.options.rootElement;
|
62 | if (!root) {
|
63 | return;
|
64 | }
|
65 | TouchBackendImpl.isSetUp = false;
|
66 | this._mouseClientOffset = {};
|
67 | this.removeEventListener(root, 'start', this.handleTopMoveStartCapture, true);
|
68 | this.removeEventListener(root, 'start', this.handleTopMoveStart);
|
69 | this.removeEventListener(root, 'move', this.handleTopMoveCapture, true);
|
70 | this.removeEventListener(root, 'move', this.handleTopMove);
|
71 | this.removeEventListener(root, 'end', this.handleTopMoveEndCapture, true);
|
72 | if (this.options.enableMouseEvents && !this.options.ignoreContextMenu) {
|
73 | this.removeEventListener(root, 'contextmenu', this.handleTopMoveEndCapture);
|
74 | }
|
75 | if (this.options.enableKeyboardEvents) {
|
76 | this.removeEventListener(root, 'keydown', this.handleCancelOnEscape, true);
|
77 | }
|
78 | this.uninstallSourceNodeRemovalObserver();
|
79 | }
|
80 | addEventListener(subject, event, handler, capture = false) {
|
81 | const options = supportsPassive ? {
|
82 | capture,
|
83 | passive: false
|
84 | } : capture;
|
85 | this.listenerTypes.forEach(function(listenerType) {
|
86 | const evt = eventNames[listenerType][event];
|
87 | if (evt) {
|
88 | subject.addEventListener(evt, handler, options);
|
89 | }
|
90 | });
|
91 | }
|
92 | removeEventListener(subject, event, handler, capture = false) {
|
93 | const options = supportsPassive ? {
|
94 | capture,
|
95 | passive: false
|
96 | } : capture;
|
97 | this.listenerTypes.forEach(function(listenerType) {
|
98 | const evt = eventNames[listenerType][event];
|
99 | if (evt) {
|
100 | subject.removeEventListener(evt, handler, options);
|
101 | }
|
102 | });
|
103 | }
|
104 | connectDragSource(sourceId, node) {
|
105 | const handleMoveStart = this.handleMoveStart.bind(this, sourceId);
|
106 | this.sourceNodes.set(sourceId, node);
|
107 | this.addEventListener(node, 'start', handleMoveStart);
|
108 | return ()=>{
|
109 | this.sourceNodes.delete(sourceId);
|
110 | this.removeEventListener(node, 'start', handleMoveStart);
|
111 | };
|
112 | }
|
113 | connectDragPreview(sourceId, node, options) {
|
114 | this.sourcePreviewNodeOptions.set(sourceId, options);
|
115 | this.sourcePreviewNodes.set(sourceId, node);
|
116 | return ()=>{
|
117 | this.sourcePreviewNodes.delete(sourceId);
|
118 | this.sourcePreviewNodeOptions.delete(sourceId);
|
119 | };
|
120 | }
|
121 | connectDropTarget(targetId, node) {
|
122 | const root = this.options.rootElement;
|
123 | if (!this.document || !root) {
|
124 | return ()=>{
|
125 | };
|
126 | }
|
127 | const handleMove = (e)=>{
|
128 | if (!this.document || !root || !this.monitor.isDragging()) {
|
129 | return;
|
130 | }
|
131 | let coords;
|
132 | |
133 |
|
134 | switch(e.type){
|
135 | case eventNames.mouse.move:
|
136 | coords = {
|
137 | x: e.clientX,
|
138 | y: e.clientY
|
139 | };
|
140 | break;
|
141 | case eventNames.touch.move:
|
142 | var ref, ref1;
|
143 | coords = {
|
144 | x: ((ref = e.touches[0]) === null || ref === void 0 ? void 0 : ref.clientX) || 0,
|
145 | y: ((ref1 = e.touches[0]) === null || ref1 === void 0 ? void 0 : ref1.clientY) || 0
|
146 | };
|
147 | break;
|
148 | }
|
149 | |
150 |
|
151 |
|
152 | const droppedOn = coords != null ? this.document.elementFromPoint(coords.x, coords.y) : undefined;
|
153 | const childMatch = droppedOn && node.contains(droppedOn);
|
154 | if (droppedOn === node || childMatch) {
|
155 | return this.handleMove(e, targetId);
|
156 | }
|
157 | };
|
158 | |
159 |
|
160 | this.addEventListener(this.document.body, 'move', handleMove);
|
161 | this.targetNodes.set(targetId, node);
|
162 | return ()=>{
|
163 | if (this.document) {
|
164 | this.targetNodes.delete(targetId);
|
165 | this.removeEventListener(this.document.body, 'move', handleMove);
|
166 | }
|
167 | };
|
168 | }
|
169 | getTopMoveStartHandler() {
|
170 | if (!this.options.delayTouchStart && !this.options.delayMouseStart) {
|
171 | return this.handleTopMoveStart;
|
172 | }
|
173 | return this.handleTopMoveStartDelay;
|
174 | }
|
175 | installSourceNodeRemovalObserver(node) {
|
176 | this.uninstallSourceNodeRemovalObserver();
|
177 | this.draggedSourceNode = node;
|
178 | this.draggedSourceNodeRemovalObserver = new MutationObserver(()=>{
|
179 | if (node && !node.parentElement) {
|
180 | this.resurrectSourceNode();
|
181 | this.uninstallSourceNodeRemovalObserver();
|
182 | }
|
183 | });
|
184 | if (!node || !node.parentElement) {
|
185 | return;
|
186 | }
|
187 | this.draggedSourceNodeRemovalObserver.observe(node.parentElement, {
|
188 | childList: true
|
189 | });
|
190 | }
|
191 | resurrectSourceNode() {
|
192 | if (this.document && this.draggedSourceNode) {
|
193 | this.draggedSourceNode.style.display = 'none';
|
194 | this.draggedSourceNode.removeAttribute('data-reactid');
|
195 | this.document.body.appendChild(this.draggedSourceNode);
|
196 | }
|
197 | }
|
198 | uninstallSourceNodeRemovalObserver() {
|
199 | if (this.draggedSourceNodeRemovalObserver) {
|
200 | this.draggedSourceNodeRemovalObserver.disconnect();
|
201 | }
|
202 | this.draggedSourceNodeRemovalObserver = undefined;
|
203 | this.draggedSourceNode = undefined;
|
204 | }
|
205 | constructor(manager, context, options){
|
206 | this.getSourceClientOffset = (sourceId)=>{
|
207 | const element = this.sourceNodes.get(sourceId);
|
208 | return element && getNodeClientOffset(element);
|
209 | };
|
210 | this.handleTopMoveStartCapture = (e)=>{
|
211 | if (!eventShouldStartDrag(e)) {
|
212 | return;
|
213 | }
|
214 | this.moveStartSourceIds = [];
|
215 | };
|
216 | this.handleMoveStart = (sourceId)=>{
|
217 |
|
218 |
|
219 | if (Array.isArray(this.moveStartSourceIds)) {
|
220 | this.moveStartSourceIds.unshift(sourceId);
|
221 | }
|
222 | };
|
223 | this.handleTopMoveStart = (e)=>{
|
224 | if (!eventShouldStartDrag(e)) {
|
225 | return;
|
226 | }
|
227 |
|
228 |
|
229 |
|
230 |
|
231 | const clientOffset = getEventClientOffset(e);
|
232 | if (clientOffset) {
|
233 | if (isTouchEvent(e)) {
|
234 | this.lastTargetTouchFallback = e.targetTouches[0];
|
235 | }
|
236 | this._mouseClientOffset = clientOffset;
|
237 | }
|
238 | this.waitingForDelay = false;
|
239 | };
|
240 | this.handleTopMoveStartDelay = (e)=>{
|
241 | if (!eventShouldStartDrag(e)) {
|
242 | return;
|
243 | }
|
244 | const delay = e.type === eventNames.touch.start ? this.options.delayTouchStart : this.options.delayMouseStart;
|
245 | this.timeout = setTimeout(this.handleTopMoveStart.bind(this, e), delay);
|
246 | this.waitingForDelay = true;
|
247 | };
|
248 | this.handleTopMoveCapture = ()=>{
|
249 | this.dragOverTargetIds = [];
|
250 | };
|
251 | this.handleMove = (_evt, targetId)=>{
|
252 | if (this.dragOverTargetIds) {
|
253 | this.dragOverTargetIds.unshift(targetId);
|
254 | }
|
255 | };
|
256 | this.handleTopMove = (e1)=>{
|
257 | if (this.timeout) {
|
258 | clearTimeout(this.timeout);
|
259 | }
|
260 | if (!this.document || this.waitingForDelay) {
|
261 | return;
|
262 | }
|
263 | const { moveStartSourceIds , dragOverTargetIds } = this;
|
264 | const enableHoverOutsideTarget = this.options.enableHoverOutsideTarget;
|
265 | const clientOffset = getEventClientOffset(e1, this.lastTargetTouchFallback);
|
266 | if (!clientOffset) {
|
267 | return;
|
268 | }
|
269 |
|
270 | if (this._isScrolling || !this.monitor.isDragging() && inAngleRanges(this._mouseClientOffset.x || 0, this._mouseClientOffset.y || 0, clientOffset.x, clientOffset.y, this.options.scrollAngleRanges)) {
|
271 | this._isScrolling = true;
|
272 | return;
|
273 | }
|
274 |
|
275 | if (!this.monitor.isDragging() &&
|
276 | this._mouseClientOffset.hasOwnProperty('x') && moveStartSourceIds && distance(this._mouseClientOffset.x || 0, this._mouseClientOffset.y || 0, clientOffset.x, clientOffset.y) > (this.options.touchSlop ? this.options.touchSlop : 0)) {
|
277 | this.moveStartSourceIds = undefined;
|
278 | this.actions.beginDrag(moveStartSourceIds, {
|
279 | clientOffset: this._mouseClientOffset,
|
280 | getSourceClientOffset: this.getSourceClientOffset,
|
281 | publishSource: false
|
282 | });
|
283 | }
|
284 | if (!this.monitor.isDragging()) {
|
285 | return;
|
286 | }
|
287 | const sourceNode = this.sourceNodes.get(this.monitor.getSourceId());
|
288 | this.installSourceNodeRemovalObserver(sourceNode);
|
289 | this.actions.publishDragSource();
|
290 | if (e1.cancelable) e1.preventDefault();
|
291 |
|
292 | const dragOverTargetNodes = (dragOverTargetIds || []).map((key)=>this.targetNodes.get(key)
|
293 | ).filter((e)=>!!e
|
294 | );
|
295 |
|
296 | const elementsAtPoint = this.options.getDropTargetElementsAtPoint ? this.options.getDropTargetElementsAtPoint(clientOffset.x, clientOffset.y, dragOverTargetNodes) : this.document.elementsFromPoint(clientOffset.x, clientOffset.y);
|
297 |
|
298 | const elementsAtPointExtended = [];
|
299 | for(const nodeId in elementsAtPoint){
|
300 |
|
301 | if (!elementsAtPoint.hasOwnProperty(nodeId)) {
|
302 | continue;
|
303 | }
|
304 | let currentNode = elementsAtPoint[nodeId];
|
305 | if (currentNode != null) {
|
306 | elementsAtPointExtended.push(currentNode);
|
307 | }
|
308 | while(currentNode){
|
309 | currentNode = currentNode.parentElement;
|
310 | if (currentNode && elementsAtPointExtended.indexOf(currentNode) === -1) {
|
311 | elementsAtPointExtended.push(currentNode);
|
312 | }
|
313 | }
|
314 | }
|
315 | const orderedDragOverTargetIds = elementsAtPointExtended
|
316 | .filter((node)=>dragOverTargetNodes.indexOf(node) > -1
|
317 | )
|
318 | .map((node)=>this._getDropTargetId(node)
|
319 | )
|
320 | .filter((node)=>!!node
|
321 | ).filter((id, index, ids)=>ids.indexOf(id) === index
|
322 | );
|
323 |
|
324 | if (enableHoverOutsideTarget) {
|
325 | for(const targetId in this.targetNodes){
|
326 | const targetNode = this.targetNodes.get(targetId);
|
327 | if (sourceNode && targetNode && targetNode.contains(sourceNode) && orderedDragOverTargetIds.indexOf(targetId) === -1) {
|
328 | orderedDragOverTargetIds.unshift(targetId);
|
329 | break;
|
330 | }
|
331 | }
|
332 | }
|
333 |
|
334 | orderedDragOverTargetIds.reverse();
|
335 | this.actions.hover(orderedDragOverTargetIds, {
|
336 | clientOffset: clientOffset
|
337 | });
|
338 | };
|
339 | |
340 |
|
341 |
|
342 | this._getDropTargetId = (node)=>{
|
343 | const keys = this.targetNodes.keys();
|
344 | let next = keys.next();
|
345 | while(next.done === false){
|
346 | const targetId = next.value;
|
347 | if (node === this.targetNodes.get(targetId)) {
|
348 | return targetId;
|
349 | } else {
|
350 | next = keys.next();
|
351 | }
|
352 | }
|
353 | return undefined;
|
354 | };
|
355 | this.handleTopMoveEndCapture = (e)=>{
|
356 | this._isScrolling = false;
|
357 | this.lastTargetTouchFallback = undefined;
|
358 | if (!eventShouldEndDrag(e)) {
|
359 | return;
|
360 | }
|
361 | if (!this.monitor.isDragging() || this.monitor.didDrop()) {
|
362 | this.moveStartSourceIds = undefined;
|
363 | return;
|
364 | }
|
365 | if (e.cancelable) e.preventDefault();
|
366 | this._mouseClientOffset = {};
|
367 | this.uninstallSourceNodeRemovalObserver();
|
368 | this.actions.drop();
|
369 | this.actions.endDrag();
|
370 | };
|
371 | this.handleCancelOnEscape = (e)=>{
|
372 | if (e.key === 'Escape' && this.monitor.isDragging()) {
|
373 | this._mouseClientOffset = {};
|
374 | this.uninstallSourceNodeRemovalObserver();
|
375 | this.actions.endDrag();
|
376 | }
|
377 | };
|
378 | this.options = new OptionsReader(options, context);
|
379 | this.actions = manager.getActions();
|
380 | this.monitor = manager.getMonitor();
|
381 | this.sourceNodes = new Map();
|
382 | this.sourcePreviewNodes = new Map();
|
383 | this.sourcePreviewNodeOptions = new Map();
|
384 | this.targetNodes = new Map();
|
385 | this.listenerTypes = [];
|
386 | this._mouseClientOffset = {};
|
387 | this._isScrolling = false;
|
388 | if (this.options.enableMouseEvents) {
|
389 | this.listenerTypes.push(ListenerType.mouse);
|
390 | }
|
391 | if (this.options.enableTouchEvents) {
|
392 | this.listenerTypes.push(ListenerType.touch);
|
393 | }
|
394 | if (this.options.enableKeyboardEvents) {
|
395 | this.listenerTypes.push(ListenerType.keyboard);
|
396 | }
|
397 | }
|
398 | }
|
399 |
|
400 |
|
\ | No newline at end of file |