diff --git a/src/components/user/ha-user-picker.ts b/src/components/user/ha-user-picker.ts index d16e68cf1d..2959a37d89 100644 --- a/src/components/user/ha-user-picker.ts +++ b/src/components/user/ha-user-picker.ts @@ -1,21 +1,30 @@ +import type { ComboBoxLitRenderer } from "@vaadin/combo-box/lit"; import type { TemplateResult } from "lit"; -import { css, html, LitElement } from "lit"; -import { property } from "lit/decorators"; +import { html, LitElement, nothing } from "lit"; +import { customElement, property } from "lit/decorators"; import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; -import { stringCompare } from "../../common/string/compare"; import type { User } from "../../data/user"; import { fetchUsers } from "../../data/user"; import type { HomeAssistant } from "../../types"; -import "../ha-select"; +import "../ha-combo-box-item"; +import "../ha-generic-picker"; +import type { PickerComboBoxItem } from "../ha-picker-combo-box"; +import type { PickerValueRenderer } from "../ha-picker-field"; import "./ha-user-badge"; -import "../ha-list-item"; +interface UserComboBoxItem extends PickerComboBoxItem { + user?: User; +} + +@customElement("ha-user-picker") class HaUserPicker extends LitElement { - public hass?: HomeAssistant; + @property({ attribute: false }) public hass!: HomeAssistant; @property() public label?: string; + @property() public placeholder?: string; + @property({ attribute: false }) public noUserLabel?: string; @property() public value = ""; @@ -24,78 +33,124 @@ class HaUserPicker extends LitElement { @property({ type: Boolean }) public disabled = false; - private _sortedUsers = memoizeOne((users?: User[]) => { + protected firstUpdated(changedProps) { + super.firstUpdated(changedProps); + if (!this.users) { + this._fetchUsers(); + } + } + + private async _fetchUsers() { + this.users = await fetchUsers(this.hass); + } + + private usersMap = memoizeOne((users?: User[]): Map => { + if (!users) { + return new Map(); + } + return new Map(users.map((user) => [user.id, user])); + }); + + private _valueRenderer: PickerValueRenderer = (value) => { + const user = this.usersMap(this.users).get(value); + if (!user) { + return html` ${value} `; + } + + return html` + + ${user.name} + `; + }; + + private _rowRenderer: ComboBoxLitRenderer = (item) => { + const user = item.user; + if (!user) { + return html` + ${item.icon + ? html`` + : item.icon_path + ? html`` + : nothing} + ${item.primary} + ${item.secondary + ? html`${item.secondary}` + : nothing} + `; + } + + return html` + + + ${item.primary} + + `; + }; + + private _getUsers = memoizeOne((users?: User[]) => { if (!users) { return []; } return users .filter((user) => !user.system_generated) - .sort((a, b) => - stringCompare(a.name, b.name, this.hass!.locale.language) - ); + .map((user) => ({ + id: user.id, + primary: user.name, + domain_name: user.name, + search_labels: [user.name, user.id, user.username].filter( + Boolean + ) as string[], + sorting_label: user.name, + user, + })); }); + private _getItems = () => this._getUsers(this.users); + protected render(): TemplateResult { + const placeholder = + this.placeholder ?? this.hass.localize("ui.components.user-picker.user"); + return html` - - ${this.users?.length === 0 - ? html` - ${this.noUserLabel || - this.hass?.localize("ui.components.user-picker.no_user")} - ` - : ""} - ${this._sortedUsers(this.users).map( - (user) => html` - - - ${user.name} - - ` + .notFoundLabel=${this.hass.localize( + "ui.components.user-picker.no_match" )} - + .placeholder=${placeholder} + .value=${this.value} + .getItems=${this._getItems} + .valueRenderer=${this._valueRenderer} + .rowRenderer=${this._rowRenderer} + @value-changed=${this._valueChanged} + > + `; } - protected firstUpdated(changedProps) { - super.firstUpdated(changedProps); - if (this.users === undefined) { - fetchUsers(this.hass!).then((users) => { - this.users = users; - }); - } + private _valueChanged(ev) { + const value = ev.detail.value; + + this.value = value; + fireEvent(this, "value-changed", { value }); + fireEvent(this, "change"); } - - private _userChanged(ev) { - const newValue = ev.target.value; - - if (newValue !== this.value) { - this.value = newValue; - setTimeout(() => { - fireEvent(this, "value-changed", { value: newValue }); - fireEvent(this, "change"); - }, 0); - } - } - - static styles = css` - :host { - display: inline-block; - } - `; } -customElements.define("ha-user-picker", HaUserPicker); - declare global { interface HTMLElementTagNameMap { "ha-user-picker": HaUserPicker; diff --git a/src/components/user/ha-users-picker.ts b/src/components/user/ha-users-picker.ts index d3160be467..a6c01df6fe 100644 --- a/src/components/user/ha-users-picker.ts +++ b/src/components/user/ha-users-picker.ts @@ -1,4 +1,3 @@ -import { mdiClose } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit"; import { customElement, property } from "lit/decorators"; import { guard } from "lit/directives/guard"; @@ -6,13 +5,15 @@ import memoizeOne from "memoize-one"; import { fireEvent } from "../../common/dom/fire_event"; import type { User } from "../../data/user"; import { fetchUsers } from "../../data/user"; -import type { ValueChangedEvent, HomeAssistant } from "../../types"; +import type { HomeAssistant, ValueChangedEvent } from "../../types"; import "../ha-icon-button"; import "./ha-user-picker"; @customElement("ha-users-picker") -class HaUsersPickerLight extends LitElement { - @property({ attribute: false }) public hass?: HomeAssistant; +class HaUsersPicker extends LitElement { + @property({ attribute: false }) public hass!: HomeAssistant; + + @property() public label?: string; @property({ attribute: false }) public value?: string[]; @@ -29,13 +30,15 @@ class HaUsersPickerLight extends LitElement { protected firstUpdated(changedProps) { super.firstUpdated(changedProps); - if (this.users === undefined) { - fetchUsers(this.hass!).then((users) => { - this.users = users; - }); + if (!this.users) { + this._fetchUsers(); } } + private async _fetchUsers() { + this.users = await fetchUsers(this.hass); + } + protected render() { if (!this.hass || !this.users) { return nothing; @@ -43,15 +46,13 @@ class HaUsersPickerLight extends LitElement { const notSelectedUsers = this._notSelectedUsers(this.users, this.value); return html` + ${this.label ? html`` : nothing} ${guard([notSelectedUsers], () => this.value?.map( (user_id, idx) => html`
- - >
` ) )} - +
+ +
`; } @@ -120,12 +113,12 @@ class HaUsersPickerLight extends LitElement { }); } - private _userChanged(event: ValueChangedEvent) { - event.stopPropagation(); - const index = (event.currentTarget as any).index; - const newValue = event.detail.value; + private _userChanged(ev: ValueChangedEvent) { + ev.stopPropagation(); + const index = (ev.currentTarget as any).index; + const newValue = ev.detail.value; const newUsers = [...this._currentUsers]; - if (newValue === "") { + if (!newValue) { newUsers.splice(index, 1); } else { newUsers.splice(index, 1, newValue); @@ -148,24 +141,15 @@ class HaUsersPickerLight extends LitElement { this._updateUsers([...currentUsers, toAdd]); } - private _removeUser(event) { - const userId = (event.currentTarget as any).userId; - this._updateUsers(this._currentUsers.filter((user) => user !== userId)); - } - - static styles = css` - :host { - display: block; - } + static override styles = css` div { - display: flex; - align-items: center; + margin-top: 8px; } `; } declare global { interface HTMLElementTagNameMap { - "ha-users-picker": HaUsersPickerLight; + "ha-users-picker": HaUsersPicker; } } diff --git a/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts b/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts index d0bd8ee870..0e4dfd1090 100644 --- a/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts +++ b/src/panels/config/automation/trigger/types/ha-automation-trigger-event.ts @@ -48,12 +48,6 @@ export class HaEventTrigger extends LitElement implements TriggerElement { "ui.panel.config.automation.editor.triggers.type.event.context_users" )}