diff --git a/gallery/src/demos/demo-util-long-press.ts b/gallery/src/demos/demo-util-long-press.ts index 5f09e3598f..86d245fe50 100644 --- a/gallery/src/demos/demo-util-long-press.ts +++ b/gallery/src/demos/demo-util-long-press.ts @@ -12,7 +12,7 @@ export class DemoUtilLongPress extends LitElement { () => html` @@ -28,7 +28,7 @@ export class DemoUtilLongPress extends LitElement { `; } - private _handleTap(ev: Event) { + private _handleClick(ev: Event) { this._addValue(ev, "tap"); } diff --git a/src/data/lovelace.ts b/src/data/lovelace.ts index 5f32b70b4b..ac8b8ba5a9 100644 --- a/src/data/lovelace.ts +++ b/src/data/lovelace.ts @@ -108,3 +108,7 @@ export const getLovelaceCollection = (conn: Connection) => export interface WindowWithLovelaceProm extends Window { llConfProm?: Promise; } + +export interface LongPressOptions { + hasDoubleClick?: boolean; +} diff --git a/src/panels/lovelace/cards/hui-entity-button-card.ts b/src/panels/lovelace/cards/hui-entity-button-card.ts index 3d91d68816..ee79f1e80b 100644 --- a/src/panels/lovelace/cards/hui-entity-button-card.ts +++ b/src/panels/lovelace/cards/hui-entity-button-card.ts @@ -28,6 +28,7 @@ import { longPress } from "../common/directives/long-press-directive"; import { handleClick } from "../common/handle-click"; import { DOMAINS_TOGGLE } from "../../../common/const"; import { EntityButtonCardConfig } from "./types"; +import { hasDoubleClick } from "../common/has-double-click"; @customElement("hui-entity-button-card") class HuiEntityButtonCard extends LitElement implements LovelaceCard { @@ -61,6 +62,7 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard { this._config = { theme: "default", hold_action: { action: "more-info" }, + double_tap_action: { action: "none" }, show_icon: true, show_name: true, ...config, @@ -118,9 +120,12 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard { return html` ${this._config.show_icon ? html` @@ -212,12 +217,16 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard { return `hsl(${hue}, 100%, ${100 - sat / 2}%)`; } - private _handleTap() { - handleClick(this, this.hass!, this._config!, false); + private _handleClick() { + handleClick(this, this.hass!, this._config!, false, false); } private _handleHold() { - handleClick(this, this.hass!, this._config!, true); + handleClick(this, this.hass!, this._config!, true, false); + } + + private _handleDblClick() { + handleClick(this, this.hass!, this._config!, false, true); } } diff --git a/src/panels/lovelace/cards/hui-glance-card.ts b/src/panels/lovelace/cards/hui-glance-card.ts index d86281c300..9494200795 100644 --- a/src/panels/lovelace/cards/hui-glance-card.ts +++ b/src/panels/lovelace/cards/hui-glance-card.ts @@ -26,6 +26,7 @@ import { longPress } from "../common/directives/long-press-directive"; import { processConfigEntities } from "../common/process-config-entities"; import { handleClick } from "../common/handle-click"; import { GlanceCardConfig, GlanceConfigEntity } from "./types"; +import { hasDoubleClick } from "../common/has-double-click"; @customElement("hui-glance-card") export class HuiGlanceCard extends LitElement implements LovelaceCard { @@ -183,9 +184,12 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
${this._config!.show_name !== false ? html` @@ -226,14 +230,19 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard { `; } - private _handleTap(ev: MouseEvent): void { + private _handleClick(ev: MouseEvent): void { const config = (ev.currentTarget as any).entityConf as GlanceConfigEntity; - handleClick(this, this.hass!, config, false); + handleClick(this, this.hass!, config, false, false); } private _handleHold(ev: MouseEvent): void { const config = (ev.currentTarget as any).entityConf as GlanceConfigEntity; - handleClick(this, this.hass!, config, true); + handleClick(this, this.hass!, config, true, false); + } + + private _handleDblClick(ev: MouseEvent): void { + const config = (ev.currentTarget as any).entityConf as GlanceConfigEntity; + handleClick(this, this.hass!, config, false, true); } } diff --git a/src/panels/lovelace/cards/hui-light-card.ts b/src/panels/lovelace/cards/hui-light-card.ts index babb4f6004..701c046e95 100644 --- a/src/panels/lovelace/cards/hui-light-card.ts +++ b/src/panels/lovelace/cards/hui-light-card.ts @@ -112,12 +112,12 @@ export class HuiLightCard extends LitElement implements LovelaceCard { filter: this._computeBrightness(stateObj), color: this._computeColor(stateObj), })}" - @click="${this._handleTap}" + @click="${this._handleClick}" >
-
+
${brightness} %
@@ -297,7 +297,7 @@ export class HuiLightCard extends LitElement implements LovelaceCard { return `hsl(${hue}, 100%, ${100 - sat / 2}%)`; } - private _handleTap() { + private _handleClick() { toggleEntity(this.hass!, this._config!.entity!); } diff --git a/src/panels/lovelace/cards/hui-picture-card.ts b/src/panels/lovelace/cards/hui-picture-card.ts index df98aba342..36dffad8ab 100644 --- a/src/panels/lovelace/cards/hui-picture-card.ts +++ b/src/panels/lovelace/cards/hui-picture-card.ts @@ -16,6 +16,7 @@ import { classMap } from "lit-html/directives/class-map"; import { handleClick } from "../common/handle-click"; import { longPress } from "../common/directives/long-press-directive"; import { PictureCardConfig } from "./types"; +import { hasDoubleClick } from "../common/has-double-click"; @customElement("hui-picture-card") export class HuiPictureCard extends LitElement implements LovelaceCard { @@ -55,9 +56,12 @@ export class HuiPictureCard extends LitElement implements LovelaceCard { return html` { diff --git a/src/panels/lovelace/common/directives/long-press-directive.ts b/src/panels/lovelace/common/directives/long-press-directive.ts index d89099d628..b4402add08 100644 --- a/src/panels/lovelace/common/directives/long-press-directive.ts +++ b/src/panels/lovelace/common/directives/long-press-directive.ts @@ -1,5 +1,6 @@ import { directive, PropertyPart } from "lit-html"; import "@material/mwc-ripple"; +import { LongPressOptions } from "../../../../data/lovelace"; const isTouch = "ontouchstart" in window || @@ -8,7 +9,7 @@ const isTouch = interface LongPress extends HTMLElement { holdTime: number; - bind(element: Element): void; + bind(element: Element, options): void; } interface LongPressElement extends Element { longPress?: boolean; @@ -21,6 +22,7 @@ class LongPress extends HTMLElement implements LongPress { protected held: boolean; protected cooldownStart: boolean; protected cooldownEnd: boolean; + private dblClickTimeout: number | undefined; constructor() { super(); @@ -65,7 +67,7 @@ class LongPress extends HTMLElement implements LongPress { }); } - public bind(element: LongPressElement) { + public bind(element: LongPressElement, options) { if (element.longPress) { return; } @@ -120,6 +122,15 @@ class LongPress extends HTMLElement implements LongPress { this.timer = undefined; if (this.held) { element.dispatchEvent(new Event("ha-hold")); + } else if (options.hasDoubleClick) { + if ((ev as MouseEvent).detail === 1) { + this.dblClickTimeout = window.setTimeout(() => { + element.dispatchEvent(new Event("ha-click")); + }, 250); + } else { + clearTimeout(this.dblClickTimeout); + element.dispatchEvent(new Event("ha-dblclick")); + } } else { element.dispatchEvent(new Event("ha-click")); } @@ -174,14 +185,19 @@ const getLongPress = (): LongPress => { return longpress as LongPress; }; -export const longPressBind = (element: LongPressElement) => { +export const longPressBind = ( + element: LongPressElement, + options: LongPressOptions +) => { const longpress: LongPress = getLongPress(); if (!longpress) { return; } - longpress.bind(element); + longpress.bind(element, options); }; -export const longPress = directive(() => (part: PropertyPart) => { - longPressBind(part.committer.element); -}); +export const longPress = directive( + (options: LongPressOptions = {}) => (part: PropertyPart) => { + longPressBind(part.committer.element, options); + } +); diff --git a/src/panels/lovelace/common/handle-click.ts b/src/panels/lovelace/common/handle-click.ts index d4ee03899a..30052d557a 100644 --- a/src/panels/lovelace/common/handle-click.ts +++ b/src/panels/lovelace/common/handle-click.ts @@ -13,12 +13,16 @@ export const handleClick = ( camera_image?: string; hold_action?: ActionConfig; tap_action?: ActionConfig; + double_tap_action?: ActionConfig; }, - hold: boolean + hold: boolean, + dblClick: boolean ): void => { let actionConfig: ActionConfig | undefined; - if (hold && config.hold_action) { + if (dblClick && config.double_tap_action) { + actionConfig = config.double_tap_action; + } else if (hold && config.hold_action) { actionConfig = config.hold_action; } else if (!hold && config.tap_action) { actionConfig = config.tap_action; diff --git a/src/panels/lovelace/common/has-double-click.ts b/src/panels/lovelace/common/has-double-click.ts new file mode 100644 index 0000000000..04e1529fbe --- /dev/null +++ b/src/panels/lovelace/common/has-double-click.ts @@ -0,0 +1,6 @@ +import { ActionConfig } from "../../../data/lovelace"; + +// Check if config or Entity changed +export function hasDoubleClick(config?: ActionConfig): boolean { + return config !== undefined && config.action !== "none"; +} diff --git a/src/panels/lovelace/elements/hui-icon-element.ts b/src/panels/lovelace/elements/hui-icon-element.ts index f0da18717d..cc57f993dc 100644 --- a/src/panels/lovelace/elements/hui-icon-element.ts +++ b/src/panels/lovelace/elements/hui-icon-element.ts @@ -15,6 +15,7 @@ import { handleClick } from "../common/handle-click"; import { longPress } from "../common/directives/long-press-directive"; import { LovelaceElement, IconElementConfig } from "./types"; import { HomeAssistant } from "../../../types"; +import { hasDoubleClick } from "../common/has-double-click"; @customElement("hui-icon-element") export class HuiIconElement extends LitElement implements LovelaceElement { @@ -38,19 +39,26 @@ export class HuiIconElement extends LitElement implements LovelaceElement { `; } - private _handleTap(): void { - handleClick(this, this.hass!, this._config!, false); + private _handleClick(): void { + handleClick(this, this.hass!, this._config!, false, false); } private _handleHold(): void { - handleClick(this, this.hass!, this._config!, true); + handleClick(this, this.hass!, this._config!, true, false); + } + + private _handleDblClick() { + handleClick(this, this.hass!, this._config!, false, true); } static get styles(): CSSResult { diff --git a/src/panels/lovelace/elements/hui-image-element.ts b/src/panels/lovelace/elements/hui-image-element.ts index e6d98e2163..c2f7645c5f 100644 --- a/src/panels/lovelace/elements/hui-image-element.ts +++ b/src/panels/lovelace/elements/hui-image-element.ts @@ -15,6 +15,7 @@ import { handleClick } from "../common/handle-click"; import { longPress } from "../common/directives/long-press-directive"; import { LovelaceElement, ImageElementConfig } from "./types"; import { HomeAssistant } from "../../../types"; +import { hasDoubleClick } from "../common/has-double-click"; @customElement("hui-image-element") export class HuiImageElement extends LitElement implements LovelaceElement { @@ -49,9 +50,12 @@ export class HuiImageElement extends LitElement implements LovelaceElement { .stateFilter="${this._config.state_filter}" .title="${computeTooltip(this.hass, this._config)}" .aspectRatio="${this._config.aspect_ratio}" - @ha-click="${this._handleTap}" - @ha-hold="${this._handleHold}" - .longPress="${longPress()}" + @ha-click=${this._handleClick} + @ha-hold=${this._handleHold} + @ha-dblclick=${this._handleDblClick} + .longPress=${longPress({ + hasDoubleClick: hasDoubleClick(this._config!.double_tap_action), + })} > `; } @@ -69,12 +73,16 @@ export class HuiImageElement extends LitElement implements LovelaceElement { `; } - private _handleTap(): void { - handleClick(this, this.hass!, this._config!, false); + private _handleClick(): void { + handleClick(this, this.hass!, this._config!, false, false); } private _handleHold(): void { - handleClick(this, this.hass!, this._config!, true); + handleClick(this, this.hass!, this._config!, true, false); + } + + private _handleDblClick() { + handleClick(this, this.hass!, this._config!, false, true); } } diff --git a/src/panels/lovelace/elements/hui-state-icon-element.ts b/src/panels/lovelace/elements/hui-state-icon-element.ts index 7f2c39057a..09a2023912 100644 --- a/src/panels/lovelace/elements/hui-state-icon-element.ts +++ b/src/panels/lovelace/elements/hui-state-icon-element.ts @@ -18,6 +18,7 @@ import { longPress } from "../common/directives/long-press-directive"; import { LovelaceElement, StateIconElementConfig } from "./types"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { hasDoubleClick } from "../common/has-double-click"; @customElement("hui-state-icon-element") export class HuiStateIconElement extends LitElement implements LovelaceElement { @@ -59,9 +60,12 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement { `; @@ -76,11 +80,15 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement { } private _handleClick(): void { - handleClick(this, this.hass!, this._config!, false); + handleClick(this, this.hass!, this._config!, false, false); } private _handleHold(): void { - handleClick(this, this.hass!, this._config!, true); + handleClick(this, this.hass!, this._config!, true, false); + } + + private _handleDblClick() { + handleClick(this, this.hass!, this._config!, false, true); } } diff --git a/src/panels/lovelace/elements/hui-state-label-element.ts b/src/panels/lovelace/elements/hui-state-label-element.ts index b6389cb842..37c629b455 100644 --- a/src/panels/lovelace/elements/hui-state-label-element.ts +++ b/src/panels/lovelace/elements/hui-state-label-element.ts @@ -19,6 +19,7 @@ import { longPress } from "../common/directives/long-press-directive"; import { LovelaceElement, StateLabelElementConfig } from "./types"; import { HomeAssistant } from "../../../types"; import { hasConfigOrEntityChanged } from "../common/has-changed"; +import { hasDoubleClick } from "../common/has-double-click"; @customElement("hui-state-label-element") class HuiStateLabelElement extends LitElement implements LovelaceElement { @@ -59,9 +60,12 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement { return html`
${this._config.prefix}${stateObj ? computeStateDisplay( @@ -74,12 +78,16 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement { `; } - private _handleTap(): void { - handleClick(this, this.hass!, this._config!, false); + private _handleClick(): void { + handleClick(this, this.hass!, this._config!, false, false); } private _handleHold(): void { - handleClick(this, this.hass!, this._config!, true); + handleClick(this, this.hass!, this._config!, true, false); + } + + private _handleDblClick() { + handleClick(this, this.hass!, this._config!, false, true); } static get styles(): CSSResult { diff --git a/src/panels/lovelace/elements/types.ts b/src/panels/lovelace/elements/types.ts index b5457e2a04..2aec666e38 100644 --- a/src/panels/lovelace/elements/types.ts +++ b/src/panels/lovelace/elements/types.ts @@ -22,6 +22,7 @@ export interface IconElementConfig extends LovelaceElementConfig { name?: string; tap_action?: ActionConfig; hold_action?: ActionConfig; + double_tap_action?: ActionConfig; icon: string; } @@ -29,6 +30,7 @@ export interface ImageElementConfig extends LovelaceElementConfig { entity?: string; tap_action?: ActionConfig; hold_action?: ActionConfig; + double_tap_action?: ActionConfig; image?: string; state_image?: string; camera_image?: string; @@ -52,6 +54,7 @@ export interface StateIconElementConfig extends LovelaceElementConfig { entity: string; tap_action?: ActionConfig; hold_action?: ActionConfig; + double_tap_action?: ActionConfig; icon?: string; } @@ -61,4 +64,5 @@ export interface StateLabelElementConfig extends LovelaceElementConfig { suffix?: string; tap_action?: ActionConfig; hold_action?: ActionConfig; + double_tap_action?: ActionConfig; }