mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 03:06:41 +00:00
Add confirmation button in lock more info (#20093)
* Add confirmation button in more info lock * Reset confirm open on service call * Use text instead of toast for success
This commit is contained in:
parent
db3709952c
commit
45a5c1c235
@ -84,6 +84,7 @@ export class HaControlButton extends LitElement {
|
|||||||
--control-button-background-color: var(--disabled-color);
|
--control-button-background-color: var(--disabled-color);
|
||||||
--control-button-background-opacity: 0.2;
|
--control-button-background-opacity: 0.2;
|
||||||
--control-button-border-radius: 10px;
|
--control-button-border-radius: 10px;
|
||||||
|
--control-button-padding: 8px;
|
||||||
--mdc-icon-size: 20px;
|
--mdc-icon-size: 20px;
|
||||||
color: var(--primary-text-color);
|
color: var(--primary-text-color);
|
||||||
width: 40px;
|
width: 40px;
|
||||||
@ -95,16 +96,20 @@ export class HaControlButton extends LitElement {
|
|||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: var(--control-button-border-radius);
|
border-radius: var(--control-button-border-radius);
|
||||||
border: none;
|
border: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: var(--control-button-padding);
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
line-height: 0;
|
line-height: inherit;
|
||||||
|
font-family: Roboto;
|
||||||
|
font-weight: 500;
|
||||||
outline: none;
|
outline: none;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
background: none;
|
background: none;
|
||||||
@ -126,6 +131,8 @@ export class HaControlButton extends LitElement {
|
|||||||
background-color 180ms ease-in-out,
|
background-color 180ms ease-in-out,
|
||||||
opacity 180ms ease-in-out;
|
opacity 180ms ease-in-out;
|
||||||
opacity: var(--control-button-background-opacity);
|
opacity: var(--control-button-background-opacity);
|
||||||
|
pointer-events: none;
|
||||||
|
white-space: normal;
|
||||||
}
|
}
|
||||||
.button {
|
.button {
|
||||||
transition: color 180ms ease-in-out;
|
transition: color 180ms ease-in-out;
|
||||||
@ -133,6 +140,7 @@ export class HaControlButton extends LitElement {
|
|||||||
}
|
}
|
||||||
.button ::slotted(*) {
|
.button ::slotted(*) {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
|
opacity: 0.95;
|
||||||
}
|
}
|
||||||
.button:disabled {
|
.button:disabled {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
import { mdiDoorOpen, mdiLock, mdiLockOff } from "@mdi/js";
|
import { mdiCheck } from "@mdi/js";
|
||||||
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
import { CSSResultGroup, LitElement, css, html, nothing } from "lit";
|
||||||
import { customElement, property } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
import { styleMap } from "lit/directives/style-map";
|
import { styleMap } from "lit/directives/style-map";
|
||||||
import { stateColorCss } from "../../../common/entity/state_color";
|
import { stateColorCss } from "../../../common/entity/state_color";
|
||||||
import { supportsFeature } from "../../../common/entity/supports-feature";
|
import { supportsFeature } from "../../../common/entity/supports-feature";
|
||||||
import "../../../components/ha-attributes";
|
import "../../../components/ha-attributes";
|
||||||
|
import "../../../components/ha-control-button";
|
||||||
|
import "../../../components/ha-control-button-group";
|
||||||
import "../../../components/ha-outlined-icon-button";
|
import "../../../components/ha-outlined-icon-button";
|
||||||
import "../../../components/ha-state-icon";
|
import "../../../components/ha-state-icon";
|
||||||
import { UNAVAILABLE } from "../../../data/entity";
|
import { UNAVAILABLE } from "../../../data/entity";
|
||||||
@ -18,14 +20,49 @@ import type { HomeAssistant } from "../../../types";
|
|||||||
import "../components/ha-more-info-state-header";
|
import "../components/ha-more-info-state-header";
|
||||||
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
import { moreInfoControlStyle } from "../components/more-info-control-style";
|
||||||
|
|
||||||
|
const CONFIRM_TIMEOUT_SECOND = 5;
|
||||||
|
const OPENED_TIMEOUT_SECOND = 3;
|
||||||
|
|
||||||
|
type ButtonState = "normal" | "confirm" | "success";
|
||||||
|
|
||||||
@customElement("more-info-lock")
|
@customElement("more-info-lock")
|
||||||
class MoreInfoLock extends LitElement {
|
class MoreInfoLock extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass!: HomeAssistant;
|
||||||
|
|
||||||
@property({ attribute: false }) public stateObj?: LockEntity;
|
@property({ attribute: false }) public stateObj?: LockEntity;
|
||||||
|
|
||||||
|
@state() public _buttonState: ButtonState = "normal";
|
||||||
|
|
||||||
|
private _buttonTimeout?: number;
|
||||||
|
|
||||||
|
private _setButtonState(buttonState: ButtonState, timeoutSecond?: number) {
|
||||||
|
clearTimeout(this._buttonTimeout);
|
||||||
|
this._buttonState = buttonState;
|
||||||
|
if (timeoutSecond) {
|
||||||
|
this._buttonTimeout = window.setTimeout(() => {
|
||||||
|
this._buttonState = "normal";
|
||||||
|
}, timeoutSecond * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async _open() {
|
private async _open() {
|
||||||
|
if (this._buttonState !== "confirm") {
|
||||||
|
this._setButtonState("confirm", CONFIRM_TIMEOUT_SECOND);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
callProtectedLockService(this, this.hass, this.stateObj!, "open");
|
callProtectedLockService(this, this.hass, this.stateObj!, "open");
|
||||||
|
|
||||||
|
this._setButtonState("success", OPENED_TIMEOUT_SECOND);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _resetButtonState() {
|
||||||
|
this._setButtonState("normal");
|
||||||
|
}
|
||||||
|
|
||||||
|
disconnectedCallback(): void {
|
||||||
|
super.disconnectedCallback();
|
||||||
|
this._resetButtonState();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _lock() {
|
private async _lock() {
|
||||||
@ -45,7 +82,7 @@ class MoreInfoLock extends LitElement {
|
|||||||
|
|
||||||
const color = stateColorCss(this.stateObj);
|
const color = stateColorCss(this.stateObj);
|
||||||
const style = {
|
const style = {
|
||||||
"--icon-color": color,
|
"--state-color": color,
|
||||||
};
|
};
|
||||||
|
|
||||||
const isJammed = this.stateObj.state === "jammed";
|
const isJammed = this.stateObj.state === "jammed";
|
||||||
@ -56,8 +93,7 @@ class MoreInfoLock extends LitElement {
|
|||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
></ha-more-info-state-header>
|
></ha-more-info-state-header>
|
||||||
<div class="controls" style=${styleMap(style)}>
|
<div class="controls" style=${styleMap(style)}>
|
||||||
${
|
${this.stateObj.state === "jammed"
|
||||||
this.stateObj.state === "jammed"
|
|
||||||
? html`
|
? html`
|
||||||
<div class="status">
|
<div class="status">
|
||||||
<span></span>
|
<span></span>
|
||||||
@ -71,59 +107,56 @@ class MoreInfoLock extends LitElement {
|
|||||||
`
|
`
|
||||||
: html`
|
: html`
|
||||||
<ha-state-control-lock-toggle
|
<ha-state-control-lock-toggle
|
||||||
|
@lock-service-called=${this._resetButtonState}
|
||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
>
|
>
|
||||||
</ha-state-control-lock-toggle>
|
</ha-state-control-lock-toggle>
|
||||||
`
|
`}
|
||||||
}
|
|
||||||
${
|
|
||||||
supportsOpen || isJammed
|
|
||||||
? html`
|
|
||||||
<div class="buttons">
|
|
||||||
${supportsOpen
|
${supportsOpen
|
||||||
? html`
|
? html`
|
||||||
<ha-outlined-icon-button
|
<div class="buttons">
|
||||||
|
${this._buttonState === "success"
|
||||||
|
? html`
|
||||||
|
<p class="open-success">
|
||||||
|
<ha-svg-icon path=${mdiCheck}></ha-svg-icon>
|
||||||
|
${this.hass.localize("ui.card.lock.open_door_success")}
|
||||||
|
</p>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<ha-control-button
|
||||||
.disabled=${this.stateObj.state === UNAVAILABLE}
|
.disabled=${this.stateObj.state === UNAVAILABLE}
|
||||||
.title=${this.hass.localize("ui.card.lock.open")}
|
class="open-button ${this._buttonState}"
|
||||||
.ariaLabel=${this.hass.localize("ui.card.lock.open")}
|
|
||||||
@click=${this._open}
|
@click=${this._open}
|
||||||
>
|
>
|
||||||
<ha-svg-icon .path=${mdiDoorOpen}></ha-svg-icon>
|
${this._buttonState === "confirm"
|
||||||
</ha-outlined-icon-button>
|
? this.hass.localize("ui.card.lock.open_door_confirm")
|
||||||
|
: this.hass.localize("ui.card.lock.open_door")}
|
||||||
|
</ha-control-button>
|
||||||
|
`}
|
||||||
|
</div>
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
${isJammed
|
${isJammed
|
||||||
? html`
|
? html`
|
||||||
<ha-outlined-icon-button
|
<ha-control-button-group class="jammed">
|
||||||
.title=${this.hass.localize("ui.card.lock.lock")}
|
<ha-control-button @click=${this._unlock}>
|
||||||
.ariaLabel=${this.hass.localize("ui.card.lock.lock")}
|
${this.hass.localize("ui.card.lock.unlock")}
|
||||||
@click=${this._lock}
|
</ha-control-button>
|
||||||
>
|
<ha-control-button @click=${this._lock}>
|
||||||
<ha-svg-icon .path=${mdiLock}></ha-svg-icon>
|
${this.hass.localize("ui.card.lock.lock")}
|
||||||
</ha-outlined-icon-button>
|
</ha-control-button>
|
||||||
<ha-outlined-icon-button
|
</ha-control-button-group>
|
||||||
.title=${this.hass.localize("ui.card.lock.unlock")}
|
|
||||||
.ariaLabel=${this.hass.localize(
|
|
||||||
"ui.card.lock.unlock"
|
|
||||||
)}
|
|
||||||
@click=${this._unlock}
|
|
||||||
>
|
|
||||||
<ha-svg-icon .path=${mdiLockOff}></ha-svg-icon>
|
|
||||||
</ha-outlined-icon-button>
|
|
||||||
`
|
`
|
||||||
: nothing}
|
: nothing}
|
||||||
</div>
|
|
||||||
`
|
|
||||||
: nothing
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ha-attributes
|
<ha-attributes
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
.stateObj=${this.stateObj}
|
.stateObj=${this.stateObj}
|
||||||
extra-filters="code_format"
|
extra-filters="code_format"
|
||||||
></ha-attributes>
|
></ha-attributes>
|
||||||
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,6 +164,36 @@ class MoreInfoLock extends LitElement {
|
|||||||
return [
|
return [
|
||||||
moreInfoControlStyle,
|
moreInfoControlStyle,
|
||||||
css`
|
css`
|
||||||
|
ha-control-button {
|
||||||
|
font-size: 14px;
|
||||||
|
height: 60px;
|
||||||
|
--control-button-border-radius: 24px;
|
||||||
|
}
|
||||||
|
.open-button {
|
||||||
|
width: 100px;
|
||||||
|
--control-button-background-color: var(--state-color);
|
||||||
|
}
|
||||||
|
.open-button.confirm {
|
||||||
|
--control-button-background-color: var(--warning-color);
|
||||||
|
}
|
||||||
|
.open-success {
|
||||||
|
line-height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: row;
|
||||||
|
gap: 8px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--success-color);
|
||||||
|
}
|
||||||
|
ha-control-button-group.jammed {
|
||||||
|
--control-button-group-thickness: 60px;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
ha-control-button-group + ha-attributes:not([empty]) {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0% {
|
0% {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
@ -155,7 +218,7 @@ class MoreInfoLock extends LitElement {
|
|||||||
position: relative;
|
position: relative;
|
||||||
--mdc-icon-size: 80px;
|
--mdc-icon-size: 80px;
|
||||||
animation: pulse 1s infinite;
|
animation: pulse 1s infinite;
|
||||||
color: var(--icon-color);
|
color: var(--state-color);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
width: 144px;
|
width: 144px;
|
||||||
height: 144px;
|
height: 144px;
|
||||||
@ -171,7 +234,7 @@ class MoreInfoLock extends LitElement {
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: var(--icon-color);
|
background-color: var(--state-color);
|
||||||
transition: background-color 180ms ease-in-out;
|
transition: background-color 180ms ease-in-out;
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,13 @@ import { UNAVAILABLE, UNKNOWN } from "../../data/entity";
|
|||||||
import { forwardHaptic } from "../../data/haptics";
|
import { forwardHaptic } from "../../data/haptics";
|
||||||
import { callProtectedLockService, LockEntity } from "../../data/lock";
|
import { callProtectedLockService, LockEntity } from "../../data/lock";
|
||||||
import { HomeAssistant } from "../../types";
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
interface HASSDomEvents {
|
||||||
|
"lock-service-called": undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@customElement("ha-state-control-lock-toggle")
|
@customElement("ha-state-control-lock-toggle")
|
||||||
export class HaStateControlLockToggle extends LitElement {
|
export class HaStateControlLockToggle extends LitElement {
|
||||||
@ -67,6 +74,7 @@ export class HaStateControlLockToggle extends LitElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
forwardHaptic("light");
|
forwardHaptic("light");
|
||||||
|
fireEvent(this, "lock-service-called");
|
||||||
callProtectedLockService(
|
callProtectedLockService(
|
||||||
this,
|
this,
|
||||||
this.hass,
|
this.hass,
|
||||||
|
@ -187,7 +187,10 @@
|
|||||||
"code": "[%key:ui::card::alarm_control_panel::code%]",
|
"code": "[%key:ui::card::alarm_control_panel::code%]",
|
||||||
"lock": "Lock",
|
"lock": "Lock",
|
||||||
"unlock": "Unlock",
|
"unlock": "Unlock",
|
||||||
"open": "Open"
|
"open": "Open",
|
||||||
|
"open_door": "Open door",
|
||||||
|
"open_door_confirm": "Really open?",
|
||||||
|
"open_door_success": "Door open"
|
||||||
},
|
},
|
||||||
"media_player": {
|
"media_player": {
|
||||||
"source": "Source",
|
"source": "Source",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user