Add tooltip to tile sliders and more info sliders (#18567)

This commit is contained in:
Paul Bottein 2023-11-08 12:47:55 +01:00 committed by GitHub
parent f2505c0798
commit bc21425981
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 269 additions and 134 deletions

View File

@ -9,6 +9,7 @@ const sliders: {
id: string; id: string;
label: string; label: string;
mode?: "start" | "end" | "cursor"; mode?: "start" | "end" | "cursor";
unit?: string;
class?: string; class?: string;
}[] = [ }[] = [
{ {
@ -31,18 +32,21 @@ const sliders: {
label: "Slider (start mode) and custom style", label: "Slider (start mode) and custom style",
mode: "start", mode: "start",
class: "custom", class: "custom",
unit: "mm",
}, },
{ {
id: "slider-end-custom", id: "slider-end-custom",
label: "Slider (end mode) and custom style", label: "Slider (end mode) and custom style",
mode: "end", mode: "end",
class: "custom", class: "custom",
unit: "mm",
}, },
{ {
id: "slider-cursor-custom", id: "slider-cursor-custom",
label: "Slider (cursor mode) and custom style", label: "Slider (cursor mode) and custom style",
mode: "cursor", mode: "cursor",
class: "custom", class: "custom",
unit: "mm",
}, },
]; ];
@ -93,6 +97,7 @@ export class DemoHaBarSlider extends LitElement {
@value-changed=${this.handleValueChanged} @value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved} @slider-moved=${this.handleSliderMoved}
aria-labelledby=${id} aria-labelledby=${id}
.tooltipUnit=${config.unit}
> >
</ha-control-slider> </ha-control-slider>
</div> </div>
@ -114,6 +119,7 @@ export class DemoHaBarSlider extends LitElement {
@value-changed=${this.handleValueChanged} @value-changed=${this.handleValueChanged}
@slider-moved=${this.handleSliderMoved} @slider-moved=${this.handleSliderMoved}
aria-label=${label} aria-label=${label}
.tooltipUnit=${config.unit}
> >
</ha-control-slider> </ha-control-slider>
`; `;

View File

@ -0,0 +1,15 @@
import { FrontendLocaleData } from "../../data/translation";
import { blankBeforePercent } from "./blank_before_percent";
export const blankBeforeUnit = (
unit: string,
localeOptions?: FrontendLocaleData
): string => {
if (unit === "°") {
return "";
}
if (localeOptions && unit === "%") {
return blankBeforePercent(localeOptions);
}
return " ";
};

View File

@ -4,13 +4,17 @@ import {
CSSResultGroup, CSSResultGroup,
html, html,
LitElement, LitElement,
nothing,
PropertyValues, PropertyValues,
TemplateResult, TemplateResult,
} from "lit"; } from "lit";
import { customElement, property, query } from "lit/decorators"; import { customElement, property, query, state } from "lit/decorators";
import { classMap } from "lit/directives/class-map"; import { classMap } from "lit/directives/class-map";
import { styleMap } from "lit/directives/style-map"; import { styleMap } from "lit/directives/style-map";
import { fireEvent } from "../common/dom/fire_event"; import { fireEvent } from "../common/dom/fire_event";
import { FrontendLocaleData } from "../data/translation";
import { formatNumber } from "../common/number/format_number";
import { blankBeforeUnit } from "../common/translations/blank_before_unit";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
@ -29,13 +33,21 @@ const A11Y_KEY_CODES = new Set([
"End", "End",
]); ]);
type TooltipPosition = "top" | "bottom" | "left" | "right";
type TooltipMode = "never" | "always" | "interaction";
type SliderMode = "start" | "end" | "cursor";
@customElement("ha-control-slider") @customElement("ha-control-slider")
export class HaControlSlider extends LitElement { export class HaControlSlider extends LitElement {
@property({ attribute: false }) public locale?: FrontendLocaleData;
@property({ type: Boolean, reflect: true }) @property({ type: Boolean, reflect: true })
public disabled = false; public disabled = false;
@property() @property()
public mode?: "start" | "end" | "cursor" = "start"; public mode?: SliderMode = "start";
@property({ type: Boolean, reflect: true }) @property({ type: Boolean, reflect: true })
public vertical = false; public vertical = false;
@ -46,6 +58,15 @@ export class HaControlSlider extends LitElement {
@property({ type: Boolean, attribute: "inverted" }) @property({ type: Boolean, attribute: "inverted" })
public inverted = false; public inverted = false;
@property({ attribute: "tooltip-position" })
public tooltipPosition?: TooltipPosition;
@property({ attribute: "tooltip-unit" })
public tooltipUnit?: string;
@property({ attribute: "tooltip-mode" })
public tooltipMode: TooltipMode = "interaction";
@property({ type: Number }) @property({ type: Number })
public value?: number; public value?: number;
@ -58,11 +79,14 @@ export class HaControlSlider extends LitElement {
@property({ type: Number }) @property({ type: Number })
public max = 100; public max = 100;
private _mc?: HammerManager; @state()
@property({ type: Boolean, reflect: true })
public pressed = false; public pressed = false;
@state()
public tooltipVisible = false;
private _mc?: HammerManager;
valueToPercentage(value: number) { valueToPercentage(value: number) {
const percentage = const percentage =
(this.boundedValue(value) - this.min) / (this.max - this.min); (this.boundedValue(value) - this.min) / (this.max - this.min);
@ -98,6 +122,7 @@ export class HaControlSlider extends LitElement {
if (changedProps.has("value")) { if (changedProps.has("value")) {
const valuenow = this.steppedValue(this.value ?? 0); const valuenow = this.steppedValue(this.value ?? 0);
this.setAttribute("aria-valuenow", valuenow.toString()); this.setAttribute("aria-valuenow", valuenow.toString());
this.setAttribute("aria-valuetext", this._formatValue(valuenow));
} }
if (changedProps.has("min")) { if (changedProps.has("min")) {
this.setAttribute("aria-valuemin", this.min.toString()); this.setAttribute("aria-valuemin", this.min.toString());
@ -143,11 +168,13 @@ export class HaControlSlider extends LitElement {
this._mc.on("panstart", () => { this._mc.on("panstart", () => {
if (this.disabled) return; if (this.disabled) return;
this.pressed = true; this.pressed = true;
this._showTooltip();
savedValue = this.value; savedValue = this.value;
}); });
this._mc.on("pancancel", () => { this._mc.on("pancancel", () => {
if (this.disabled) return; if (this.disabled) return;
this.pressed = false; this.pressed = false;
this._hideTooltip();
this.value = savedValue; this.value = savedValue;
}); });
this._mc.on("panmove", (e) => { this._mc.on("panmove", (e) => {
@ -160,6 +187,7 @@ export class HaControlSlider extends LitElement {
this._mc.on("panend", (e) => { this._mc.on("panend", (e) => {
if (this.disabled) return; if (this.disabled) return;
this.pressed = false; this.pressed = false;
this._hideTooltip();
const percentage = this._getPercentageFromEvent(e); const percentage = this._getPercentageFromEvent(e);
this.value = this.steppedValue(this.percentageToValue(percentage)); this.value = this.steppedValue(this.percentageToValue(percentage));
fireEvent(this, "slider-moved", { value: undefined }); fireEvent(this, "slider-moved", { value: undefined });
@ -191,6 +219,21 @@ export class HaControlSlider extends LitElement {
return Math.max(this.step, (this.max - this.min) / 10); return Math.max(this.step, (this.max - this.min) / 10);
} }
_showTooltip() {
if (this._tooltipTimeout != null) window.clearTimeout(this._tooltipTimeout);
this.tooltipVisible = true;
}
_hideTooltip(delay?: number) {
if (!delay) {
this.tooltipVisible = false;
return;
}
this._tooltipTimeout = window.setTimeout(() => {
this.tooltipVisible = false;
}, delay);
}
_handleKeyDown(e: KeyboardEvent) { _handleKeyDown(e: KeyboardEvent) {
if (!A11Y_KEY_CODES.has(e.code)) return; if (!A11Y_KEY_CODES.has(e.code)) return;
e.preventDefault(); e.preventDefault();
@ -220,12 +263,16 @@ export class HaControlSlider extends LitElement {
this.value = this.max; this.value = this.max;
break; break;
} }
this._showTooltip();
fireEvent(this, "slider-moved", { value: this.value }); fireEvent(this, "slider-moved", { value: this.value });
} }
private _tooltipTimeout?: number;
_handleKeyUp(e: KeyboardEvent) { _handleKeyUp(e: KeyboardEvent) {
if (!A11Y_KEY_CODES.has(e.code)) return; if (!A11Y_KEY_CODES.has(e.code)) return;
e.preventDefault(); e.preventDefault();
this._hideTooltip(500);
fireEvent(this, "value-changed", { value: this.value }); fireEvent(this, "value-changed", { value: this.value });
} }
@ -242,36 +289,76 @@ export class HaControlSlider extends LitElement {
return Math.max(Math.min(1, (x - offset) / total), 0); return Math.max(Math.min(1, (x - offset) / total), 0);
}; };
private _formatValue(value: number) {
const formattedValue = formatNumber(value, this.locale);
const formattedUnit = this.tooltipUnit
? `${blankBeforeUnit(this.tooltipUnit, this.locale)}${this.tooltipUnit}`
: "";
return `${formattedValue}${formattedUnit}`;
}
private _renderTooltip() {
if (this.tooltipMode === "never") return nothing;
const position = this.tooltipPosition ?? (this.vertical ? "left" : "top");
const visible =
this.tooltipMode === "always" ||
(this.tooltipVisible && this.tooltipMode === "interaction");
const value = this.steppedValue(this.value ?? 0);
return html`
<span
aria-hidden="true"
class="tooltip ${classMap({
visible,
[position]: true,
[this.mode ?? "start"]: true,
"show-handle": this.showHandle,
})}"
>
${this._formatValue(value)}
</span>
`;
}
protected render(): TemplateResult { protected render(): TemplateResult {
return html` return html`
<div <div
id="slider" class="container${classMap({
class="slider" pressed: this.pressed,
})}"
style=${styleMap({ style=${styleMap({
"--value": `${this.valueToPercentage(this.value ?? 0)}`, "--value": `${this.valueToPercentage(this.value ?? 0)}`,
})} })}
> >
<div class="slider-track-background"></div> <div id="slider" class="slider">
<slot name="background"></slot> <div class="slider-track-background"></div>
${this.mode === "cursor" <slot name="background"></slot>
? this.value != null ${this.mode === "cursor"
? html` ? this.value != null
? html`
<div
class=${classMap({
"slider-track-cursor": true,
})}
></div>
`
: null
: html`
<div <div
class=${classMap({ class=${classMap({
"slider-track-cursor": true, "slider-track-bar": true,
[this.mode ?? "start"]: true,
"show-handle": this.showHandle,
})} })}
></div> ></div>
` `}
: null </div>
: html` ${this._renderTooltip()}
<div
class=${classMap({
"slider-track-bar": true,
[this.mode ?? "start"]: true,
"show-handle": this.showHandle,
})}
></div>
`}
</div> </div>
`; `;
} }
@ -285,6 +372,7 @@ export class HaControlSlider extends LitElement {
--control-slider-background-opacity: 0.2; --control-slider-background-opacity: 0.2;
--control-slider-thickness: 40px; --control-slider-thickness: 40px;
--control-slider-border-radius: 10px; --control-slider-border-radius: 10px;
--control-slider-tooltip-font-size: 14px;
height: var(--control-slider-thickness); height: var(--control-slider-thickness);
width: 100%; width: 100%;
border-radius: var(--control-slider-border-radius); border-radius: var(--control-slider-border-radius);
@ -298,6 +386,88 @@ export class HaControlSlider extends LitElement {
width: var(--control-slider-thickness); width: var(--control-slider-thickness);
height: 100%; height: 100%;
} }
.container {
position: relative;
height: 100%;
width: 100%;
--handle-size: 4px;
--handle-margin: calc(var(--control-slider-thickness) / 8);
}
.tooltip {
user-select: none;
position: absolute;
background-color: var(--clear-background-color);
color: var(--primary-text-color);
font-size: var(--control-slider-tooltip-font-size);
border-radius: 0.8em;
padding: 0.2em 0.4em;
opacity: 0;
white-space: nowrap;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
transition:
opacity 180ms ease-in-out,
left 180ms ease-in-out,
bottom 180ms ease-in-out;
--handle-spacing: calc(2 * var(--handle-margin) + var(--handle-size));
--slider-tooltip-margin: -4px;
--slider-tooltip-range: 100%;
--slider-tooltip-offset: 0px;
--slider-tooltip-position: calc(
min(
max(
var(--value) * var(--slider-tooltip-range) +
var(--slider-tooltip-offset),
0%
),
100%
)
);
}
.tooltip.start {
--slider-tooltip-offset: calc(-0.5 * (var(--handle-spacing)));
}
.tooltip.end {
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
}
.tooltip.cursor {
--slider-tooltip-range: calc(100% - var(--handle-spacing));
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
}
.tooltip.show-handle {
--slider-tooltip-range: calc(100% - var(--handle-spacing));
--slider-tooltip-offset: calc(0.5 * (var(--handle-spacing)));
}
.tooltip.visible {
opacity: 1;
}
.tooltip.top {
transform: translate3d(-50%, -100%, 0);
top: var(--slider-tooltip-margin);
left: 50%;
}
.tooltip.bottom {
transform: translate3d(-50%, 100%, 0);
bottom: var(--slider-tooltip-margin);
left: 50%;
}
.tooltip.left {
transform: translate3d(-100%, 50%, 0);
bottom: 50%;
left: var(--slider-tooltip-margin);
}
.tooltip.right {
transform: translate3d(100%, 50%, 0);
bottom: 50%;
right: var(--slider-tooltip-margin);
}
:host(:not([vertical])) .tooltip.top,
:host(:not([vertical])) .tooltip.bottom {
left: var(--slider-tooltip-position);
}
:host([vertical]) .tooltip.right,
:host([vertical]) .tooltip.left {
bottom: var(--slider-tooltip-position);
}
.slider { .slider {
position: relative; position: relative;
height: 100%; height: 100%;
@ -328,8 +498,6 @@ export class HaControlSlider extends LitElement {
} }
.slider .slider-track-bar { .slider .slider-track-bar {
--border-radius: var(--control-slider-border-radius); --border-radius: var(--control-slider-border-radius);
--handle-size: 4px;
--handle-margin: calc(var(--control-slider-thickness) / 8);
--slider-size: 100%; --slider-size: 100%;
position: absolute; position: absolute;
height: 100%; height: 100%;
@ -432,7 +600,6 @@ export class HaControlSlider extends LitElement {
.slider .slider-track-cursor { .slider .slider-track-cursor {
--cursor-size: calc(var(--control-slider-thickness) / 4); --cursor-size: calc(var(--control-slider-thickness) / 4);
--handle-size: 4px;
position: absolute; position: absolute;
background-color: white; background-color: white;
border-radius: var(--handle-size); border-radius: var(--handle-size);
@ -462,9 +629,11 @@ export class HaControlSlider extends LitElement {
height: var(--handle-size); height: var(--handle-size);
width: 50%; width: 50%;
} }
.pressed .tooltip {
:host([pressed]) .slider-track-bar, transition: opacity 180ms ease-in-out;
:host([pressed]) .slider-track-cursor { }
.pressed .slider-track-bar,
.pressed .slider-track-cursor {
transition: none; transition: none;
} }
:host(:disabled) .slider { :host(:disabled) .slider {

View File

@ -34,7 +34,7 @@ export const TEMPERATURE_ATTRIBUTES = new Set([
"max_temp", "max_temp",
]); ]);
export const DOMAIN_ATTRIBUTES_UNITS: Record<string, Record<string, string>> = { export const DOMAIN_ATTRIBUTES_UNITS = {
climate: { climate: {
humidity: "%", humidity: "%",
current_humidity: "%", current_humidity: "%",
@ -74,4 +74,4 @@ export const DOMAIN_ATTRIBUTES_UNITS: Record<string, Record<string, string>> = {
sensor: { sensor: {
battery_level: "%", battery_level: "%",
}, },
}; } as const satisfies Record<string, Record<string, string>>;

View File

@ -6,6 +6,7 @@ import { stateColorCss } from "../../../../common/entity/state_color";
import "../../../../components/ha-control-slider"; import "../../../../components/ha-control-slider";
import { CoverEntity } from "../../../../data/cover"; import { CoverEntity } from "../../../../data/cover";
import { UNAVAILABLE } from "../../../../data/entity"; import { UNAVAILABLE } from "../../../../data/entity";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
@customElement("ha-more-info-cover-position") @customElement("ha-more-info-cover-position")
@ -60,6 +61,8 @@ export class HaMoreInfoCoverPosition extends LitElement {
"--control-slider-background": color, "--control-slider-background": color,
})} })}
.disabled=${this.stateObj.state === UNAVAILABLE} .disabled=${this.stateObj.state === UNAVAILABLE}
.tooltipUnit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_position}
.locale=${this.hass.locale}
> >
</ha-control-slider> </ha-control-slider>
`; `;
@ -76,6 +79,7 @@ export class HaMoreInfoCoverPosition extends LitElement {
--control-slider-color: var(--primary-color); --control-slider-color: var(--primary-color);
--control-slider-background: var(--disabled-color); --control-slider-background: var(--disabled-color);
--control-slider-background-opacity: 0.2; --control-slider-background-opacity: 0.2;
--control-slider-tooltip-font-size: 20px;
} }
`; `;
} }

View File

@ -13,6 +13,7 @@ import { stateColorCss } from "../../../../common/entity/state_color";
import "../../../../components/ha-control-slider"; import "../../../../components/ha-control-slider";
import { CoverEntity } from "../../../../data/cover"; import { CoverEntity } from "../../../../data/cover";
import { UNAVAILABLE } from "../../../../data/entity"; import { UNAVAILABLE } from "../../../../data/entity";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
export function generateTiltSliderTrackBackgroundGradient() { export function generateTiltSliderTrackBackgroundGradient() {
@ -96,6 +97,8 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
"--control-slider-background": color, "--control-slider-background": color,
})} })}
.disabled=${this.stateObj.state === UNAVAILABLE} .disabled=${this.stateObj.state === UNAVAILABLE}
.tooltipUnit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_tilt_position}
.locale=${this.hass.locale}
> >
<div slot="background" class="gradient"></div> <div slot="background" class="gradient"></div>
</ha-control-slider> </ha-control-slider>
@ -113,6 +116,7 @@ export class HaMoreInfoCoverTiltPosition extends LitElement {
--control-slider-color: var(--primary-color); --control-slider-color: var(--primary-color);
--control-slider-background: var(--disabled-color); --control-slider-background: var(--disabled-color);
--control-slider-background-opacity: 0.2; --control-slider-background-opacity: 0.2;
--control-slider-tooltip-font-size: 20px;
} }
.gradient { .gradient {
background: -webkit-linear-gradient(top, ${GRADIENT}); background: -webkit-linear-gradient(top, ${GRADIENT});

View File

@ -8,6 +8,7 @@ import "../../../../components/ha-control-select";
import type { ControlSelectOption } from "../../../../components/ha-control-select"; import type { ControlSelectOption } from "../../../../components/ha-control-select";
import "../../../../components/ha-control-slider"; import "../../../../components/ha-control-slider";
import { UNAVAILABLE } from "../../../../data/entity"; import { UNAVAILABLE } from "../../../../data/entity";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
import { import {
computeFanSpeedCount, computeFanSpeedCount,
computeFanSpeedIcon, computeFanSpeedIcon,
@ -130,6 +131,8 @@ export class HaMoreInfoFanSpeed extends LitElement {
"--control-slider-background": color, "--control-slider-background": color,
})} })}
.disabled=${this.stateObj.state === UNAVAILABLE} .disabled=${this.stateObj.state === UNAVAILABLE}
.tooltipUnit=${DOMAIN_ATTRIBUTES_UNITS.fan.percentage}
.locale=${this.hass.locale}
> >
</ha-control-slider> </ha-control-slider>
`; `;
@ -146,6 +149,7 @@ export class HaMoreInfoFanSpeed extends LitElement {
--control-slider-color: var(--primary-color); --control-slider-color: var(--primary-color);
--control-slider-background: var(--disabled-color); --control-slider-background: var(--disabled-color);
--control-slider-background-opacity: 0.2; --control-slider-background-opacity: 0.2;
--control-slider-tooltip-font-size: 20px;
} }
ha-control-select { ha-control-select {
height: 45vh; height: 45vh;

View File

@ -77,6 +77,8 @@ export class HaMoreInfoLightBrightness extends LitElement {
"--control-slider-background": color, "--control-slider-background": color,
})} })}
.disabled=${this.stateObj.state === UNAVAILABLE} .disabled=${this.stateObj.state === UNAVAILABLE}
.tooltipUnit=${"%"}
.locale=${this.hass.locale}
> >
</ha-control-slider> </ha-control-slider>
`; `;
@ -93,6 +95,7 @@ export class HaMoreInfoLightBrightness extends LitElement {
--control-slider-color: var(--primary-color); --control-slider-color: var(--primary-color);
--control-slider-background: var(--disabled-color); --control-slider-background: var(--disabled-color);
--control-slider-background-opacity: 0.2; --control-slider-background-opacity: 0.2;
--control-slider-tooltip-font-size: 20px;
} }
`; `;
} }

