diff --git a/src/state-summary/state-card-input_number.js b/src/state-summary/state-card-input_number.js
deleted file mode 100644
index ced62e2984..0000000000
--- a/src/state-summary/state-card-input_number.js
+++ /dev/null
@@ -1,200 +0,0 @@
-import "@polymer/iron-flex-layout/iron-flex-layout-classes";
-import { IronResizableBehavior } from "@polymer/iron-resizable-behavior/iron-resizable-behavior";
-import { mixinBehaviors } from "@polymer/polymer/lib/legacy/class";
-import { html } from "@polymer/polymer/lib/utils/html-tag";
-/* eslint-plugin-disable lit */
-import { PolymerElement } from "@polymer/polymer/polymer-element";
-import { computeStateDisplay } from "../common/entity/compute_state_display";
-import "../components/entity/state-info";
-import "../components/ha-slider";
-import "../components/ha-textfield";
-
-class StateCardInputNumber extends mixinBehaviors(
- [IronResizableBehavior],
- PolymerElement
-) {
- static get template() {
- return html`
-
-
-
-
- `;
- }
-
- static get stateInfoTemplate() {
- return html`
-
- `;
- }
-
- 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);
- }
- }
-
- static get properties() {
- return {
- hass: Object,
- hiddenbox: {
- type: Boolean,
- value: true,
- },
- hiddenslider: {
- type: Boolean,
- value: true,
- },
- inDialog: {
- type: Boolean,
- value: false,
- },
- stateObj: {
- type: Object,
- observer: "stateObjectChanged",
- },
- min: {
- type: Number,
- value: 0,
- },
- max: {
- type: Number,
- value: 100,
- },
- maxlength: {
- type: Number,
- value: 3,
- },
- step: Number,
- value: Number,
- formattedState: String,
- mode: String,
- };
- }
-
- hiddenState() {
- if (this.mode !== "slider") return;
- const sliderwidth = this.$.slider.offsetWidth;
- if (sliderwidth < 100) {
- this.$.sliderstate.hidden = true;
- } else if (sliderwidth >= 145) {
- this.$.sliderstate.hidden = false;
- }
- }
-
- stateObjectChanged(newVal) {
- const prevMode = this.mode;
- this.setProperties({
- min: Number(newVal.attributes.min),
- max: Number(newVal.attributes.max),
- step: Number(newVal.attributes.step),
- value: Number(newVal.state),
- formattedState: computeStateDisplay(
- this.hass.localize,
- newVal,
- this.hass.locale,
- this.hass.entities,
- newVal.state
- ),
- mode: String(newVal.attributes.mode),
- maxlength: String(newVal.attributes.max).length,
- hiddenbox: newVal.attributes.mode !== "box",
- hiddenslider: newVal.attributes.mode !== "slider",
- });
- if (this.mode === "slider" && prevMode !== "slider") {
- this.hiddenState();
- }
- }
-
- onInput(ev) {
- this.value = ev.target.value;
- }
-
- 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,
- });
- }
-
- stopPropagation(ev) {
- ev.stopPropagation();
- }
-}
-
-customElements.define("state-card-input_number", StateCardInputNumber);
diff --git a/src/state-summary/state-card-input_number.ts b/src/state-summary/state-card-input_number.ts
new file mode 100644
index 0000000000..02829dd96a
--- /dev/null
+++ b/src/state-summary/state-card-input_number.ts
@@ -0,0 +1,169 @@
+import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit";
+import { HassEntity } from "home-assistant-js-websocket";
+import { customElement, property } from "lit/decorators";
+import { computeStateDisplay } from "../common/entity/compute_state_display";
+import { computeRTLDirection } from "../common/util/compute_rtl";
+import { debounce } from "../common/util/debounce";
+import "../components/ha-slider";
+import "../components/ha-textfield";
+import "../components/entity/state-info";
+import { isUnavailableState } from "../data/entity";
+import { setValue } from "../data/input_text";
+import { HomeAssistant } from "../types";
+import { installResizeObserver } from "../panels/lovelace/common/install-resize-observer";
+
+@customElement("state-card-input_number")
+class StateCardInputNumber extends LitElement {
+ @property({ attribute: false }) public hass!: HomeAssistant;
+
+ @property() public stateObj!: HassEntity;
+
+ @property({ type: Boolean }) public inDialog = false;
+
+ private _loaded?: boolean;
+
+ private _updated?: boolean;
+
+ private _resizeObserver?: ResizeObserver;
+
+ public connectedCallback(): void {
+ super.connectedCallback();
+ if (this._updated && !this._loaded) {
+ this._initialLoad();
+ }
+ this._attachObserver();
+ }
+
+ public disconnectedCallback(): void {
+ this._resizeObserver?.disconnect();
+ }
+
+ protected firstUpdated(): void {
+ this._updated = true;
+ if (this.isConnected && !this._loaded) {
+ this._initialLoad();
+ }
+ this._attachObserver();
+ }
+
+ protected render(): TemplateResult {
+ return html`
+
+ ${this.stateObj.attributes.mode === "slider"
+ ? html`
+
+
+
+ ${computeStateDisplay(
+ this.hass.localize,
+ this.stateObj,
+ this.hass.locale,
+ this.hass.entities,
+ this.stateObj.state
+ )}
+
+
+ `
+ : html`
+
+
+
+
+ `}
+ `;
+ }
+
+ static get styles(): CSSResultGroup {
+ return css`
+ :host {
+ display: flex;
+ }
+ .flex {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ flex-grow: 2;
+ }
+ .state {
+ min-width: 45px;
+ text-align: end;
+ }
+ ha-textfield {
+ text-align: end;
+ }
+ ha-slider {
+ width: 100%;
+ max-width: 200px;
+ }
+ `;
+ }
+
+ private async _initialLoad(): Promise {
+ this._loaded = true;
+ await this.updateComplete;
+ this._measureCard();
+ }
+
+ private _measureCard() {
+ if (!this.isConnected) {
+ return;
+ }
+ const element = this.shadowRoot!.querySelector(".state") as HTMLElement;
+ if (!element) {
+ return;
+ }
+ element.hidden = this.clientWidth <= 300;
+ }
+
+ private async _attachObserver(): Promise {
+ if (!this._resizeObserver) {
+ await installResizeObserver();
+ this._resizeObserver = new ResizeObserver(
+ debounce(() => this._measureCard(), 250, false)
+ );
+ }
+ if (this.isConnected) {
+ this._resizeObserver.observe(this);
+ }
+ }
+
+ private _selectedValueChanged(ev: Event): void {
+ if ((ev.target as HTMLInputElement).value !== this.stateObj.state) {
+ setValue(
+ this.hass!,
+ this.stateObj.entity_id,
+ (ev.target as HTMLInputElement).value
+ );
+ }
+ }
+}
+
+declare global {
+ interface HTMLElementTagNameMap {
+ "state-card-input_number": StateCardInputNumber;
+ }
+}