action-handler (#4115)

* action-handler

* comments

* address comments

* finish conversion

* move haptics

* address comments

* lint

* keyup

* double enter

* address comments

* keyup
pull/4171/head
Ian Richardson 2019-11-01 13:15:11 -05:00 committed by GitHub
parent 9b3891f778
commit 274c2016c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 210 additions and 303 deletions

View File

@ -161,8 +161,8 @@ if (!window.cardTools) {
}; };
cardTools.longpress = (element) => { cardTools.longpress = (element) => {
customElements.whenDefined("long-press").then(() => { customElements.whenDefined("action-handler").then(() => {
const longpress = document.body.querySelector("long-press"); const longpress = document.body.querySelector("action-handler");
longpress.bind(element); longpress.bind(element);
}); });
return element; return element;

View File

@ -2,7 +2,8 @@ import { html, LitElement, TemplateResult } from "lit-element";
import "@material/mwc-button"; import "@material/mwc-button";
import "../../../src/components/ha-card"; import "../../../src/components/ha-card";
import { longPress } from "../../../src/panels/lovelace/common/directives/long-press-directive"; import { actionHandler } from "../../../src/panels/lovelace/common/directives/action-handler-directive";
import { ActionHandlerEvent } from "../../../src/data/lovelace";
export class DemoUtilLongPress extends LitElement { export class DemoUtilLongPress extends LitElement {
protected render(): TemplateResult | void { protected render(): TemplateResult | void {
@ -12,9 +13,8 @@ export class DemoUtilLongPress extends LitElement {
() => html` () => html`
<ha-card> <ha-card>
<mwc-button <mwc-button
@ha-click="${this._handleClick}" @action=${this._handleAction}
@ha-hold="${this._handleHold}" .actionHandler=${actionHandler({})}
.longPress="${longPress()}"
> >
(long) press me! (long) press me!
</mwc-button> </mwc-button>
@ -28,12 +28,8 @@ export class DemoUtilLongPress extends LitElement {
`; `;
} }
private _handleClick(ev: Event) { private _handleAction(ev: ActionHandlerEvent) {
this._addValue(ev, "tap"); this._addValue(ev, ev.detail.action!);
}
private _handleHold(ev: Event) {
this._addValue(ev, "hold");
} }
private _addValue(ev: Event, value: string) { private _addValue(ev: Event, value: string) {

View File

@ -1,5 +1,6 @@
import { HomeAssistant } from "../types"; import { HomeAssistant } from "../types";
import { Connection, getCollection } from "home-assistant-js-websocket"; import { Connection, getCollection } from "home-assistant-js-websocket";
import { HASSDomEvent } from "../common/dom/fire_event";
export interface LovelaceConfig { export interface LovelaceConfig {
title?: string; title?: string;
@ -127,6 +128,13 @@ export interface WindowWithLovelaceProm extends Window {
llConfProm?: Promise<LovelaceConfig>; llConfProm?: Promise<LovelaceConfig>;
} }
export interface LongPressOptions { export interface ActionHandlerOptions {
hasHold?: boolean;
hasDoubleClick?: boolean; hasDoubleClick?: boolean;
} }
export interface ActionHandlerDetail {
action: string;
}
export type ActionHandlerEvent = HASSDomEvent<ActionHandlerDetail>;

View File

@ -12,9 +12,10 @@ import "../components/hui-warning-element";
import { LovelaceBadge } from "../types"; import { LovelaceBadge } from "../types";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { StateLabelBadgeConfig } from "./types"; import { StateLabelBadgeConfig } from "./types";
import { longPress } from "../common/directives/long-press-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { hasDoubleClick } from "../common/has-double-click"; import { hasAction } from "../common/has-action";
import { handleClick } from "../common/handle-click"; import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-state-label-badge") @customElement("hui-state-label-badge")
export class HuiStateLabelBadge extends LitElement implements LovelaceBadge { export class HuiStateLabelBadge extends LitElement implements LovelaceBadge {
@ -39,26 +40,17 @@ export class HuiStateLabelBadge extends LitElement implements LovelaceBadge {
.name=${this._config.name} .name=${this._config.name}
.icon=${this._config.icon} .icon=${this._config.icon}
.image=${this._config.image} .image=${this._config.image}
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
></ha-state-label-badge> ></ha-state-label-badge>
`; `;
} }
private _handleClick() { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold() {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
} }

View File

@ -24,11 +24,12 @@ import { computeDomain } from "../../../common/entity/compute_domain";
import { HomeAssistant, LightEntity } from "../../../types"; import { HomeAssistant, LightEntity } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { longPress } from "../common/directives/long-press-directive";
import { handleClick } from "../common/handle-click";
import { DOMAINS_TOGGLE } from "../../../common/const"; import { DOMAINS_TOGGLE } from "../../../common/const";
import { EntityButtonCardConfig } from "./types"; import { EntityButtonCardConfig } from "./types";
import { hasDoubleClick } from "../common/has-double-click"; import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { handleAction } from "../common/handle-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
@customElement("hui-entity-button-card") @customElement("hui-entity-button-card")
class HuiEntityButtonCard extends LitElement implements LovelaceCard { class HuiEntityButtonCard extends LitElement implements LovelaceCard {
@ -126,11 +127,10 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard {
return html` return html`
<ha-card <ha-card
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
> >
${this._config.show_icon ${this._config.show_icon
@ -232,16 +232,8 @@ class HuiEntityButtonCard extends LitElement implements LovelaceCard {
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`; return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
} }
private _handleClick() { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold() {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
} }

View File

@ -22,11 +22,12 @@ import "../components/hui-warning-element";
import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { longPress } from "../common/directives/long-press-directive";
import { processConfigEntities } from "../common/process-config-entities"; import { processConfigEntities } from "../common/process-config-entities";
import { handleClick } from "../common/handle-click";
import { GlanceCardConfig, GlanceConfigEntity } from "./types"; import { GlanceCardConfig, GlanceConfigEntity } from "./types";
import { hasDoubleClick } from "../common/has-double-click"; import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-glance-card") @customElement("hui-glance-card")
export class HuiGlanceCard extends LitElement implements LovelaceCard { export class HuiGlanceCard extends LitElement implements LovelaceCard {
@ -199,11 +200,10 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
<div <div
class="entity" class="entity"
.config="${entityConf}" .config="${entityConf}"
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(entityConf.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(entityConf.double_tap_action),
hasDoubleClick: hasDoubleClick(entityConf.double_tap_action),
})} })}
> >
${this._config!.show_name !== false ${this._config!.show_name !== false
@ -245,19 +245,9 @@ export class HuiGlanceCard extends LitElement implements LovelaceCard {
`; `;
} }
private _handleClick(ev: MouseEvent): void { private _handleAction(ev: ActionHandlerEvent) {
const config = (ev.currentTarget as any).config as GlanceConfigEntity; const config = (ev.currentTarget as any).config as GlanceConfigEntity;
handleClick(this, this.hass!, config, false, false); handleAction(this, this.hass!, config, ev.detail.action!);
}
private _handleHold(ev: MouseEvent): void {
const config = (ev.currentTarget as any).config as GlanceConfigEntity;
handleClick(this, this.hass!, config, true, false);
}
private _handleDblClick(ev: MouseEvent): void {
const config = (ev.currentTarget as any).config as GlanceConfigEntity;
handleClick(this, this.hass!, config, false, true);
} }
} }

View File

@ -14,11 +14,12 @@ import "../../../components/ha-card";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { classMap } from "lit-html/directives/class-map"; 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 { PictureCardConfig } from "./types";
import { hasDoubleClick } from "../common/has-double-click";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-picture-card") @customElement("hui-picture-card")
export class HuiPictureCard extends LitElement implements LovelaceCard { export class HuiPictureCard extends LitElement implements LovelaceCard {
@ -78,11 +79,10 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
return html` return html`
<ha-card <ha-card
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
class="${classMap({ class="${classMap({
clickable: Boolean( clickable: Boolean(
@ -112,16 +112,8 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
`; `;
} }
private _handleClick() { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold() {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
} }

View File

@ -18,15 +18,16 @@ import { computeDomain } from "../../../common/entity/compute_domain";
import { computeStateName } from "../../../common/entity/compute_state_name"; import { computeStateName } from "../../../common/entity/compute_state_name";
import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { longPress } from "../common/directives/long-press-directive";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { handleClick } from "../common/handle-click";
import { UNAVAILABLE } from "../../../data/entity"; import { UNAVAILABLE } from "../../../data/entity";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { PictureEntityCardConfig } from "./types"; import { PictureEntityCardConfig } from "./types";
import { hasDoubleClick } from "../common/has-double-click";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-picture-entity-card") @customElement("hui-picture-entity-card")
class HuiPictureEntityCard extends LitElement implements LovelaceCard { class HuiPictureEntityCard extends LitElement implements LovelaceCard {
@ -146,11 +147,10 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
.cameraView=${this._config.camera_view} .cameraView=${this._config.camera_view}
.entity=${this._config.entity} .entity=${this._config.entity}
.aspectRatio=${this._config.aspect_ratio} .aspectRatio=${this._config.aspect_ratio}
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
class=${classMap({ class=${classMap({
clickable: stateObj.state !== UNAVAILABLE, clickable: stateObj.state !== UNAVAILABLE,
@ -202,16 +202,8 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
`; `;
} }
private _handleClick() { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold() {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
} }

View File

@ -22,13 +22,14 @@ import { computeStateDisplay } from "../../../common/entity/compute_state_displa
import { DOMAINS_TOGGLE } from "../../../common/const"; import { DOMAINS_TOGGLE } from "../../../common/const";
import { LovelaceCard, LovelaceCardEditor } from "../types"; import { LovelaceCard, LovelaceCardEditor } from "../types";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { longPress } from "../common/directives/long-press-directive";
import { processConfigEntities } from "../common/process-config-entities"; import { processConfigEntities } from "../common/process-config-entities";
import { handleClick } from "../common/handle-click";
import { hasDoubleClick } from "../common/has-double-click";
import { PictureGlanceCardConfig, PictureGlanceEntityConfig } from "./types"; import { PictureGlanceCardConfig, PictureGlanceEntityConfig } from "./types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element"; import { applyThemesOnElement } from "../../../common/dom/apply_themes_on_element";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]); const STATES_OFF = new Set(["closed", "locked", "not_home", "off"]);
@ -160,11 +161,10 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
this._config.camera_image this._config.camera_image
), ),
})} })}
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
.config=${this._config} .config=${this._config}
.hass=${this.hass} .hass=${this.hass}
@ -223,11 +223,10 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
return html` return html`
<div class="wrapper"> <div class="wrapper">
<ha-icon <ha-icon
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(entityConf.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(entityConf.double_tap_action),
hasDoubleClick: hasDoubleClick(entityConf.double_tap_action),
})} })}
.config=${entityConf} .config=${entityConf}
class="${classMap({ class="${classMap({
@ -259,19 +258,9 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
`; `;
} }
private _handleClick(ev: MouseEvent): void { private _handleAction(ev: ActionHandlerEvent) {
const config = (ev.currentTarget as any).config as any; const config = (ev.currentTarget as any).config as any;
handleClick(this, this.hass!, config, false, false); handleAction(this, this.hass!, config, ev.detail.action!);
}
private _handleHold(ev: MouseEvent): void {
const config = (ev.currentTarget as any).config as any;
handleClick(this, this.hass!, config, true, false);
}
private _handleDblClick(ev: MouseEvent): void {
const config = (ev.currentTarget as any).config as any;
handleClick(this, this.hass!, config, false, true);
} }
static get styles(): CSSResult { static get styles(): CSSResult {

View File

@ -1,21 +1,31 @@
import { directive, PropertyPart } from "lit-html"; import { directive, PropertyPart } from "lit-html";
import "@material/mwc-ripple"; import "@material/mwc-ripple";
import { LongPressOptions } from "../../../../data/lovelace"; import {
ActionHandlerOptions,
ActionHandlerDetail,
} from "../../../../data/lovelace";
import { fireEvent } from "../../../../common/dom/fire_event";
const isTouch = const isTouch =
"ontouchstart" in window || "ontouchstart" in window ||
navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 ||
navigator.msMaxTouchPoints > 0; navigator.msMaxTouchPoints > 0;
interface LongPress extends HTMLElement { interface ActionHandler extends HTMLElement {
holdTime: number; holdTime: number;
bind(element: Element, options): void; bind(element: Element, options): void;
} }
interface LongPressElement extends Element { interface ActionHandlerElement extends HTMLElement {
longPress?: boolean; actionHandler?: boolean;
} }
class LongPress extends HTMLElement implements LongPress { declare global {
interface HASSDomEvents {
action: ActionHandlerDetail;
}
}
class ActionHandler extends HTMLElement implements ActionHandler {
public holdTime: number; public holdTime: number;
public ripple: any; public ripple: any;
protected timer: number | undefined; protected timer: number | undefined;
@ -67,11 +77,11 @@ class LongPress extends HTMLElement implements LongPress {
}); });
} }
public bind(element: LongPressElement, options) { public bind(element: ActionHandlerElement, options) {
if (element.longPress) { if (element.actionHandler) {
return; return;
} }
element.longPress = true; element.actionHandler = true;
element.addEventListener("contextmenu", (ev: Event) => { element.addEventListener("contextmenu", (ev: Event) => {
const e = ev || window.event; const e = ev || window.event;
@ -100,10 +110,13 @@ class LongPress extends HTMLElement implements LongPress {
x = (ev as MouseEvent).pageX; x = (ev as MouseEvent).pageX;
y = (ev as MouseEvent).pageY; y = (ev as MouseEvent).pageY;
} }
this.timer = window.setTimeout(() => {
this.startAnimation(x, y); if (options.hasHold) {
this.held = true; this.timer = window.setTimeout(() => {
}, this.holdTime); this.startAnimation(x, y);
this.held = true;
}, this.holdTime);
}
this.cooldownStart = true; this.cooldownStart = true;
window.setTimeout(() => (this.cooldownStart = false), 100); window.setTimeout(() => (this.cooldownStart = false), 100);
@ -121,18 +134,18 @@ class LongPress extends HTMLElement implements LongPress {
this.stopAnimation(); this.stopAnimation();
this.timer = undefined; this.timer = undefined;
if (this.held) { if (this.held) {
element.dispatchEvent(new Event("ha-hold")); fireEvent(element, "action", { action: "hold" });
} else if (options.hasDoubleClick) { } else if (options.hasDoubleClick) {
if ((ev as MouseEvent).detail === 1 || ev.type === "keyup") { if ((ev as MouseEvent).detail === 1 || ev.type === "keyup") {
this.dblClickTimeout = window.setTimeout(() => { this.dblClickTimeout = window.setTimeout(() => {
element.dispatchEvent(new Event("ha-click")); fireEvent(element, "action", { action: "tap" });
}, 250); }, 250);
} else { } else {
clearTimeout(this.dblClickTimeout); clearTimeout(this.dblClickTimeout);
element.dispatchEvent(new Event("ha-dblclick")); fireEvent(element, "action", { action: "double_tap" });
} }
} else { } else {
element.dispatchEvent(new Event("ha-click")); fireEvent(element, "action", { action: "tap" });
} }
this.cooldownEnd = true; this.cooldownEnd = true;
window.setTimeout(() => (this.cooldownEnd = false), 100); window.setTimeout(() => (this.cooldownEnd = false), 100);
@ -150,7 +163,7 @@ class LongPress extends HTMLElement implements LongPress {
element.addEventListener("keyup", handleEnter); element.addEventListener("keyup", handleEnter);
// iOS 13 sends a complete normal touchstart-touchend series of events followed by a mousedown-click series. // iOS 13 sends a complete normal touchstart-touchend series of events followed by a mousedown-click series.
// That might be a bug, but until it's fixed, this should make long-press work. // That might be a bug, but until it's fixed, this should make action-handler work.
// If it's not a bug that is fixed, this might need updating with the next iOS version. // If it's not a bug that is fixed, this might need updating with the next iOS version.
// Note that all events (both touch and mouse) must be listened for in order to work on computers with both mouse and touchscreen. // Note that all events (both touch and mouse) must be listened for in order to work on computers with both mouse and touchscreen.
const isIOS13 = window.navigator.userAgent.match(/iPhone OS 13_/); const isIOS13 = window.navigator.userAgent.match(/iPhone OS 13_/);
@ -178,33 +191,33 @@ class LongPress extends HTMLElement implements LongPress {
} }
} }
customElements.define("long-press", LongPress); customElements.define("action-handler", ActionHandler);
const getLongPress = (): LongPress => { const geActionHandler = (): ActionHandler => {
const body = document.body; const body = document.body;
if (body.querySelector("long-press")) { if (body.querySelector("action-handler")) {
return body.querySelector("long-press") as LongPress; return body.querySelector("action-handler") as ActionHandler;
} }
const longpress = document.createElement("long-press"); const actionhandler = document.createElement("action-handler");
body.appendChild(longpress); body.appendChild(actionhandler);
return longpress as LongPress; return actionhandler as ActionHandler;
}; };
export const longPressBind = ( export const actionHandlerBind = (
element: LongPressElement, element: ActionHandlerElement,
options: LongPressOptions options: ActionHandlerOptions
) => { ) => {
const longpress: LongPress = getLongPress(); const actionhandler: ActionHandler = geActionHandler();
if (!longpress) { if (!actionhandler) {
return; return;
} }
longpress.bind(element, options); actionhandler.bind(element, options);
}; };
export const longPress = directive( export const actionHandler = directive(
(options: LongPressOptions = {}) => (part: PropertyPart) => { (options: ActionHandlerOptions = {}) => (part: PropertyPart) => {
longPressBind(part.committer.element, options); actionHandlerBind(part.committer.element as ActionHandlerElement, options);
} }
); );

View File

@ -1,11 +1,11 @@
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { fireEvent } from "../../../common/dom/fire_event"; import { fireEvent } from "../../../common/dom/fire_event";
import { navigate } from "../../../common/navigate"; import { navigate } from "../../../common/navigate";
import { toggleEntity } from "../../../../src/panels/lovelace/common/entity/toggle-entity"; import { toggleEntity } from "./entity/toggle-entity";
import { ActionConfig } from "../../../data/lovelace"; import { ActionConfig } from "../../../data/lovelace";
import { forwardHaptic } from "../../../data/haptics"; import { forwardHaptic } from "../../../data/haptics";
export const handleClick = ( export const handleAction = (
node: HTMLElement, node: HTMLElement,
hass: HomeAssistant, hass: HomeAssistant,
config: { config: {
@ -15,16 +15,15 @@ export const handleClick = (
tap_action?: ActionConfig; tap_action?: ActionConfig;
double_tap_action?: ActionConfig; double_tap_action?: ActionConfig;
}, },
hold: boolean, action: string
dblClick: boolean
): void => { ): void => {
let actionConfig: ActionConfig | undefined; let actionConfig: ActionConfig | undefined;
if (dblClick && config.double_tap_action) { if (action === "double_tap" && config.double_tap_action) {
actionConfig = config.double_tap_action; actionConfig = config.double_tap_action;
} else if (hold && config.hold_action) { } else if (action === "hold" && config.hold_action) {
actionConfig = config.hold_action; actionConfig = config.hold_action;
} else if (!hold && config.tap_action) { } else if (action === "tap" && config.tap_action) {
actionConfig = config.tap_action; actionConfig = config.tap_action;
} }
@ -41,6 +40,8 @@ export const handleClick = (
(e) => e.user === hass!.user!.id (e) => e.user === hass!.user!.id
)) ))
) { ) {
forwardHaptic("warning");
if ( if (
!confirm( !confirm(
actionConfig.confirmation.text || actionConfig.confirmation.text ||

View File

@ -1,6 +1,5 @@
import { ActionConfig } from "../../../data/lovelace"; import { ActionConfig } from "../../../data/lovelace";
// Check if config or Entity changed export function hasAction(config?: ActionConfig): boolean {
export function hasDoubleClick(config?: ActionConfig): boolean {
return config !== undefined && config.action !== "none"; return config !== undefined && config.action !== "none";
} }

View File

@ -17,13 +17,14 @@ import "../components/hui-warning";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { computeRTL } from "../../../common/util/compute_rtl"; import { computeRTL } from "../../../common/util/compute_rtl";
import { toggleAttribute } from "../../../common/dom/toggle_attribute"; import { toggleAttribute } from "../../../common/dom/toggle_attribute";
import { longPress } from "../common/directives/long-press-directive";
import { hasDoubleClick } from "../common/has-double-click";
import { handleClick } from "../common/handle-click";
import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const"; import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
import { EntitiesCardEntityConfig } from "../cards/types"; import { EntitiesCardEntityConfig } from "../cards/types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
class HuiGenericEntityRow extends LitElement { class HuiGenericEntityRow extends LitElement {
@property() public hass?: HomeAssistant; @property() public hass?: HomeAssistant;
@ -66,11 +67,10 @@ class HuiGenericEntityRow extends LitElement {
.stateObj=${stateObj} .stateObj=${stateObj}
.overrideIcon=${this.config.icon} .overrideIcon=${this.config.icon}
.overrideImage=${this.config.image} .overrideImage=${this.config.image}
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this.config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this.config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this.config.double_tap_action),
})} })}
tabindex="0" tabindex="0"
></state-badge> ></state-badge>
@ -84,11 +84,10 @@ class HuiGenericEntityRow extends LitElement {
!this.showSecondary || this.config.secondary_info !this.showSecondary || this.config.secondary_info
), ),
})} })}
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this.config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this.config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this.config.double_tap_action),
})} })}
> >
${this.config.name || computeStateName(stateObj)} ${this.config.name || computeStateName(stateObj)}
@ -122,16 +121,8 @@ class HuiGenericEntityRow extends LitElement {
} }
} }
private _handleClick(): void { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this.config!, false, false); handleAction(this, this.hass!, this.config!, ev.detail.action!);
}
private _handleHold(): void {
handleClick(this, this.hass!, this.config!, true, false);
}
private _handleDblClick(): void {
handleClick(this, this.hass!, this.config!, false, true);
} }
static get styles(): CSSResult { static get styles(): CSSResult {

View File

@ -11,11 +11,12 @@ import {
import "../../../components/ha-icon"; import "../../../components/ha-icon";
import { computeTooltip } from "../common/compute-tooltip"; import { computeTooltip } from "../common/compute-tooltip";
import { handleClick } from "../common/handle-click";
import { longPress } from "../common/directives/long-press-directive";
import { LovelaceElement, IconElementConfig } from "./types"; import { LovelaceElement, IconElementConfig } from "./types";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasDoubleClick } from "../common/has-double-click"; import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-icon-element") @customElement("hui-icon-element")
export class HuiIconElement extends LitElement implements LovelaceElement { export class HuiIconElement extends LitElement implements LovelaceElement {
@ -39,26 +40,17 @@ export class HuiIconElement extends LitElement implements LovelaceElement {
<ha-icon <ha-icon
.icon="${this._config.icon}" .icon="${this._config.icon}"
.title="${computeTooltip(this.hass, this._config)}" .title="${computeTooltip(this.hass, this._config)}"
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
></ha-icon> ></ha-icon>
`; `;
} }
private _handleClick(): void { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold(): void {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
static get styles(): CSSResult { static get styles(): CSSResult {

View File

@ -11,11 +11,12 @@ import {
import "../components/hui-image"; import "../components/hui-image";
import { computeTooltip } from "../common/compute-tooltip"; import { computeTooltip } from "../common/compute-tooltip";
import { handleClick } from "../common/handle-click";
import { longPress } from "../common/directives/long-press-directive";
import { LovelaceElement, ImageElementConfig } from "./types"; import { LovelaceElement, ImageElementConfig } from "./types";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasDoubleClick } from "../common/has-double-click"; import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-image-element") @customElement("hui-image-element")
export class HuiImageElement extends LitElement implements LovelaceElement { export class HuiImageElement extends LitElement implements LovelaceElement {
@ -50,11 +51,10 @@ export class HuiImageElement extends LitElement implements LovelaceElement {
.stateFilter="${this._config.state_filter}" .stateFilter="${this._config.state_filter}"
.title="${computeTooltip(this.hass, this._config)}" .title="${computeTooltip(this.hass, this._config)}"
.aspectRatio="${this._config.aspect_ratio}" .aspectRatio="${this._config.aspect_ratio}"
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
></hui-image> ></hui-image>
`; `;
@ -73,16 +73,8 @@ export class HuiImageElement extends LitElement implements LovelaceElement {
`; `;
} }
private _handleClick(): void { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold(): void {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
} }

View File

@ -14,9 +14,10 @@ import { computeStateName } from "../../../common/entity/compute_state_name";
import { LovelaceElement, StateBadgeElementConfig } from "./types"; import { LovelaceElement, StateBadgeElementConfig } from "./types";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { longPress } from "../common/directives/long-press-directive"; import { actionHandler } from "../common/directives/action-handler-directive";
import { hasDoubleClick } from "../common/has-double-click"; import { hasAction } from "../common/has-action";
import { handleClick } from "../common/handle-click"; import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-state-badge-element") @customElement("hui-state-badge-element")
export class HuiStateBadgeElement extends LitElement export class HuiStateBadgeElement extends LitElement
@ -64,26 +65,17 @@ export class HuiStateBadgeElement extends LitElement
: this._config.title === null : this._config.title === null
? "" ? ""
: this._config.title}" : this._config.title}"
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
></ha-state-label-badge> ></ha-state-label-badge>
`; `;
} }
private _handleClick() { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold() {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
} }

View File

@ -13,12 +13,13 @@ import "../../../components/entity/state-badge";
import "../components/hui-warning-element"; import "../components/hui-warning-element";
import { computeTooltip } from "../common/compute-tooltip"; import { computeTooltip } from "../common/compute-tooltip";
import { handleClick } from "../common/handle-click";
import { longPress } from "../common/directives/long-press-directive";
import { LovelaceElement, StateIconElementConfig } from "./types"; import { LovelaceElement, StateIconElementConfig } from "./types";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { hasDoubleClick } from "../common/has-double-click"; import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-state-icon-element") @customElement("hui-state-icon-element")
export class HuiStateIconElement extends LitElement implements LovelaceElement { export class HuiStateIconElement extends LitElement implements LovelaceElement {
@ -60,11 +61,10 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement {
<state-badge <state-badge
.stateObj="${stateObj}" .stateObj="${stateObj}"
.title="${computeTooltip(this.hass, this._config)}" .title="${computeTooltip(this.hass, this._config)}"
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
.overrideIcon=${this._config.icon} .overrideIcon=${this._config.icon}
></state-badge> ></state-badge>
@ -79,16 +79,8 @@ export class HuiStateIconElement extends LitElement implements LovelaceElement {
`; `;
} }
private _handleClick(): void { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold(): void {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
} }

View File

@ -13,12 +13,13 @@ import "../components/hui-warning-element";
import { computeStateDisplay } from "../../../common/entity/compute_state_display"; import { computeStateDisplay } from "../../../common/entity/compute_state_display";
import { computeTooltip } from "../common/compute-tooltip"; import { computeTooltip } from "../common/compute-tooltip";
import { handleClick } from "../common/handle-click";
import { longPress } from "../common/directives/long-press-directive";
import { LovelaceElement, StateLabelElementConfig } from "./types"; import { LovelaceElement, StateLabelElementConfig } from "./types";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { hasDoubleClick } from "../common/has-double-click"; import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-state-label-element") @customElement("hui-state-label-element")
class HuiStateLabelElement extends LitElement implements LovelaceElement { class HuiStateLabelElement extends LitElement implements LovelaceElement {
@ -59,11 +60,10 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
return html` return html`
<div <div
.title="${computeTooltip(this.hass, this._config)}" .title="${computeTooltip(this.hass, this._config)}"
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})} })}
> >
${this._config.prefix}${stateObj ${this._config.prefix}${stateObj
@ -77,16 +77,8 @@ class HuiStateLabelElement extends LitElement implements LovelaceElement {
`; `;
} }
private _handleClick(): void { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold(): void {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick() {
handleClick(this, this.hass!, this._config!, false, true);
} }
static get styles(): CSSResult { static get styles(): CSSResult {

View File

@ -23,13 +23,14 @@ import { setInputSelectOption } from "../../../data/input-select";
import { hasConfigOrEntityChanged } from "../common/has-changed"; import { hasConfigOrEntityChanged } from "../common/has-changed";
import { forwardHaptic } from "../../../data/haptics"; import { forwardHaptic } from "../../../data/haptics";
import { stopPropagation } from "../../../common/dom/stop_propagation"; import { stopPropagation } from "../../../common/dom/stop_propagation";
import { longPress } from "../common/directives/long-press-directive";
import { hasDoubleClick } from "../common/has-double-click";
import { handleClick } from "../common/handle-click";
import { classMap } from "lit-html/directives/class-map"; import { classMap } from "lit-html/directives/class-map";
import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const"; import { DOMAINS_HIDE_MORE_INFO } from "../../../common/const";
import { computeDomain } from "../../../common/entity/compute_domain"; import { computeDomain } from "../../../common/entity/compute_domain";
import { EntitiesCardEntityConfig } from "../cards/types"; import { EntitiesCardEntityConfig } from "../cards/types";
import { actionHandler } from "../common/directives/action-handler-directive";
import { hasAction } from "../common/has-action";
import { ActionHandlerEvent } from "../../../data/lovelace";
import { handleAction } from "../common/handle-action";
@customElement("hui-input-select-entity-row") @customElement("hui-input-select-entity-row")
class HuiInputSelectEntityRow extends LitElement implements EntityRow { class HuiInputSelectEntityRow extends LitElement implements EntityRow {
@ -81,11 +82,10 @@ class HuiInputSelectEntityRow extends LitElement implements EntityRow {
class=${classMap({ class=${classMap({
pointer, pointer,
})} })}
@ha-click=${this._handleClick} @action=${this._handleAction}
@ha-hold=${this._handleHold} .actionHandler=${actionHandler({
@ha-dblclick=${this._handleDblClick} hasHold: hasAction(this._config!.hold_action),
.longPress=${longPress({ hasDoubleClick: hasAction(this._config!.double_tap_action),
hasDoubleClick: hasDoubleClick(this._config.double_tap_action),
})} })}
tabindex="0" tabindex="0"
></state-badge> ></state-badge>
@ -127,16 +127,8 @@ class HuiInputSelectEntityRow extends LitElement implements EntityRow {
)!.selected = stateObj.attributes.options.indexOf(stateObj.state); )!.selected = stateObj.attributes.options.indexOf(stateObj.state);
} }
private _handleClick(): void { private _handleAction(ev: ActionHandlerEvent) {
handleClick(this, this.hass!, this._config!, false, false); handleAction(this, this.hass!, this._config!, ev.detail.action!);
}
private _handleHold(): void {
handleClick(this, this.hass!, this._config!, true, false);
}
private _handleDblClick(): void {
handleClick(this, this.hass!, this._config!, false, true);
} }
static get styles(): CSSResult { static get styles(): CSSResult {