Convert input-number to Lit/TS (#2792)

* Convert input-number to Lit/TS

Should I worry about width for the state display with slider?

* address review comments

* clientWidth not currently working
* unsure about the typing of _InputElement

* remove unused import

* get clientwidth

* added comment
This commit is contained in:
Ian Richardson 2019-02-22 14:08:18 -06:00 committed by Paulus Schoutsen
parent 7d1991ac78
commit 6da311078a
3 changed files with 163 additions and 189 deletions

View File

@ -1,7 +1,7 @@
import { HomeAssistant } from "../types";
export const setValue = (hass: HomeAssistant, entity: string, value: string) =>
hass.callService("input_text", "set_value", {
hass.callService(entity.split(".", 1)[0], "set_value", {
value,
entity_id: entity,
});

View File

@ -1,188 +0,0 @@
import { html } from "@polymer/polymer/lib/utils/html-tag";
import { PolymerElement } from "@polymer/polymer/polymer-element";
import "@polymer/paper-input/paper-input";
import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior";
import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
import "../components/hui-generic-entity-row";
import "../../../components/ha-slider";
import { computeRTL } from "../../../common/util/compute_rtl";
class HuiInputNumberEntityRow extends mixinBehaviors(
[IronResizableBehavior],
PolymerElement
) {
static get template() {
return html`
${this.styleTemplate}
<hui-generic-entity-row
hass="[[hass]]"
config="[[_config]]"
id="input_number_card"
>
${this.inputNumberControlTemplate}
</hui-generic-entity-row>
`;
}
static get styleTemplate() {
return html`
<style>
.flex {
display: flex;
align-items: center;
}
.state {
min-width: 45px;
text-align: center;
}
paper-input {
text-align: right;
}
</style>
`;
}
static get inputNumberControlTemplate() {
return html`
<div>
<template
is="dom-if"
if="[[_equals(_stateObj.attributes.mode, 'slider')]]"
>
<div class="flex">
<ha-slider
dir="[[_rtl]]"
min="[[_min]]"
max="[[_max]]"
value="{{_value}}"
step="[[_step]]"
pin
on-change="_selectedValueChanged"
ignore-bar-touch
></ha-slider>
<span class="state"
>[[_value]] [[_stateObj.attributes.unit_of_measurement]]</span
>
</div>
</template>
<template
is="dom-if"
if="[[_equals(_stateObj.attributes.mode, 'box')]]"
>
<paper-input
no-label-float
auto-validate
pattern="[0-9]+([\\.][0-9]+)?"
step="[[_step]]"
min="[[_min]]"
max="[[_max]]"
value="{{_value}}"
type="number"
on-change="_selectedValueChanged"
></paper-input>
</template>
</div>
`;
}
static get properties() {
return {
hass: Object,
_config: Object,
_stateObj: {
type: Object,
computed: "_computeStateObj(hass.states, _config.entity)",
observer: "_stateObjChanged",
},
_min: {
type: Number,
value: 0,
},
_max: {
type: Number,
value: 100,
},
_step: Number,
_value: Number,
_rtl: {
type: String,
computed: "_computeRTLDirection(hass)",
},
};
}
ready() {
super.ready();
if (typeof ResizeObserver === "function") {
const ro = new ResizeObserver((entries) => {
entries.forEach(() => {
this._hiddenState();
});
});
ro.observe(this.$.input_number_card);
} else {
this.addEventListener("iron-resize", this._hiddenState);
}
}
_equals(a, b) {
return a === b;
}
_computeStateObj(states, entityId) {
return states && entityId in states ? states[entityId] : null;
}
setConfig(config) {
if (!config || !config.entity) {
throw new Error("Entity not configured.");
}
this._config = config;
}
_hiddenState() {
if (
!this.$ ||
!this._stateObj ||
this._stateObj.attributes.mode !== "slider"
)
return;
const width = this.$.input_number_card.offsetWidth;
const stateEl = this.shadowRoot.querySelector(".state");
if (!stateEl) return;
stateEl.hidden = width <= 350;
}
_stateObjChanged(stateObj, oldStateObj) {
if (!stateObj) return;
this.setProperties({
_min: Number(stateObj.attributes.min),
_max: Number(stateObj.attributes.max),
_step: Number(stateObj.attributes.step),
_value: Number(stateObj.state),
});
if (
oldStateObj &&
stateObj.attributes.mode === "slider" &&
oldStateObj.attributes.mode !== "slider"
) {
this._hiddenState();
}
}
_selectedValueChanged() {
if (this._value === Number(this._stateObj.state)) return;
this.hass.callService("input_number", "set_value", {
value: this._value,
entity_id: this._stateObj.entity_id,
});
}
_computeRTLDirection(hass) {
return computeRTL(hass) ? "rtl" : "ltr";
}
}
customElements.define("hui-input-number-entity-row", HuiInputNumberEntityRow);

View File

@ -0,0 +1,162 @@
import {
html,
LitElement,
TemplateResult,
property,
customElement,
css,
CSSResult,
} from "lit-element";
import "../components/hui-generic-entity-row";
import "../../../components/ha-slider";
import "../components/hui-warning";
import { computeRTLDirection } from "../../../common/util/compute_rtl";
import { EntityRow, EntityConfig } from "./types";
import { HomeAssistant } from "../../../types";
import { setValue } from "../../../data/input_text";
@customElement("hui-input-number-entity-row")
class HuiInputNumberEntityRow extends LitElement implements EntityRow {
@property() public hass?: HomeAssistant;
@property() private _config?: EntityConfig;
private _loaded?: boolean;
private _updated?: boolean;
public setConfig(config: EntityConfig): void {
if (!config) {
throw new Error("Configuration error");
}
this._config = config;
}
public connectedCallback(): void {
super.connectedCallback();
if (this._updated && !this._loaded) {
this._initialLoad();
}
}
protected firstUpdated(): void {
this._updated = true;
if (this.isConnected && !this._loaded) {
this._initialLoad();
}
}
protected render(): TemplateResult | void {
if (!this._config || !this.hass) {
return html``;
}
const stateObj = this.hass.states[this._config.entity];
if (!stateObj) {
return html`
<hui-warning
>${this.hass.localize(
"ui.panel.lovelace.warning.entity_not_found",
"entity",
this._config.entity
)}</hui-warning
>
`;
}
return html`
<hui-generic-entity-row .hass="${this.hass}" .config="${this._config}">
<div>
${stateObj.attributes.mode === "slider"
? html`
<div class="flex">
<ha-slider
.dir="${this._computeRTLDirection}"
.step="${Number(stateObj.attributes.step)}"
.min="${Number(stateObj.attributes.min)}"
.max="${Number(stateObj.attributes.max)}"
.value="${Number(stateObj.state)}"
pin
@change="${this._selectedValueChanged}"
ignore-bar-touch
id="input"
></ha-slider>
<span class="state">
${Number(stateObj.state)}
${stateObj.attributes.unit_of_measurement}
</span>
</div>
`
: html`
<paper-input
no-label-float
auto-validate
.pattern="[0-9]+([\\.][0-9]+)?"
.step="${Number(stateObj.attributes.step)}"
.min="${Number(stateObj.attributes.min)}"
.max="${Number(stateObj.attributes.max)}"
.value="${Number(stateObj.state)}"
type="number"
@change="${this._selectedValueChanged}"
id="input"
></paper-input>
`}
</div>
</hui-generic-entity-row>
`;
}
static get styles(): CSSResult {
return css`
.flex {
display: flex;
align-items: center;
}
.state {
min-width: 45px;
text-align: center;
}
paper-input {
text-align: right;
}
`;
}
private async _initialLoad(): Promise<void> {
this._loaded = true;
await this.updateComplete;
const element = this.shadowRoot!.querySelector(".state") as HTMLElement;
if (!element || !this.parentElement) {
return;
}
element.hidden = this.parentElement.clientWidth <= 350;
}
private get _inputElement(): { value: string } {
// linter recommended the following syntax
return (this.shadowRoot!.getElementById("input") as unknown) as {
value: string;
};
}
private _selectedValueChanged(): void {
const element = this._inputElement;
const stateObj = this.hass!.states[this._config!.entity];
if (element.value !== stateObj.state) {
setValue(this.hass!, stateObj.entity_id, element.value!);
}
}
private _computeRTLDirection(): string {
return computeRTLDirection(this.hass!);
}
}
declare global {
interface HTMLElementTagNameMap {
"hui-input-number-entity-row": HuiInputNumberEntityRow;
}
}