Improve disabled state in more info (#17570)

This commit is contained in:
Paul Bottein 2023-08-15 13:50:44 +02:00 committed by GitHub
parent f6087f3805
commit baba02f563
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 358 additions and 550 deletions

View File

@ -238,6 +238,11 @@ export class HaControlSelectMenu extends SelectBase {
opacity: var(--control-select-menu-background-opacity); opacity: var(--control-select-menu-background-opacity);
} }
.select-disabled .select-anchor {
cursor: not-allowed;
color: var(--disabled-color);
}
mwc-menu { mwc-menu {
--mdc-shape-medium: 8px; --mdc-shape-medium: 8px;
} }

View File

@ -38,7 +38,7 @@ export class HaIconButtonToggle extends HaIconButton {
:host([selected]) mwc-icon-button { :host([selected]) mwc-icon-button {
color: var(--primary-background-color); color: var(--primary-background-color);
} }
:host([selected]) mwc-icon-button::before { :host([selected]:not([disabled])) mwc-icon-button::before {
opacity: 1; opacity: 1;
} }
`; `;

View File

@ -16,6 +16,7 @@ import { ClimateEntity, ClimateEntityFeature } from "../../../../data/climate";
import { UNAVAILABLE } from "../../../../data/entity"; import { UNAVAILABLE } from "../../../../data/entity";
import { computeCssVariable } from "../../../../resources/css-variables"; import { computeCssVariable } from "../../../../resources/css-variables";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { moreInfoControlCircularSliderStyle } from "../ha-more-info-control-circular-slider-style";
@customElement("ha-more-info-climate-humidity") @customElement("ha-more-info-climate-humidity")
export class HaMoreInfoClimateHumidity extends LitElement { export class HaMoreInfoClimateHumidity extends LitElement {
@ -78,8 +79,16 @@ export class HaMoreInfoClimateHumidity extends LitElement {
} }
private _renderLabel() { private _renderLabel() {
if (this.stateObj.state === UNAVAILABLE) {
return html`
<p class="label disabled">
${this.hass.formatEntityState(this.stateObj, UNAVAILABLE)}
</p>
`;
}
return html` return html`
<p class="action"> <p class="label">
${this.hass.localize( ${this.hass.localize(
"ui.dialogs.more_info_control.climate.humidity_target" "ui.dialogs.more_info_control.climate.humidity_target"
)} )}
@ -131,7 +140,7 @@ export class HaMoreInfoClimateHumidity extends LitElement {
const active = stateActive(this.stateObj); const active = stateActive(this.stateObj);
// Use humidifier state color // Use humidifier state color
const mainColor = computeCssVariable( const stateColor = computeCssVariable(
domainStateColorProperties( domainStateColorProperties(
"humidifier", "humidifier",
this.stateObj, this.stateObj,
@ -142,12 +151,16 @@ export class HaMoreInfoClimateHumidity extends LitElement {
const targetHumidity = this._targetHumidity; const targetHumidity = this._targetHumidity;
const currentHumidity = this.stateObj.attributes.current_humidity; const currentHumidity = this.stateObj.attributes.current_humidity;
if (supportsTargetHumidity && targetHumidity != null) { if (
supportsTargetHumidity &&
targetHumidity != null &&
this.stateObj.state !== UNAVAILABLE
) {
return html` return html`
<div <div
class="container" class="container"
style=${styleMap({ style=${styleMap({
"--main-color": mainColor, "--state-color": stateColor,
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
@ -156,13 +169,12 @@ export class HaMoreInfoClimateHumidity extends LitElement {
.max=${this._max} .max=${this._max}
.step=${this._step} .step=${this._step}
.current=${currentHumidity} .current=${currentHumidity}
.disabled=${this.stateObj!.state === UNAVAILABLE}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@value-changing=${this._valueChanging} @value-changing=${this._valueChanging}
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info"> <div class="info">
<div class="action-container">${this._renderLabel()}</div> <div class="label-container">${this._renderLabel()}</div>
<div class="target-container"> <div class="target-container">
${this._renderTarget(targetHumidity)} ${this._renderTarget(targetHumidity)}
</div> </div>
@ -175,152 +187,40 @@ export class HaMoreInfoClimateHumidity extends LitElement {
return html` return html`
<div class="container"> <div class="container">
<ha-control-circular-slider <ha-control-circular-slider
.current=${this.stateObj.attributes.current_temperature} .current=${this.stateObj.attributes.current_humidity}
.min=${this._min} .min=${this._min}
.max=${this._max} .max=${this._max}
.step=${this._step} .step=${this._step}
disabled disabled
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info">
<div class="label-container">${this._renderLabel()}</div>
</div>
</div> </div>
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return css` return [
/* Layout */ moreInfoControlCircularSliderStyle,
.container { css`
position: relative; /* Elements */
} .target-container {
.info { margin-bottom: 30px;
position: absolute; }
top: 0; .target .value {
left: 0; font-size: 58px;
right: 0; line-height: 1;
bottom: 0; letter-spacing: -0.25px;
display: flex; }
flex-direction: column; .target .value .unit {
align-items: center; font-size: 0.4em;
justify-content: center; line-height: 1;
pointer-events: none; margin-left: 2px;
font-size: 16px; }
line-height: 24px; `,
letter-spacing: 0.1px; ];
}
.info * {
margin: 0;
pointer-events: auto;
}
/* Elements */
.target-container {
margin-bottom: 30px;
}
.target .value {
font-size: 56px;
line-height: 1;
letter-spacing: -0.25px;
}
.target .value .unit {
font-size: 0.4em;
line-height: 1;
margin-left: 2px;
}
.action-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 200px;
height: 48px;
margin-bottom: 6px;
}
.action {
font-weight: 500;
text-align: center;
color: var(--action-color, inherit);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.dual {
display: flex;
flex-direction: row;
gap: 24px;
margin-bottom: 40px;
}
.dual button {
outline: none;
background: none;
color: inherit;
font-family: inherit;
-webkit-tap-highlight-color: transparent;
border: none;
opacity: 0.5;
padding: 0;
transition:
opacity 180ms ease-in-out,
transform 180ms ease-in-out;
cursor: pointer;
}
.dual button:focus-visible {
transform: scale(1.1);
}
.dual button.selected {
opacity: 1;
}
.buttons {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
margin: 0 auto;
width: 120px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.buttons ha-outlined-icon-button {
--md-outlined-icon-button-container-size: 48px;
--md-outlined-icon-button-icon-size: 24px;
}
/* Accessibility */
.visually-hidden {
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
}
/* Slider */
ha-control-circular-slider {
--control-circular-slider-color: var(
--main-color,
var(--disabled-color)
);
}
ha-control-circular-slider::after {
display: block;
content: "";
position: absolute;
top: -10%;
left: -10%;
right: -10%;
bottom: -10%;
background: radial-gradient(
50% 50% at 50% 50%,
var(--action-color, transparent) 0%,
transparent 100%
);
opacity: 0.15;
pointer-events: none;
}
`;
} }
} }

View File

@ -27,6 +27,7 @@ import {
} from "../../../../data/climate"; } from "../../../../data/climate";
import { UNAVAILABLE } from "../../../../data/entity"; import { UNAVAILABLE } from "../../../../data/entity";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { moreInfoControlCircularSliderStyle } from "../ha-more-info-control-circular-slider-style";
type Target = "value" | "low" | "high"; type Target = "value" | "low" | "high";
@ -137,7 +138,15 @@ export class HaMoreInfoClimateTemperature extends LitElement {
this._selectTargetTemperature = target; this._selectTargetTemperature = target;
} }
private _renderHvacAction() { private _renderLabel() {
if (this.stateObj.state === UNAVAILABLE) {
return html`
<p class="label disabled">
${this.hass.formatEntityState(this.stateObj, UNAVAILABLE)}
</p>
`;
}
const action = this.stateObj.attributes.hvac_action; const action = this.stateObj.attributes.hvac_action;
const actionLabel = computeAttributeValueDisplay( const actionLabel = computeAttributeValueDisplay(
@ -150,7 +159,7 @@ export class HaMoreInfoClimateTemperature extends LitElement {
) as string; ) as string;
return html` return html`
<p class="action"> <p class="label">
${action && ["preheating", "heating", "cooling"].includes(action) ${action && ["preheating", "heating", "cooling"].includes(action)
? this.hass.localize( ? this.hass.localize(
"ui.dialogs.more_info_control.climate.target_label", "ui.dialogs.more_info_control.climate.target_label",
@ -246,7 +255,7 @@ export class HaMoreInfoClimateTemperature extends LitElement {
const action = this.stateObj.attributes.hvac_action; const action = this.stateObj.attributes.hvac_action;
const active = stateActive(this.stateObj); const active = stateActive(this.stateObj);
const mainColor = stateColorCss(this.stateObj); const stateColor = stateColorCss(this.stateObj);
const lowColor = stateColorCss(this.stateObj, active ? "heat" : "off"); const lowColor = stateColorCss(this.stateObj, active ? "heat" : "off");
const highColor = stateColorCss(this.stateObj, active ? "cool" : "off"); const highColor = stateColorCss(this.stateObj, active ? "cool" : "off");
@ -260,7 +269,11 @@ export class HaMoreInfoClimateTemperature extends LitElement {
const hvacModes = this.stateObj.attributes.hvac_modes; const hvacModes = this.stateObj.attributes.hvac_modes;
if (supportsTargetTemperature && this._targetTemperature.value != null) { if (
supportsTargetTemperature &&
this._targetTemperature.value != null &&
this.stateObj.state !== UNAVAILABLE
) {
const hasOnlyCoolMode = const hasOnlyCoolMode =
hvacModes.length === 2 && hvacModes.length === 2 &&
hvacModes.includes("cool") && hvacModes.includes("cool") &&
@ -269,7 +282,7 @@ export class HaMoreInfoClimateTemperature extends LitElement {
<div <div
class="container" class="container"
style=${styleMap({ style=${styleMap({
"--main-color": mainColor, "--state-color": stateColor,
"--action-color": actionColor, "--action-color": actionColor,
})} })}
> >
@ -280,13 +293,12 @@ export class HaMoreInfoClimateTemperature extends LitElement {
.max=${this._max} .max=${this._max}
.step=${this._step} .step=${this._step}
.current=${this.stateObj.attributes.current_temperature} .current=${this.stateObj.attributes.current_temperature}
.disabled=${this.stateObj!.state === UNAVAILABLE}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@value-changing=${this._valueChanging} @value-changing=${this._valueChanging}
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info"> <div class="info">
<div class="action-container">${this._renderHvacAction()}</div> <div class="label-container">${this._renderLabel()}</div>
<div class="temperature-container"> <div class="temperature-container">
${this._renderTargetTemperature(this._targetTemperature.value)} ${this._renderTargetTemperature(this._targetTemperature.value)}
</div> </div>
@ -299,7 +311,8 @@ export class HaMoreInfoClimateTemperature extends LitElement {
if ( if (
supportsTargetTemperatureRange && supportsTargetTemperatureRange &&
this._targetTemperature.low != null && this._targetTemperature.low != null &&
this._targetTemperature.high != null this._targetTemperature.high != null &&
this.stateObj.state !== UNAVAILABLE
) { ) {
return html` return html`
<div <div
@ -312,7 +325,6 @@ export class HaMoreInfoClimateTemperature extends LitElement {
> >
<ha-control-circular-slider <ha-control-circular-slider
dual dual
.disabled=${this.stateObj!.state === UNAVAILABLE}
.low=${this._targetTemperature.low} .low=${this._targetTemperature.low}
.high=${this._targetTemperature.high} .high=${this._targetTemperature.high}
.min=${this._min} .min=${this._min}
@ -326,7 +338,7 @@ export class HaMoreInfoClimateTemperature extends LitElement {
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info"> <div class="info">
<div class="action-container">${this._renderHvacAction()}</div> <div class="label-container">${this._renderLabel()}</div>
<div class="temperature-container dual"> <div class="temperature-container dual">
<button <button
@click=${this._handleSelectTemp} @click=${this._handleSelectTemp}
@ -368,163 +380,87 @@ export class HaMoreInfoClimateTemperature extends LitElement {
disabled disabled
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info">
<div class="label-container">${this._renderLabel()}</div>
</div>
</div> </div>
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return css` return [
/* Layout */ moreInfoControlCircularSliderStyle,
.container { css`
position: relative; /* Elements */
} .temperature-container {
.info { margin-bottom: 30px;
position: absolute; }
top: 0; .temperature {
left: 0; display: inline-flex;
right: 0; font-size: 58px;
bottom: 0; line-height: 64px;
display: flex; letter-spacing: -0.25px;
flex-direction: column; margin: 0;
align-items: center; }
justify-content: center; .temperature span {
pointer-events: none; display: inline-flex;
font-size: 16px; }
line-height: 24px; .temperature .decimal {
letter-spacing: 0.1px; font-size: 24px;
} line-height: 32px;
.info * { align-self: flex-end;
margin: 0; width: 20px;
pointer-events: auto; margin-bottom: 4px;
} }
/* Elements */ .temperature .unit {
.temperature-container { font-size: 20px;
margin-bottom: 30px; line-height: 24px;
} align-self: flex-start;
.temperature { margin-left: -20px;
display: inline-flex; width: 20px;
font-size: 58px; margin-top: 4px;
line-height: 64px; }
letter-spacing: -0.25px;
margin: 0;
}
.temperature span {
display: inline-flex;
}
.temperature .unit {
font-size: 24px;
line-height: 40px;
}
.temperature .decimal {
font-size: 24px;
line-height: 40px;
align-self: flex-end;
margin-right: -18px;
}
.action-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 200px;
height: 48px;
margin-bottom: 6px;
}
.action {
font-weight: 500;
text-align: center;
color: var(--action-color, inherit);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.dual {
display: flex;
flex-direction: row;
gap: 24px;
margin-bottom: 40px;
}
.dual button { .dual {
outline: none; display: flex;
background: none; flex-direction: row;
color: inherit; gap: 24px;
font-family: inherit; margin-bottom: 40px;
-webkit-tap-highlight-color: transparent; }
border: none;
opacity: 0.5; .dual button {
padding: 0; outline: none;
transition: background: none;
opacity 180ms ease-in-out, color: inherit;
transform 180ms ease-in-out; font-family: inherit;
cursor: pointer; -webkit-tap-highlight-color: transparent;
} border: none;
.dual button:focus-visible { opacity: 0.5;
transform: scale(1.1); padding: 0;
} transition:
.dual button.selected { opacity 180ms ease-in-out,
opacity: 1; transform 180ms ease-in-out;
} cursor: pointer;
.buttons { }
position: absolute; .dual button:focus-visible {
bottom: 10px; transform: scale(1.1);
left: 0; }
right: 0; .dual button.selected {
margin: 0 auto; opacity: 1;
width: 120px; }
display: flex; /* Slider */
flex-direction: row; ha-control-circular-slider {
align-items: center; --control-circular-slider-low-color: var(
justify-content: space-between; --low-color,
} var(--disabled-color)
.buttons ha-outlined-icon-button { );
--md-outlined-icon-button-container-size: 48px; --control-circular-slider-high-color: var(
--md-outlined-icon-button-icon-size: 24px; --high-color,
} var(--disabled-color)
/* Accessibility */ );
.visually-hidden { }
position: absolute; `,
overflow: hidden; ];
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
}
/* Slider */
ha-control-circular-slider {
--control-circular-slider-color: var(
--main-color,
var(--disabled-color)
);
--control-circular-slider-low-color: var(
--low-color,
var(--disabled-color)
);
--control-circular-slider-high-color: var(
--high-color,
var(--disabled-color)
);
}
ha-control-circular-slider::after {
display: block;
content: "";
position: absolute;
top: -10%;
left: -10%;
right: -10%;
bottom: -10%;
background: radial-gradient(
50% 50% at 50% 50%,
var(--action-color, transparent) 0%,
transparent 100%
);
opacity: 0.15;
pointer-events: none;
}
`;
} }
} }

View File

@ -0,0 +1,97 @@
import { css } from "lit";
export const moreInfoControlCircularSliderStyle = css`
/* Layout elements */
.container {
position: relative;
}
.info {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none;
font-size: 16px;
line-height: 24px;
letter-spacing: 0.1px;
}
.info * {
margin: 0;
pointer-events: auto;
}
/* Info elements */
.label-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 200px;
height: 48px;
margin-bottom: 6px;
}
.label {
font-weight: 500;
text-align: center;
color: var(--action-color, inherit);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.label.disabled {
color: var(--secondary-text-color);
}
.buttons {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
margin: 0 auto;
width: 120px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.buttons ha-outlined-icon-button {
--md-outlined-icon-button-container-size: 48px;
--md-outlined-icon-button-icon-size: 24px;
}
/* Accessibility */
.visually-hidden {
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
}
/* Slider */
ha-control-circular-slider {
--control-circular-slider-color: var(--state-color, var(--disabled-color));
}
ha-control-circular-slider::after {
display: block;
content: "";
position: absolute;
top: -10%;
left: -10%;
right: -10%;
bottom: -10%;
background: radial-gradient(
50% 50% at 50% 50%,
var(--action-color, transparent) 0%,
transparent 100%
);
opacity: 0.15;
pointer-events: none;
}
`;

View File

@ -19,6 +19,7 @@ import {
HumidifierEntityDeviceClass, HumidifierEntityDeviceClass,
} from "../../../../data/humidifier"; } from "../../../../data/humidifier";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { moreInfoControlCircularSliderStyle } from "../ha-more-info-control-circular-slider-style";
@customElement("ha-more-info-humidifier-humidity") @customElement("ha-more-info-humidifier-humidity")
export class HaMoreInfoHumidifierHumidity extends LitElement { export class HaMoreInfoHumidifierHumidity extends LitElement {
@ -80,7 +81,15 @@ export class HaMoreInfoHumidifierHumidity extends LitElement {
this._debouncedCallService(); this._debouncedCallService();
} }
private _renderAction() { private _renderLabel() {
if (this.stateObj.state === UNAVAILABLE) {
return html`
<p class="label disabled">
${this.hass.formatEntityState(this.stateObj, UNAVAILABLE)}
</p>
`;
}
const action = this.stateObj.attributes.action; const action = this.stateObj.attributes.action;
const actionLabel = computeAttributeValueDisplay( const actionLabel = computeAttributeValueDisplay(
@ -93,7 +102,7 @@ export class HaMoreInfoHumidifierHumidity extends LitElement {
) as string; ) as string;
return html` return html`
<p class="action"> <p class="label">
${action && ["drying", "humidifying"].includes(action) ${action && ["drying", "humidifying"].includes(action)
? this.hass.localize( ? this.hass.localize(
"ui.dialogs.more_info_control.humidifier.target_label", "ui.dialogs.more_info_control.humidifier.target_label",
@ -145,7 +154,7 @@ export class HaMoreInfoHumidifierHumidity extends LitElement {
} }
protected render() { protected render() {
const mainColor = stateColorCss(this.stateObj); const stateColor = stateColorCss(this.stateObj);
const active = stateActive(this.stateObj); const active = stateActive(this.stateObj);
const action = this.stateObj.attributes.action; const action = this.stateObj.attributes.action;
@ -161,7 +170,7 @@ export class HaMoreInfoHumidifierHumidity extends LitElement {
const targetHumidity = this._targetHumidity; const targetHumidity = this._targetHumidity;
const currentHumidity = this.stateObj.attributes.current_humidity; const currentHumidity = this.stateObj.attributes.current_humidity;
if (targetHumidity != null) { if (targetHumidity != null && this.stateObj.state !== UNAVAILABLE) {
const inverted = const inverted =
this.stateObj.attributes.device_class === this.stateObj.attributes.device_class ===
HumidifierEntityDeviceClass.DEHUMIDIFIER; HumidifierEntityDeviceClass.DEHUMIDIFIER;
@ -170,7 +179,7 @@ export class HaMoreInfoHumidifierHumidity extends LitElement {
<div <div
class="container" class="container"
style=${styleMap({ style=${styleMap({
"--main-color": mainColor, "--state-color": stateColor,
"--action-color": actionColor, "--action-color": actionColor,
})} })}
> >
@ -181,13 +190,12 @@ export class HaMoreInfoHumidifierHumidity extends LitElement {
.max=${this._max} .max=${this._max}
.step=${this._step} .step=${this._step}
.current=${currentHumidity} .current=${currentHumidity}
.disabled=${this.stateObj.state === UNAVAILABLE}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@value-changing=${this._valueChanging} @value-changing=${this._valueChanging}
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info"> <div class="info">
<div class="action-container">${this._renderAction()}</div> <div class="label-container">${this._renderLabel()}</div>
<div class="target-container"> <div class="target-container">
${this._renderTarget(targetHumidity)} ${this._renderTarget(targetHumidity)}
</div> </div>
@ -212,118 +220,33 @@ export class HaMoreInfoHumidifierHumidity extends LitElement {
disabled disabled
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info">
<div class="label-container">${this._renderLabel()}</div>
</div>
</div> </div>
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return css` return [
/* Layout */ moreInfoControlCircularSliderStyle,
.container { css`
position: relative; /* Elements */
} .target-container {
.info { margin-bottom: 30px;
position: absolute; }
top: 0; .target .value {
left: 0; font-size: 58px;
right: 0; line-height: 1;
bottom: 0; letter-spacing: -0.25px;
display: flex; }
flex-direction: column; .target .value .unit {
align-items: center; font-size: 0.4em;
justify-content: center; line-height: 1;
pointer-events: none; margin-left: 2px;
font-size: 16px; }
line-height: 24px; `,
letter-spacing: 0.1px; ];
}
.info * {
margin: 0;
pointer-events: auto;
}
/* Elements */
.target-container {
margin-bottom: 30px;
}
.target .value {
font-size: 56px;
line-height: 1;
letter-spacing: -0.25px;
}
.target .value .unit {
font-size: 0.4em;
line-height: 1;
margin-left: 2px;
}
.action-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 200px;
height: 48px;
margin-bottom: 6px;
}
.action {
font-weight: 500;
text-align: center;
color: var(--action-color, inherit);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.buttons {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
margin: 0 auto;
width: 120px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.buttons ha-outlined-icon-button {
--md-outlined-icon-button-container-size: 48px;
--md-outlined-icon-button-icon-size: 24px;
}
/* Accessibility */
.visually-hidden {
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
}
/* Slider */
ha-control-circular-slider {
--control-circular-slider-color: var(
--main-color,
var(--disabled-color)
);
}
ha-control-circular-slider::after {
display: block;
content: "";
position: absolute;
top: -10%;
left: -10%;
right: -10%;
bottom: -10%;
background: radial-gradient(
50% 50% at 50% 50%,
var(--action-color, transparent) 0%,
transparent 100%
);
opacity: 0.15;
pointer-events: none;
}
`;
} }
} }

View File

@ -23,6 +23,7 @@ import {
WaterHeaterEntityFeature, WaterHeaterEntityFeature,
} from "../../../../data/water_heater"; } from "../../../../data/water_heater";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { moreInfoControlCircularSliderStyle } from "../ha-more-info-control-circular-slider-style";
@customElement("ha-more-info-water_heater-temperature") @customElement("ha-more-info-water_heater-temperature")
export class HaMoreInfoWaterHeaterTemperature extends LitElement { export class HaMoreInfoWaterHeaterTemperature extends LitElement {
@ -88,8 +89,16 @@ export class HaMoreInfoWaterHeaterTemperature extends LitElement {
} }
private _renderLabel() { private _renderLabel() {
if (this.stateObj.state === UNAVAILABLE) {
return html`
<p class="label disabled">
${this.hass.formatEntityState(this.stateObj, UNAVAILABLE)}
</p>
`;
}
return html` return html`
<p class="action"> <p class="label">
${this.hass.localize( ${this.hass.localize(
"ui.dialogs.more_info_control.water_heater.target" "ui.dialogs.more_info_control.water_heater.target"
)} )}
@ -153,14 +162,18 @@ export class HaMoreInfoWaterHeaterTemperature extends LitElement {
WaterHeaterEntityFeature.TARGET_TEMPERATURE WaterHeaterEntityFeature.TARGET_TEMPERATURE
); );
const mainColor = stateColorCss(this.stateObj); const stateColor = stateColorCss(this.stateObj);
if (supportsTargetTemperature && this._targetTemperature != null) { if (
supportsTargetTemperature &&
this._targetTemperature != null &&
this.stateObj.state !== UNAVAILABLE
) {
return html` return html`
<div <div
class="container" class="container"
style=${styleMap({ style=${styleMap({
"--main-color": mainColor, "--state-color": stateColor,
})} })}
> >
<ha-control-circular-slider <ha-control-circular-slider
@ -169,13 +182,12 @@ export class HaMoreInfoWaterHeaterTemperature extends LitElement {
.max=${this._max} .max=${this._max}
.step=${this._step} .step=${this._step}
.current=${this.stateObj.attributes.current_temperature} .current=${this.stateObj.attributes.current_temperature}
.disabled=${this.stateObj!.state === UNAVAILABLE}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
@value-changing=${this._valueChanging} @value-changing=${this._valueChanging}
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info"> <div class="info">
<div class="action-container">${this._renderLabel()}</div> <div class="label-container">${this._renderLabel()}</div>
<div class="temperature-container"> <div class="temperature-container">
${this._renderTargetTemperature(this._targetTemperature)} ${this._renderTargetTemperature(this._targetTemperature)}
</div> </div>
@ -195,128 +207,48 @@ export class HaMoreInfoWaterHeaterTemperature extends LitElement {
disabled disabled
> >
</ha-control-circular-slider> </ha-control-circular-slider>
<div class="info">
<div class="label-container">${this._renderLabel()}</div>
</div>
</div> </div>
`; `;
} }
static get styles(): CSSResultGroup { static get styles(): CSSResultGroup {
return css` return [
/* Layout */ moreInfoControlCircularSliderStyle,
.container { css`
position: relative; /* Elements */
} .temperature-container {
.info { margin-bottom: 30px;
position: absolute; }
top: 0; .temperature {
left: 0; display: inline-flex;
right: 0; font-size: 58px;
bottom: 0; line-height: 64px;
display: flex; letter-spacing: -0.25px;
flex-direction: column; margin: 0;
align-items: center; }
justify-content: center; .temperature span {
pointer-events: none; display: inline-flex;
font-size: 16px; }
line-height: 24px; .temperature .decimal {
letter-spacing: 0.1px; font-size: 24px;
} line-height: 32px;
.info * { align-self: flex-end;
margin: 0; width: 20px;
pointer-events: auto; margin-bottom: 4px;
} }
/* Elements */ .temperature .unit {
.temperature-container { font-size: 20px;
margin-bottom: 30px; line-height: 24px;
} align-self: flex-start;
.temperature { margin-left: -20px;
display: inline-flex; width: 20px;
font-size: 58px; margin-top: 4px;
line-height: 64px; }
letter-spacing: -0.25px; `,
margin: 0; ];
}
.temperature span {
display: inline-flex;
}
.temperature .unit {
font-size: 24px;
line-height: 40px;
}
.temperature .decimal {
font-size: 24px;
line-height: 40px;
align-self: flex-end;
margin-right: -18px;
}
.action-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 200px;
height: 48px;
margin-bottom: 6px;
}
.action {
font-weight: 500;
text-align: center;
color: var(--action-color, inherit);
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.buttons {
position: absolute;
bottom: 10px;
left: 0;
right: 0;
margin: 0 auto;
width: 120px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.buttons ha-outlined-icon-button {
--md-outlined-icon-button-container-size: 48px;
--md-outlined-icon-button-icon-size: 24px;
}
/* Accessibility */
.visually-hidden {
position: absolute;
overflow: hidden;
clip: rect(0 0 0 0);
height: 1px;
width: 1px;
margin: -1px;
padding: 0;
border: 0;
}
/* Slider */
ha-control-circular-slider {
--control-circular-slider-color: var(
--main-color,
var(--disabled-color)
);
}
ha-control-circular-slider::after {
display: block;
content: "";
position: absolute;
top: -10%;
left: -10%;
right: -10%;
bottom: -10%;
background: radial-gradient(
50% 50% at 50% 50%,
var(--action-color, transparent) 0%,
transparent 100%
);
opacity: 0.15;
pointer-events: none;
}
`;
} }
} }

View File

@ -170,6 +170,7 @@ class MoreInfoClimate extends LitElement {
<ha-control-select-menu <ha-control-select-menu
.label=${hass.localize("ui.card.climate.operation")} .label=${hass.localize("ui.card.climate.operation")}
.value=${stateObj.state} .value=${stateObj.state}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleOperationModeChanged} @selected=${this._handleOperationModeChanged}
@ -203,6 +204,7 @@ class MoreInfoClimate extends LitElement {
"preset_mode" "preset_mode"
)} )}
.value=${stateObj.attributes.preset_mode} .value=${stateObj.attributes.preset_mode}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handlePresetmodeChanged} @selected=${this._handlePresetmodeChanged}
@ -240,6 +242,7 @@ class MoreInfoClimate extends LitElement {
"fan_mode" "fan_mode"
)} )}
.value=${stateObj.attributes.fan_mode} .value=${stateObj.attributes.fan_mode}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleFanModeChanged} @selected=${this._handleFanModeChanged}
@ -277,6 +280,7 @@ class MoreInfoClimate extends LitElement {
"swing_mode" "swing_mode"
)} )}
.value=${stateObj.attributes.swing_mode} .value=${stateObj.attributes.swing_mode}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleSwingmodeChanged} @selected=${this._handleSwingmodeChanged}

View File

@ -201,6 +201,7 @@ class MoreInfoFan extends LitElement {
"preset_mode" "preset_mode"
)} )}
.value=${this.stateObj.attributes.preset_mode} .value=${this.stateObj.attributes.preset_mode}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handlePresetMode} @selected=${this._handlePresetMode}
@ -232,6 +233,7 @@ class MoreInfoFan extends LitElement {
"direction" "direction"
)} )}
.value=${this.stateObj.attributes.direction} .value=${this.stateObj.attributes.direction}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleDirection} @selected=${this._handleDirection}
@ -243,7 +245,7 @@ class MoreInfoFan extends LitElement {
? mdiRotateLeft ? mdiRotateLeft
: mdiRotateRight} : mdiRotateRight}
></ha-svg-icon> ></ha-svg-icon>
<ha-list-item .value=${"forward"} graphic="icon"> <ha-list-item value="forward" graphic="icon">
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="graphic"
.path=${mdiRotateRight} .path=${mdiRotateRight}
@ -254,7 +256,7 @@ class MoreInfoFan extends LitElement {
"forward" "forward"
)} )}
</ha-list-item> </ha-list-item>
<ha-list-item .value=${"reverse"} graphic="icon"> <ha-list-item value="reverse" graphic="icon">
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="graphic"
.path=${mdiRotateLeft} .path=${mdiRotateLeft}
@ -276,6 +278,7 @@ class MoreInfoFan extends LitElement {
"oscillating" "oscillating"
)} )}
.value=${this.stateObj.attributes.oscillating ? "on" : "off"} .value=${this.stateObj.attributes.oscillating ? "on" : "off"}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleOscillating} @selected=${this._handleOscillating}
@ -287,14 +290,14 @@ class MoreInfoFan extends LitElement {
? haOscillating ? haOscillating
: haOscillatingOff} : haOscillatingOff}
></ha-svg-icon> ></ha-svg-icon>
<ha-list-item .value=${"on"} graphic="icon"> <ha-list-item value="on" graphic="icon">
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="graphic"
.path=${haOscillating} .path=${haOscillating}
></ha-svg-icon> ></ha-svg-icon>
${this.hass.localize("state.default.on")} ${this.hass.localize("state.default.on")}
</ha-list-item> </ha-list-item>
<ha-list-item .value=${"off"} graphic="icon"> <ha-list-item value="off" graphic="icon">
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="graphic"
.path=${haOscillatingOff} .path=${haOscillatingOff}

View File

@ -19,6 +19,8 @@ import { supportsFeature } from "../../../common/entity/supports-feature";
import { formatNumber } from "../../../common/number/format_number"; import { formatNumber } from "../../../common/number/format_number";
import { blankBeforePercent } from "../../../common/translations/blank_before_percent"; import { blankBeforePercent } from "../../../common/translations/blank_before_percent";
import "../../../components/ha-control-select-menu"; import "../../../components/ha-control-select-menu";
import "../../../components/ha-list-item";
import { UNAVAILABLE } from "../../../data/entity";
import { import {
HumidifierEntity, HumidifierEntity,
HumidifierEntityFeature, HumidifierEntityFeature,
@ -95,13 +97,14 @@ class MoreInfoHumidifier extends LitElement {
<ha-control-select-menu <ha-control-select-menu
.label=${this.hass.localize("ui.card.humidifier.state")} .label=${this.hass.localize("ui.card.humidifier.state")}
.value=${this.stateObj.state} .value=${this.stateObj.state}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleStateChanged} @selected=${this._handleStateChanged}
@closed=${stopPropagation} @closed=${stopPropagation}
> >
<ha-svg-icon slot="icon" .path=${mdiPower}></ha-svg-icon> <ha-svg-icon slot="icon" .path=${mdiPower}></ha-svg-icon>
<mwc-list-item value="off"> <ha-list-item value="off">
${computeStateDisplay( ${computeStateDisplay(
this.hass.localize, this.hass.localize,
this.stateObj, this.stateObj,
@ -110,8 +113,8 @@ class MoreInfoHumidifier extends LitElement {
this.hass.entities, this.hass.entities,
"off" "off"
)} )}
</mwc-list-item> </ha-list-item>
<mwc-list-item value="on"> <ha-list-item value="on">
${computeStateDisplay( ${computeStateDisplay(
this.hass.localize, this.hass.localize,
this.stateObj, this.stateObj,
@ -120,7 +123,7 @@ class MoreInfoHumidifier extends LitElement {
this.hass.entities, this.hass.entities,
"on" "on"
)} )}
</mwc-list-item> </ha-list-item>
</ha-control-select-menu> </ha-control-select-menu>
${supportModes ${supportModes
@ -128,6 +131,7 @@ class MoreInfoHumidifier extends LitElement {
<ha-control-select-menu <ha-control-select-menu
.label=${hass.localize("ui.card.humidifier.mode")} .label=${hass.localize("ui.card.humidifier.mode")}
.value=${stateObj.attributes.mode} .value=${stateObj.attributes.mode}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleModeChanged} @selected=${this._handleModeChanged}

View File

@ -294,6 +294,7 @@ class MoreInfoLight extends LitElement {
"effect" "effect"
)} )}
.value=${this.stateObj.attributes.effect} .value=${this.stateObj.attributes.effect}
.disabled=${this.stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleEffect} @selected=${this._handleEffect}

View File

@ -16,6 +16,7 @@ import {
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { moreInfoControlStyle } from "../components/ha-more-info-control-style"; import { moreInfoControlStyle } from "../components/ha-more-info-control-style";
import "../components/water_heater/ha-more-info-water_heater-temperature"; import "../components/water_heater/ha-more-info-water_heater-temperature";
import { UNAVAILABLE } from "../../../data/entity";
@customElement("more-info-water_heater") @customElement("more-info-water_heater")
class MoreInfoWaterHeater extends LitElement { class MoreInfoWaterHeater extends LitElement {
@ -77,6 +78,7 @@ class MoreInfoWaterHeater extends LitElement {
"operation" "operation"
)} )}
.value=${stateObj.state} .value=${stateObj.state}
.disabled=${stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleOperationModeChanged} @selected=${this._handleOperationModeChanged}
@ -113,6 +115,7 @@ class MoreInfoWaterHeater extends LitElement {
"away_mode" "away_mode"
)} )}
.value=${stateObj.attributes.away_mode} .value=${stateObj.attributes.away_mode}
.disabled=${stateObj.state === UNAVAILABLE}
fixedMenuPosition fixedMenuPosition
naturalMenuWidth naturalMenuWidth
@selected=${this._handleAwayModeChanged} @selected=${this._handleAwayModeChanged}
@ -124,14 +127,14 @@ class MoreInfoWaterHeater extends LitElement {
? mdiAccountArrowRight ? mdiAccountArrowRight
: mdiAccount} : mdiAccount}
></ha-svg-icon> ></ha-svg-icon>
<ha-list-item .value=${"on"} graphic="icon"> <ha-list-item value="on" graphic="icon">
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="graphic"
.path=${mdiAccountArrowRight} .path=${mdiAccountArrowRight}
></ha-svg-icon> ></ha-svg-icon>
${this.hass.localize("state.default.on")} ${this.hass.localize("state.default.on")}
</ha-list-item> </ha-list-item>
<ha-list-item .value=${"off"} graphic="icon"> <ha-list-item value="off" graphic="icon">
<ha-svg-icon <ha-svg-icon
slot="graphic" slot="graphic"
.path=${mdiAccount} .path=${mdiAccount}