Skip to content

Commit a1a40a5

Browse files
committed
refactor(grid): use pointer events instead of mouse and touch events if possible
1 parent f74824f commit a1a40a5

File tree

5 files changed

+70
-21
lines changed

5 files changed

+70
-21
lines changed

projects/angular-grid-layout/src/lib/grid-item/grid-item.component.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
position: absolute;
55
z-index: 1;
66
overflow: hidden;
7+
touch-action: none;
78

89
div {
910
position: absolute;

projects/angular-grid-layout/src/lib/grid-item/grid-item.component.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
} from '@angular/core';
55
import { BehaviorSubject, iif, merge, NEVER, Observable, Subject, Subscription } from 'rxjs';
66
import { exhaustMap, filter, map, startWith, switchMap, take, takeUntil } from 'rxjs/operators';
7-
import { ktdMouseOrTouchDown, ktdMouseOrTouchEnd, ktdPointerClient } from '../utils/pointer.utils';
7+
import { ktdPointerDown, ktdPointerUp, ktdPointerClient } from '../utils/pointer.utils';
88
import { GRID_ITEM_GET_RENDER_DATA_TOKEN, KtdGridItemRenderDataTokenType } from '../grid.definitions';
99
import { KTD_GRID_DRAG_HANDLE, KtdGridDragHandle } from '../directives/drag-handle';
1010
import { KTD_GRID_RESIZE_HANDLE, KtdGridResizeHandle } from '../directives/resize-handle';
@@ -158,8 +158,8 @@ export class KtdGridItemComponent implements OnInit, OnDestroy, AfterContentInit
158158
switchMap((dragHandles: QueryList<KtdGridDragHandle>) => {
159159
return iif(
160160
() => dragHandles.length > 0,
161-
merge(...dragHandles.toArray().map(dragHandle => ktdMouseOrTouchDown(dragHandle.element.nativeElement, 1))),
162-
ktdMouseOrTouchDown(this.elementRef.nativeElement, 1)
161+
merge(...dragHandles.toArray().map(dragHandle => ktdPointerDown(dragHandle.element.nativeElement))),
162+
ktdPointerDown(this.elementRef.nativeElement)
163163
)
164164
})
165165
);
@@ -179,7 +179,7 @@ export class KtdGridItemComponent implements OnInit, OnDestroy, AfterContentInit
179179

180180
const startPointer = ktdPointerClient(startEvent);
181181
return this.gridService.mouseOrTouchMove$(document).pipe(
182-
takeUntil(ktdMouseOrTouchEnd(document, 1)),
182+
takeUntil(ktdPointerUp(document)),
183183
ktdOutsideZone(this.ngZone),
184184
filter((moveEvent) => {
185185
moveEvent.preventDefault();
@@ -211,10 +211,10 @@ export class KtdGridItemComponent implements OnInit, OnDestroy, AfterContentInit
211211
if (resizeHandles.length > 0) {
212212
// Side effect to hide the resizeElem if there are resize handles.
213213
this.renderer.setStyle(this.resizeElem.nativeElement, 'display', 'none');
214-
return merge(...resizeHandles.toArray().map(resizeHandle => ktdMouseOrTouchDown(resizeHandle.element.nativeElement, 1)));
214+
return merge(...resizeHandles.toArray().map(resizeHandle => ktdPointerDown(resizeHandle.element.nativeElement)));
215215
} else {
216216
this.renderer.setStyle(this.resizeElem.nativeElement, 'display', 'block');
217-
return ktdMouseOrTouchDown(this.resizeElem.nativeElement, 1);
217+
return ktdPointerDown(this.resizeElem.nativeElement);
218218
}
219219
})
220220
);

