Newer
Older
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
import {
Directive, EventEmitter, HostListener,
NgZone, Output, Renderer2
} from '@angular/core';
@Directive({
standalone: true,
selector: '[aspect-draggable]'
})
/*
Maps mouse and touch events to drag events
*/
export class DraggableDirective {
@Output() dragStart = new EventEmitter<DragStartEvent>();
@Output() dragMove = new EventEmitter<DragEvent>();
@Output() dragEnd = new EventEmitter<DragEvent>();
private unlistenMouseMove: (() => void) | undefined;
private unlistenMouseUp: (() => void) | undefined;
constructor(private renderer2: Renderer2, private ngZone: NgZone) {}
@HostListener('touchstart', ['$event'])
@HostListener('mousedown', ['$event'])
onEvent(event: MouseEvent | TouchEvent) {
event.preventDefault();
if (!isTouchEvent(event) && event.button !== 0) return; // no right-click
if ((event.target as HTMLElement).getAttribute('data-draggable-audio')) return;
const sourceItem: HTMLElement | null = (event.target as HTMLElement).closest('.drop-list-item');
if (!sourceItem) return;
this.dragStart.emit({
sourceElement: sourceItem,
x: isTouchEvent(event) ? event.touches?.[0].clientX : event.clientX,
y: isTouchEvent(event) ? event.touches?.[0].clientY : event.clientY,
dragType: isTouchEvent(event) ? 'touch' : 'mouse'
});
if (!isTouchEvent(event)) { // mousemove events appear even in touch mode, when the pointer leaves the area
this.ngZone.runOutsideAngular(() => {
this.unlistenMouseMove = this.renderer2.listen('document', 'mousemove', (e: MouseEvent) => {
e.preventDefault();
this.dragMove.emit({
x: e.clientX,
y: e.clientY
});
});
this.unlistenMouseUp = this.renderer2.listen('document', 'mouseup', (e: MouseEvent) => {
e.preventDefault();
this.dragEnd.emit({
x: e.clientX,
y: e.clientY
});
this.unlistenMouseMove?.();
this.unlistenMouseUp?.();
});
});
}
}
@HostListener('touchmove', ['$event'])
onTouchMove(event: TouchEvent) {
event.preventDefault();
this.dragMove.emit({
x: event.touches?.[0].clientX,
y: event.touches?.[0].clientY
});
}
@HostListener('touchend', ['$event'])
onTouchEnd(event: TouchEvent) {
event.preventDefault();
this.dragEnd.emit({
x: event.changedTouches?.[0].clientX,
y: event.changedTouches?.[0].clientY
});
}
}
function isTouchEvent(event: MouseEvent | TouchEvent): event is TouchEvent {
return (event as TouchEvent).touches !== undefined;
}
export interface DragEvent {
x: number;
y: number;
}
export interface DragStartEvent extends DragEvent {
sourceElement: HTMLElement;
dragType: 'mouse' | 'touch';
}