From 7d335d7d852ea0e25438a82cad51aba4d7ca32f3 Mon Sep 17 00:00:00 2001 From: Philip Allgaier Date: Wed, 26 Jan 2022 17:50:50 +0100 Subject: [PATCH] Convert `ha-climate-control` ot Lit and add tooltips to buttons (#10921) Co-authored-by: Zack Barett --- src/common/number/clamp.ts | 8 + src/components/ha-climate-control.js | 141 ------------------ src/components/ha-climate-control.ts | 138 +++++++++++++++++ .../more-info/controls/more-info-climate.ts | 9 +- src/translations/en.json | 4 + 5 files changed, 156 insertions(+), 144 deletions(-) delete mode 100644 src/components/ha-climate-control.js create mode 100644 src/components/ha-climate-control.ts diff --git a/src/common/number/clamp.ts b/src/common/number/clamp.ts index 4368d20add..3b2488afe8 100644 --- a/src/common/number/clamp.ts +++ b/src/common/number/clamp.ts @@ -1,2 +1,10 @@ export const clamp = (value: number, min: number, max: number) => Math.min(Math.max(value, min), max); + +// Variant that only applies the clamping to a border if the border is defined +export const conditionalClamp = (value: number, min?: number, max?: number) => { + let result: number; + result = min ? Math.max(value, min) : value; + result = max ? Math.min(value, max) : value; + return result; +}; diff --git a/src/components/ha-climate-control.js b/src/components/ha-climate-control.js deleted file mode 100644 index 39398a8cd6..0000000000 --- a/src/components/ha-climate-control.js +++ /dev/null @@ -1,141 +0,0 @@ -import "@polymer/iron-flex-layout/iron-flex-layout-classes"; -import { html } from "@polymer/polymer/lib/utils/html-tag"; -/* eslint-plugin-disable lit */ -import { PolymerElement } from "@polymer/polymer/polymer-element"; -import { EventsMixin } from "../mixins/events-mixin"; -import "./ha-icon"; -import "./ha-icon-button"; - -/* - * @appliesMixin EventsMixin - */ -class HaClimateControl extends EventsMixin(PolymerElement) { - static get template() { - return html` - - - - -
[[value]] [[units]]
-
-
- - - -
-
- - - -
-
- `; - } - - static get properties() { - return { - value: { - type: Number, - observer: "valueChanged", - }, - units: { - type: String, - }, - min: { - type: Number, - }, - max: { - type: Number, - }, - step: { - type: Number, - value: 1, - }, - }; - } - - temperatureStateInFlux(inFlux) { - this.$.target_temperature.classList.toggle("in-flux", inFlux); - } - - _round(val) { - // round value to precision derived from step - // insired by https://github.com/soundar24/roundSlider/blob/master/src/roundslider.js - const s = this.step.toString().split("."); - return s[1] ? parseFloat(val.toFixed(s[1].length)) : Math.round(val); - } - - incrementValue() { - const newval = this._round(this.value + this.step); - if (this.value < this.max) { - this.last_changed = Date.now(); - this.temperatureStateInFlux(true); - } - if (newval <= this.max) { - // If no initial target_temp - // this forces control to start - // from the min configured instead of 0 - if (newval <= this.min) { - this.value = this.min; - } else { - this.value = newval; - } - } else { - this.value = this.max; - } - } - - decrementValue() { - const newval = this._round(this.value - this.step); - if (this.value > this.min) { - this.last_changed = Date.now(); - this.temperatureStateInFlux(true); - } - if (newval >= this.min) { - this.value = newval; - } else { - this.value = this.min; - } - } - - valueChanged() { - // when the last_changed timestamp is changed, - // trigger a potential event fire in - // the future, as long as last changed is far enough in the - // past. - if (this.last_changed) { - window.setTimeout(() => { - const now = Date.now(); - if (now - this.last_changed >= 2000) { - this.fire("change"); - this.temperatureStateInFlux(false); - this.last_changed = null; - } - }, 2010); - } - } -} - -customElements.define("ha-climate-control", HaClimateControl); diff --git a/src/components/ha-climate-control.ts b/src/components/ha-climate-control.ts new file mode 100644 index 0000000000..b57cbb5053 --- /dev/null +++ b/src/components/ha-climate-control.ts @@ -0,0 +1,138 @@ +import { mdiChevronDown, mdiChevronUp } from "@mdi/js"; +import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; +import { customElement, property, query } from "lit/decorators"; +import { fireEvent } from "../common/dom/fire_event"; +import { conditionalClamp } from "../common/number/clamp"; +import { HomeAssistant } from "../types"; +import "./ha-icon"; +import "./ha-icon-button"; + +@customElement("ha-climate-control") +class HaClimateControl extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public value!: number; + + @property() public unit = ""; + + @property() public min?: number; + + @property() public max?: number; + + @property() public step = 1; + + private _lastChanged?: number; + + @query("#target_temperature") private _targetTemperature!: HTMLElement; + + protected render(): TemplateResult { + return html` +
${this.value} ${this.unit}
+
+
+ + +
+
+ + +
+
+ `; + } + + protected updated(changedProperties) { + if (changedProperties.has("value")) { + this._valueChanged(); + } + } + + private _temperatureStateInFlux(inFlux) { + this._targetTemperature.classList.toggle("in-flux", inFlux); + } + + private _round(value) { + // Round value to precision derived from step. + // Inspired by https://github.com/soundar24/roundSlider/blob/master/src/roundslider.js + const s = this.step.toString().split("."); + return s[1] ? parseFloat(value.toFixed(s[1].length)) : Math.round(value); + } + + private _incrementValue() { + const newValue = this._round(this.value + this.step); + this._processNewValue(newValue); + } + + private _decrementValue() { + const newValue = this._round(this.value - this.step); + this._processNewValue(newValue); + } + + private _processNewValue(value) { + const newValue = conditionalClamp(value, this.min, this.max); + + if (this.value !== newValue) { + this.value = newValue; + this._lastChanged = Date.now(); + this._temperatureStateInFlux(true); + } + } + + private _valueChanged() { + // When the last_changed timestamp is changed, + // trigger a potential event fire in the future, + // as long as last_changed is far enough in the past. + if (this._lastChanged) { + window.setTimeout(() => { + const now = Date.now(); + if (now - this._lastChanged! >= 2000) { + fireEvent(this, "change"); + this._temperatureStateInFlux(false); + this._lastChanged = undefined; + } + }, 2010); + } + } + + static get styles(): CSSResultGroup { + return css` + :host { + display: flex; + justify-content: space-between; + } + .in-flux { + color: var(--error-color); + } + #target_temperature { + align-self: center; + font-size: 28px; + direction: ltr; + } + .control-buttons { + font-size: 24px; + text-align: right; + } + ha-icon-button { + --mdc-icon-size: 32px; + } + `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-climate-control": HaClimateControl; + } +} diff --git a/src/dialogs/more-info/controls/more-info-climate.ts b/src/dialogs/more-info/controls/more-info-climate.ts index dbae025e7e..6074e2be62 100644 --- a/src/dialogs/more-info/controls/more-info-climate.ts +++ b/src/dialogs/more-info/controls/more-info-climate.ts @@ -103,8 +103,9 @@ class MoreInfoClimate extends LitElement { stateObj.attributes.temperature !== null ? html`