From f6d029570c6ef9aaeb9d6470b62efc7f47f26723 Mon Sep 17 00:00:00 2001 From: Pierre-Etienne Lord Date: Tue, 17 Aug 2021 14:07:40 -0400 Subject: [PATCH 1/2] fix(context-menu): ios context menu was not available. --- .../context-menu/context-menu.directive.ts | 49 ++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/packages/common/src/lib/context-menu/context-menu.directive.ts b/packages/common/src/lib/context-menu/context-menu.directive.ts index eda44704d1..f6b677283d 100644 --- a/packages/common/src/lib/context-menu/context-menu.directive.ts +++ b/packages/common/src/lib/context-menu/context-menu.directive.ts @@ -4,35 +4,72 @@ import { EventEmitter, HostListener, Input, + OnDestroy, Output, ViewContainerRef } from '@angular/core'; import type { TemplateRef } from '@angular/core'; import { TemplatePortal } from '@angular/cdk/portal'; -import { fromEvent, Subscription } from 'rxjs'; -import { filter, take } from 'rxjs/operators'; +import { fromEvent, Observable, of, Subscription } from 'rxjs'; +import { delay, filter, mergeMap, take, takeUntil } from 'rxjs/operators'; import { Overlay, OverlayRef } from '@angular/cdk/overlay'; @Directive({ selector: '[igoContextMenu]' }) -export class ContextMenuDirective { +export class ContextMenuDirective implements OnDestroy { private overlayRef: OverlayRef | null; private sub: Subscription; + private longTouch$$: Subscription; @Input('igoContextMenu') menuContext: TemplateRef; + @Input() touchDelayMs: number = 1500; @Output() menuPosition = new EventEmitter<{ x: number; y: number }>(); constructor( public overlay: Overlay, public viewContainerRef: ViewContainerRef, private elementRef: ElementRef - ) {} + ) { + + const touchstart$: Observable = fromEvent(elementRef.nativeElement, 'touchstart'); + const touchend$: Observable = fromEvent(elementRef.nativeElement, 'touchend'); + + const longTouch$ = touchstart$.pipe( + mergeMap((e) => { + return of(e).pipe( + delay(this.touchDelayMs), + takeUntil(touchend$), + ); + }), + ); + this.longTouch$$ = longTouch$.pipe( + delay(this.touchDelayMs), + ).subscribe((event) => this.onContextMenu(event)); + } + ngOnDestroy(): void { + if (this.longTouch$$){ + this.longTouch$$.unsubscribe(); + } + } @HostListener('contextmenu', ['$event']) - public onContextMenu(e: MouseEvent): void { - const {x, y} = e; + public onContextMenu(e: MouseEvent | TouchEvent): void { + + let x = 0; + let y = 0; + if (e instanceof TouchEvent) { + if (e.touches.length > 1) { + return; // prevent map rotation conflict + } + x = e.touches[0].clientX; + y = e.touches[0].clientY; + } else { + x = e.x; + y = e.y; + } + this.close(); e.preventDefault(); this.menuPosition.emit({ x, y }); From 9edcc3269fedf3136ef68832a4fbfbda5bfb78d8 Mon Sep 17 00:00:00 2001 From: Pierre-Etienne Lord Date: Wed, 18 Aug 2021 16:45:40 -0400 Subject: [PATCH 2/2] refactor(context-menu): enhance the touchstart behavior --- .../context-menu/context-menu.directive.ts | 35 ++++++++++++++++--- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/packages/common/src/lib/context-menu/context-menu.directive.ts b/packages/common/src/lib/context-menu/context-menu.directive.ts index f6b677283d..65deddb27e 100644 --- a/packages/common/src/lib/context-menu/context-menu.directive.ts +++ b/packages/common/src/lib/context-menu/context-menu.directive.ts @@ -12,7 +12,7 @@ import type { TemplateRef } from '@angular/core'; import { TemplatePortal } from '@angular/cdk/portal'; import { fromEvent, Observable, of, Subscription } from 'rxjs'; -import { delay, filter, mergeMap, take, takeUntil } from 'rxjs/operators'; +import { delay, filter, mergeMap, take, takeUntil, tap } from 'rxjs/operators'; import { Overlay, OverlayRef } from '@angular/cdk/overlay'; @Directive({ @@ -22,9 +22,12 @@ export class ContextMenuDirective implements OnDestroy { private overlayRef: OverlayRef | null; private sub: Subscription; private longTouch$$: Subscription; + private touchmove$$: Subscription; + private touchend$$: Subscription; + private isDragging: boolean = false; @Input('igoContextMenu') menuContext: TemplateRef; - @Input() touchDelayMs: number = 1500; + @Input() touchDelayMs: number = 500; @Output() menuPosition = new EventEmitter<{ x: number; y: number }>(); constructor( @@ -34,9 +37,20 @@ export class ContextMenuDirective implements OnDestroy { ) { const touchstart$: Observable = fromEvent(elementRef.nativeElement, 'touchstart'); + const touchmove$: Observable = fromEvent(elementRef.nativeElement, 'touchmove'); const touchend$: Observable = fromEvent(elementRef.nativeElement, 'touchend'); + touchmove$.subscribe(() => { + this.isDragging = true; + this.close(); + }); + touchend$.subscribe(() => this.isDragging = false); + const longTouch$ = touchstart$.pipe( + tap((event) => { + event.preventDefault(); + window.document.body.style['-webkit-user-select'] = 'none'; + }), mergeMap((e) => { return of(e).pipe( delay(this.touchDelayMs), @@ -45,17 +59,28 @@ export class ContextMenuDirective implements OnDestroy { }), ); this.longTouch$$ = longTouch$.pipe( - delay(this.touchDelayMs), - ).subscribe((event) => this.onContextMenu(event)); + tap((event) => { + this.onContextMenu(event); + }), delay(2000) + ).subscribe(() => window.document.body.style['-webkit-user-select'] = 'auto'); } ngOnDestroy(): void { - if (this.longTouch$$){ + if (this.longTouch$$) { this.longTouch$$.unsubscribe(); } + if (this.touchmove$$) { + this.touchmove$$.unsubscribe(); + } + if (this.touchend$$) { + this.touchend$$.unsubscribe(); + } } @HostListener('contextmenu', ['$event']) public onContextMenu(e: MouseEvent | TouchEvent): void { + if (this.isDragging) { + return; + } let x = 0; let y = 0;