Compare commits

..

1 Commits

Author SHA1 Message Date
Paul Bottein
c080ebbf46 Add user selector with multiple and system option 2023-10-31 11:19:16 +01:00
22 changed files with 365 additions and 270 deletions

View File

@@ -1,9 +1,8 @@
import Fuse from "fuse.js"; import Fuse from "fuse.js";
import type { IFuseOptions } from "fuse.js";
import { StoreAddon } from "../../../src/data/supervisor/store"; import { StoreAddon } from "../../../src/data/supervisor/store";
export function filterAndSort(addons: StoreAddon[], filter: string) { export function filterAndSort(addons: StoreAddon[], filter: string) {
const options: IFuseOptions<StoreAddon> = { const options: Fuse.IFuseOptions<StoreAddon> = {
keys: ["name", "description", "slug"], keys: ["name", "description", "slug"],
isCaseSensitive: false, isCaseSensitive: false,
minMatchCharLength: 2, minMatchCharLength: 2,

View File

@@ -111,7 +111,7 @@
"date-fns-tz": "2.0.0", "date-fns-tz": "2.0.0",
"deep-clone-simple": "1.1.1", "deep-clone-simple": "1.1.1",
"deep-freeze": "0.0.1", "deep-freeze": "0.0.1",
"fuse.js": "7.0.0", "fuse.js": "6.6.2",
"google-timezones-json": "1.2.0", "google-timezones-json": "1.2.0",
"hls.js": "1.4.12", "hls.js": "1.4.12",
"home-assistant-js-websocket": "9.1.0", "home-assistant-js-websocket": "9.1.0",
@@ -138,7 +138,7 @@
"tinykeys": "2.1.0", "tinykeys": "2.1.0",
"tsparticles-engine": "2.12.0", "tsparticles-engine": "2.12.0",
"tsparticles-preset-links": "2.12.0", "tsparticles-preset-links": "2.12.0",
"ua-parser-js": "1.0.37", "ua-parser-js": "1.0.36",
"unfetch": "5.0.0", "unfetch": "5.0.0",
"vis-data": "7.1.7", "vis-data": "7.1.7",
"vis-network": "9.1.8", "vis-network": "9.1.8",
@@ -241,7 +241,7 @@
"systemjs": "6.14.2", "systemjs": "6.14.2",
"tar": "6.2.0", "tar": "6.2.0",
"terser-webpack-plugin": "5.3.9", "terser-webpack-plugin": "5.3.9",
"ts-lit-plugin": "2.0.1", "ts-lit-plugin": "2.0.0",
"typescript": "5.2.2", "typescript": "5.2.2",
"vinyl-buffer": "1.0.1", "vinyl-buffer": "1.0.1",
"vinyl-source-stream": "2.0.0", "vinyl-source-stream": "2.0.0",

View File

@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project] [project]
name = "home-assistant-frontend" name = "home-assistant-frontend"
version = "20231030.0" version = "20231027.0"
license = {text = "Apache-2.0"} license = {text = "Apache-2.0"}
description = "The Home Assistant frontend" description = "The Home Assistant frontend"
readme = "README.md" readme = "README.md"

View File

@@ -134,7 +134,7 @@ _adapters._date.override({
this.options.config this.options.config
); );
case "week": case "week":
return formatDateVeryShort( return formatDate(
new Date(time), new Date(time),
this.options.locale, this.options.locale,
this.options.config this.options.config

View File

@@ -73,8 +73,6 @@ export class StatisticsChart extends LitElement {
@property({ type: Boolean }) public isLoadingData = false; @property({ type: Boolean }) public isLoadingData = false;
@property() public period?: string;
@state() private _chartData: ChartData = { datasets: [] }; @state() private _chartData: ChartData = { datasets: [] };
@state() private _statisticIds: string[] = []; @state() private _statisticIds: string[] = [];
@@ -94,12 +92,7 @@ export class StatisticsChart extends LitElement {
} }
public willUpdate(changedProps: PropertyValues) { public willUpdate(changedProps: PropertyValues) {
if ( if (!this.hasUpdated || changedProps.has("unit")) {
!this.hasUpdated ||
changedProps.has("unit") ||
changedProps.has("period") ||
changedProps.has("chartType")
) {
this._createOptions(); this._createOptions();
} }
if ( if (
@@ -167,7 +160,6 @@ export class StatisticsChart extends LitElement {
}, },
}, },
ticks: { ticks: {
source: this.chartType === "bar" ? "data" : undefined,
maxRotation: 0, maxRotation: 0,
sampleSize: 5, sampleSize: 5,
autoSkipPadding: 20, autoSkipPadding: 20,
@@ -181,12 +173,6 @@ export class StatisticsChart extends LitElement {
}, },
time: { time: {
tooltipFormat: "datetime", tooltipFormat: "datetime",
unit:
this.chartType === "bar" &&
this.period &&
["hour", "day", "week", "month"].includes(this.period)
? this.period
: undefined,
}, },
}, },
y: { y: {

View File

@@ -71,7 +71,8 @@ class HaEntitiesPickerLight extends LitElement {
@property({ attribute: "picked-entity-label" }) @property({ attribute: "picked-entity-label" })
public pickedEntityLabel?: string; public pickedEntityLabel?: string;
@property({ attribute: "pick-entity-label" }) public pickEntityLabel?: string; @property({ attribute: "pick-entity-label" })
public pickEntityLabel?: string;
@property() public entityFilter?: HaEntityPickerEntityFilterFunc; @property() public entityFilter?: HaEntityPickerEntityFilterFunc;

View File

@@ -21,7 +21,8 @@ export class HaButton extends Button {
display: flex; display: flex;
} }
.slot-container { .slot-container {
overflow: var(--button-slot-container-overflow, visible); width: 100%;
overflow: hidden;
} }
`, `,
]; ];

View File

@@ -0,0 +1,65 @@
import { LitElement, css, html } from "lit";
import { customElement, property } from "lit/decorators";
import type { UserSelector } from "../../data/selector";
import { HomeAssistant } from "../../types";
import "../user/ha-user-picker";
import "../user/ha-users-picker";
@customElement("ha-selector-user")
export class HaUserSelector extends LitElement {
@property() public hass!: HomeAssistant;
@property() public selector!: UserSelector;
@property() public value?: any;
@property() public label?: string;
@property() public helper?: string;
@property({ type: Boolean }) public disabled = false;
@property({ type: Boolean }) public required = true;
protected render() {
if (this.selector.user?.multiple) {
return html`
<ha-users-picker
.hass=${this.hass}
.value=${this.value}
.label=${this.label}
.helper=${this.helper}
.disabled=${this.disabled}
.required=${this.required}
.includeSystem=${this.selector.user.include_system}
></ha-users-picker>
`;
}
return html`
<ha-user-picker
.hass=${this.hass}
.value=${this.value}
.label=${this.label}
.helper=${this.helper}
.disabled=${this.disabled}
.required=${this.required}
.includeSystem=${this.selector.user?.include_system}
></ha-user-picker>
`;
}
static get styles() {
return css`
ha-user-picker {
width: 100%;
}
`;
}
}
declare global {
interface HTMLElementTagNameMap {
"ha-selector-user": HaUserSelector;
}
}

View File

@@ -50,6 +50,7 @@ const LOAD_ELEMENTS = {
color_temp: () => import("./ha-selector-color-temp"), color_temp: () => import("./ha-selector-color-temp"),
ui_action: () => import("./ha-selector-ui-action"), ui_action: () => import("./ha-selector-ui-action"),
ui_color: () => import("./ha-selector-ui-color"), ui_color: () => import("./ha-selector-ui-color"),
user: () => import("./ha-selector-user"),
}; };
const LEGACY_UI_SELECTORS = new Set(["ui-action", "ui-color"]); const LEGACY_UI_SELECTORS = new Set(["ui-action", "ui-color"]);

View File

@@ -1,68 +1,84 @@
import "@material/mwc-list/mwc-list-item"; import { ComboBoxLitRenderer } from "@vaadin/combo-box/lit";
import { css, CSSResultGroup, html, LitElement, TemplateResult } from "lit"; import {
import { property } from "lit/decorators"; css,
CSSResultGroup,
html,
LitElement,
PropertyValues,
TemplateResult,
} from "lit";
import { property, query, state } from "lit/decorators";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
import { fireEvent } from "../../common/dom/fire_event"; import { fireEvent } from "../../common/dom/fire_event";
import { stringCompare } from "../../common/string/compare"; import { stringCompare } from "../../common/string/compare";
import {
fuzzyFilterSort,
ScorableTextItem,
} from "../../common/string/filter/sequence-matching";
import { fetchUsers, User } from "../../data/user"; import { fetchUsers, User } from "../../data/user";
import { HomeAssistant } from "../../types"; import { HomeAssistant, ValueChangedEvent } from "../../types";
import "../ha-combo-box";
import type { HaComboBox } from "../ha-combo-box";
import "../ha-list-item";
import "../ha-select"; import "../ha-select";
import "./ha-user-badge"; import "./ha-user-badge";
import "../ha-list-item";
type ScorableUser = ScorableTextItem & User;
class HaUserPicker extends LitElement { class HaUserPicker extends LitElement {
public hass?: HomeAssistant; @property({ attribute: false }) public hass!: HomeAssistant;
@property() public label?: string; @property() public label?: string;
@property() public noUserLabel?: string; @property() public value?: string;
@property() public value = ""; @property() public helper?: string;
@property() public users?: User[]; @property({ attribute: false }) public users?: User[];
@property({ type: Boolean }) public disabled = false; @property({ type: Boolean }) public disabled?: boolean;
private _sortedUsers = memoizeOne((users?: User[]) => { @property({ type: Boolean }) public required?: boolean;
if (!users) {
return []; @state() private _opened?: boolean;
@query("ha-combo-box", true) public comboBox!: HaComboBox;
@property({ type: Boolean, attribute: "include-system" })
public includeSystem?: boolean;
private _init = false;
private _getUserItems = memoizeOne(
(users: User[] | undefined): ScorableUser[] => {
if (!users) {
return [];
}
return users
.sort((a, b) =>
stringCompare(a.name, b.name, this.hass!.locale.language)
)
.map((user) => ({
...user,
strings: [user.name, ...(user.username ? [user.username] : [])],
}));
} }
);
return users private _filteredUsers = memoizeOne(
.filter((user) => !user.system_generated) (users: User[], includeSystem?: boolean) =>
.sort((a, b) => users.filter((user) => includeSystem || !user.system_generated)
stringCompare(a.name, b.name, this.hass!.locale.language) );
);
});
protected render(): TemplateResult { public async open() {
return html` await this.updateComplete;
<ha-select await this.comboBox?.open();
.label=${this.label} }
.disabled=${this.disabled}
.value=${this.value} public async focus() {
@selected=${this._userChanged} await this.updateComplete;
> await this.comboBox?.focus();
${this.users?.length === 0
? html`<mwc-list-item value="">
${this.noUserLabel ||
this.hass?.localize("ui.components.user-picker.no_user")}
</mwc-list-item>`
: ""}
${this._sortedUsers(this.users).map(
(user) => html`
<ha-list-item graphic="avatar" .value=${user.id}>
<ha-user-badge
.hass=${this.hass}
.user=${user}
slot="graphic"
></ha-user-badge>
${user.name}
</ha-list-item>
`
)}
</ha-select>
`;
} }
protected firstUpdated(changedProps) { protected firstUpdated(changedProps) {
@@ -74,25 +90,104 @@ class HaUserPicker extends LitElement {
} }
} }
private _userChanged(ev) { protected updated(changedProps: PropertyValues) {
const newValue = ev.target.value; super.updated(changedProps);
if (
(!this._init && this.users) ||
(this._init && changedProps.has("_opened") && this._opened)
) {
this._init = true;
const filteredUsers = this._filteredUsers(
this.users ?? [],
this.includeSystem
);
const items = this._getUserItems(filteredUsers);
if (newValue !== this.value) { this.comboBox.items = items;
this.value = newValue; this.comboBox.filteredItems = items;
setTimeout(() => {
fireEvent(this, "value-changed", { value: newValue });
fireEvent(this, "change");
}, 0);
} }
} }
private _rowRenderer: ComboBoxLitRenderer<User> = (item) => html`
<ha-list-item
graphic="avatar"
.value=${item.id}
.twoline=${!!item.username}
>
<ha-user-badge
.hass=${this.hass}
.user=${item}
slot="graphic"
></ha-user-badge>
${item.name}
<span slot="secondary">${item.username}</span>
</ha-list-item>
`;
protected render(): TemplateResult {
return html`
<ha-combo-box
.hass=${this.hass}
.label=${this.label === undefined && this.hass
? this.hass.localize("ui.components.user-picker.user")
: this.label}
.value=${this._value}
.helper=${this.helper}
.renderer=${this._rowRenderer}
.disabled=${this.disabled}
.required=${this.required}
item-id-path="id"
item-value-path="id"
item-label-path="name"
@opened-changed=${this._openedChanged}
@value-changed=${this._valueChanged}
@filter-changed=${this._filterChanged}
>
</ha-combo-box>
`;
}
private _filterChanged(ev: CustomEvent): void {
const target = ev.target as HaComboBox;
const filterString = ev.detail.value.toLowerCase();
target.filteredItems = filterString.length
? fuzzyFilterSort<ScorableUser>(filterString, target.items || [])
: target.items;
}
private _valueChanged(ev: ValueChangedEvent<string>) {
ev.stopPropagation();
let newValue = ev.detail.value;
if (newValue === "no_users") {
newValue = "";
}
if (newValue !== this._value) {
this._setValue(newValue);
}
}
private _openedChanged(ev: ValueChangedEvent<boolean>) {
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 { static get styles(): CSSResultGroup {
return css` return css`
:host { ha-select {
display: inline-block; width: 100%;
}
mwc-list {
display: block;
} }
`; `;
} }

View File

@@ -1,5 +1,4 @@
import { mdiClose } from "@mdi/js"; import { css, html, LitElement, nothing } from "lit";
import { css, CSSResultGroup, html, LitElement, nothing } from "lit";
import { customElement, property } from "lit/decorators"; import { customElement, property } from "lit/decorators";
import { guard } from "lit/directives/guard"; import { guard } from "lit/directives/guard";
import memoizeOne from "memoize-one"; import memoizeOne from "memoize-one";
@@ -15,6 +14,10 @@ class HaUsersPickerLight extends LitElement {
@property() public value?: string[]; @property() public value?: string[];
@property({ type: Boolean }) public disabled?: boolean;
@property({ type: Boolean }) public required?: boolean;
@property({ attribute: "picked-user-label" }) @property({ attribute: "picked-user-label" })
public pickedUserLabel?: string; public pickedUserLabel?: string;
@@ -24,6 +27,9 @@ class HaUsersPickerLight extends LitElement {
@property({ attribute: false }) @property({ attribute: false })
public users?: User[]; public users?: User[];
@property({ type: Boolean, attribute: "include-system" })
public includeSystem?: boolean;
protected firstUpdated(changedProps) { protected firstUpdated(changedProps) {
super.firstUpdated(changedProps); super.firstUpdated(changedProps);
if (this.users === undefined) { if (this.users === undefined) {
@@ -38,69 +44,80 @@ class HaUsersPickerLight extends LitElement {
return nothing; 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` return html`
${guard( ${guard([notSelectedUsers], () =>
[notSelectedUsers], selectedUsers.map(
() => (user, idx) => html`
this.value?.map( <div>
(user_id, idx) => html` <ha-user-picker
<div> .label=${this.pickedUserLabel}
<ha-user-picker .index=${idx}
.label=${this.pickedUserLabel} .hass=${this.hass}
.noUserLabel=${this.hass!.localize( .value=${user.id}
"ui.components.user-picker.remove_user" .users=${this._notSelectedUsersAndCurrent(
)} user,
.index=${idx} filteredUsers,
.hass=${this.hass} notSelectedUsers
.value=${user_id} )}
.users=${this._notSelectedUsersAndSelected( @value-changed=${this._userChanged}
user_id, .disabled=${this.disabled}
this.users, .includeSystem=${this.includeSystem}
notSelectedUsers ></ha-user-picker>
)} </div>
@value-changed=${this._userChanged} `
></ha-user-picker> )
<ha-icon-button
.userId=${user_id}
.label=${this.hass!.localize(
"ui.components.user-picker.remove_user"
)}
.path=${mdiClose}
@click=${this._removeUser}
>
></ha-icon-button
>
</div>
`
)
)} )}
<div>${this._renderPicker(notSelectedUsers)}</div>
`;
}
private _renderPicker(users?: User[]) {
return html`
<ha-user-picker <ha-user-picker
.label=${this.pickUserLabel || .label=${this.pickUserLabel}
this.hass!.localize("ui.components.user-picker.add_user")}
.hass=${this.hass} .hass=${this.hass}
.users=${notSelectedUsers} .users=${users}
.disabled=${!notSelectedUsers?.length}
@value-changed=${this._addUser} @value-changed=${this._addUser}
.disabled=${this.disabled || users?.length === 0}
.required=${this.required}
.includeSystem=${this.includeSystem}
></ha-user-picker> ></ha-user-picker>
`; `;
} }
private _notSelectedUsers = memoizeOne( private _filteredUsers = memoizeOne(
(users?: User[], currentUsers?: string[]) => (users: User[], includeSystem?: boolean) =>
currentUsers users.filter((user) => includeSystem || !user.system_generated)
? users?.filter(
(user) => !user.system_generated && !currentUsers.includes(user.id)
)
: users?.filter((user) => !user.system_generated)
); );
private _notSelectedUsersAndSelected = ( private _selectedUsers = memoizeOne(
userId: string, (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[], users?: User[],
notSelected?: User[] notSelected?: User[]
) => { ) => {
const selectedUser = users?.find((user) => user.id === userId); const selectedUser = users?.find((user) => user.id === currentUser.id);
if (selectedUser) { if (selectedUser) {
return notSelected ? [...notSelected, selectedUser] : [selectedUser]; return notSelected ? [...notSelected, selectedUser] : [selectedUser];
} }
@@ -123,7 +140,7 @@ class HaUsersPickerLight extends LitElement {
const index = (event.currentTarget as any).index; const index = (event.currentTarget as any).index;
const newValue = event.detail.value; const newValue = event.detail.value;
const newUsers = [...this._currentUsers]; const newUsers = [...this._currentUsers];
if (newValue === "") { if (newValue === undefined) {
newUsers.splice(index, 1); newUsers.splice(index, 1);
} else { } else {
newUsers.splice(index, 1, newValue); newUsers.splice(index, 1, newValue);
@@ -146,22 +163,11 @@ class HaUsersPickerLight extends LitElement {
this._updateUsers([...currentUsers, toAdd]); this._updateUsers([...currentUsers, toAdd]);
} }
private _removeUser(event) { static override styles = css`
const userId = (event.currentTarget as any).userId; div {
this._updateUsers(this._currentUsers.filter((user) => user !== userId)); margin-top: 8px;
} }
`;
static get styles(): CSSResultGroup {
return css`
:host {
display: block;
}
div {
display: flex;
align-items: center;
}
`;
}
} }
declare global { declare global {

View File

@@ -642,7 +642,10 @@ const tryDescribeTrigger = (
} }
// Device Trigger // Device Trigger
if (trigger.platform === "device" && trigger.device_id) { if (trigger.platform === "device") {
if (!trigger.device_id) {
return "Device trigger";
}
const config = trigger as DeviceTrigger; const config = trigger as DeviceTrigger;
const localized = localizeDeviceAutomationTrigger( const localized = localizeDeviceAutomationTrigger(
hass, hass,
@@ -658,12 +661,9 @@ const tryDescribeTrigger = (
}`; }`;
} }
return ( return `${
hass.localize( trigger.platform ? trigger.platform.replace(/_/g, " ") : "Unknown"
`ui.panel.config.automation.editor.triggers.type.${trigger.platform}.label` } trigger`;
) ||
hass.localize(`ui.panel.config.automation.editor.triggers.unknown_trigger`)
);
}; };
export const describeCondition = ( export const describeCondition = (
@@ -1074,7 +1074,10 @@ const tryDescribeCondition = (
); );
} }
if (condition.condition === "device" && condition.device_id) { if (condition.condition === "device") {
if (!condition.device_id) {
return "Device condition";
}
const config = condition as DeviceCondition; const config = condition as DeviceCondition;
const localized = localizeDeviceAutomationCondition( const localized = localizeDeviceAutomationCondition(
hass, hass,
@@ -1090,16 +1093,14 @@ const tryDescribeCondition = (
}`; }`;
} }
if (condition.condition === "trigger" && condition.id) { if (condition.condition === "trigger") {
if (!condition.id) {
return "Trigger condition";
}
return `When triggered by ${condition.id}`; return `When triggered by ${condition.id}`;
} }
return ( return `${
hass.localize( condition.condition ? condition.condition.replace(/_/g, " ") : "Unknown"
`ui.panel.config.automation.editor.conditions.type.${condition.condition}.label` } condition`;
) ||
hass.localize(
`ui.panel.config.automation.editor.conditions.unknown_condition`
)
);
}; };

View File

@@ -52,7 +52,8 @@ export type Selector =
| TTSSelector | TTSSelector
| TTSVoiceSelector | TTSVoiceSelector
| UiActionSelector | UiActionSelector
| UiColorSelector; | UiColorSelector
| UserSelector;
export interface ActionSelector { export interface ActionSelector {
action: { action: {
@@ -392,6 +393,13 @@ export interface UiColorSelector {
ui_color: {} | null; ui_color: {} | null;
} }
export interface UserSelector {
user: {
multiple?: boolean;
include_system?: boolean;
} | null;
}
export const expandAreaTarget = ( export const expandAreaTarget = (
hass: HomeAssistant, hass: HomeAssistant,
areaId: string, areaId: string,

View File

@@ -61,12 +61,7 @@ export const updateItem = (
entity_id: string, entity_id: string,
item: TodoItem item: TodoItem
): Promise<ServiceCallResponse> => ): Promise<ServiceCallResponse> =>
hass.callService( hass.callService("todo", "update_item", item, { entity_id });
"todo",
"update_item",
{ item: item.uid, rename: item.summary, status: item.status },
{ entity_id }
);
export const createItem = ( export const createItem = (
hass: HomeAssistant, hass: HomeAssistant,
@@ -75,9 +70,9 @@ export const createItem = (
): Promise<ServiceCallResponse> => ): Promise<ServiceCallResponse> =>
hass.callService( hass.callService(
"todo", "todo",
"add_item", "create_item",
{ {
item: summary, summary,
}, },
{ entity_id } { entity_id }
); );
@@ -89,9 +84,9 @@ export const deleteItem = (
): Promise<ServiceCallResponse> => ): Promise<ServiceCallResponse> =>
hass.callService( hass.callService(
"todo", "todo",
"remove_item", "delete_item",
{ {
item: uid, uid,
}, },
{ entity_id } { entity_id }
); );

View File

@@ -1,6 +1,6 @@
import "@material/mwc-button"; import "@material/mwc-button";
import "@material/mwc-list/mwc-list"; import "@material/mwc-list/mwc-list";
import Fuse, { IFuseOptions } from "fuse.js"; import Fuse from "fuse.js";
import { HassConfig } from "home-assistant-js-websocket"; import { HassConfig } from "home-assistant-js-websocket";
import { import {
css, css,
@@ -239,7 +239,7 @@ class AddIntegrationDialog extends LitElement {
}); });
if (filter) { if (filter) {
const options: IFuseOptions<IntegrationListItem> = { const options: Fuse.IFuseOptions<IntegrationListItem> = {
keys: [ keys: [
{ name: "name", weight: 5 }, { name: "name", weight: 5 },
{ name: "domain", weight: 5 }, { name: "domain", weight: 5 },

View File

@@ -1,7 +1,6 @@
import { ActionDetail } from "@material/mwc-list"; import { ActionDetail } from "@material/mwc-list";
import { mdiFilterVariant, mdiPlus } from "@mdi/js"; import { mdiFilterVariant, mdiPlus } from "@mdi/js";
import Fuse from "fuse.js"; import Fuse from "fuse.js";
import type { IFuseOptions } from "fuse.js";
import type { UnsubscribeFunc } from "home-assistant-js-websocket"; import type { UnsubscribeFunc } from "home-assistant-js-websocket";
import { import {
css, css,
@@ -158,7 +157,7 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
const disabled: ConfigEntryExtended[] = []; const disabled: ConfigEntryExtended[] = [];
const integrations: ConfigEntryExtended[] = []; const integrations: ConfigEntryExtended[] = [];
if (filter) { if (filter) {
const options: IFuseOptions<ConfigEntryExtended> = { const options: Fuse.IFuseOptions<ConfigEntryExtended> = {
keys: ["domain", "localized_domain_name", "title"], keys: ["domain", "localized_domain_name", "title"],
isCaseSensitive: false, isCaseSensitive: false,
minMatchCharLength: 2, minMatchCharLength: 2,
@@ -202,7 +201,7 @@ class HaConfigIntegrationsDashboard extends SubscribeMixin(LitElement) {
): DataEntryFlowProgressExtended[] => { ): DataEntryFlowProgressExtended[] => {
let filteredEntries: DataEntryFlowProgressExtended[]; let filteredEntries: DataEntryFlowProgressExtended[];
if (filter) { if (filter) {
const options: IFuseOptions<DataEntryFlowProgressExtended> = { const options: Fuse.IFuseOptions<DataEntryFlowProgressExtended> = {
keys: ["handler", "localized_title"], keys: ["handler", "localized_title"],
isCaseSensitive: false, isCaseSensitive: false,
minMatchCharLength: 2, minMatchCharLength: 2,

View File

@@ -193,7 +193,6 @@ export class HuiStatisticsGraphCard extends LitElement implements LovelaceCard {
.isLoadingData=${!this._statistics} .isLoadingData=${!this._statistics}
.statisticsData=${this._statistics} .statisticsData=${this._statistics}
.metadata=${this._metadata} .metadata=${this._metadata}
.period=${this._config.period}
.chartType=${this._config.chart_type || "line"} .chartType=${this._config.chart_type || "line"}
.statTypes=${this._statTypes!} .statTypes=${this._statTypes!}
.names=${this._names} .names=${this._names}

View File

@@ -1,10 +1,4 @@
import { import { mdiDrag, mdiNotificationClearAll, mdiPlus, mdiSort } from "@mdi/js";
mdiDelete,
mdiDrag,
mdiNotificationClearAll,
mdiPlus,
mdiSort,
} from "@mdi/js";
import { UnsubscribeFunc } from "home-assistant-js-websocket"; import { UnsubscribeFunc } from "home-assistant-js-websocket";
import { import {
CSSResultGroup, CSSResultGroup,
@@ -299,23 +293,6 @@ export class HuiTodoListCard
.itemId=${item.uid} .itemId=${item.uid}
@change=${this._saveEdit} @change=${this._saveEdit}
></ha-textfield> ></ha-textfield>
${this.todoListSupportsFeature(
TodoListEntityFeature.DELETE_TODO_ITEM
) &&
!this.todoListSupportsFeature(
TodoListEntityFeature.UPDATE_TODO_ITEM
)
? html`<ha-icon-button
.title=${this.hass!.localize(
"ui.panel.lovelace.cards.todo-list.delete_item"
)}
class="deleteItemButton"
.path=${mdiDelete}
.itemId=${item.uid}
@click=${this._deleteItem}
>
</ha-icon-button>`
: nothing}
</div> </div>
` `
)} )}
@@ -364,23 +341,7 @@ export class HuiTodoListCard
> >
</ha-svg-icon> </ha-svg-icon>
` `
: this.todoListSupportsFeature( : ""}
TodoListEntityFeature.DELETE_TODO_ITEM
) &&
!this.todoListSupportsFeature(
TodoListEntityFeature.UPDATE_TODO_ITEM
)
? html`<ha-icon-button
.title=${this.hass!.localize(
"ui.panel.lovelace.cards.todo-list.delete_item"
)}
class="deleteItemButton"
.path=${mdiDelete}
.itemId=${item.uid}
@click=${this._deleteItem}
>
</ha-icon-button>`
: nothing}
</div> </div>
` `
)} )}
@@ -470,16 +431,6 @@ export class HuiTodoListCard
} }
} }
private _deleteItem(ev): void {
const item = this._getItem(ev.target.itemId);
if (!item) {
return;
}
deleteItem(this.hass!, this._entityId!, item.uid).finally(() =>
this._fetchData()
);
}
private _addKeyPress(ev): void { private _addKeyPress(ev): void {
if (ev.key === "Enter") { if (ev.key === "Enter") {
this._addItem(null); this._addItem(null);
@@ -590,12 +541,6 @@ export class HuiTodoListCard
direction: var(--direction); direction: var(--direction);
} }
.deleteItemButton {
margin-right: -12px;
margin-inline-end: -12px;
direction: var(--direction);
}
.reorderButton { .reorderButton {
margin-right: -12px; margin-right: -12px;
margin-inline-end: -12px; margin-inline-end: -12px;

View File

@@ -1,4 +1,4 @@
import Fuse, { IFuseOptions } from "fuse.js"; import Fuse from "fuse.js";
import { import {
css, css,
CSSResultGroup, CSSResultGroup,
@@ -80,7 +80,7 @@ export class HuiCardPicker extends LitElement {
let cards = cardElements.map( let cards = cardElements.map(
(cardElement: CardElement) => cardElement.card (cardElement: CardElement) => cardElement.card
); );
const options: IFuseOptions<Card> = { const options: Fuse.IFuseOptions<Card> = {
keys: ["type", "name", "description"], keys: ["type", "name", "description"],
isCaseSensitive: false, isCaseSensitive: false,
minMatchCharLength: 2, minMatchCharLength: 2,

View File

@@ -349,7 +349,6 @@ class PanelTodo extends LitElement {
max-width: 100%; max-width: 100%;
} }
ha-button-menu ha-button { ha-button-menu ha-button {
--button-slot-container-overflow: hidden;
max-width: 100%; max-width: 100%;
--mdc-theme-primary: currentColor; --mdc-theme-primary: currentColor;
--mdc-typography-button-text-transform: none; --mdc-typography-button-text-transform: none;

View File

@@ -444,9 +444,7 @@
"none": "None" "none": "None"
}, },
"user-picker": { "user-picker": {
"no_user": "No user", "user": "User"
"add_user": "Add user",
"remove_user": "Remove user"
}, },
"blueprint-picker": { "blueprint-picker": {
"select_blueprint": "Select a blueprint" "select_blueprint": "Select a blueprint"
@@ -2401,7 +2399,6 @@
"delete_confirm_text": "It will be permanently deleted.", "delete_confirm_text": "It will be permanently deleted.",
"unsupported_platform": "No visual editor support for platform: {platform}", "unsupported_platform": "No visual editor support for platform: {platform}",
"type_select": "Trigger type", "type_select": "Trigger type",
"unknown_trigger": "[%key:ui::panel::config::devices::automation::triggers::unknown_trigger%]",
"type": { "type": {
"calendar": { "calendar": {
"label": "Calendar", "label": "Calendar",
@@ -2597,7 +2594,6 @@
"delete_confirm_text": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm_text%]", "delete_confirm_text": "[%key:ui::panel::config::automation::editor::triggers::delete_confirm_text%]",
"unsupported_condition": "No visual editor support for condition: {condition}", "unsupported_condition": "No visual editor support for condition: {condition}",
"type_select": "Condition type", "type_select": "Condition type",
"unknown_condition": "[%key:ui::panel::config::devices::automation::conditions::unknown_condition%]",
"type": { "type": {
"and": { "and": {
"label": "And", "label": "And",
@@ -4528,8 +4524,7 @@
"clear_items": "Clear checked items", "clear_items": "Clear checked items",
"add_item": "Add item", "add_item": "Add item",
"reorder_items": "Reorder items", "reorder_items": "Reorder items",
"drag_and_drop": "Drag and drop", "drag_and_drop": "Drag and drop"
"delete_item": "Delete item"
}, },
"picture-elements": { "picture-elements": {
"hold": "Hold:", "hold": "Hold:",

View File

@@ -9025,10 +9025,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fuse.js@npm:7.0.0": "fuse.js@npm:6.6.2":
version: 7.0.0 version: 6.6.2
resolution: "fuse.js@npm:7.0.0" resolution: "fuse.js@npm:6.6.2"
checksum: d15750efec1808370c0cae92ec9473aa7261c59bca1f15f1cf60039ba6f804b8f95340b5cabd83a4ef55839c1034764856e0128e443921f072aa0d8a20e4cacf checksum: 17ae758ce205276ebd88bd9c9f088a100be0b4896abac9f6b09847151269d1690f41d7f98ff5813d4a58973162dbd99d0072ce807020fee6f9de60170f6b08eb
languageName: node languageName: node
linkType: hard linkType: hard
@@ -9772,7 +9772,7 @@ __metadata:
esprima: 4.0.1 esprima: 4.0.1
fancy-log: 2.0.0 fancy-log: 2.0.0
fs-extra: 11.1.1 fs-extra: 11.1.1
fuse.js: 7.0.0 fuse.js: 6.6.2
glob: 10.3.10 glob: 10.3.10
google-timezones-json: 1.2.0 google-timezones-json: 1.2.0
gulp: 4.0.2 gulp: 4.0.2
@@ -9828,11 +9828,11 @@ __metadata:
tar: 6.2.0 tar: 6.2.0
terser-webpack-plugin: 5.3.9 terser-webpack-plugin: 5.3.9
tinykeys: 2.1.0 tinykeys: 2.1.0
ts-lit-plugin: 2.0.1 ts-lit-plugin: 2.0.0
tsparticles-engine: 2.12.0 tsparticles-engine: 2.12.0
tsparticles-preset-links: 2.12.0 tsparticles-preset-links: 2.12.0
typescript: 5.2.2 typescript: 5.2.2
ua-parser-js: 1.0.37 ua-parser-js: 1.0.36
unfetch: 5.0.0 unfetch: 5.0.0
vinyl-buffer: 1.0.1 vinyl-buffer: 1.0.1
vinyl-source-stream: 2.0.0 vinyl-source-stream: 2.0.0
@@ -11424,7 +11424,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"lit-analyzer@npm:2.0.1, lit-analyzer@npm:^2.0.1": "lit-analyzer@npm:2.0.1, lit-analyzer@npm:^2.0.0":
version: 2.0.1 version: 2.0.1
resolution: "lit-analyzer@npm:2.0.1" resolution: "lit-analyzer@npm:2.0.1"
dependencies: dependencies:
@@ -15385,13 +15385,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ts-lit-plugin@npm:2.0.1": "ts-lit-plugin@npm:2.0.0":
version: 2.0.1 version: 2.0.0
resolution: "ts-lit-plugin@npm:2.0.1" resolution: "ts-lit-plugin@npm:2.0.0"
dependencies: dependencies:
lit-analyzer: ^2.0.1 lit-analyzer: ^2.0.0
web-component-analyzer: ^2.0.0 web-component-analyzer: ^2.0.0
checksum: 027d632664933958667965c21b34fffb01533a2004266750a52d15b3acfe3ee39fcb7e206086ea046bdb6e437f4d91daf40c66ba6c9fe23a04769340bc895278 checksum: 1dd6ceffb98d03c81de5a7874c81058cfda6be7a4b4cd760acab1776b9bb32143117aca9ff4c922118e83d919e0a09fc4fa21160ff7133a2133ef9d9e7eea2fc
languageName: node languageName: node
linkType: hard linkType: hard
@@ -15673,10 +15673,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ua-parser-js@npm:1.0.37": "ua-parser-js@npm:1.0.36":
version: 1.0.37 version: 1.0.36
resolution: "ua-parser-js@npm:1.0.37" resolution: "ua-parser-js@npm:1.0.36"
checksum: 4d481c720d523366d7762dc8a46a1b58967d979aacf786f9ceceb1cd767de069f64a4bdffb63956294f1c0696eb465ddb950f28ba90571709e33521b4bd75e07 checksum: 5b2c8a5e3443dfbba7624421805de946457c26ae167cb2275781a2729d1518f7067c9d5c74c3b0acac4b9ff3278cae4eace08ca6eecb63848bc3b2f6a63cc975
languageName: node languageName: node
linkType: hard linkType: hard