View File

@ -26,6 +26,7 @@ import {
LightEntity, LightEntity,
} from "../../../../data/light"; } from "../../../../data/light";
import { HomeAssistant } from "../../../../types"; import { HomeAssistant } from "../../../../types";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../../data/entity_attributes";
declare global { declare global {
interface HASSDomEvents { interface HASSDomEvents {
@ -93,6 +94,8 @@ class LightColorTempPicker extends LitElement {
"--gradient": gradient, "--gradient": gradient,
})} })}
.disabled=${this.stateObj.state === UNAVAILABLE} .disabled=${this.stateObj.state === UNAVAILABLE}
.tooltipUnit=${DOMAIN_ATTRIBUTES_UNITS.light.color_temp_kelvin}
.locale=${this.hass.locale}
> >
</ha-control-slider> </ha-control-slider>
`; `;
@ -193,6 +196,7 @@ class LightColorTempPicker extends LitElement {
top, top,
var(--gradient) var(--gradient)
); );
--control-slider-tooltip-font-size: 20px;
--control-slider-background-opacity: 1; --control-slider-background-opacity: 1;
} }
`, `,

View File

@ -33,36 +33,12 @@ class MoreInfoCover extends LitElement {
@property({ attribute: false }) public stateObj?: CoverEntity; @property({ attribute: false }) public stateObj?: CoverEntity;
@state() private _livePosition?: number;
@state() private _liveTilt?: number;
@state() private _mode?: Mode; @state() private _mode?: Mode;
private _setMode(ev) { private _setMode(ev) {
this._mode = ev.currentTarget.mode; this._mode = ev.currentTarget.mode;
} }
private _positionSliderMoved(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._livePosition = value;
}
private _positionValueChanged() {
this._livePosition = undefined;
}
private _tiltSliderMoved(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._liveTilt = value;
}
private _tiltValueChanged() {
this._liveTilt = undefined;
}
protected willUpdate(changedProps: PropertyValues): void { protected willUpdate(changedProps: PropertyValues): void {
super.willUpdate(changedProps); super.willUpdate(changedProps);
if (changedProps.has("stateObj") && this.stateObj) { if (changedProps.has("stateObj") && this.stateObj) {
@ -77,20 +53,11 @@ class MoreInfoCover extends LitElement {
} }
private get _stateOverride() { private get _stateOverride() {
const liveValue = this._livePosition ?? this._liveTilt; const stateDisplay = this.hass.formatEntityState(this.stateObj!);
const forcedState =
liveValue != null ? (liveValue ? "open" : "closed") : undefined;
const stateDisplay = this.hass.formatEntityState(
this.stateObj!,
forcedState
);
const positionStateDisplay = computeCoverPositionStateDisplay( const positionStateDisplay = computeCoverPositionStateDisplay(
this.stateObj!, this.stateObj!,
this.hass, this.hass
liveValue
); );
if (positionStateDisplay) { if (positionStateDisplay) {
@ -147,8 +114,6 @@ class MoreInfoCover extends LitElement {
<ha-more-info-cover-position <ha-more-info-cover-position
.stateObj=${this.stateObj} .stateObj=${this.stateObj}
.hass=${this.hass} .hass=${this.hass}
@slider-moved=${this._positionSliderMoved}
@value-changed=${this._positionValueChanged}
></ha-more-info-cover-position> ></ha-more-info-cover-position>
` `
: nothing} : nothing}
@ -157,8 +122,6 @@ class MoreInfoCover extends LitElement {
<ha-more-info-cover-tilt-position <ha-more-info-cover-tilt-position
.stateObj=${this.stateObj} .stateObj=${this.stateObj}
.hass=${this.hass} .hass=${this.hass}
@slider-moved=${this._tiltSliderMoved}
@value-changed=${this._tiltValueChanged}
></ha-more-info-cover-tilt-position> ></ha-more-info-cover-tilt-position>
` `
: nothing} : nothing}

View File

@ -40,18 +40,6 @@ class MoreInfoFan extends LitElement {
@state() public _presetMode?: string; @state() public _presetMode?: string;
@state() private _liveSpeed?: number;
private _speedSliderMoved(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._liveSpeed = value;
}
private _speedValueChanged() {
this._liveSpeed = undefined;
}
private _toggle = () => { private _toggle = () => {
const service = this.stateObj?.state === "on" ? "turn_off" : "turn_on"; const service = this.stateObj?.state === "on" ? "turn_off" : "turn_on";
forwardHaptic("light"); forwardHaptic("light");
@ -104,23 +92,14 @@ class MoreInfoFan extends LitElement {
} }
private get _stateOverride() { private get _stateOverride() {
const liveValue = this._liveSpeed; const stateDisplay = this.hass.formatEntityState(this.stateObj!);
const forcedState =
liveValue != null ? (liveValue ? "on" : "off") : undefined;
const stateDisplay = this.hass.formatEntityState(
this.stateObj!,
forcedState
);
const positionStateDisplay = computeFanSpeedStateDisplay( const positionStateDisplay = computeFanSpeedStateDisplay(
this.stateObj!, this.stateObj!,
this.hass, this.hass
liveValue
); );
if (positionStateDisplay && (stateActive(this.stateObj!) || liveValue)) { if (positionStateDisplay && stateActive(this.stateObj!)) {
return positionStateDisplay; return positionStateDisplay;
} }
return stateDisplay; return stateDisplay;
@ -165,8 +144,6 @@ class MoreInfoFan extends LitElement {
<ha-more-info-fan-speed <ha-more-info-fan-speed
.stateObj=${this.stateObj} .stateObj=${this.stateObj}
.hass=${this.hass} .hass=${this.hass}
@slider-moved=${this._speedSliderMoved}
@value-changed=${this._speedValueChanged}
> >
</ha-more-info-fan-speed> </ha-more-info-fan-speed>
` `

View File

@ -60,29 +60,10 @@ class MoreInfoLight extends LitElement {
@state() private _effect?: string; @state() private _effect?: string;
@state() private _selectedBrightness?: number;
@state() private _colorTempPreview?: number;
@state() private _mainControl: MainControl = "brightness"; @state() private _mainControl: MainControl = "brightness";
private _brightnessChanged(ev) {
const value = (ev.detail as any).value;
if (isNaN(value)) return;
this._selectedBrightness = (value * 255) / 100;
}
private _tempColorHovered(ev: CustomEvent<HASSDomEvents["color-hovered"]>) {
if (ev.detail && "color_temp_kelvin" in ev.detail) {
this._colorTempPreview = ev.detail.color_temp_kelvin;
} else {
this._colorTempPreview = undefined;
}
}
protected updated(changedProps: PropertyValues<typeof this>): void { protected updated(changedProps: PropertyValues<typeof this>): void {
if (changedProps.has("stateObj")) { if (changedProps.has("stateObj")) {
this._selectedBrightness = this.stateObj?.attributes.brightness;
this._effect = this.stateObj?.attributes.effect; this._effect = this.stateObj?.attributes.effect;
} }
} }
@ -98,19 +79,8 @@ class MoreInfoLight extends LitElement {
} }
private get _stateOverride() { private get _stateOverride() {
if (this._colorTempPreview) { if (this.stateObj?.attributes.brightness) {
return this.hass.formatEntityAttributeValue( return this.hass.formatEntityAttributeValue(this.stateObj!, "brightness");
this.stateObj!,
"color_temp_kelvin",
this._colorTempPreview
);
}
if (this._selectedBrightness) {
return this.hass.formatEntityAttributeValue(
this.stateObj!,
"brightness",
this._selectedBrightness
);
} }
return undefined; return undefined;
} }
@ -168,7 +138,6 @@ class MoreInfoLight extends LitElement {
<ha-more-info-light-brightness <ha-more-info-light-brightness
.stateObj=${this.stateObj} .stateObj=${this.stateObj}
.hass=${this.hass} .hass=${this.hass}
@slider-moved=${this._brightnessChanged}
> >
</ha-more-info-light-brightness> </ha-more-info-light-brightness>
` `
@ -187,7 +156,6 @@ class MoreInfoLight extends LitElement {
<light-color-temp-picker <light-color-temp-picker
.hass=${this.hass} .hass=${this.hass}
.stateObj=${this.stateObj} .stateObj=${this.stateObj}
@color-hovered=${this._tempColorHovered}
> >
</light-color-temp-picker> </light-color-temp-picker>
` `

View File

@ -350,7 +350,6 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
return html` return html`
<ha-card style=${styleMap(style)} class=${classMap({ active })}> <ha-card style=${styleMap(style)} class=${classMap({ active })}>
${this._shouldRenderRipple ? html`<mwc-ripple></mwc-ripple>` : nothing}
<div class="tile"> <div class="tile">
<div <div
class="background" class="background"
@ -366,7 +365,11 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
@touchstart=${this.handleRippleActivate} @touchstart=${this.handleRippleActivate}
@touchend=${this.handleRippleDeactivate} @touchend=${this.handleRippleDeactivate}
@touchcancel=${this.handleRippleDeactivate} @touchcancel=${this.handleRippleDeactivate}
></div> >
${this._shouldRenderRipple
? html`<mwc-ripple></mwc-ripple>`
: nothing}
</div>
<div class="content ${classMap(contentClasses)}"> <div class="content ${classMap(contentClasses)}">
<div <div
class="icon-container" class="icon-container"
@ -437,7 +440,6 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
ha-card { ha-card {
--mdc-ripple-color: var(--tile-color); --mdc-ripple-color: var(--tile-color);
height: 100%; height: 100%;
overflow: hidden;
transition: transition:
box-shadow 180ms ease-in-out, box-shadow 180ms ease-in-out,
border-color 180ms ease-in-out; border-color 180ms ease-in-out;
@ -457,6 +459,8 @@ export class HuiTileCard extends LitElement implements LovelaceCard {
left: 0; left: 0;
bottom: 0; bottom: 0;
right: 0; right: 0;
border-radius: var(--ha-card-border-radius, 12px);
overflow: hidden;
} }
.content { .content {
display: flex; display: flex;

View File

@ -13,6 +13,7 @@ import { UNAVAILABLE } from "../../../data/entity";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceTileFeature } from "../types"; import { LovelaceTileFeature } from "../types";
import { CoverPositionTileFeatureConfig } from "./types"; import { CoverPositionTileFeatureConfig } from "./types";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity_attributes";
export const supportsCoverPositionTileFeature = (stateObj: HassEntity) => { export const supportsCoverPositionTileFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id); const domain = computeDomain(stateObj.entity_id);
@ -93,6 +94,8 @@ class HuiCoverPositionTileFeature
"current_position" "current_position"
)} )}
.disabled=${this.stateObj!.state === UNAVAILABLE} .disabled=${this.stateObj!.state === UNAVAILABLE}
.tooltipUnit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_position}
.locale=${this.hass.locale}
></ha-control-slider> ></ha-control-slider>
</div> </div>
`; `;

View File

@ -13,6 +13,7 @@ import { generateTiltSliderTrackBackgroundGradient } from "../../../dialogs/more
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceTileFeature } from "../types"; import { LovelaceTileFeature } from "../types";
import { CoverTiltPositionTileFeatureConfig } from "./types"; import { CoverTiltPositionTileFeatureConfig } from "./types";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity_attributes";
const GRADIENT = generateTiltSliderTrackBackgroundGradient(); const GRADIENT = generateTiltSliderTrackBackgroundGradient();
@ -92,6 +93,8 @@ class HuiCoverTiltPositionTileFeature
"current_tilt_position" "current_tilt_position"
)} )}
.disabled=${this.stateObj!.state === UNAVAILABLE} .disabled=${this.stateObj!.state === UNAVAILABLE}
.tooltipUnit=${DOMAIN_ATTRIBUTES_UNITS.cover.current_tilt_position}
.locale=${this.hass.locale}
> >
<div slot="background" class="gradient"></div <div slot="background" class="gradient"></div
></ha-control-slider> ></ha-control-slider>

View File

@ -23,6 +23,7 @@ import {
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
import { LovelaceTileFeature } from "../types"; import { LovelaceTileFeature } from "../types";
import { FanSpeedTileFeatureConfig } from "./types"; import { FanSpeedTileFeatureConfig } from "./types";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity_attributes";
export const supportsFanSpeedTileFeature = (stateObj: HassEntity) => { export const supportsFanSpeedTileFeature = (stateObj: HassEntity) => {
const domain = computeDomain(stateObj.entity_id); const domain = computeDomain(stateObj.entity_id);
@ -126,6 +127,8 @@ class HuiFanSpeedTileFeature extends LitElement implements LovelaceTileFeature {
"percentage" "percentage"
)} )}
.disabled=${this.stateObj!.state === UNAVAILABLE} .disabled=${this.stateObj!.state === UNAVAILABLE}
.tooltipUnit=${DOMAIN_ATTRIBUTES_UNITS.fan.percentage}
.locale=${this.hass.locale}
></ha-control-slider> ></ha-control-slider>
</div> </div>
`; `;

View File

@ -67,6 +67,8 @@ class HuiLightBrightnessTileFeature
.disabled=${this.stateObj!.state === UNAVAILABLE} .disabled=${this.stateObj!.state === UNAVAILABLE}
@value-changed=${this._valueChanged} @value-changed=${this._valueChanged}
.label=${this.hass.localize("ui.card.light.brightness")} .label=${this.hass.localize("ui.card.light.brightness")}
.tooltipUnit=${"%"}
.locale=${this.hass.locale}
></ha-control-slider> ></ha-control-slider>
</div> </div>
`; `;

View File

@ -11,6 +11,7 @@ import { computeDomain } from "../../../common/entity/compute_domain";
import { stateActive } from "../../../common/entity/state_active"; import { stateActive } from "../../../common/entity/state_active";
import "../../../components/ha-control-slider"; import "../../../components/ha-control-slider";
import { UNAVAILABLE } from "../../../data/entity"; import { UNAVAILABLE } from "../../../data/entity";
import { DOMAIN_ATTRIBUTES_UNITS } from "../../../data/entity_attributes";
import { LightColorMode, lightSupportsColorMode } from "../../../data/light"; import { LightColorMode, lightSupportsColorMode } from "../../../data/light";
import { generateColorTemperatureGradient } from "../../../dialogs/more-info/components/lights/light-color-temp-picker"; import { generateColorTemperatureGradient } from "../../../dialogs/more-info/components/lights/light-color-temp-picker";
import { HomeAssistant } from "../../../types"; import { HomeAssistant } from "../../../types";
@ -85,6 +86,8 @@ class HuiLightColorTempTileFeature
style=${styleMap({ style=${styleMap({
"--gradient": gradient, "--gradient": gradient,
})} })}
.tooltipUnit=${DOMAIN_ATTRIBUTES_UNITS.light.color_temp_kelvin}
.locale=${this.hass.locale}
></ha-control-slider> ></ha-control-slider>
</div> </div>
`; `;