diff --git a/src/components/ha-combo-box.ts b/src/components/ha-combo-box.ts index 6a44d33a6f..5dd0c0527f 100644 --- a/src/components/ha-combo-box.ts +++ b/src/components/ha-combo-box.ts @@ -1,5 +1,4 @@ import "@material/mwc-list/mwc-list-item"; -import "@material/mwc-textfield/mwc-textfield"; import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js"; import "@vaadin/combo-box/theme/material/vaadin-combo-box-light"; import type { ComboBoxLight } from "@vaadin/combo-box/vaadin-combo-box-light"; @@ -11,6 +10,7 @@ import { fireEvent } from "../common/dom/fire_event"; import { PolymerChangedEvent } from "../polymer-types"; import { HomeAssistant } from "../types"; import "./ha-icon-button"; +import "./ha-textfield"; registerStyles( "vaadin-combo-box-item", @@ -54,12 +54,22 @@ registerStyles( @customElement("ha-combo-box") export class HaComboBox extends LitElement { - @property({ attribute: false }) public hass!: HomeAssistant; + @property({ attribute: false }) public hass?: HomeAssistant; @property() public label?: string; @property() public value?: string; + @property() public placeholder?: string; + + @property() public validationMessage?: string; + + @property({ attribute: "error-message" }) public errorMessage?: string; + + @property({ type: Boolean }) public invalid?: boolean; + + @property({ type: Boolean }) public icon?: boolean; + @property() public items?: any[]; @property() public filteredItems?: any[]; @@ -115,27 +125,33 @@ export class HaComboBox extends LitElement { @value-changed=${this._valueChanged} attr-for-value="value" > - `} + .icon=${this.icon} + .invalid=${this.invalid} > - + + ${this.value ? html`` : ""} ha-icon-button { + ha-textfield > ha-icon-button { --mdc-icon-button-size: 24px; padding: 2px; color: var(--secondary-text-color); diff --git a/src/components/ha-icon-picker.ts b/src/components/ha-icon-picker.ts index 972a2f7a61..2d7c1002c5 100644 --- a/src/components/ha-icon-picker.ts +++ b/src/components/ha-icon-picker.ts @@ -1,16 +1,13 @@ -import { mdiCheck, mdiMenuDown, mdiMenuUp } from "@mdi/js"; -import "@polymer/paper-input/paper-input"; -import "@polymer/paper-item/paper-icon-item"; -import "@polymer/paper-item/paper-item-body"; -import "@vaadin/combo-box/theme/material/vaadin-combo-box-light"; import { css, html, LitElement, TemplateResult } from "lit"; -import { ComboBoxLitRenderer, comboBoxRenderer } from "lit-vaadin-helpers"; +import { ComboBoxLitRenderer } from "lit-vaadin-helpers"; import { customElement, property, query, state } from "lit/decorators"; import { fireEvent } from "../common/dom/fire_event"; import { customIcons } from "../data/custom_icons"; import { PolymerChangedEvent } from "../polymer-types"; +import { HomeAssistant } from "../types"; +import "./ha-combo-box"; +import type { HaComboBox } from "./ha-combo-box"; import "./ha-icon"; -import "./ha-icon-button"; type IconItem = { icon: string; @@ -19,35 +16,17 @@ type IconItem = { let iconItems: IconItem[] = []; // eslint-disable-next-line lit/prefer-static-styles -const rowRenderer: ComboBoxLitRenderer = (item) => html` - - - - - ${item.icon} - `; +const rowRenderer: ComboBoxLitRenderer = (item) => html` + + ${item.icon} +`; @customElement("ha-icon-picker") export class HaIconPicker extends LitElement { + @property() public hass?: HomeAssistant; + @property() public value?: string; @property() public label?: string; @@ -64,51 +43,40 @@ export class HaIconPicker extends LitElement { @state() private _opened = false; - @query("vaadin-combo-box-light", true) private comboBox!: HTMLElement; + @query("ha-combo-box", true) private comboBox!: HaComboBox; protected render(): TemplateResult { return html` - - - ${this._value || this.placeholder - ? html` - - - ` - : this.fallbackPath - ? html`` - : ""} - - - + ${this._value || this.placeholder + ? html` + + + ` + : this.fallbackPath + ? html`` + : ""} + `; } @@ -150,6 +118,7 @@ export class HaIconPicker extends LitElement { } private _valueChanged(ev: PolymerChangedEvent) { + ev.stopPropagation(); this._setValue(ev.detail.value); } @@ -158,7 +127,7 @@ export class HaIconPicker extends LitElement { fireEvent( this, "value-changed", - { value }, + { value: this._value }, { bubbles: false, composed: false, @@ -211,11 +180,6 @@ export class HaIconPicker extends LitElement { *[slot="prefix"] { margin-right: 8px; } - paper-input > ha-icon-button { - --mdc-icon-button-size: 24px; - padding: 2px; - color: var(--secondary-text-color); - } `; } } diff --git a/src/components/ha-textfield.ts b/src/components/ha-textfield.ts index 55daec0349..c5e0e21a90 100644 --- a/src/components/ha-textfield.ts +++ b/src/components/ha-textfield.ts @@ -1,10 +1,31 @@ import { TextField } from "@material/mwc-textfield"; -import { TemplateResult, html } from "lit"; -import { customElement } from "lit/decorators"; +import { TemplateResult, html, PropertyValues } from "lit"; +import { customElement, property } from "lit/decorators"; @customElement("ha-textfield") export class HaTextField extends TextField { - override renderIcon(_icon: string, isTrailingIcon = false): TemplateResult { + @property({ type: Boolean }) public invalid?: boolean; + + @property({ attribute: "error-message" }) public errorMessage?: string; + + override updated(changedProperties: PropertyValues) { + super.updated(changedProperties); + if ( + (changedProperties.has("invalid") && + (this.invalid || changedProperties.get("invalid") !== undefined)) || + changedProperties.has("errorMessage") + ) { + this.setCustomValidity( + this.invalid ? this.errorMessage || "Invalid" : "" + ); + this.reportValidity(); + } + } + + protected override renderIcon( + _icon: string, + isTrailingIcon = false + ): TemplateResult { const type = isTrailingIcon ? "trailing" : "leading"; return html`