From 37a56b250a37e8f5b2827a62775b0fb5a376abd6 Mon Sep 17 00:00:00 2001 From: Paul Bottein Date: Tue, 28 Nov 2023 11:28:29 +0100 Subject: [PATCH] Add multiple option to text selector (#18785) * Add multiple option to text selector * Make multiple optional * Update src/components/ha-selector/ha-selector-text.ts Co-authored-by: Bram Kragten --------- Co-authored-by: Bram Kragten --- src/components/ha-aliases-editor.ts | 118 ++------------ src/components/ha-multi-textfield.ts | 144 ++++++++++++++++++ .../ha-selector/ha-selector-text.ts | 27 +++- src/data/selector.ts | 1 + src/translations/en.json | 6 +- 5 files changed, 187 insertions(+), 109 deletions(-) create mode 100644 src/components/ha-multi-textfield.ts diff --git a/src/components/ha-aliases-editor.ts b/src/components/ha-aliases-editor.ts index 21ec5f0e4e..e466b60123 100644 --- a/src/components/ha-aliases-editor.ts +++ b/src/components/ha-aliases-editor.ts @@ -1,12 +1,8 @@ -import "@material/mwc-button/mwc-button"; -import { mdiDeleteOutline, mdiPlus } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement, nothing } from "lit"; +import { LitElement, html, nothing } from "lit"; import { customElement, property } from "lit/decorators"; -import { haStyle } from "../resources/styles"; -import { HomeAssistant } from "../types"; -import "./ha-textfield"; -import type { HaTextField } from "./ha-textfield"; import { fireEvent } from "../common/dom/fire_event"; +import { HomeAssistant } from "../types"; +import "./ha-multi-textfield"; @customElement("ha-aliases-editor") class AliasesEditor extends LitElement { @@ -22,107 +18,23 @@ class AliasesEditor extends LitElement { } return html` - ${this.aliases.map( - (alias, index) => html` -
- - -
- ` - )} -
- - ${this.hass!.localize("ui.dialogs.aliases.add_alias")} - - -
+ + `; } - private async _addAlias() { - this.aliases = [...this.aliases, ""]; - this._fireChanged(this.aliases); - await this.updateComplete; - const field = this.shadowRoot?.querySelector(`ha-textfield[data-last]`) as - | HaTextField - | undefined; - field?.focus(); - } - - private async _editAlias(ev: Event) { - const index = (ev.target as any).index; - const aliases = [...this.aliases]; - aliases[index] = (ev.target as any).value; - this._fireChanged(aliases); - } - - private async _keyDownAlias(ev: KeyboardEvent) { - if (ev.key === "Enter") { - ev.stopPropagation(); - this._addAlias(); - } - } - - private async _removeAlias(ev: Event) { - const index = (ev.target as any).index; - const aliases = [...this.aliases]; - aliases.splice(index, 1); - this._fireChanged(aliases); - } - - private _fireChanged(value) { + private _aliasesChanged(value) { fireEvent(this, "value-changed", { value }); } - - static get styles(): CSSResultGroup { - return [ - haStyle, - css` - .row { - margin-bottom: 8px; - } - ha-textfield { - display: block; - } - ha-icon-button { - display: block; - } - mwc-button { - margin-left: 8px; - } - #alias_input { - margin-top: 8px; - } - .alias { - border: 1px solid var(--divider-color); - border-radius: 4px; - margin-top: 4px; - --mdc-icon-button-size: 24px; - } - `, - ]; - } } declare global { diff --git a/src/components/ha-multi-textfield.ts b/src/components/ha-multi-textfield.ts new file mode 100644 index 0000000000..c16bfb02ad --- /dev/null +++ b/src/components/ha-multi-textfield.ts @@ -0,0 +1,144 @@ +import { mdiDeleteOutline, mdiPlus } from "@mdi/js"; +import { CSSResultGroup, LitElement, css, html } from "lit"; +import { customElement, property } from "lit/decorators"; +import { fireEvent } from "../common/dom/fire_event"; +import { haStyle } from "../resources/styles"; +import type { HomeAssistant } from "../types"; +import "./ha-button"; +import "./ha-textfield"; +import type { HaTextField } from "./ha-textfield"; + +@customElement("ha-multi-textfield") +class HaMultiTextField extends LitElement { + @property({ attribute: false }) public hass?: HomeAssistant; + + @property({ attribute: false }) public value?: string[]; + + @property({ type: Boolean }) public disabled = false; + + @property() public label?: string; + + @property() public inputType?: string; + + @property() public inputSuffix?: string; + + @property() public inputPrefix?: string; + + @property() public autocomplete?: string; + + @property() public addLabel?: string; + + @property() public removeLabel?: string; + + @property({ attribute: "item-index", type: Boolean }) + public itemIndex?: boolean; + + protected render() { + return html` + ${this._items.map((item, index) => { + const indexSuffix = `${this.itemIndex ? ` ${index + 1}` : ""}`; + return html` +
+ + +
+ `; + })} +
+ + ${this.addLabel ?? this.hass?.localize("ui.common.add") ?? "Add"} + + +
+ `; + } + + private get _items() { + return this.value ?? []; + } + + private async _addItem() { + const items = [...this._items, ""]; + this._fireChanged(items); + await this.updateComplete; + const field = this.shadowRoot?.querySelector(`ha-textfield[data-last]`) as + | HaTextField + | undefined; + field?.focus(); + } + + private async _editItem(ev: Event) { + const index = (ev.target as any).index; + const items = [...this._items]; + items[index] = (ev.target as any).value; + this._fireChanged(items); + } + + private async _keyDown(ev: KeyboardEvent) { + if (ev.key === "Enter") { + ev.stopPropagation(); + this._addItem(); + } + } + + private async _removeItem(ev: Event) { + const index = (ev.target as any).index; + const items = [...this._items]; + items.splice(index, 1); + this._fireChanged(items); + } + + private _fireChanged(value) { + this.value = value; + fireEvent(this, "value-changed", { value }); + } + + static get styles(): CSSResultGroup { + return [ + haStyle, + css` + .row { + margin-bottom: 8px; + } + ha-textfield { + display: block; + } + ha-icon-button { + display: block; + } + ha-button { + margin-left: 8px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-multi-textfield": HaMultiTextField; + } +} diff --git a/src/components/ha-selector/ha-selector-text.ts b/src/components/ha-selector/ha-selector-text.ts index 3629ba3e22..6d175017a4 100644 --- a/src/components/ha-selector/ha-selector-text.ts +++ b/src/components/ha-selector/ha-selector-text.ts @@ -1,10 +1,12 @@ import { mdiEye, mdiEyeOff } from "@mdi/js"; -import { css, CSSResultGroup, html, LitElement } from "lit"; +import { CSSResultGroup, LitElement, css, html } from "lit"; import { customElement, property, state } from "lit/decorators"; +import { ensureArray } from "../../common/array/ensure-array"; import { fireEvent } from "../../common/dom/fire_event"; import { StringSelector } from "../../data/selector"; import { HomeAssistant } from "../../types"; import "../ha-icon-button"; +import "../ha-multi-textfield"; import "../ha-textarea"; import "../ha-textfield"; @@ -38,6 +40,22 @@ export class HaTextSelector extends LitElement { } protected render() { + if (this.selector.text?.multiple) { + return html` + + + `; + } if (this.selector.text?.multiline) { return html`