Merge pull request #422 from dermotduffy/stop-prop
Don't trigger card-wide actions on card controls
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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 {
|
||||
<div class="mdc-image-list__image-aspect-container">
|
||||
<div class="mdc-image-list__image">
|
||||
<ha-card
|
||||
@click=${() => {
|
||||
@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`<div class="mdc-image-list__image">
|
||||
<ha-card
|
||||
@click=${() => {
|
||||
@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);
|
||||
}}
|
||||
/>`
|
||||
: ``}
|
||||
|
||||
@@ -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);
|
||||
}}
|
||||
>
|
||||
</frigate-card-next-previous-control>
|
||||
@@ -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);
|
||||
}}
|
||||
>
|
||||
</frigate-card-next-previous-control>
|
||||
|
||||
@@ -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);
|
||||
}}
|
||||
>
|
||||
<ha-icon icon="${this.submenu.icon}"></ha-icon>
|
||||
</ha-icon-button>
|
||||
<mwc-menu
|
||||
.corner=${this.corner || "BOTTOM_LEFT"}
|
||||
.corner=${this.corner || 'BOTTOM_LEFT'}
|
||||
fixed
|
||||
@closed=${
|
||||
// Prevent the submenu closing from closing anything upstream (e.g.
|
||||
|
||||
@@ -5,7 +5,7 @@ import { customElement, property } from 'lit/decorators.js';
|
||||
|
||||
import type { BrowseMediaSource, ThumbnailsControlConfig } from '../types.js';
|
||||
import { FrigateCardCarousel } from './carousel.js';
|
||||
import { dispatchFrigateCardEvent } from '../common.js';
|
||||
import { dispatchFrigateCardEvent, stopEventFromActivatingCardWideActions } from '../common.js';
|
||||
|
||||
import thumbnailCarouselStyle from '../scss/thumbnail-carousel.scss';
|
||||
|
||||
@@ -111,7 +111,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
||||
|
||||
return html`<div
|
||||
class="embla__slide"
|
||||
@click=${() => {
|
||||
@click=${(ev) => {
|
||||
if (this._carousel && this._carousel.clickAllowed()) {
|
||||
dispatchFrigateCardEvent<ThumbnailCarouselTap>(this, 'carousel:tap', {
|
||||
slideIndex: slideIndex,
|
||||
@@ -119,6 +119,7 @@ export class FrigateCardThumbnailCarousel extends FrigateCardCarousel {
|
||||
childIndex: childIndex,
|
||||
});
|
||||
}
|
||||
stopEventFromActivatingCardWideActions(ev);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
|
||||
@@ -38,6 +38,7 @@ import {
|
||||
contentsChanged,
|
||||
createMediaShowInfo,
|
||||
dispatchErrorMessageEvent,
|
||||
stopEventFromActivatingCardWideActions,
|
||||
} from '../common.js';
|
||||
import { renderProgressIndicator } from '../components/message.js';
|
||||
|
||||
@@ -655,8 +656,9 @@ export class FrigateCardViewerCarousel extends FrigateCardMediaCarousel {
|
||||
.thumbnail=${prev && prev.thumbnail ? prev.thumbnail : undefined}
|
||||
.label=${prev ? prev.title : ''}
|
||||
?disabled=${!prev}
|
||||
@click=${() => {
|
||||
@click=${(ev) => {
|
||||
this._nextPreviousHandler('previous');
|
||||
stopEventFromActivatingCardWideActions(ev);
|
||||
}}
|
||||
></frigate-card-next-previous-control>
|
||||
<div class="embla__viewport">
|
||||
@@ -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);
|
||||
}}
|
||||
></frigate-card-next-previous-control>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user