projects/angular-grid-layout/src/lib/grid.component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { compact } from './utils/react-grid-layout.utils';
1212
import {
1313
GRID_ITEM_GET_RENDER_DATA_TOKEN, KtdGridBackgroundCfg, KtdGridCfg, KtdGridCompactType, KtdGridItemRenderData, KtdGridLayout, KtdGridLayoutItem
1414
} from './grid.definitions';
15-
import { ktdMouseOrTouchEnd, ktdPointerClientX, ktdPointerClientY } from './utils/pointer.utils';
15+
import { ktdPointerUp, ktdPointerClientX, ktdPointerClientY } from './utils/pointer.utils';
1616
import { KtdDictionary } from '../types';
1717
import { KtdGridService } from './grid.service';
1818
import { getMutableClientRect, KtdClientRect } from './utils/client-rect';
@@ -516,7 +516,7 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
516516
})),
517517
ktdScrollIfNearElementClientRect$(scrollableParent, {scrollStep: this.scrollSpeed})
518518
)).pipe(
519-
takeUntil(ktdMouseOrTouchEnd(document))
519+
takeUntil(ktdPointerUp(document))
520520
).subscribe());
521521

522522
/**
@@ -533,8 +533,8 @@ export class KtdGridComponent implements OnChanges, AfterContentInit, AfterConte
533533
])
534534
])
535535
).pipe(
536-
takeUntil(ktdMouseOrTouchEnd(document)),
537-
).subscribe(([pointerDragEvent, scrollDifference]: [MouseEvent | TouchEvent, { top: number, left: number }]) => {
536+
takeUntil(ktdPointerUp(document)),
537+
).subscribe(([pointerDragEvent, scrollDifference]: [MouseEvent | TouchEvent | PointerEvent, { top: number, left: number }]) => {
538538
pointerDragEvent.preventDefault();
539539

540540
/**

projects/angular-grid-layout/src/lib/grid.service.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Injectable, NgZone, OnDestroy } from '@angular/core';
22
import { ktdNormalizePassiveListenerOptions } from './utils/passive-listeners';
33
import { fromEvent, iif, Observable, Subject, Subscription } from 'rxjs';
44
import { filter } from 'rxjs/operators';
5-
import { ktdIsMobileOrTablet } from './utils/pointer.utils';
5+
import { ktdIsMobileOrTablet, ktdSupportsPointerEvents } from './utils/pointer.utils';
66

77
/** Event options that can be used to bind an active, capturing event. */
88
const activeCapturingEventOptions = ktdNormalizePassiveListenerOptions({
@@ -27,11 +27,15 @@ export class KtdGridService implements OnDestroy {
2727
}
2828

2929
mouseOrTouchMove$(element): Observable<MouseEvent | TouchEvent> {
30-
return iif(
31-
() => ktdIsMobileOrTablet(),
32-
this.touchMove$,
33-
fromEvent<MouseEvent>(element, 'mousemove', activeCapturingEventOptions as AddEventListenerOptions) // TODO: Fix rxjs typings, boolean should be a good param too.
34-
);
30+
if (!ktdSupportsPointerEvents()) {
31+
return iif(
32+
() => ktdIsMobileOrTablet(),
33+
this.touchMove$,
34+
fromEvent<MouseEvent>(element, 'mousemove', activeCapturingEventOptions as AddEventListenerOptions) // TODO: Fix rxjs typings, boolean should be a good param too.
35+
);
36+
}
37+
38+
return fromEvent<MouseEvent>(element, 'pointermove', activeCapturingEventOptions as AddEventListenerOptions);
3539
}
3640

3741
private registerTouchMoveSubscription() {

projects/angular-grid-layout/src/lib/utils/pointer.utils.ts

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,19 +43,24 @@ export function ktdPointerClientY(event: MouseEvent | TouchEvent): number {
4343
return ktdIsMouseEvent(event) ? event.clientY : event.touches[0].clientY;
4444
}
4545

46-
export function ktdPointerClient(event: MouseEvent | TouchEvent): {clientX: number, clientY: number} {
47-
return {
46+
export function ktdPointerClient(event: MouseEvent | TouchEvent): { clientX: number, clientY: number } {
47+
return {
4848
clientX: ktdIsMouseEvent(event) ? event.clientX : event.touches[0].clientX,
4949
clientY: ktdIsMouseEvent(event) ? event.clientY : event.touches[0].clientY
5050
};
5151
}
5252

53+
/** Returns true if browser supports pointer events */
54+
export function ktdSupportsPointerEvents(): boolean {
55+
return !!window.PointerEvent;
56+
}
57+
5358
/**
5459
* Emits when a mousedown or touchstart emits. Avoids conflicts between both events.
5560
* @param element, html element where to listen the events.
5661
* @param touchNumber number of the touch to track the event, default to the first one.
5762
*/
58-
export function ktdMouseOrTouchDown(element, touchNumber = 1): Observable<MouseEvent | TouchEvent> {
63+
function ktdMouseOrTouchDown(element, touchNumber = 1): Observable<MouseEvent | TouchEvent> {
5964
return iif(
6065
() => ktdIsMobileOrTablet(),
6166
fromEvent<TouchEvent>(element, 'touchstart', passiveEventListenerOptions as AddEventListenerOptions).pipe(
@@ -79,7 +84,7 @@ export function ktdMouseOrTouchDown(element, touchNumber = 1): Observable<MouseE
7984
* @param element, html element where to listen the events.
8085
* @param touchNumber number of the touch to track the event, default to the first one.
8186
*/
82-
export function ktdMouseOrTouchMove(element, touchNumber = 1): Observable<MouseEvent | TouchEvent> {
87+
function ktdMouserOrTouchMove(element: HTMLElement, touchNumber = 1): Observable<MouseEvent | TouchEvent> {
8388
return iif(
8489
() => ktdIsMobileOrTablet(),
8590
fromEvent<TouchEvent>(element, 'touchmove', activeEventListenerOptions as AddEventListenerOptions).pipe(
@@ -105,10 +110,49 @@ export function ktdTouchEnd(element, touchNumber = 1): Observable<TouchEvent> {
105110
* @param element, html element where to listen the events.
106111
* @param touchNumber number of the touch to track the event, default to the first one.
107112
*/
108-
export function ktdMouseOrTouchEnd(element, touchNumber = 1): Observable<MouseEvent | TouchEvent> {
113+
function ktdMouserOrTouchEnd(element: HTMLElement, touchNumber = 1): Observable<MouseEvent | TouchEvent> {
109114
return iif(
110115
() => ktdIsMobileOrTablet(),
111116
ktdTouchEnd(element, touchNumber),
112117
fromEvent<MouseEvent>(element, 'mouseup'),
113118
);
114119
}
120+
121+
122+
/**
123+
* Emits when a 'pointerdown' event occurs (only for the primary pointer). Fallbacks to 'mousemove' or a 'touchmove' if pointer events are not supported.
124+
* @param element, html element where to listen the events.
125+
*/
126+
export function ktdPointerDown(element): Observable<MouseEvent | TouchEvent | PointerEvent> {
127+
if (!ktdSupportsPointerEvents()) {
128+
return ktdMouseOrTouchDown(element);
129+
}
130+
131+
return fromEvent<PointerEvent>(element, 'pointerdown', passiveEventListenerOptions as AddEventListenerOptions).pipe(
132+
filter((pointerEvent) => pointerEvent.isPrimary)
133+
)
134+
}
135+
136+
/**
137+
* Emits when a 'pointermove' event occurs (only for the primary pointer). Fallbacks to 'mousemove' or a 'touchmove' if pointer events are not supported.
138+
* @param element, html element where to listen the events.
139+
*/
140+
export function ktdPointerMove(element): Observable<MouseEvent | TouchEvent | PointerEvent> {
141+
if (!ktdSupportsPointerEvents()) {
142+
return ktdMouserOrTouchMove(element);
143+
}
144+
return fromEvent<PointerEvent>(element, 'pointermove', activeEventListenerOptions as AddEventListenerOptions).pipe(
145+
filter((pointerEvent) => pointerEvent.isPrimary),
146+
);
147+
}
148+
149+
/**
150+
* Emits when a 'pointerup' event occurs (only for the primary pointer). Fallbacks to 'mousemove' or a 'touchmove' if pointer events are not supported.
151+
* @param element, html element where to listen the events.
152+
*/
153+
export function ktdPointerUp(element): Observable<MouseEvent | TouchEvent | PointerEvent> {
154+
if (!ktdSupportsPointerEvents()) {
155+
return ktdMouserOrTouchEnd(element);
156+
}
157+
return fromEvent<PointerEvent>(element, 'pointerup').pipe(filter(pointerEvent => pointerEvent.isPrimary));
158+
}

0 commit comments

Comments
 (0)