mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-23 17:26:42 +00:00
Lovelace - Long Press for everything (#1848)
* Long-press controller and lit-directive * Enable long-press for glance card * Enable long-press for entity-button * Use new long-press for picture-elements * Enable long-press for picture-entity card
This commit is contained in:
parent
8bf60d502a
commit
8cbd667286
@ -13,6 +13,7 @@ import { styleMap } from "lit-html/directives/styleMap.js";
|
|||||||
import { HomeAssistant } from "../../../types.js";
|
import { HomeAssistant } from "../../../types.js";
|
||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types.js";
|
import { LovelaceCard, LovelaceConfig } from "../types.js";
|
||||||
|
import { longPress } from "../common/directives/long-press-directive";
|
||||||
|
|
||||||
interface Config extends LovelaceConfig {
|
interface Config extends LovelaceConfig {
|
||||||
entity: string;
|
entity: string;
|
||||||
@ -20,6 +21,7 @@ interface Config extends LovelaceConfig {
|
|||||||
icon?: string;
|
icon?: string;
|
||||||
theme?: string;
|
theme?: string;
|
||||||
tap_action?: "toggle" | "call-service" | "more-info";
|
tap_action?: "toggle" | "call-service" | "more-info";
|
||||||
|
hold_action?: "toggle" | "call-service" | "more-info";
|
||||||
service?: string;
|
service?: string;
|
||||||
service_data?: object;
|
service_data?: object;
|
||||||
}
|
}
|
||||||
@ -62,7 +64,11 @@ class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
|
|
||||||
return html`
|
return html`
|
||||||
${this.renderStyle()}
|
${this.renderStyle()}
|
||||||
<ha-card @click="${this.handleClick}">
|
<ha-card
|
||||||
|
@ha-click="${() => this.handleClick(false)}"
|
||||||
|
@ha-hold="${() => this.handleClick(true)}"
|
||||||
|
.longPress="${longPress()}"
|
||||||
|
>
|
||||||
${
|
${
|
||||||
!stateObj
|
!stateObj
|
||||||
? html`<div class="not-found">Entity not available: ${
|
? html`<div class="not-found">Entity not available: ${
|
||||||
@ -157,7 +163,7 @@ class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
|
return `hsl(${hue}, 100%, ${100 - sat / 2}%)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleClick() {
|
private handleClick(hold) {
|
||||||
const config = this.config;
|
const config = this.config;
|
||||||
if (!config) {
|
if (!config) {
|
||||||
return;
|
return;
|
||||||
@ -167,11 +173,12 @@ class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const entityId = stateObj.entity_id;
|
const entityId = stateObj.entity_id;
|
||||||
switch (config.tap_action) {
|
const action = hold ? config.hold_action : config.tap_action || "more-info";
|
||||||
|
switch (action) {
|
||||||
case "toggle":
|
case "toggle":
|
||||||
toggleEntity(this.hass, entityId);
|
toggleEntity(this.hass, entityId);
|
||||||
break;
|
break;
|
||||||
case "call-service": {
|
case "call-service":
|
||||||
if (!config.service) {
|
if (!config.service) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -179,9 +186,10 @@ class HuiEntityButtonCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
const serviceData = { entity_id: entityId, ...config.service_data };
|
const serviceData = { entity_id: entityId, ...config.service_data };
|
||||||
this.hass!.callService(domain, service, serviceData);
|
this.hass!.callService(domain, service, serviceData);
|
||||||
break;
|
break;
|
||||||
}
|
case "more-info":
|
||||||
default:
|
|
||||||
fireEvent(this, "hass-more-info", { entityId });
|
fireEvent(this, "hass-more-info", { entityId });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,14 @@ import { fireEvent } from "../../../common/dom/fire_event.js";
|
|||||||
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
import { hassLocalizeLitMixin } from "../../../mixins/lit-localize-mixin";
|
||||||
import { HomeAssistant } from "../../../types.js";
|
import { HomeAssistant } from "../../../types.js";
|
||||||
import { LovelaceCard, LovelaceConfig } from "../types.js";
|
import { LovelaceCard, LovelaceConfig } from "../types.js";
|
||||||
|
import { longPress } from "../common/directives/long-press-directive";
|
||||||
|
|
||||||
interface EntityConfig {
|
interface EntityConfig {
|
||||||
name: string;
|
name: string;
|
||||||
icon: string;
|
icon: string;
|
||||||
entity: string;
|
entity: string;
|
||||||
tap_action: "toggle" | "call-service" | "more-info";
|
tap_action: "toggle" | "call-service" | "more-info";
|
||||||
|
hold_action?: "toggle" | "call-service" | "more-info";
|
||||||
service?: string;
|
service?: string;
|
||||||
service_data?: object;
|
service_data?: object;
|
||||||
}
|
}
|
||||||
@ -59,9 +61,13 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
const entities = processConfigEntities(config.entities);
|
const entities = processConfigEntities(config.entities);
|
||||||
|
|
||||||
for (const entity of entities) {
|
for (const entity of entities) {
|
||||||
if (entity.tap_action === "call-service" && !entity.service) {
|
if (
|
||||||
|
(entity.tap_action === "call-service" ||
|
||||||
|
entity.hold_action === "call-service") &&
|
||||||
|
!entity.service
|
||||||
|
) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
'Missing required property "service" when tap_action is call-service'
|
'Missing required property "service" when tap_action or hold_action is call-service'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,7 +157,9 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
<div
|
<div
|
||||||
class="entity"
|
class="entity"
|
||||||
.entityConf="${entityConf}"
|
.entityConf="${entityConf}"
|
||||||
@click="${this.handleClick}"
|
@ha-click="${(ev) => this.handleClick(ev, false)}"
|
||||||
|
@ha-hold="${(ev) => this.handleClick(ev, true)}"
|
||||||
|
.longPress="${longPress()}"
|
||||||
>
|
>
|
||||||
${
|
${
|
||||||
this.config!.show_name !== false
|
this.config!.show_name !== false
|
||||||
@ -175,21 +183,23 @@ export class HuiGlanceCard extends hassLocalizeLitMixin(LitElement)
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleClick(ev: MouseEvent) {
|
private handleClick(ev: MouseEvent, hold) {
|
||||||
const config = (ev.currentTarget as any).entityConf as EntityConfig;
|
const config = (ev.currentTarget as any).entityConf as EntityConfig;
|
||||||
const entityId = config.entity;
|
const entityId = config.entity;
|
||||||
switch (config.tap_action) {
|
const action = hold ? config.hold_action : config.tap_action || "more-info";
|
||||||
|
switch (action) {
|
||||||
case "toggle":
|
case "toggle":
|
||||||
toggleEntity(this.hass, entityId);
|
toggleEntity(this.hass, entityId);
|
||||||
break;
|
break;
|
||||||
case "call-service": {
|
case "call-service":
|
||||||
const [domain, service] = config.service!.split(".", 2);
|
const [domain, service] = config.service!.split(".", 2);
|
||||||
const serviceData = { entity_id: entityId, ...config.service_data };
|
const serviceData = { entity_id: entityId, ...config.service_data };
|
||||||
this.hass!.callService(domain, service, serviceData);
|
this.hass!.callService(domain, service, serviceData);
|
||||||
break;
|
break;
|
||||||
}
|
case "more-info":
|
||||||
default:
|
|
||||||
fireEvent(this, "hass-more-info", { entityId });
|
fireEvent(this, "hass-more-info", { entityId });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@ import toggleEntity from "../common/entity/toggle-entity.js";
|
|||||||
|
|
||||||
import EventsMixin from "../../../mixins/events-mixin.js";
|
import EventsMixin from "../../../mixins/events-mixin.js";
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin.js";
|
import LocalizeMixin from "../../../mixins/localize-mixin.js";
|
||||||
|
import { longPressBind } from "../common/directives/long-press-directive";
|
||||||
|
|
||||||
const UNAVAILABLE = "Unavailable";
|
const UNAVAILABLE = "Unavailable";
|
||||||
|
|
||||||
@ -51,7 +52,7 @@ class HuiPictureEntityCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<ha-card id='card' on-click="_cardClicked">
|
<ha-card id='card'>
|
||||||
<hui-image
|
<hui-image
|
||||||
hass="[[hass]]"
|
hass="[[hass]]"
|
||||||
image="[[_config.image]]"
|
image="[[_config.image]]"
|
||||||
@ -112,6 +113,14 @@ class HuiPictureEntityCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
this._config = config;
|
this._config = config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ready() {
|
||||||
|
super.ready();
|
||||||
|
const card = this.shadowRoot.querySelector("#card");
|
||||||
|
longPressBind(card);
|
||||||
|
card.addEventListener("ha-click", () => this._cardClicked(false));
|
||||||
|
card.addEventListener("ha-hold", () => this._cardClicked(true));
|
||||||
|
}
|
||||||
|
|
||||||
_hassChanged(hass) {
|
_hassChanged(hass) {
|
||||||
const config = this._config;
|
const config = this._config;
|
||||||
const entityId = config.entity;
|
const entityId = config.entity;
|
||||||
@ -163,16 +172,22 @@ class HuiPictureEntityCard extends EventsMixin(LocalizeMixin(PolymerElement)) {
|
|||||||
return config.show_name === false && config.show_state !== false;
|
return config.show_name === false && config.show_state !== false;
|
||||||
}
|
}
|
||||||
|
|
||||||
_cardClicked() {
|
_cardClicked(hold) {
|
||||||
const config = this._config;
|
const config = this._config;
|
||||||
const entityId = config.entity;
|
const entityId = config.entity;
|
||||||
|
|
||||||
if (!(entityId in this.hass.states)) return;
|
if (!(entityId in this.hass.states)) return;
|
||||||
|
|
||||||
if (config.tap_action === "toggle") {
|
const action = hold ? config.hold_action : config.tap_action || "more-info";
|
||||||
toggleEntity(this.hass, entityId);
|
|
||||||
} else {
|
switch (action) {
|
||||||
this.fire("hass-more-info", { entityId });
|
case "toggle":
|
||||||
|
toggleEntity(this.hass, entityId);
|
||||||
|
break;
|
||||||
|
case "more-info":
|
||||||
|
this.fire("hass-more-info", { entityId });
|
||||||
|
break;
|
||||||
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
140
src/panels/lovelace/common/directives/long-press-directive.ts
Normal file
140
src/panels/lovelace/common/directives/long-press-directive.ts
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
import { directive, PropertyPart } from "lit-html";
|
||||||
|
import "@material/mwc-ripple";
|
||||||
|
|
||||||
|
const isTouch =
|
||||||
|
"ontouchstart" in window ||
|
||||||
|
navigator.maxTouchPoints > 0 ||
|
||||||
|
navigator.msMaxTouchPoints > 0;
|
||||||
|
|
||||||
|
interface LongPress extends HTMLElement {
|
||||||
|
holdTime: number;
|
||||||
|
bind(element: Element): void;
|
||||||
|
}
|
||||||
|
interface LongPressElement extends Element {
|
||||||
|
longPress?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
class LongPress extends HTMLElement implements LongPress {
|
||||||
|
public holdTime: number;
|
||||||
|
protected ripple: any;
|
||||||
|
protected timer: number | undefined;
|
||||||
|
protected held: boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.holdTime = 500;
|
||||||
|
this.ripple = document.createElement("mwc-ripple");
|
||||||
|
this.timer = undefined;
|
||||||
|
this.held = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public connectedCallback() {
|
||||||
|
Object.assign(this.style, {
|
||||||
|
position: "absolute",
|
||||||
|
width: isTouch ? "100px" : "50px",
|
||||||
|
height: isTouch ? "100px" : "50px",
|
||||||
|
transform: "translate(-50%, -50%)",
|
||||||
|
pointerEvents: "none",
|
||||||
|
});
|
||||||
|
|
||||||
|
this.appendChild(this.ripple);
|
||||||
|
this.ripple.primary = true;
|
||||||
|
|
||||||
|
[
|
||||||
|
isTouch ? "touchcancel" : "mouseout",
|
||||||
|
"mouseup",
|
||||||
|
"touchmove",
|
||||||
|
"mousewheel",
|
||||||
|
"wheel",
|
||||||
|
"scroll",
|
||||||
|
].forEach((ev) => {
|
||||||
|
document.addEventListener(
|
||||||
|
ev,
|
||||||
|
() => {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.stopAnimation();
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public bind(element: LongPressElement) {
|
||||||
|
if (element.longPress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
element.longPress = true;
|
||||||
|
element.addEventListener(
|
||||||
|
isTouch ? "touchstart" : "mousedown",
|
||||||
|
(ev: Event) => {
|
||||||
|
this.held = false;
|
||||||
|
let x;
|
||||||
|
let y;
|
||||||
|
if ((ev as TouchEvent).touches) {
|
||||||
|
x = (ev as TouchEvent).touches[0].pageX;
|
||||||
|
y = (ev as TouchEvent).touches[0].pageY;
|
||||||
|
} else {
|
||||||
|
x = (ev as MouseEvent).pageX;
|
||||||
|
y = (ev as MouseEvent).pageY;
|
||||||
|
}
|
||||||
|
this.timer = window.setTimeout(() => {
|
||||||
|
this.startAnimation(x, y);
|
||||||
|
this.held = true;
|
||||||
|
}, this.holdTime);
|
||||||
|
},
|
||||||
|
{ passive: true }
|
||||||
|
);
|
||||||
|
element.addEventListener("click", () => {
|
||||||
|
this.stopAnimation();
|
||||||
|
if (this.held) {
|
||||||
|
element.dispatchEvent(new Event("ha-hold"));
|
||||||
|
} else {
|
||||||
|
element.dispatchEvent(new Event("ha-click"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private startAnimation(x: number, y: number) {
|
||||||
|
Object.assign(this.style, {
|
||||||
|
left: `${x}px`,
|
||||||
|
top: `${y}px`,
|
||||||
|
display: null,
|
||||||
|
});
|
||||||
|
this.ripple.disabled = false;
|
||||||
|
this.ripple.active = true;
|
||||||
|
this.ripple.unbounded = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private stopAnimation() {
|
||||||
|
this.ripple.active = false;
|
||||||
|
this.ripple.disabled = true;
|
||||||
|
this.style.display = "none";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("long-press", LongPress);
|
||||||
|
|
||||||
|
const getLongPress = (): LongPress => {
|
||||||
|
const body = document.body;
|
||||||
|
if (body.querySelector("long-press")) {
|
||||||
|
return body.querySelector("long-press") as LongPress;
|
||||||
|
}
|
||||||
|
|
||||||
|
const longpress = document.createElement("long-press");
|
||||||
|
body.appendChild(longpress);
|
||||||
|
|
||||||
|
return longpress as LongPress;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const longPressBind = (element: LongPressElement) => {
|
||||||
|
const longpress: LongPress = getLongPress();
|
||||||
|
if (!longpress) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
longpress.bind(element);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const longPress = () =>
|
||||||
|
directive((part: PropertyPart) => {
|
||||||
|
longPressBind(part.committer.element);
|
||||||
|
});
|
@ -4,6 +4,7 @@ import { PolymerElement } from "@polymer/polymer/polymer-element.js";
|
|||||||
import "../../../components/ha-icon.js";
|
import "../../../components/ha-icon.js";
|
||||||
|
|
||||||
import ElementClickMixin from "../mixins/element-click-mixin.js";
|
import ElementClickMixin from "../mixins/element-click-mixin.js";
|
||||||
|
import { longPressBind } from "../common/directives/long-press-directive";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin ElementClickMixin
|
* @appliesMixin ElementClickMixin
|
||||||
@ -32,7 +33,13 @@ class HuiIconElement extends ElementClickMixin(PolymerElement) {
|
|||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
this.registerMouse(this._config);
|
longPressBind(this);
|
||||||
|
this.addEventListener("ha-click", () =>
|
||||||
|
this.handleClick(this.hass, this._config, false)
|
||||||
|
);
|
||||||
|
this.addEventListener("ha-hold", () =>
|
||||||
|
this.handleClick(this.hass, this._config, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
|
@ -4,6 +4,7 @@ import { PolymerElement } from "@polymer/polymer/polymer-element.js";
|
|||||||
import "../components/hui-image.js";
|
import "../components/hui-image.js";
|
||||||
|
|
||||||
import ElementClickMixin from "../mixins/element-click-mixin.js";
|
import ElementClickMixin from "../mixins/element-click-mixin.js";
|
||||||
|
import { longPressBind } from "../common/directives/long-press-directive";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin ElementClickMixin
|
* @appliesMixin ElementClickMixin
|
||||||
@ -44,7 +45,13 @@ class HuiImageElement extends ElementClickMixin(PolymerElement) {
|
|||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
this.registerMouse(this._config);
|
longPressBind(this);
|
||||||
|
this.addEventListener("ha-click", () =>
|
||||||
|
this.handleClick(this.hass, this._config, false)
|
||||||
|
);
|
||||||
|
this.addEventListener("ha-hold", () =>
|
||||||
|
this.handleClick(this.hass, this._config, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
|
@ -4,6 +4,7 @@ import { PolymerElement } from "@polymer/polymer/polymer-element.js";
|
|||||||
import "../../../components/entity/state-badge.js";
|
import "../../../components/entity/state-badge.js";
|
||||||
|
|
||||||
import ElementClickMixin from "../mixins/element-click-mixin.js";
|
import ElementClickMixin from "../mixins/element-click-mixin.js";
|
||||||
|
import { longPressBind } from "../common/directives/long-press-directive";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin ElementClickMixin
|
* @appliesMixin ElementClickMixin
|
||||||
@ -36,7 +37,13 @@ class HuiStateIconElement extends ElementClickMixin(PolymerElement) {
|
|||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
this.registerMouse(this._config);
|
longPressBind(this);
|
||||||
|
this.addEventListener("ha-click", () =>
|
||||||
|
this.handleClick(this.hass, this._config, false)
|
||||||
|
);
|
||||||
|
this.addEventListener("ha-hold", () =>
|
||||||
|
this.handleClick(this.hass, this._config, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
|
@ -7,6 +7,7 @@ import "../../../components/entity/ha-state-label-badge.js";
|
|||||||
|
|
||||||
import LocalizeMixin from "../../../mixins/localize-mixin.js";
|
import LocalizeMixin from "../../../mixins/localize-mixin.js";
|
||||||
import ElementClickMixin from "../mixins/element-click-mixin.js";
|
import ElementClickMixin from "../mixins/element-click-mixin.js";
|
||||||
|
import { longPressBind } from "../common/directives/long-press-directive";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @appliesMixin ElementClickMixin
|
* @appliesMixin ElementClickMixin
|
||||||
@ -45,7 +46,13 @@ class HuiStateLabelElement extends LocalizeMixin(
|
|||||||
|
|
||||||
ready() {
|
ready() {
|
||||||
super.ready();
|
super.ready();
|
||||||
this.registerMouse(this._config);
|
longPressBind(this);
|
||||||
|
this.addEventListener("ha-click", () =>
|
||||||
|
this.handleClick(this.hass, this._config, false)
|
||||||
|
);
|
||||||
|
this.addEventListener("ha-hold", () =>
|
||||||
|
this.handleClick(this.hass, this._config, true)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
setConfig(config) {
|
setConfig(config) {
|
||||||
|
@ -3,7 +3,6 @@ import toggleEntity from "../common/entity/toggle-entity.js";
|
|||||||
import NavigateMixin from "../../../mixins/navigate-mixin";
|
import NavigateMixin from "../../../mixins/navigate-mixin";
|
||||||
import EventsMixin from "../../../mixins/events-mixin.js";
|
import EventsMixin from "../../../mixins/events-mixin.js";
|
||||||
import computeStateName from "../../../common/entity/compute_state_name";
|
import computeStateName from "../../../common/entity/compute_state_name";
|
||||||
import "@material/mwc-ripple";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* @polymerMixin
|
* @polymerMixin
|
||||||
@ -13,92 +12,14 @@ import "@material/mwc-ripple";
|
|||||||
export default dedupingMixin(
|
export default dedupingMixin(
|
||||||
(superClass) =>
|
(superClass) =>
|
||||||
class extends NavigateMixin(EventsMixin(superClass)) {
|
class extends NavigateMixin(EventsMixin(superClass)) {
|
||||||
registerMouse(config) {
|
handleClick(hass, config, hold) {
|
||||||
var isTouch =
|
let action = config.tap_action || "more-info";
|
||||||
"ontouchstart" in window ||
|
if (hold) {
|
||||||
navigator.MaxTouchPoints > 0 ||
|
action = config.hold_action;
|
||||||
navigator.msMaxTouchPoints > 0;
|
|
||||||
|
|
||||||
let ripple = null;
|
|
||||||
const rippleWrapper = document.createElement("div");
|
|
||||||
this.parentElement.appendChild(rippleWrapper);
|
|
||||||
Object.assign(rippleWrapper.style, {
|
|
||||||
position: this.style.position || "absolute",
|
|
||||||
width: isTouch ? "100px" : "50px",
|
|
||||||
height: isTouch ? "100px" : "50px",
|
|
||||||
top: this.style.top,
|
|
||||||
left: this.style.left,
|
|
||||||
bottom: this.style.bottom,
|
|
||||||
right: this.style.right,
|
|
||||||
transform: "translate(-50%, -50%)",
|
|
||||||
pointerEvents: "none",
|
|
||||||
});
|
|
||||||
|
|
||||||
const loadRipple = () => {
|
|
||||||
if (ripple) return;
|
|
||||||
ripple = document.createElement("mwc-ripple");
|
|
||||||
rippleWrapper.appendChild(ripple);
|
|
||||||
ripple.unbounded = true;
|
|
||||||
ripple.primary = true;
|
|
||||||
};
|
|
||||||
const startAnimation = () => {
|
|
||||||
ripple.style.visibility = "visible";
|
|
||||||
ripple.disabled = false;
|
|
||||||
ripple.active = true;
|
|
||||||
};
|
|
||||||
const stopAnimation = () => {
|
|
||||||
if (ripple) {
|
|
||||||
ripple.active = false;
|
|
||||||
ripple.disabled = true;
|
|
||||||
ripple.style.visibility = "hidden";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var mouseDown = isTouch ? "touchstart" : "mousedown";
|
|
||||||
var mouseOut = isTouch ? "touchcancel" : "mouseout";
|
|
||||||
var click = isTouch ? "touchend" : "click";
|
|
||||||
|
|
||||||
var timer = null;
|
|
||||||
var held = false;
|
|
||||||
var holdTime = config.hold_time || 500;
|
|
||||||
|
|
||||||
this.addEventListener(mouseDown, () => {
|
|
||||||
held = false;
|
|
||||||
loadRipple();
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
startAnimation();
|
|
||||||
held = true;
|
|
||||||
}, holdTime);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.addEventListener(click, () => {
|
|
||||||
stopAnimation();
|
|
||||||
this.handleClick(this.hass, config, held);
|
|
||||||
});
|
|
||||||
|
|
||||||
[
|
|
||||||
mouseOut,
|
|
||||||
"mouseup",
|
|
||||||
"touchmove",
|
|
||||||
"mousewheel",
|
|
||||||
"wheel",
|
|
||||||
"scroll",
|
|
||||||
].forEach((ev) => {
|
|
||||||
document.addEventListener(ev, () => {
|
|
||||||
clearTimeout(timer);
|
|
||||||
stopAnimation();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
handleClick(hass, config, held = false) {
|
|
||||||
let tapAction = config.tap_action || "more-info";
|
|
||||||
if (held) {
|
|
||||||
tapAction = config.hold_action || "none";
|
|
||||||
}
|
}
|
||||||
if (tapAction === "none") return;
|
if (action === "none") return;
|
||||||
|
|
||||||
switch (tapAction) {
|
switch (action) {
|
||||||
case "more-info":
|
case "more-info":
|
||||||
this.fire("hass-more-info", { entityId: config.entity });
|
this.fire("hass-more-info", { entityId: config.entity });
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user