Add double tap action (#3879)

* add dbltap_action

* apply to picture-glance

* types and boolean

* fix typo

* simplify double tap logic

* extract hasDoubleClick functionality

* address comments

* address comments

* double_tap_action
This commit is contained in:
Ian Richardson 2019-10-16 08:57:05 -05:00 committed by GitHub
parent fb589337f8
commit df29a5becb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 185 additions and 67 deletions

View File

@ -12,7 +12,7 @@ export class DemoUtilLongPress extends LitElement {
() => html`
<ha-card>
<mwc-button
@ha-click="${this._handleTap}"
@ha-click="${this._handleClick}"
@ha-hold="${this._handleHold}"
.longPress="${longPress()}"
>
@ -28,7 +28,7 @@ export class DemoUtilLongPress extends LitElement {
`;
}
private _handleTap(ev: Event) {
private _handleClick(ev: Event) {
this._addValue(ev, "tap");
}

View File

@ -108,3 +108,7 @@ export const getLovelaceCollection = (conn: Connection) =>
export interface WindowWithLovelaceProm extends Window {
llConfProm?: Promise<LovelaceConfig>;
}
export interface LongPressOptions {
hasDoubleClick?: boolean;
}

View File

@ -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`
<ha-card
@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),
})}
>
${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);
}
}

View File

@ -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 {
<div
class="entity"
.entityConf="${entityConf}"
@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(entityConf.double_tap_action),
})}
>
${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);
}
}

View File

@ -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}"
></ha-icon>
</div>
<div id="tooltip">
<div class="brightness" @ha-click="${this._handleTap}">
<div class="brightness" @ha-click="${this._handleClick}">
${brightness} %
</div>
<div class="name">
@ -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!);
}

View File

@ -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`
<ha-card
@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),
})}
class="${classMap({
clickable: Boolean(
this._config.tap_action || this._config.hold_action
@ -86,12 +90,16 @@ export class HuiPictureCard extends LitElement implements LovelaceCard {
`;
}
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);
}
}

View File

@ -25,6 +25,7 @@ import { handleClick } from "../common/handle-click";
import { UNAVAILABLE } from "../../../data/entity";
import { hasConfigOrEntityChanged } from "../common/has-changed";
import { PictureEntityCardConfig } from "./types";
import { hasDoubleClick } from "../common/has-double-click";
@customElement("hui-picture-entity-card")
class HuiPictureEntityCard extends LitElement implements LovelaceCard {
@ -124,9 +125,12 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
.cameraView=${this._config.camera_view}
.entity=${this._config.entity}
.aspectRatio=${this._config.aspect_ratio}
@ha-click=${this._handleTap}
@ha-click=${this._handleClick}
@ha-hold=${this._handleHold}
.longPress=${longPress()}
@ha-dblclick=${this._handleDblClick}
.longPress=${longPress({
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})}
class=${classMap({
clickable: stateObj.state !== UNAVAILABLE,
})}
@ -177,12 +181,16 @@ class HuiPictureEntityCard extends LitElement implements LovelaceCard {
`;
}
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);
}
}

View File

@ -25,6 +25,7 @@ import { HomeAssistant } from "../../../types";
import { longPress } from "../common/directives/long-press-directive";
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 { hasConfigOrEntityChanged } from "../common/has-changed";
@ -130,9 +131,12 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
this._config.camera_image
),
})}
@ha-click=${this._handleTap}
@ha-click=${this._handleClick}
@ha-hold=${this._handleHold}
.longPress=${longPress()}
@ha-dblclick=${this._handleDblClick}
.longPress=${longPress({
hasDoubleClick: hasDoubleClick(this._config!.double_tap_action),
})}
.config=${this._config}
.hass=${this.hass}
.image=${this._config.image}
@ -190,9 +194,12 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
return html`
<div class="wrapper">
<ha-icon
@ha-click=${this._handleTap}
@ha-click=${this._handleClick}
@ha-hold=${this._handleHold}
.longPress=${longPress()}
@ha-dblclick=${this._handleDblClick}
.longPress=${longPress({
hasDoubleClick: hasDoubleClick(entityConf.double_tap_action),
})}
.config=${entityConf}
class="${classMap({
"state-on": !STATES_OFF.has(stateObj.state),
@ -223,14 +230,19 @@ class HuiPictureGlanceCard extends LitElement implements LovelaceCard {
`;
}
private _handleTap(ev: MouseEvent): void {
private _handleClick(ev: MouseEvent): void {
const config = (ev.currentTarget as any).config as any;
handleClick(this, this.hass!, config, false);
handleClick(this, this.hass!, config, false, false);
}
private _handleHold(ev: MouseEvent): void {
const config = (ev.currentTarget as any).config as any;
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 any;
handleClick(this, this.hass!, config, false, true);
}
static get styles(): CSSResult {

View File

@ -46,6 +46,7 @@ export interface EntityButtonCardConfig extends LovelaceCardConfig {
theme?: string;
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}
export interface EntityFilterCardConfig extends LovelaceCardConfig {
@ -80,6 +81,7 @@ export interface GaugeCardConfig extends LovelaceCardConfig {
export interface ConfigEntity extends EntityConfig {
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}
export interface PictureGlanceEntityConfig extends ConfigEntity {
@ -141,6 +143,7 @@ export interface PictureCardConfig extends LovelaceCardConfig {
image?: string;
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}
export interface PictureElementsCardConfig extends LovelaceCardConfig {
@ -166,6 +169,7 @@ export interface PictureEntityCardConfig extends LovelaceCardConfig {
aspect_ratio?: string;
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
show_name?: boolean;
show_state?: boolean;
}
@ -182,6 +186,7 @@ export interface PictureGlanceCardConfig extends LovelaceCardConfig {
entity?: string;
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
show_state?: boolean;
}

View File

@ -8,6 +8,7 @@ interface Config extends LovelaceElementConfig {
title?: string;
tap_action?: ActionConfig;
hold_action?: ActionConfig;
double_tap_action?: ActionConfig;
}
export const computeTooltip = (hass: HomeAssistant, config: Config): string => {

View File

@ -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);
}
);

View File

@ -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;

View File

@ -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";
}

View File

@ -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 {
<ha-icon
.icon="${this._config.icon}"
.title="${computeTooltip(this.hass, this._config)}"
@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),
})}
></ha-icon>
`;
}
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 {

View File

@ -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),
})}
></hui-image>
`;
}
@ -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);
}
}

View File

@ -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 {
<state-badge
.stateObj="${stateObj}"
.title="${computeTooltip(this.hass, this._config)}"
@ha-click="${this._handleClick}"
@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),
})}
.overrideIcon=${this._config.icon}
></state-badge>
`;
@ -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);
}
}

View File

@ -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`
<div
.title="${computeTooltip(this.hass, this._config)}"
@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),
})}
>
${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 {

View File

@ -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;
}