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`