From be9d9469ab26f982066c25d30e99dfbeefe02b62 Mon Sep 17 00:00:00 2001 From: Dermot Duffy Date: Sun, 27 Feb 2022 11:17:10 -0800 Subject: [PATCH] Don't trigger card-wide actions on card controls --- README.md | 9 +++++---- src/action-handler-directive.ts | 24 +++++++++++++++++------- src/common.ts | 7 +++++++ src/components/gallery.ts | 10 +++++++--- src/components/live.ts | 7 +++++-- src/components/submenu.ts | 16 ++++++++++++---- src/components/thumbnail-carousel.ts | 5 +++-- src/components/viewer.ts | 7 +++++-- 8 files changed, 61 insertions(+), 24 deletions(-) diff --git a/README.md b/README.md index 9a84d32a..8bca0032 100644 --- a/README.md +++ b/README.md @@ -697,10 +697,11 @@ specific overriding the less specific (see example below). **Note:** The card itself relies on user interactions to function (e.g. `tap` on the menu should activate that button, `tap` on a gallery thumbnail should open -that piece of media, etc). These internal actions are executed _also_, which -means that a card-wide `tap` action probably isn't that useful as it may be -disorienting to the user and will trigger on all kinds of basic interaction on -the card (e.g. tapping/clicking a menu button). +that piece of media, etc). Efforts are taken to de-duplicate interactions (e.g. +card-wide actions will not be activated through interaction with menu buttons, +next/previous controls, thumbnails, etc), but in some cases this is not possible +(e.g. embedded WebRTC card controls) -- in these cases duplicate actions may +occur with certain configurations (e.g. `tap`). ## Menu Modes diff --git a/src/action-handler-directive.ts b/src/action-handler-directive.ts index fc79388f..d7d94027 100644 --- a/src/action-handler-directive.ts +++ b/src/action-handler-directive.ts @@ -1,11 +1,17 @@ import { noChange } from 'lit'; -import { AttributePart, directive, Directive, DirectiveParameters } from 'lit/directive.js'; +import { + AttributePart, + directive, + Directive, + DirectiveParameters, +} from 'lit/directive.js'; import type { ActionHandlerDetail, ActionHandlerOptions, } from 'custom-card-helpers/dist/types.d.js'; import { fireEvent } from 'custom-card-helpers'; +import { stopEventFromActivatingCardWideActions } from './common'; interface ActionHandler extends HTMLElement { holdTime: number; @@ -31,7 +37,6 @@ class ActionHandler extends HTMLElement implements ActionHandler { private dblClickTimeout?: number; public connectedCallback(): void { - [ 'touchcancel', 'mouseout', @@ -89,12 +94,17 @@ class ActionHandler extends HTMLElement implements ActionHandler { }; const end = (ev: Event): void => { + // This will ensure only 1 actionHandler is invoked for a given interaction. + stopEventFromActivatingCardWideActions(ev); + const options = element.actionHandlerOptions; - if (['touchend', 'touchcancel'].includes(ev.type) - // This action handler by default relies on synthetic click events for - // touch devices, in order to ensure that embedded cards (e.g. WebRTC) - // can use stock click handlers. The exception is for hold events. - && !(options?.hasHold && this.held)) { + if ( + ['touchend', 'touchcancel'].includes(ev.type) && + // This action handler by default relies on synthetic click events for + // touch devices, in order to ensure that embedded cards (e.g. WebRTC) + // can use stock click handlers. The exception is for hold events. + !(options?.hasHold && this.held) + ) { return; } if (options?.hasHold) { diff --git a/src/common.ts b/src/common.ts index bdc60e9e..9e3793e9 100644 --- a/src/common.ts +++ b/src/common.ts @@ -583,3 +583,10 @@ export const frigateCardHasAction = ( } return hasAction(config); }; + +/** + * Stop an event from activating card wide actions. + */ +export const stopEventFromActivatingCardWideActions = (ev: Event): void => { + ev.stopPropagation(); +} \ No newline at end of file diff --git a/src/components/gallery.ts b/src/components/gallery.ts index 6ba4c20c..13fa510f 100644 --- a/src/components/gallery.ts +++ b/src/components/gallery.ts @@ -13,6 +13,7 @@ import { import { BrowseMediaUtil } from '../browse-media-util.js'; import { View } from '../view.js'; import { renderProgressIndicator } from './message.js'; +import { stopEventFromActivatingCardWideActions } from '../common.js'; import galleryStyle from '../scss/gallery.scss'; @@ -161,10 +162,11 @@ export class FrigateCardGalleryCore extends LitElement {
{ + @click=${(ev) => { if (this.view && this.view.previous) { this.view.previous.dispatchChangeEvent(this); } + stopEventFromActivatingCardWideActions(ev); }} outlined="" class="frigate-card-gallery-folder" @@ -182,7 +184,7 @@ export class FrigateCardGalleryCore extends LitElement { ${child.can_expand ? html`
{ + @click=${(ev) => { if (this.hass && this.view) { BrowseMediaUtil.fetchChildMediaAndDispatchViewChange( this, @@ -191,6 +193,7 @@ export class FrigateCardGalleryCore extends LitElement { child, ); } + stopEventFromActivatingCardWideActions(ev); }} outlined="" class="frigate-card-gallery-folder" @@ -204,7 +207,7 @@ export class FrigateCardGalleryCore extends LitElement { class="mdc-image-list__image" src="${child.thumbnail}" title="${child.title}" - @click=${() => { + @click=${(ev) => { if (this.view) { this.view .evolve({ @@ -214,6 +217,7 @@ export class FrigateCardGalleryCore extends LitElement { }) .dispatchChangeEvent(this); } + stopEventFromActivatingCardWideActions(ev); }} />` : ``} diff --git a/src/components/live.ts b/src/components/live.ts index 8bdfbd9d..6cd8bb39 100644 --- a/src/components/live.ts +++ b/src/components/live.ts @@ -50,6 +50,7 @@ import { getCameraIcon, getCameraTitle, homeAssistantSignPath, + stopEventFromActivatingCardWideActions, } from '../common.js'; import { renderProgressIndicator } from '../components/message.js'; @@ -550,8 +551,9 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel { .label=${getCameraTitle(this.hass, prev)} .icon=${getCameraIcon(this.hass, prev)} ?disabled=${prev == null} - @click=${() => { + @click=${(ev) => { this._nextPreviousHandler('previous'); + stopEventFromActivatingCardWideActions(ev); }} > @@ -565,8 +567,9 @@ export class FrigateCardLiveCarousel extends FrigateCardMediaCarousel { .label=${getCameraTitle(this.hass, next)} .icon=${getCameraIcon(this.hass, next)} ?disabled=${next == null} - @click=${() => { + @click=${(ev) => { this._nextPreviousHandler('next'); + stopEventFromActivatingCardWideActions(ev); }} > diff --git a/src/components/submenu.ts b/src/components/submenu.ts index 4476b8eb..79956c56 100644 --- a/src/components/submenu.ts +++ b/src/components/submenu.ts @@ -2,7 +2,11 @@ import type { Corner, Menu } from '@material/mwc-menu'; import { CSSResultGroup, html, LitElement, TemplateResult, unsafeCSS } from 'lit'; import { HomeAssistant } from 'custom-card-helpers'; import { customElement, property, query } from 'lit/decorators'; -import { frigateCardHasAction, refreshDynamicStateParameters } from '../common.js'; +import { + frigateCardHasAction, + refreshDynamicStateParameters, + stopEventFromActivatingCardWideActions, +} from '../common.js'; import { ifDefined } from 'lit/directives/if-defined'; import { styleMap } from 'lit/directives/style-map'; @@ -68,7 +72,10 @@ export class FrigateCardSubmenu extends LitElement { protected _getFixedRoot(): HTMLElement | null { let n = this as Node | null; while (n) { - if (n.nodeType === Node.ELEMENT_NODE && (n as Element).tagName === 'HA-APP-LAYOUT') { + if ( + n.nodeType === Node.ELEMENT_NODE && + (n as Element).tagName === 'HA-APP-LAYOUT' + ) { return n as HTMLElement; } n = n.parentNode @@ -93,7 +100,7 @@ export class FrigateCardSubmenu extends LitElement { hasHold: frigateCardHasAction(this.submenu.hold_action), hasDoubleClick: frigateCardHasAction(this.submenu.double_tap_action), })} - @click=${() => { + @click=${(ev) => { if (this._menu) { // Hack: This insanity is brought about by lack of MWCMenu playing // nicely with the Home Assistant view/sidepanel. The menu must be @@ -116,12 +123,13 @@ export class FrigateCardSubmenu extends LitElement { } this._menu.show(); } + stopEventFromActivatingCardWideActions(ev); }} > { + @click=${(ev) => { if (this._carousel && this._carousel.clickAllowed()) { dispatchFrigateCardEvent(this, 'carousel:tap', { slideIndex: slideIndex, @@ -119,6 +119,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel { childIndex: childIndex, }); } + stopEventFromActivatingCardWideActions(ev); }} > { + @click=${(ev) => { this._nextPreviousHandler('previous'); + stopEventFromActivatingCardWideActions(ev); }} >
@@ -669,8 +671,9 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel { .thumbnail=${next && next.thumbnail ? next.thumbnail : undefined} .label=${next ? next.title : ''} ?disabled=${!next} - @click=${() => { + @click=${(ev) => { this._nextPreviousHandler('next'); + stopEventFromActivatingCardWideActions(ev); }} >