diff --git a/ui-ngx/src/app/core/utils.ts b/ui-ngx/src/app/core/utils.ts index 2ab104f8f4..9076be76f1 100644 --- a/ui-ngx/src/app/core/utils.ts +++ b/ui-ngx/src/app/core/utils.ts @@ -25,6 +25,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { TranslateService } from '@ngx-translate/core'; import { serverErrorCodesTranslations } from '@shared/models/constants'; import { SubscriptionEntityInfo } from '@core/api/widget-api.models'; +import Timeout = NodeJS.Timeout; const varsRegex = /\${([^}]*)}/g; @@ -894,3 +895,30 @@ export const camelCase = (str: string): string => { export const convertKeysToCamelCase = (obj: Record): Record => { return _.mapKeys(obj, (value, key) => _.camelCase(key)); }; + +export const isIOSDevice = (): boolean => + /iPhone|iPad|iPod/i.test(navigator.userAgent) || (navigator.userAgent.includes('Mac') && 'ontouchend' in document); + +export const onLongPress = (element: HTMLElement, callback: (event: TouchEvent) => void) => { + let timeoutId: Timeout; + + $(element).on('touchstart', (e) => { + timeoutId = setTimeout(() => { + timeoutId = null; + e.stopPropagation(); + callback(e.originalEvent); + }, 500); + }); + + $(element).on('contextmenu', (e) => e.preventDefault()); + $(element).on('touchend', (e) => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }); + $(element).on('touchmove', (e) => { + if (timeoutId) { + clearTimeout(timeoutId); + } + }); +}; diff --git a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html index 33c8d659f6..f1fdae62f7 100644 --- a/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html +++ b/ui-ngx/src/app/modules/home/components/dashboard/dashboard.component.html @@ -22,7 +22,7 @@ -
; @ViewChild('dashboardMenuTrigger', {static: true}) dashboardMenuTrigger: MatMenuTrigger; dashboardMenuPosition = { x: '0px', y: '0px' }; - dashboardContextMenuEvent: MouseEvent; + dashboardContextMenuEvent: MouseEvent | TouchEvent; @ViewChild('widgetMenuTrigger', {static: true}) widgetMenuTrigger: MatMenuTrigger; widgetMenuPosition = { x: '0px', y: '0px' }; - widgetContextMenuEvent: MouseEvent; + widgetContextMenuEvent: MouseEvent | TouchEvent; dashboardWidgets = new DashboardWidgets(this, this.differs.find([]).create((_, item) => item), @@ -281,6 +283,10 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo ); this.updateWidgets(); + + if (isIOSDevice()) { + onLongPress(this.gridsterParent.nativeElement, (event) => this.openDashboardContextMenu(event)); + } } ngOnDestroy(): void { @@ -406,30 +412,30 @@ export class DashboardComponent extends PageComponent implements IDashboardCompo } } - openDashboardContextMenu($event: MouseEvent) { + openDashboardContextMenu($event: MouseEvent | TouchEvent) { if (this.callbacks && this.callbacks.prepareDashboardContextMenu) { const items = this.callbacks.prepareDashboardContextMenu($event); if (items && items.length) { $event.preventDefault(); $event.stopPropagation(); this.dashboardContextMenuEvent = $event; - this.dashboardMenuPosition.x = $event.clientX + 'px'; - this.dashboardMenuPosition.y = $event.clientY + 'px'; + this.dashboardMenuPosition.x = ('touches' in $event ? $event.changedTouches[0].clientX : $event.clientX) + 'px'; + this.dashboardMenuPosition.y = ('touches' in $event ? $event.changedTouches[0].clientY : $event.clientY) + 'px'; this.dashboardMenuTrigger.menuData = { items }; this.dashboardMenuTrigger.openMenu(); } } } - private openWidgetContextMenu($event: MouseEvent, widget: DashboardWidget) { + private openWidgetContextMenu($event: MouseEvent | TouchEvent, widget: DashboardWidget) { if (this.callbacks && this.callbacks.prepareWidgetContextMenu) { const items = this.callbacks.prepareWidgetContextMenu($event, widget.widget, widget.isReference); if (items && items.length) { $event.preventDefault(); $event.stopPropagation(); this.widgetContextMenuEvent = $event; - this.widgetMenuPosition.x = $event.clientX + 'px'; - this.widgetMenuPosition.y = $event.clientY + 'px'; + this.widgetMenuPosition.x = ('touches' in $event ? $event.changedTouches[0].clientX : $event.clientX) + 'px'; + this.widgetMenuPosition.y = ('touches' in $event ? $event.changedTouches[0].clientY : $event.clientY) + 'px'; this.widgetMenuTrigger.menuData = { items, widget: widget.widget }; this.widgetMenuTrigger.openMenu(); } diff --git a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts index 7c8e3c12ea..ca49a1af18 100644 --- a/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts +++ b/ui-ngx/src/app/modules/home/components/widget/widget-container.component.ts @@ -39,7 +39,7 @@ import { DashboardWidget, DashboardWidgets } from '@home/models/dashboard-compon import { Store } from '@ngrx/store'; import { AppState } from '@core/core.state'; import { SafeStyle } from '@angular/platform-browser'; -import { isNotEmptyStr } from '@core/utils'; +import { isIOSDevice, isNotEmptyStr, onLongPress } from '@core/utils'; import { GridsterItemComponent } from 'angular-gridster2'; import { UtilsService } from '@core/services/utils.service'; import { from } from 'rxjs'; @@ -57,7 +57,7 @@ export enum WidgetComponentActionType { } export class WidgetComponentAction { - event: MouseEvent; + event: MouseEvent | TouchEvent; actionType: WidgetComponentActionType; } @@ -151,7 +151,11 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O } $(this.gridsterItem.el).on('mousedown', (e) => this.onMouseDown(e.originalEvent)); $(this.gridsterItem.el).on('click', (e) => this.onClicked(e.originalEvent)); - $(this.gridsterItem.el).on('contextmenu', (e) => this.onContextMenu(e.originalEvent)); + if (isIOSDevice()) { + onLongPress(this.gridsterItem.el, (event) => this.onContextMenu(event)); + } else { + $(this.gridsterItem.el).on('contextmenu', (e) => this.onContextMenu(e.originalEvent)); + } const dashboardContentElement = this.widget.widgetContext.dashboardContentElement; if (dashboardContentElement) { this.initEditWidgetActionTooltip(dashboardContentElement); @@ -213,7 +217,7 @@ export class WidgetContainerComponent extends PageComponent implements OnInit, O }); } - onContextMenu(event: MouseEvent) { + onContextMenu(event: MouseEvent | TouchEvent) { this.widgetComponentAction.emit({ event, actionType: WidgetComponentActionType.CONTEXT_MENU