mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-25 18:26:35 +00:00
Convert icon picker to ha-combobox (#11586)
Co-authored-by: Zack <zackbarett@hey.com>
This commit is contained in:
parent
869fa91ae5
commit
2cb37820df
@ -1,5 +1,4 @@
|
|||||||
import "@material/mwc-list/mwc-list-item";
|
import "@material/mwc-list/mwc-list-item";
|
||||||
import "@material/mwc-textfield/mwc-textfield";
|
|
||||||
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
|
import { mdiClose, mdiMenuDown, mdiMenuUp } from "@mdi/js";
|
||||||
import "@vaadin/combo-box/theme/material/vaadin-combo-box-light";
|
import "@vaadin/combo-box/theme/material/vaadin-combo-box-light";
|
||||||
import type { ComboBoxLight } from "@vaadin/combo-box/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 { PolymerChangedEvent } from "../polymer-types";
|
||||||
import { HomeAssistant } from "../types";
|
import { HomeAssistant } from "../types";
|
||||||
import "./ha-icon-button";
|
import "./ha-icon-button";
|
||||||
|
import "./ha-textfield";
|
||||||
|
|
||||||
registerStyles(
|
registerStyles(
|
||||||
"vaadin-combo-box-item",
|
"vaadin-combo-box-item",
|
||||||
@ -54,12 +54,22 @@ registerStyles(
|
|||||||
|
|
||||||
@customElement("ha-combo-box")
|
@customElement("ha-combo-box")
|
||||||
export class HaComboBox extends LitElement {
|
export class HaComboBox extends LitElement {
|
||||||
@property({ attribute: false }) public hass!: HomeAssistant;
|
@property({ attribute: false }) public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
|
|
||||||
@property() public value?: 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 items?: any[];
|
||||||
|
|
||||||
@property() public filteredItems?: any[];
|
@property() public filteredItems?: any[];
|
||||||
@ -115,27 +125,33 @@ export class HaComboBox extends LitElement {
|
|||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
attr-for-value="value"
|
attr-for-value="value"
|
||||||
>
|
>
|
||||||
<mwc-textfield
|
<ha-textfield
|
||||||
.label=${this.label}
|
.label=${this.label}
|
||||||
|
.placeholder=${this.placeholder}
|
||||||
.disabled=${this.disabled}
|
.disabled=${this.disabled}
|
||||||
|
.validationMessage=${this.validationMessage}
|
||||||
|
.errorMessage=${this.errorMessage}
|
||||||
class="input"
|
class="input"
|
||||||
autocapitalize="none"
|
autocapitalize="none"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
autocorrect="off"
|
autocorrect="off"
|
||||||
spellcheck="false"
|
spellcheck="false"
|
||||||
.suffix=${html`<div style="width: 28px;"></div>`}
|
.suffix=${html`<div style="width: 28px;"></div>`}
|
||||||
|
.icon=${this.icon}
|
||||||
|
.invalid=${this.invalid}
|
||||||
>
|
>
|
||||||
</mwc-textfield>
|
<slot name="icon" slot="leadingIcon"></slot>
|
||||||
|
</ha-textfield>
|
||||||
${this.value
|
${this.value
|
||||||
? html`<ha-svg-icon
|
? html`<ha-svg-icon
|
||||||
aria-label=${this.hass.localize("ui.components.combo-box.clear")}
|
aria-label=${this.hass?.localize("ui.components.combo-box.clear")}
|
||||||
class="clear-button"
|
class="clear-button"
|
||||||
.path=${mdiClose}
|
.path=${mdiClose}
|
||||||
@click=${this._clearValue}
|
@click=${this._clearValue}
|
||||||
></ha-svg-icon>`
|
></ha-svg-icon>`
|
||||||
: ""}
|
: ""}
|
||||||
<ha-svg-icon
|
<ha-svg-icon
|
||||||
aria-label=${this.hass.localize("ui.components.combo-box.show")}
|
aria-label=${this.hass?.localize("ui.components.combo-box.show")}
|
||||||
class="toggle-button"
|
class="toggle-button"
|
||||||
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
||||||
@click=${this._toggleOpen}
|
@click=${this._toggleOpen}
|
||||||
@ -198,10 +214,10 @@ export class HaComboBox extends LitElement {
|
|||||||
vaadin-combo-box-light {
|
vaadin-combo-box-light {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
mwc-textfield {
|
ha-textfield {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
mwc-textfield > ha-icon-button {
|
ha-textfield > ha-icon-button {
|
||||||
--mdc-icon-button-size: 24px;
|
--mdc-icon-button-size: 24px;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
|
@ -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 { 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 { customElement, property, query, state } from "lit/decorators";
|
||||||
import { fireEvent } from "../common/dom/fire_event";
|
import { fireEvent } from "../common/dom/fire_event";
|
||||||
import { customIcons } from "../data/custom_icons";
|
import { customIcons } from "../data/custom_icons";
|
||||||
import { PolymerChangedEvent } from "../polymer-types";
|
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";
|
||||||
import "./ha-icon-button";
|
|
||||||
|
|
||||||
type IconItem = {
|
type IconItem = {
|
||||||
icon: string;
|
icon: string;
|
||||||
@ -19,35 +16,17 @@ type IconItem = {
|
|||||||
let iconItems: IconItem[] = [];
|
let iconItems: IconItem[] = [];
|
||||||
|
|
||||||
// eslint-disable-next-line lit/prefer-static-styles
|
// eslint-disable-next-line lit/prefer-static-styles
|
||||||
const rowRenderer: ComboBoxLitRenderer<IconItem> = (item) => html`<style>
|
const rowRenderer: ComboBoxLitRenderer<IconItem> = (item) => html`<mwc-list-item
|
||||||
paper-icon-item {
|
graphic="avatar"
|
||||||
padding: 0;
|
>
|
||||||
margin: -8px;
|
<ha-icon .icon=${item.icon} slot="graphic"></ha-icon>
|
||||||
}
|
${item.icon}
|
||||||
#content {
|
</mwc-list-item>`;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
ha-svg-icon {
|
|
||||||
padding-left: 2px;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
:host(:not([selected])) ha-svg-icon {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
:host([selected]) paper-icon-item {
|
|
||||||
margin-left: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<ha-svg-icon .path=${mdiCheck}></ha-svg-icon>
|
|
||||||
<paper-icon-item>
|
|
||||||
<ha-icon .icon=${item.icon} slot="item-icon"></ha-icon>
|
|
||||||
<paper-item-body>${item.icon}</paper-item-body>
|
|
||||||
</paper-icon-item>`;
|
|
||||||
|
|
||||||
@customElement("ha-icon-picker")
|
@customElement("ha-icon-picker")
|
||||||
export class HaIconPicker extends LitElement {
|
export class HaIconPicker extends LitElement {
|
||||||
|
@property() public hass?: HomeAssistant;
|
||||||
|
|
||||||
@property() public value?: string;
|
@property() public value?: string;
|
||||||
|
|
||||||
@property() public label?: string;
|
@property() public label?: string;
|
||||||
@ -64,51 +43,40 @@ export class HaIconPicker extends LitElement {
|
|||||||
|
|
||||||
@state() private _opened = false;
|
@state() private _opened = false;
|
||||||
|
|
||||||
@query("vaadin-combo-box-light", true) private comboBox!: HTMLElement;
|
@query("ha-combo-box", true) private comboBox!: HaComboBox;
|
||||||
|
|
||||||
protected render(): TemplateResult {
|
protected render(): TemplateResult {
|
||||||
return html`
|
return html`
|
||||||
<vaadin-combo-box-light
|
<ha-combo-box
|
||||||
|
.hass=${this.hass}
|
||||||
item-value-path="icon"
|
item-value-path="icon"
|
||||||
item-label-path="icon"
|
item-label-path="icon"
|
||||||
.value=${this._value}
|
.value=${this._value}
|
||||||
allow-custom-value
|
allow-custom-value
|
||||||
.filteredItems=${iconItems}
|
.filteredItems=${iconItems}
|
||||||
${comboBoxRenderer(rowRenderer)}
|
.label=${this.label}
|
||||||
|
.disabled=${this.disabled}
|
||||||
|
.placeholder=${this.placeholder}
|
||||||
|
.errorMessage=${this.errorMessage}
|
||||||
|
.invalid=${this.invalid}
|
||||||
|
.renderer=${rowRenderer}
|
||||||
|
icon
|
||||||
@opened-changed=${this._openedChanged}
|
@opened-changed=${this._openedChanged}
|
||||||
@value-changed=${this._valueChanged}
|
@value-changed=${this._valueChanged}
|
||||||
@filter-changed=${this._filterChanged}
|
@filter-changed=${this._filterChanged}
|
||||||
>
|
>
|
||||||
<paper-input
|
${this._value || this.placeholder
|
||||||
.label=${this.label}
|
? html`
|
||||||
.placeholder=${this.placeholder}
|
<ha-icon .icon=${this._value || this.placeholder} slot="icon">
|
||||||
.disabled=${this.disabled}
|
</ha-icon>
|
||||||
class="input"
|
`
|
||||||
autocapitalize="none"
|
: this.fallbackPath
|
||||||
autocomplete="off"
|
? html`<ha-svg-icon
|
||||||
autocorrect="off"
|
.path=${this.fallbackPath}
|
||||||
spellcheck="false"
|
slot="icon"
|
||||||
.errorMessage=${this.errorMessage}
|
></ha-svg-icon>`
|
||||||
.invalid=${this.invalid}
|
: ""}
|
||||||
>
|
</ha-combo-box>
|
||||||
${this._value || this.placeholder
|
|
||||||
? html`
|
|
||||||
<ha-icon .icon=${this._value || this.placeholder} slot="prefix">
|
|
||||||
</ha-icon>
|
|
||||||
`
|
|
||||||
: this.fallbackPath
|
|
||||||
? html`<ha-svg-icon
|
|
||||||
.path=${this.fallbackPath}
|
|
||||||
slot="prefix"
|
|
||||||
></ha-svg-icon>`
|
|
||||||
: ""}
|
|
||||||
<ha-icon-button
|
|
||||||
.path=${this._opened ? mdiMenuUp : mdiMenuDown}
|
|
||||||
slot="suffix"
|
|
||||||
class="toggle-button"
|
|
||||||
></ha-icon-button>
|
|
||||||
</paper-input>
|
|
||||||
</vaadin-combo-box-light>
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,6 +118,7 @@ export class HaIconPicker extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _valueChanged(ev: PolymerChangedEvent<string>) {
|
private _valueChanged(ev: PolymerChangedEvent<string>) {
|
||||||
|
ev.stopPropagation();
|
||||||
this._setValue(ev.detail.value);
|
this._setValue(ev.detail.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +127,7 @@ export class HaIconPicker extends LitElement {
|
|||||||
fireEvent(
|
fireEvent(
|
||||||
this,
|
this,
|
||||||
"value-changed",
|
"value-changed",
|
||||||
{ value },
|
{ value: this._value },
|
||||||
{
|
{
|
||||||
bubbles: false,
|
bubbles: false,
|
||||||
composed: false,
|
composed: false,
|
||||||
@ -211,11 +180,6 @@ export class HaIconPicker extends LitElement {
|
|||||||
*[slot="prefix"] {
|
*[slot="prefix"] {
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
paper-input > ha-icon-button {
|
|
||||||
--mdc-icon-button-size: 24px;
|
|
||||||
padding: 2px;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,31 @@
|
|||||||
import { TextField } from "@material/mwc-textfield";
|
import { TextField } from "@material/mwc-textfield";
|
||||||
import { TemplateResult, html } from "lit";
|
import { TemplateResult, html, PropertyValues } from "lit";
|
||||||
import { customElement } from "lit/decorators";
|
import { customElement, property } from "lit/decorators";
|
||||||
|
|
||||||
@customElement("ha-textfield")
|
@customElement("ha-textfield")
|
||||||
export class HaTextField extends 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";
|
const type = isTrailingIcon ? "trailing" : "leading";
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
|
Loading…
x
Reference in New Issue
Block a user