) {
+ this._opened = ev.detail.value;
+ }
+
+ private get _value() {
+ return this.value || "";
+ }
+
+ private _setValue(value: string) {
+ this.value = value;
+ setTimeout(() => {
+ fireEvent(this, "value-changed", { value });
+ fireEvent(this, "change");
+ }, 0);
+ }
+
static get styles(): CSSResultGroup {
return css`
- :host {
- display: inline-block;
- }
- mwc-list {
- display: block;
+ ha-select {
+ width: 100%;
}
`;
}
diff --git a/src/components/user/ha-users-picker.ts b/src/components/user/ha-users-picker.ts
index aeda071a88..e06c199626 100644
--- a/src/components/user/ha-users-picker.ts
+++ b/src/components/user/ha-users-picker.ts
@@ -1,5 +1,4 @@
-import { mdiClose } from "@mdi/js";
-import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
+import { css, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators";
import { guard } from "lit/directives/guard";
import memoizeOne from "memoize-one";
@@ -15,6 +14,10 @@ class HaUsersPickerLight extends LitElement {
@property() public value?: string[];
+ @property({ type: Boolean }) public disabled?: boolean;
+
+ @property({ type: Boolean }) public required?: boolean;
+
@property({ attribute: "picked-user-label" })
public pickedUserLabel?: string;
@@ -24,6 +27,9 @@ class HaUsersPickerLight extends LitElement {
@property({ attribute: false })
public users?: User[];
+ @property({ type: Boolean, attribute: "include-system" })
+ public includeSystem?: boolean;
+
protected firstUpdated(changedProps) {
super.firstUpdated(changedProps);
if (this.users === undefined) {
@@ -38,69 +44,80 @@ class HaUsersPickerLight extends LitElement {
return nothing;
}
- const notSelectedUsers = this._notSelectedUsers(this.users, this.value);
+ const filteredUsers = this._filteredUsers(this.users, this.includeSystem);
+ const selectedUsers = this._selectedUsers(filteredUsers, this.value);
+ const notSelectedUsers = this._notSelectedUsers(filteredUsers, this.value);
+
return html`
- ${guard(
- [notSelectedUsers],
- () =>
- this.value?.map(
- (user_id, idx) => html`
-
-
-
- >
-
- `
- )
+ ${guard([notSelectedUsers], () =>
+ selectedUsers.map(
+ (user, idx) => html`
+
+
+
+ `
+ )
)}
+ ${this._renderPicker(notSelectedUsers)}
+ `;
+ }
+
+ private _renderPicker(users?: User[]) {
+ return html`
`;
}
- private _notSelectedUsers = memoizeOne(
- (users?: User[], currentUsers?: string[]) =>
- currentUsers
- ? users?.filter(
- (user) => !user.system_generated && !currentUsers.includes(user.id)
- )
- : users?.filter((user) => !user.system_generated)
+ private _filteredUsers = memoizeOne(
+ (users: User[], includeSystem?: boolean) =>
+ users.filter((user) => includeSystem || !user.system_generated)
);
- private _notSelectedUsersAndSelected = (
- userId: string,
+ private _selectedUsers = memoizeOne(
+ (users: User[], selectedUserIds?: string[]) => {
+ if (!selectedUserIds) {
+ return [];
+ }
+ return users.filter((user) => selectedUserIds.includes(user.id));
+ }
+ );
+
+ private _notSelectedUsers = memoizeOne(
+ (users: User[], selectedUserIds?: string[]) => {
+ if (!selectedUserIds) {
+ return users;
+ }
+ return users.filter((user) => !selectedUserIds.includes(user.id));
+ }
+ );
+
+ private _notSelectedUsersAndCurrent = (
+ currentUser: User,
users?: User[],
notSelected?: User[]
) => {
- const selectedUser = users?.find((user) => user.id === userId);
+ const selectedUser = users?.find((user) => user.id === currentUser.id);
if (selectedUser) {
return notSelected ? [...notSelected, selectedUser] : [selectedUser];
}
@@ -123,7 +140,7 @@ class HaUsersPickerLight extends LitElement {
const index = (event.currentTarget as any).index;
const newValue = event.detail.value;
const newUsers = [...this._currentUsers];
- if (newValue === "") {
+ if (newValue === undefined) {
newUsers.splice(index, 1);
} else {
newUsers.splice(index, 1, newValue);
@@ -146,22 +163,11 @@ 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 get styles(): CSSResultGroup {
- return css`
- :host {
- display: block;
- }
- div {
- display: flex;
- align-items: center;
- }
- `;
- }
+ static override styles = css`
+ div {
+ margin-top: 8px;
+ }
+ `;
}
declare global {
diff --git a/src/data/selector.ts b/src/data/selector.ts
index aaa0a23c0f..618691c271 100644
--- a/src/data/selector.ts
+++ b/src/data/selector.ts
@@ -52,7 +52,8 @@ export type Selector =
| TTSSelector
| TTSVoiceSelector
| UiActionSelector
- | UiColorSelector;
+ | UiColorSelector
+ | UserSelector;
export interface ActionSelector {
action: {
@@ -392,6 +393,13 @@ export interface UiColorSelector {
ui_color: {} | null;
}
+export interface UserSelector {
+ user: {
+ multiple?: boolean;
+ include_system?: boolean;
+ } | null;
+}
+
export const expandAreaTarget = (
hass: HomeAssistant,
areaId: string,
diff --git a/src/translations/en.json b/src/translations/en.json
index e3047241c3..3ecee3faca 100644
--- a/src/translations/en.json
+++ b/src/translations/en.json
@@ -444,9 +444,7 @@
"none": "None"
},
"user-picker": {
- "no_user": "No user",
- "add_user": "Add user",
- "remove_user": "Remove user"
+ "user": "User"
},
"blueprint-picker": {
"select_blueprint": "Select a blueprint"