diff --git a/src/panels/config/automation/ha-automation-picker.ts b/src/panels/config/automation/ha-automation-picker.ts index ac42955c05..50d14b34ef 100644 --- a/src/panels/config/automation/ha-automation-picker.ts +++ b/src/panels/config/automation/ha-automation-picker.ts @@ -1,7 +1,7 @@ +import "@home-assistant/webawesome/dist/components/divider/divider"; import { ResizeController } from "@lit-labs/observers/resize-controller"; import { consume } from "@lit/context"; import { - mdiChevronRight, mdiCog, mdiContentDuplicate, mdiDelete, @@ -50,6 +50,9 @@ import type { } from "../../../components/data-table/ha-data-table"; import "../../../components/data-table/ha-data-table-labels"; import "../../../components/entity/ha-entity-toggle"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; +import type { HaDropdownItem } from "../../../components/ha-dropdown-item"; import "../../../components/ha-fab"; import "../../../components/ha-filter-blueprints"; import "../../../components/ha-filter-categories"; @@ -59,7 +62,6 @@ import "../../../components/ha-filter-floor-areas"; import "../../../components/ha-filter-labels"; import "../../../components/ha-filter-voice-assistants"; import "../../../components/ha-icon-button"; -import "../../../components/ha-md-divider"; import "../../../components/ha-md-menu"; import type { HaMdMenu } from "../../../components/ha-md-menu"; import "../../../components/ha-md-menu-item"; @@ -87,9 +89,9 @@ import { fullEntitiesContext } from "../../../data/context"; import type { DataTableFilters } from "../../../data/data_table_filters"; import { deserializeFilters, - serializeFilters, isUsedFilter as isFilterUsed, isUsedRelatedItemsFilter as isRelatedItemsFilterUsed, + serializeFilters, } from "../../../data/data_table_filters"; import { UNAVAILABLE } from "../../../data/entity/entity"; import type { @@ -97,6 +99,7 @@ import type { UpdateEntityRegistryEntryResult, } from "../../../data/entity/entity_registry"; import { updateEntityRegistryEntry } from "../../../data/entity/entity_registry"; +import { getEntityVoiceAssistantsIds } from "../../../data/expose"; import type { LabelRegistryEntry } from "../../../data/label/label_registry"; import { createLabelRegistryEntry, @@ -118,13 +121,12 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; -import { showNewAutomationDialog } from "./show-dialog-new-automation"; -import { getEntityVoiceAssistantsIds } from "../../../data/expose"; -import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants"; import { - getAssistantsTableColumn, getAssistantsSortableKey, + getAssistantsTableColumn, } from "../voice-assistants/expose/assistants-table-column"; +import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants"; +import { showNewAutomationDialog } from "./show-dialog-new-automation"; type AutomationItem = AutomationEntity & { name: string; @@ -441,103 +443,6 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { } protected render(): TemplateResult { - const categoryItems = html`${this._categories?.map( - (category) => - html` - ${category.icon - ? html`` - : html``} -
${category.name}
-
` - )} - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.no_category" - )} -
-
- - -
- ${this.hass.localize("ui.panel.config.category.editor.add")} -
-
`; - - const labelItems = html`${this._labels?.map((label) => { - const color = label.color ? computeCssColor(label.color) : undefined; - const selected = this._selected.every((entityId) => - this.hass.entities[entityId]?.labels.includes(label.label_id) - ); - const partial = - !selected && - this._selected.some((entityId) => - this.hass.entities[entityId]?.labels.includes(label.label_id) - ); - return html` - - - ${label.icon - ? html`` - : nothing} - ${label.name} - - `; - })} - - -
- ${this.hass.localize("ui.panel.config.labels.add_label")} -
`; - - const areaItems = html`${Object.values(this.hass.areas).map( - (area) => - html` - ${area.icon - ? html`` - : html``} -
${area.name}
-
` - )} - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.no_area" - )} -
-
- - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.add_area" - )} -
-
`; - const areasInOverflow = (this._sizeController.value && this._sizeController.value < 900) || (!this._sizeController.value && this.hass.dockedSidebar === "docked"); @@ -558,9 +463,9 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { - Array.isArray(filter.value) - ? filter.value.length - : filter.value && - Object.values(filter.value).some((val) => - Array.isArray(val) ? val.length : val - ) - ).length - } + .filters=${Object.values(this._filters).filter((filter) => + Array.isArray(filter.value) + ? filter.value.length + : filter.value && + Object.values(filter.value).some((val) => + Array.isArray(val) ? val.length : val + ) + ).length} .columns=${this._columns( this.narrow, this.hass.localize, @@ -684,13 +587,34 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { .narrow=${this.narrow} @expanded-changed=${this._filterExpanded} > - ${ - !this.narrow - ? html` + ${!this.narrow + ? html` + + + + ${this._renderCategoryItems()} + + ${labelsInOverflow + ? nothing + : html` - ${categoryItems} - - ${labelsInOverflow - ? nothing - : html` - - - - ${labelItems} - `} - ${areasInOverflow - ? nothing - : html` - - - - ${areaItems} - `}` - : nothing - } - - ${ - this.narrow - ? html``} + ${areasInOverflow + ? nothing + : html` - - ` - : html`` - } - - ${ - this.narrow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.move_category" - )} -
+ -
- ${categoryItems} -
` - : nothing - } - ${ - this.narrow || labelsInOverflow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.add_label" - )} -
- -
- ${labelItems} -
` - : nothing - } - ${ - this.narrow || areasInOverflow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.move_area" - )} -
- -
- ${areaItems} -
` - : nothing - } - - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.enable" + + ${this._renderAreaItems()} + `}` + : nothing} + + ${this.narrow + ? html` - - - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.disable" + slot="trigger" + > + + ` + : html` - - - ${ - !this.automations.length - ? html`
- -

- ${this.hass.localize( - "ui.panel.config.automation.picker.empty_header" - )} -

-

- ${this.hass.localize( - "ui.panel.config.automation.picker.empty_text_1" - )} -

-

- ${this.hass.localize( - "ui.panel.config.automation.picker.empty_text_2", - { user: this.hass.user?.name || "Alice" } - )} -

- - ${this.hass.localize("ui.panel.config.common.learn_more")} - - -
` - : nothing - } + slot="trigger" + >
`} + ${this.narrow + ? html` + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.move_category" + )} + ${this._renderCategoryItems("submenu")} + ` + : nothing} + ${this.narrow || labelsInOverflow + ? html` + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.add_label" + )} + ${this._renderLabelItems("submenu")} + ` + : nothing} + ${this.narrow || areasInOverflow + ? html` + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.move_area" + )} + ${this._renderAreaItems("submenu")} + ` + : nothing} + + + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.enable" + )} + + + + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.disable" + )} + + + ${!this.automations.length + ? html`
+ +

+ ${this.hass.localize( + "ui.panel.config.automation.picker.empty_header" + )} +

+

+ ${this.hass.localize( + "ui.panel.config.automation.picker.empty_text_1" + )} +

+

+ ${this.hass.localize( + "ui.panel.config.automation.picker.empty_text_2", + { user: this.hass.user?.name || "Alice" } + )} +

+ + ${this.hass.localize("ui.panel.config.common.learn_more")} + + +
` + : nothing}
- ${ - this._overflowAutomation?.state === "off" - ? this.hass.localize("ui.panel.config.automation.editor.enable") - : this.hass.localize( - "ui.panel.config.automation.editor.disable" - ) - } + ${this._overflowAutomation?.state === "off" + ? this.hass.localize("ui.panel.config.automation.editor.enable") + : this.hass.localize("ui.panel.config.automation.editor.disable")}
@@ -1282,12 +1140,22 @@ class HaAutomationPicker extends SubscribeMixin(LitElement) { } } - private _handleBulkCategory = async (item) => { - const category = item.value; - this._bulkAddCategory(category); + private _handleBulkCategory = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + const value = ev.detail.item.value; + if (value === "category_create") { + this._bulkCreateCategory(); + return; + } + if (value === "category_none") { + this._bulkAddCategory(null); + return; + } + if (value?.startsWith("category_")) { + this._bulkAddCategory(value.substring(9)); + } }; - private async _bulkAddCategory(category: string) { + private async _bulkAddCategory(category: string | null) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1312,11 +1180,20 @@ ${rejected } } - private async _handleBulkLabel(ev) { - const label = ev.currentTarget.value; - const action = ev.currentTarget.action; - this._bulkLabel(label, action); - } + private _handleBulkLabel = (ev) => { + ev.preventDefault(); // keep menu open + const item = ev.detail.item; + const value = item.value; + if (value === "label_create") { + this._bulkCreateLabel(); + return; + } + + if (value?.startsWith("label_")) { + const action = item.action; + this._bulkLabel(value.substring(6), action); + } + }; private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; @@ -1348,12 +1225,23 @@ ${rejected } } - private _handleBulkArea = (item) => { - const area = item.value; - this._bulkAddArea(area); + private _handleBulkArea = (ev) => { + const value = ev.detail.item.value; + if (value === "area_create") { + this._bulkCreateArea(); + return; + } + if (value === "area_none") { + this._bulkAddArea(null); + return; + } + + if (value?.startsWith("area_")) { + this._bulkAddArea(value.substring(5)); + } }; - private async _bulkAddArea(area: string) { + private async _bulkAddArea(area: string | null) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1454,6 +1342,148 @@ ${rejected }); }; + private _renderCategoryItems = (slot = "") => + html`${this._categories?.map( + (category) => + html` + ${category.icon + ? html`` + : html``} + ${category.name} + ` + )} + + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.no_category" + )} + + + + ${this.hass.localize("ui.panel.config.category.editor.add")} + `; + + private _renderLabelItems = (slot = "") => + html`${this._labels?.map((label) => { + const color = label.color ? computeCssColor(label.color) : undefined; + const selected = this._selected.every((entityId) => + this.hass.entities[entityId]?.labels.includes(label.label_id) + ); + const partial = + !selected && + this._selected.some((entityId) => + this.hass.entities[entityId]?.labels.includes(label.label_id) + ); + return html` + + + ${label.icon + ? html`` + : nothing} + ${label.name} + + `; + })} + + + ${this.hass.localize("ui.panel.config.labels.add_label")} + `; + + private _renderAreaItems = (slot = "") => + html`${Object.values(this.hass.areas).map( + (area) => + html` + ${area.icon + ? html`` + : html``} + ${area.name} + ` + )} + + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.no_area" + )} + + + + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.add_area" + )} + `; + + private _handleBulkAction = (ev) => { + const item = ev.detail.item; + const value = item.value; + + if (!value) { + return; + } + + if (value === "enable") { + this._handleBulkEnable(); + return; + } + if (value === "disable") { + this._handleBulkDisable(); + return; + } + + if (value.startsWith("category_")) { + if (value === "category_create") { + this._bulkCreateCategory(); + return; + } + if (value === "category_none") { + this._bulkAddCategory(null); + return; + } + + this._bulkAddCategory(value.substring(9)); + return; + } + + if (value.startsWith("label_")) { + if (value === "label_create") { + this._bulkCreateLabel(); + return; + } + + const action = item.action; + this._bulkLabel(value.substring(6), action); + return; + } + + if (value.startsWith("area_")) { + if (value === "area_create") { + this._bulkCreateArea(); + return; + } + if (value === "area_none") { + this._bulkAddArea(null); + return; + } + + this._bulkAddArea(value.substring(5)); + } + }; + private _handleSortingChanged(ev: CustomEvent) { this._activeSorting = ev.detail; } @@ -1498,7 +1528,7 @@ ${rejected ha-assist-chip { --ha-assist-chip-container-shape: 10px; } - ha-md-button-menu ha-assist-chip { + ha-dropdown ha-assist-chip { --md-assist-chip-trailing-space: 8px; } ha-label { diff --git a/src/panels/config/helpers/ha-config-helpers.ts b/src/panels/config/helpers/ha-config-helpers.ts index 168865102f..60a3c4b50a 100644 --- a/src/panels/config/helpers/ha-config-helpers.ts +++ b/src/panels/config/helpers/ha-config-helpers.ts @@ -1,9 +1,9 @@ +import "@home-assistant/webawesome/dist/components/divider/divider"; import { ResizeController } from "@lit-labs/observers/resize-controller"; import { consume } from "@lit/context"; import { mdiAlertCircle, mdiCancel, - mdiChevronRight, mdiCog, mdiDelete, mdiDotsVertical, @@ -45,6 +45,9 @@ import type { SortingChangedEvent, } from "../../../components/data-table/ha-data-table"; import "../../../components/data-table/ha-data-table-labels"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; +import type { HaDropdownItem } from "../../../components/ha-dropdown-item"; import "../../../components/ha-fab"; import "../../../components/ha-filter-categories"; import "../../../components/ha-filter-devices"; @@ -54,7 +57,6 @@ import "../../../components/ha-filter-labels"; import "../../../components/ha-filter-voice-assistants"; import "../../../components/ha-icon"; import "../../../components/ha-icon-overflow-menu"; -import "../../../components/ha-md-divider"; import "../../../components/ha-state-icon"; import "../../../components/ha-svg-icon"; import "../../../components/ha-tooltip"; @@ -91,6 +93,7 @@ import { updateEntityRegistryEntry, } from "../../../data/entity/entity_registry"; import { fetchEntitySourcesWithCache } from "../../../data/entity/entity_sources"; +import { getEntityVoiceAssistantsIds } from "../../../data/expose"; import { HELPERS_CRUD } from "../../../data/helpers_crud"; import type { IntegrationManifest } from "../../../data/integration"; import { @@ -122,14 +125,13 @@ import { configSections } from "../ha-panel-config"; import { renderConfigEntryError } from "../integrations/ha-config-integration-page"; import "../integrations/ha-integration-overflow-menu"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; +import { + getAssistantsSortableKey, + getAssistantsTableColumn, +} from "../voice-assistants/expose/assistants-table-column"; +import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants"; import { isHelperDomain, type HelperDomain } from "./const"; import { showHelperDetailDialog } from "./show-dialog-helper-detail"; -import { getEntityVoiceAssistantsIds } from "../../../data/expose"; -import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants"; -import { - getAssistantsTableColumn, - getAssistantsSortableKey, -} from "../voice-assistants/expose/assistants-table-column"; interface HelperItem { id: string; @@ -652,69 +654,6 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { return html``; } - const categoryItems = html`${this._categories?.map( - (category) => - html` - ${category.icon - ? html`` - : html``} -
${category.name}
-
` - )} - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.no_category" - )} -
-
- - -
- ${this.hass.localize("ui.panel.config.category.editor.add")} -
-
`; - const labelItems = html`${this._labels?.map((label) => { - const color = label.color ? computeCssColor(label.color) : undefined; - const selected = this._selected.every((entityId) => - this._labelsForEntity(entityId).includes(label.label_id) - ); - const partial = - !selected && - this._selected.some((entityId) => - this._labelsForEntity(entityId).includes(label.label_id) - ); - return html` - - - ${label.icon - ? html`` - : nothing} - ${label.name} - - `; - })} - -
- ${this.hass.localize("ui.panel.config.labels.add_label")} -
-
`; const labelsInOverflow = (this._sizeController.value && this._sizeController.value < 700) || (!this._sizeController.value && this.hass.dockedSidebar === "docked"); @@ -825,7 +764,10 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) { > ${!this.narrow - ? html` + ? html` - ${categoryItems} - + ${this._renderCategoryItems()} + ${labelsInOverflow ? nothing - : html` + : html` - ${labelItems} - `}` + ${this._renderLabelItems()} + `}` : nothing} ${this.narrow || labelsInOverflow - ? html` - - ${ - this.narrow + ? html` + ${this.narrow ? html`` - } - - ${ - this.narrow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.move_category" - )} -
- -
- ${categoryItems} -
` - : nothing - } - ${ - this.narrow || this.hass.dockedSidebar === "docked" - ? html` - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.add_label" - )} -
- -
- ${labelItems} -
` - : nothing - } -
` + >`} + ${this.narrow + ? html` + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.move_category" + )} + ${this._renderCategoryItems("submenu")} + ` + : nothing} + ${this.narrow || this.hass.dockedSidebar === "docked" + ? html` + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.add_label" + )} + ${this._renderLabelItems("submenu")} + ` + : nothing} + ` : nothing} { - const category = item.value; - this._bulkAddCategory(category); + private _handleBulkCategory = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + const value = ev.detail.item.value; + if (value === "category_create") { + this._bulkCreateCategory(); + return; + } + if (value === "category_none") { + this._bulkAddCategory(null); + return; + } + if (value?.startsWith("category_")) { + this._bulkAddCategory(value.substring(9)); + } }; - private async _bulkAddCategory(category: string) { + private async _bulkAddCategory(category: string | null) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1168,11 +1098,18 @@ ${rejected } } - private async _handleBulkLabel(ev) { - const label = ev.currentTarget.value; - const action = ev.currentTarget.action; - this._bulkLabel(label, action); - } + private _handleBulkLabel = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + ev.preventDefault(); + const value = ev.detail.item.value; + if (value === "label_create") { + this._bulkCreateLabel(); + return; + } + if (value?.startsWith("label_")) { + const action = (ev.detail.item as any).action; + this._bulkLabel(value.substring(6), action); + } + }; private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; @@ -1516,6 +1453,96 @@ ${rejected }); }; + private _renderCategoryItems = (slot = "") => + html`${this._categories?.map( + (category) => + html` + ${category.icon + ? html`` + : html``} + ${category.name} + ` + )} + + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.no_category" + )} + + + + ${this.hass.localize("ui.panel.config.category.editor.add")} + `; + + private _renderLabelItems = (slot = "") => + html`${this._labels?.map((label) => { + const color = label.color ? computeCssColor(label.color) : undefined; + const selected = this._selected.every((entityId) => + this._labelsForEntity(entityId).includes(label.label_id) + ); + const partial = + !selected && + this._selected.some((entityId) => + this._labelsForEntity(entityId).includes(label.label_id) + ); + return html` + + + ${label.icon + ? html`` + : nothing} + ${label.name} + + `; + })} + + + ${this.hass.localize("ui.panel.config.labels.add_label")} + `; + + private _handleBulkAction = (ev) => { + const item = ev.detail.item; + const value = item.value; + + if (!value) { + return; + } + + if (value.startsWith("category_")) { + if (value === "category_create") { + this._bulkCreateCategory(); + } else if (value === "category_none") { + this._bulkAddCategory(null); + } else { + this._bulkAddCategory(value.substring(9)); + } + return; + } + + if (value.startsWith("label_")) { + if (value === "label_create") { + this._bulkCreateLabel(); + } else { + const action = item.action; + this._bulkLabel(value.substring(6), action); + } + } + }; + private _handleSortingChanged(ev: CustomEvent) { this._activeSorting = ev.detail; } @@ -1553,7 +1580,7 @@ ${rejected ha-assist-chip { --ha-assist-chip-container-shape: 10px; } - ha-md-button-menu ha-assist-chip { + ha-dropdown ha-assist-chip { --md-assist-chip-trailing-space: 8px; } ha-label { diff --git a/src/panels/config/integrations/ha-config-entry-device-row.ts b/src/panels/config/integrations/ha-config-entry-device-row.ts index eb9d2bc327..7ae3ae058f 100644 --- a/src/panels/config/integrations/ha-config-entry-device-row.ts +++ b/src/panels/config/integrations/ha-config-entry-device-row.ts @@ -14,6 +14,8 @@ import { stopPropagation } from "../../../common/dom/stop_propagation"; import { computeDeviceNameDisplay } from "../../../common/entity/compute_device_name"; import { getDeviceContext } from "../../../common/entity/context/get_device_context"; import { navigate } from "../../../common/navigate"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; import { disableConfigEntry, type ConfigEntry, @@ -95,10 +97,10 @@ class HaConfigEntryDeviceRow extends LitElement { >` : nothing} - ${this.narrow - ? html` - + ? html` + ${this.hass.localize( "ui.panel.config.integrations.config_entry.device.edit" )} - ` + ` : nothing} ${entities.length ? html` - - + + ${this.hass.localize( `ui.panel.config.integrations.config_entry.entities`, { count: entities.length } )} - - + + ` : nothing} - - + ${device.disabled_by && device.disabled_by !== "user" ? this.hass.localize( @@ -156,25 +155,40 @@ class HaConfigEntryDeviceRow extends LitElement { : this.hass.localize( "ui.panel.config.integrations.config_entry.device.disable" )} - + ${this.entry.supports_remove_device - ? html` - + ? html` + ${this.hass.localize( "ui.panel.config.integrations.config_entry.device.delete" )} - ` + ` : nothing} - + `; } private _getEntities = (): EntityRegistryEntry[] => this.entities?.filter((entity) => entity.device_id === this.device.id); + private _handleMenuAction = (ev: CustomEvent) => { + ev.stopPropagation(); + const value = ev.detail.item.value; + switch (value) { + case "edit": + this._handleEditDevice(); + return; + case "entities": + this._handleNavigateToEntities(); + return; + case "disable": + this._doDisableDevice(); + return; + case "delete": + this._handleDeleteDevice(); + } + }; + private _handleEditDeviceButton(ev: MouseEvent) { ev.stopPropagation(); // Prevent triggering the click handler on the list item this._handleEditDevice(); @@ -193,7 +207,7 @@ class HaConfigEntryDeviceRow extends LitElement { navigate(`/config/entities/?historyBack=1&device=${this.device.id}`); }; - private _handleDisableDevice = async () => { + private _doDisableDevice = async () => { const disable = this.device.disabled_by === null; if (disable) { @@ -205,10 +219,8 @@ class HaConfigEntryDeviceRow extends LitElement { ) ) { const config_entry = this.entry; - if ( - config_entry && - !config_entry.disabled_by && - (await showConfirmationDialog(this, { + if (config_entry && !config_entry.disabled_by) { + const confirm = await showConfirmationDialog(this, { title: this.hass.localize( "ui.panel.config.devices.confirm_disable_config_entry_title" ), @@ -219,8 +231,12 @@ class HaConfigEntryDeviceRow extends LitElement { destructive: true, confirmText: this.hass.localize("ui.common.yes"), dismissText: this.hass.localize("ui.common.no"), - })) - ) { + }); + + if (!confirm) { + return; + } + let result: DisableConfigEntryResult; try { result = await disableConfigEntry(this.hass, this.entry.entry_id); diff --git a/src/panels/config/integrations/ha-config-entry-row.ts b/src/panels/config/integrations/ha-config-entry-row.ts index 64be533665..a7c53c26a0 100644 --- a/src/panels/config/integrations/ha-config-entry-row.ts +++ b/src/panels/config/integrations/ha-config-entry-row.ts @@ -60,6 +60,9 @@ import { showConfigEntrySystemOptionsDialog } from "../../../dialogs/config-entr import { showConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-config-flow"; import { showOptionsFlowDialog } from "../../../dialogs/config-flow/show-dialog-options-flow"; import { showSubConfigFlowDialog } from "../../../dialogs/config-flow/show-dialog-sub-config-flow"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; +import type { HaDropdownItem } from "../../../components/ha-dropdown-item"; import { haStyle } from "../../../resources/styles"; import type { HomeAssistant } from "../../../types"; import { documentationUrl } from "../../../util/documentation-url"; @@ -237,7 +240,7 @@ class HaConfigEntryRow extends LitElement { ` : nothing} - + ${devices.length ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.devices`, - { count: devices.length } - )} - - + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.devices`, + { count: devices.length } + )} + + + ` : nothing} ${services.length - ? html` - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.services`, - { count: services.length } - )} - - ` + ? html` + + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.services`, + { count: services.length } + )} + + + + ` : nothing} ${entities.length ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.entities`, - { count: entities.length } - )} - - + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.entities`, + { count: entities.length } + )} + + + ` : nothing} ${!item.disabled_by && @@ -298,126 +309,108 @@ class HaConfigEntryRow extends LitElement { item.supports_unload && item.source !== "system" ? html` - - + + ${this.hass.localize( "ui.panel.config.integrations.config_entry.reload" )} - + ` : nothing} - - + + ${this.hass.localize( "ui.panel.config.integrations.config_entry.rename" )} - + - - + + ${this.hass.localize( "ui.panel.config.integrations.config_entry.copy" )} - + ${Object.keys(item.supported_subentry_types).map( (flowType) => - html` - + html` + ${this.hass.localize( `component.${item.domain}.config_subentries.${flowType}.initiate_flow.user` - )}` + )} + ` )} - + ${this.diagnosticHandler && item.state === "loaded" ? html` - - - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.download_diagnostics" - )} - + + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.download_diagnostics" + )} + + ` : nothing} ${!item.disabled_by && item.supports_reconfigure && item.source !== "system" ? html` - - + + ${this.hass.localize( "ui.panel.config.integrations.config_entry.reconfigure" )} - + ` : nothing} - - + + ${this.hass.localize( "ui.panel.config.integrations.config_entry.system_options" )} - + ${item.disabled_by === "user" ? html` - + ${this.hass.localize("ui.common.enable")} - + ` : item.source !== "system" ? html` - + ${this.hass.localize("ui.common.disable")} - + ` : nothing} ${item.source !== "system" ? html` - - + + ${this.hass.localize( "ui.panel.config.integrations.config_entry.delete" )} - + ` : nothing} - + ${this._expanded ? subEntries.length @@ -548,6 +541,48 @@ class HaConfigEntryRow extends LitElement { this._devicesExpanded = !this._devicesExpanded; } + private _handleMenuAction = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + ev.stopPropagation(); + const value = ev.detail.item.value; + if (value === "reload") { + this._handleReload(); + return; + } + if (value === "rename") { + this._handleRename(); + return; + } + if (value === "copy") { + this._handleCopy(); + return; + } + if (value === "reconfigure") { + this._handleReconfigure(); + return; + } + if (value === "system_options") { + this._handleSystemOptions(); + return; + } + if (value === "enable") { + this._handleEnable(); + return; + } + if (value === "disable") { + this._handleDisable(); + return; + } + if (value === "delete") { + this._handleDelete(); + return; + } + if (value?.startsWith("subentry_")) { + const flowType = value.substring(9); + this._addSubEntry(flowType); + } + // devices, services, entities, diagnostics are handled by href navigation + }; + private _showOptions() { showOptionsFlowDialog(this, this.entry, { manifest: this.manifest }); } @@ -778,8 +813,8 @@ class HaConfigEntryRow extends LitElement { }); }; - private _addSubEntry = (item) => { - showSubConfigFlowDialog(this, this.entry, item.flowType, { + private _addSubEntry = (flowType: string) => { + showSubConfigFlowDialog(this, this.entry, flowType, { startFlowHandler: this.entry.entry_id, }); }; @@ -821,6 +856,9 @@ class HaConfigEntryRow extends LitElement { border-bottom-left-radius: 0; border-bottom-right-radius: 0; } + ha-dropdown a { + text-decoration: none; + } `, ]; } diff --git a/src/panels/config/integrations/ha-config-integration-page.ts b/src/panels/config/integrations/ha-config-integration-page.ts index 3d086a808a..9a715248be 100644 --- a/src/panels/config/integrations/ha-config-integration-page.ts +++ b/src/panels/config/integrations/ha-config-integration-page.ts @@ -24,11 +24,10 @@ import { import { caseInsensitiveStringCompare } from "../../../common/string/compare"; import { nextRender } from "../../../common/util/render-status"; import "../../../components/ha-button"; -import "../../../components/ha-md-button-menu"; -import "../../../components/ha-md-divider"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; import "../../../components/ha-md-list"; import "../../../components/ha-md-list-item"; -import "../../../components/ha-md-menu-item"; import { getSignedPath } from "../../../data/auth"; import type { ConfigEntry } from "../../../data/config_entries"; import { ERROR_STATES, getConfigEntries } from "../../../data/config_entries"; @@ -316,7 +315,7 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { ` : nothing} ${this._manifest?.config_flow || this._logInfo - ? html` + ? html` - ${this.hass.localize( - "ui.panel.config.integrations.config_entry.known_issues" - )} - - -
+ + + ${this.hass.localize( + "ui.panel.config.integrations.config_entry.known_issues" + )} + + + ` : nothing} ${this._logInfo - ? html` + ${this._logInfo.level === LogSeverity.DEBUG ? this.hass.localize( "ui.panel.config.integrations.config_entry.disable_debug_logging" @@ -354,18 +364,9 @@ class HaConfigIntegrationPage extends SubscribeMixin(LitElement) { : this.hass.localize( "ui.panel.config.integrations.config_entry.enable_debug_logging" )} - - ` + ` : nothing} - ` + ` : nothing}
diff --git a/src/panels/config/integrations/ha-config-sub-entry-row.ts b/src/panels/config/integrations/ha-config-sub-entry-row.ts index a2c7c32de0..cad0c049c5 100644 --- a/src/panels/config/integrations/ha-config-sub-entry-row.ts +++ b/src/panels/config/integrations/ha-config-sub-entry-row.ts @@ -11,6 +11,8 @@ import { import { css, html, LitElement, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import { classMap } from "lit/directives/class-map"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; import type { ConfigEntry, SubEntry } from "../../../data/config_entries"; import { deleteSubEntry, updateSubEntry } from "../../../data/config_entries"; import type { DeviceRegistryEntry } from "../../../data/device/device_registry"; @@ -89,7 +91,7 @@ class HaConfigSubEntryRow extends LitElement { ` : nothing} - + ${devices.length || services.length ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.devices`, - { count: devices.length } - )} - - + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.devices`, + { count: devices.length } + )} + + + ` : nothing} ${services.length - ? html` - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.services`, - { count: services.length } - )} - - ` + ? html` + + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.services`, + { count: services.length } + )} + + + + ` : nothing} ${entities.length ? html` - - - ${this.hass.localize( - `ui.panel.config.integrations.config_entry.entities`, - { count: entities.length } - )} - - + + + ${this.hass.localize( + `ui.panel.config.integrations.config_entry.entities`, + { count: entities.length } + )} + + + ` : nothing} - - + + ${this.hass.localize( "ui.panel.config.integrations.config_entry.rename" )} - - - + + + ${this.hass.localize( "ui.panel.config.integrations.config_entry.delete" )} - - + + ${this._expanded ? html` @@ -225,6 +228,19 @@ class HaConfigSubEntryRow extends LitElement { }); } + private _handleMenuAction = (ev: CustomEvent) => { + const value = ev.detail.item.value; + switch (value) { + case "rename": + this._handleRenameSub(); + break; + case "delete": + this._handleDeleteSub(); + break; + // devices, services, entities are handled by href navigation + } + }; + private _handleRenameSub = async (): Promise => { const newName = await showPromptDialog(this, { title: this.hass.localize("ui.common.rename"), @@ -286,6 +302,9 @@ class HaConfigSubEntryRow extends LitElement { ha-md-list-item.has-subentries { border-bottom: 1px solid var(--divider-color); } + ha-dropdown a { + text-decoration: none; + } `; } diff --git a/src/panels/config/integrations/ha-integration-overflow-menu.ts b/src/panels/config/integrations/ha-integration-overflow-menu.ts index 2b3413a684..135a3e9f41 100644 --- a/src/panels/config/integrations/ha-integration-overflow-menu.ts +++ b/src/panels/config/integrations/ha-integration-overflow-menu.ts @@ -1,10 +1,10 @@ import { mdiDotsVertical } from "@mdi/js"; -import { html, LitElement } from "lit"; +import { css, html, LitElement } from "lit"; import { customElement, property } from "lit/decorators"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; import "../../../components/ha-icon-button"; import type { HomeAssistant } from "../../../types"; -import "../../../components/ha-md-list-item"; -import "../../../components/ha-md-button-menu"; @customElement("ha-integration-overflow-menu") export class HaIntegrationOverflowMenu extends LitElement { @@ -12,22 +12,32 @@ export class HaIntegrationOverflowMenu extends LitElement { protected render() { return html` - + - - ${this.hass.localize( - "ui.panel.config.application_credentials.caption" - )} - - + + + ${this.hass.localize( + "ui.panel.config.application_credentials.caption" + )} + + + `; } -} + static styles = css` + :host { + display: flex; + } + a { + text-decoration: none; + } + `; +} declare global { interface HTMLElementTagNameMap { "ha-integration-overflow-menu": HaIntegrationOverflowMenu; diff --git a/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts b/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts index af27793629..4f18fc081d 100644 --- a/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts +++ b/src/panels/config/lovelace/dashboards/ha-config-lovelace-dashboards.ts @@ -8,7 +8,7 @@ import { mdiPlus, } from "@mdi/js"; import type { PropertyValues } from "lit"; -import { LitElement, html, nothing } from "lit"; +import { LitElement, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators"; import memoize from "memoize-one"; import { storage } from "../../../../common/decorators/storage"; @@ -21,12 +21,12 @@ import type { SortingChangedEvent, } from "../../../../components/data-table/ha-data-table"; import "../../../../components/ha-button"; +import "../../../../components/ha-dropdown"; +import "../../../../components/ha-dropdown-item"; import "../../../../components/ha-fab"; import "../../../../components/ha-icon"; import "../../../../components/ha-icon-button"; import "../../../../components/ha-icon-overflow-menu"; -import "../../../../components/ha-md-button-menu"; -import "../../../../components/ha-md-list-item"; import "../../../../components/ha-svg-icon"; import "../../../../components/ha-tooltip"; import { saveFrontendSystemData } from "../../../../data/frontend"; @@ -413,16 +413,20 @@ export class HaConfigLovelaceDashboards extends LitElement { has-fab clickable > - + - - ${this.hass.localize("ui.panel.config.lovelace.resources.caption")} - - + + + ${this.hass.localize( + "ui.panel.config.lovelace.resources.caption" + )} + + + - html` - ${category.icon - ? html`` - : html``} -
${category.name}
-
` - )} - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.no_category" - )} -
-
- - -
- ${this.hass.localize("ui.panel.config.category.editor.add")} -
-
`; - - const labelItems = html` ${this._labels?.map((label) => { - const color = label.color ? computeCssColor(label.color) : undefined; - const selected = this._selected.every((entityId) => - this.hass.entities[entityId]?.labels.includes(label.label_id) - ); - const partial = - !selected && - this._selected.some((entityId) => - this.hass.entities[entityId]?.labels.includes(label.label_id) - ); - return html` - - - ${label.icon - ? html`` - : nothing} - ${label.name} - - `; - })} - - -
- ${this.hass.localize("ui.panel.config.labels.add_label")} -
`; - - const areaItems = html`${Object.values(this.hass.areas).map( - (area) => - html` - ${area.icon - ? html`` - : html``} -
${area.name}
-
` - )} - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.no_area" - )} -
-
- - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.add_area" - )} -
-
`; - const areasInOverflow = (this._sizeController.value && this._sizeController.value < 900) || (!this._sizeController.value && this.hass.dockedSidebar === "docked"); @@ -705,7 +611,10 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { > ${!this.narrow - ? html` + ? html` - ${categoryItems} - + ${this._renderCategoryItems()} + ${labelsInOverflow ? nothing - : html` + : html` - ${labelItems} - `} + ${this._renderLabelItems()} + `} ${areasInOverflow ? nothing - : html` + : html` - ${areaItems} - `}` + ${this._renderAreaItems()} + `}` : nothing} ${this.narrow || areasInOverflow - ? html` - - ${ - this.narrow + ? html` + ${this.narrow ? html`` - } - - ${ - this.narrow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.move_category" - )} -
- -
- ${categoryItems} -
` - : nothing - } - ${ - this.narrow || labelsInOverflow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.add_label" - )} -
- -
- ${labelItems} -
` - : nothing - } - ${ - this.narrow || areasInOverflow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.move_area" - )} -
- -
- ${areaItems} -
` - : nothing - } -
` + >`} + ${this.narrow + ? html` + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.move_category" + )} + ${this._renderCategoryItems("submenu")} + ` + : nothing} + ${this.narrow || labelsInOverflow + ? html` + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.add_label" + )} + ${this._renderLabelItems("submenu")} + ` + : nothing} + ${this.narrow || areasInOverflow + ? html` + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.move_area" + )} + ${this._renderAreaItems("submenu")} + ` + : nothing} + ` : nothing} ${!this.scenes.length ? html`
@@ -1027,12 +907,22 @@ class HaSceneDashboard extends SubscribeMixin(LitElement) { } } - private _handleBulkCategory = (item) => { - const category = item.value; - this._bulkAddCategory(category); + private _handleBulkCategory = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + const value = ev.detail.item.value; + if (value === "category_create") { + this._bulkCreateCategory(); + return; + } + if (value === "category_none") { + this._bulkAddCategory(null); + return; + } + if (value?.startsWith("category_")) { + this._bulkAddCategory(value.substring(9)); + } }; - private async _bulkAddCategory(category: string) { + private async _bulkAddCategory(category: string | null) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1057,11 +947,18 @@ ${rejected } } - private async _handleBulkLabel(ev) { - const label = ev.currentTarget.value; - const action = ev.currentTarget.action; - this._bulkLabel(label, action); - } + private _handleBulkLabel = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + ev.preventDefault(); + const value = ev.detail.item.value; + if (value === "label_create") { + this._bulkCreateLabel(); + return; + } + if (value?.startsWith("label_")) { + const action = (ev.detail.item as any).action; + this._bulkLabel(value.substring(6), action); + } + }; private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; @@ -1093,12 +990,22 @@ ${rejected } } - private _handleBulkArea = (item) => { - const area = item.value; - this._bulkAddArea(area); + private _handleBulkArea = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + const value = ev.detail.item.value; + if (value === "area_create") { + this._bulkCreateArea(); + return; + } + if (value === "area_none") { + this._bulkAddArea(null); + return; + } + if (value?.startsWith("area_")) { + this._bulkAddArea(value.substring(5)); + } }; - private async _bulkAddArea(area: string) { + private async _bulkAddArea(area: string | null) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1286,6 +1193,132 @@ ${rejected }); }; + private _renderCategoryItems = (slot = "") => + html`${this._categories?.map( + (category) => + html` + ${category.icon + ? html`` + : html``} + ${category.name} + ` + )} + + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.no_category" + )} + + + + ${this.hass.localize("ui.panel.config.category.editor.add")} + `; + + private _renderLabelItems = (slot = "") => + html`${this._labels?.map((label) => { + const color = label.color ? computeCssColor(label.color) : undefined; + const selected = this._selected.every((entityId) => + this.hass.entities[entityId]?.labels.includes(label.label_id) + ); + const partial = + !selected && + this._selected.some((entityId) => + this.hass.entities[entityId]?.labels.includes(label.label_id) + ); + return html` + + + ${label.icon + ? html`` + : nothing} + ${label.name} + + `; + })} + + + ${this.hass.localize("ui.panel.config.labels.add_label")} + `; + + private _renderAreaItems = (slot = "") => + html`${Object.values(this.hass.areas).map( + (area) => + html` + ${area.icon + ? html`` + : html``} + ${area.name} + ` + )} + + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.no_area" + )} + + + + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.add_area" + )} + `; + + private _handleBulkAction = (ev) => { + const item = ev.detail.item; + const value = item.value; + + if (!value) { + return; + } + + if (value.startsWith("category_")) { + if (value === "category_create") { + this._bulkCreateCategory(); + } else if (value === "category_none") { + this._bulkAddCategory(null); + } else { + this._bulkAddCategory(value.substring(9)); + } + return; + } + + if (value.startsWith("label_")) { + if (value === "label_create") { + this._bulkCreateLabel(); + } else { + const action = item.action; + this._bulkLabel(value.substring(6), action); + } + return; + } + + if (value.startsWith("area_")) { + if (value === "area_create") { + this._bulkCreateArea(); + } else if (value === "area_none") { + this._bulkAddArea(null); + } else { + this._bulkAddArea(value.substring(5)); + } + } + }; + private _handleSortingChanged(ev: CustomEvent) { this._activeSorting = ev.detail; } @@ -1334,7 +1367,7 @@ ${rejected ha-assist-chip { --ha-assist-chip-container-shape: 10px; } - ha-md-button-menu ha-assist-chip { + ha-dropdown ha-assist-chip { --md-assist-chip-trailing-space: 8px; } ha-label { diff --git a/src/panels/config/script/ha-script-picker.ts b/src/panels/config/script/ha-script-picker.ts index 7f89c43f60..742427b802 100644 --- a/src/panels/config/script/ha-script-picker.ts +++ b/src/panels/config/script/ha-script-picker.ts @@ -1,7 +1,7 @@ +import "@home-assistant/webawesome/dist/components/divider/divider"; import { ResizeController } from "@lit-labs/observers/resize-controller"; import { consume } from "@lit/context"; import { - mdiChevronRight, mdiCog, mdiContentDuplicate, mdiDelete, @@ -46,6 +46,9 @@ import type { SortingChangedEvent, } from "../../../components/data-table/ha-data-table"; import "../../../components/data-table/ha-data-table-labels"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; +import type { HaDropdownItem } from "../../../components/ha-dropdown-item"; import "../../../components/ha-fab"; import "../../../components/ha-filter-blueprints"; import "../../../components/ha-filter-categories"; @@ -81,6 +84,7 @@ import type { UpdateEntityRegistryEntryResult, } from "../../../data/entity/entity_registry"; import { updateEntityRegistryEntry } from "../../../data/entity/entity_registry"; +import { getEntityVoiceAssistantsIds } from "../../../data/expose"; import type { LabelRegistryEntry } from "../../../data/label/label_registry"; import { createLabelRegistryEntry, @@ -113,12 +117,11 @@ import { showAssignCategoryDialog } from "../category/show-dialog-assign-categor import { showCategoryRegistryDetailDialog } from "../category/show-dialog-category-registry-detail"; import { configSections } from "../ha-panel-config"; import { showLabelDetailDialog } from "../labels/show-dialog-label-detail"; -import { getEntityVoiceAssistantsIds } from "../../../data/expose"; -import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants"; import { - getAssistantsTableColumn, getAssistantsSortableKey, + getAssistantsTableColumn, } from "../voice-assistants/expose/assistants-table-column"; +import { getAvailableAssistants } from "../voice-assistants/expose/available-assistants"; type ScriptItem = ScriptEntity & { name: string; @@ -451,102 +454,6 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { } protected render(): TemplateResult { - const categoryItems = html`${this._categories?.map( - (category) => - html` - ${category.icon - ? html`` - : html``} -
${category.name}
-
` - )} - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.no_category" - )} -
- -
- ${this.hass.localize("ui.panel.config.category.editor.add")} -
-
`; - - const labelItems = html`${this._labels?.map((label) => { - const color = label.color ? computeCssColor(label.color) : undefined; - const selected = this._selected.every((entityId) => - this.hass.entities[entityId]?.labels.includes(label.label_id) - ); - const partial = - !selected && - this._selected.some((entityId) => - this.hass.entities[entityId]?.labels.includes(label.label_id) - ); - return html` - - - ${label.icon - ? html`` - : nothing} - ${label.name} - - `; - })} - - -
- ${this.hass.localize("ui.panel.config.labels.add_label")} -
`; - - const areaItems = html`${Object.values(this.hass.areas).map( - (area) => - html` - ${area.icon - ? html`` - : html``} -
${area.name}
-
` - )} - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.no_area" - )} -
-
- - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.add_area" - )} -
-
`; - const areasInOverflow = (this._sizeController.value && this._sizeController.value < 900) || (!this._sizeController.value && this.hass.dockedSidebar === "docked"); @@ -687,7 +594,10 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { > ${!this.narrow - ? html` + ? html` - ${categoryItems} - + ${this._renderCategoryItems()} + ${labelsInOverflow ? nothing - : html` + : html` - ${labelItems} - `} + ${this._renderLabelItems()} + `} ${areasInOverflow ? nothing - : html` + : html` - ${areaItems} - `}` + ${this._renderAreaItems()} + `}` : nothing} ${this.narrow || areasInOverflow - ? html` - - ${ - this.narrow + ? html` + ${this.narrow ? html`` - } - - ${ - this.narrow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.move_category" - )} -
- -
- ${categoryItems} -
` - : nothing - } - ${ - this.narrow || labelsInOverflow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.automation.picker.bulk_actions.add_label" - )} -
- -
- ${labelItems} -
` - : nothing - } - ${ - this.narrow || areasInOverflow - ? html` - -
- ${this.hass.localize( - "ui.panel.config.devices.picker.bulk_actions.move_area" - )} -
- -
- ${areaItems} -
` - : nothing - } -
` + >`} + ${this.narrow + ? html` + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.move_category" + )} + ${this._renderCategoryItems("submenu")} + ` + : nothing} + ${this.narrow || labelsInOverflow + ? html` + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.add_label" + )} + ${this._renderLabelItems("submenu")} + ` + : nothing} + ${this.narrow || areasInOverflow + ? html` + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.move_area" + )} + ${this._renderAreaItems("submenu")} + ` + : nothing} + ` : nothing} ${!this.scripts.length ? html`
@@ -1056,12 +937,22 @@ class HaScriptPicker extends SubscribeMixin(LitElement) { this._selected = ev.detail.value; } - private _handleBulkCategory = (item) => { - const category = item.value; - this._bulkAddCategory(category); + private _handleBulkCategory = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + const value = ev.detail.item.value; + if (value === "category_create") { + this._bulkCreateCategory(); + return; + } + if (value === "category_none") { + this._bulkAddCategory(null); + return; + } + if (value?.startsWith("category_")) { + this._bulkAddCategory(value.substring(9)); + } }; - private async _bulkAddCategory(category: string) { + private async _bulkAddCategory(category: string | null) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1086,11 +977,18 @@ ${rejected } } - private async _handleBulkLabel(ev) { - const label = ev.currentTarget.value; - const action = ev.currentTarget.action; - this._bulkLabel(label, action); - } + private _handleBulkLabel = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + ev.preventDefault(); + const value = ev.detail.item.value; + if (value === "label_create") { + this._bulkCreateLabel(); + return; + } + if (value?.startsWith("label_")) { + const action = (ev.detail.item as any).action; + this._bulkLabel(value.substring(6), action); + } + }; private async _bulkLabel(label: string, action: "add" | "remove") { const promises: Promise[] = []; @@ -1296,12 +1194,22 @@ ${rejected }); }; - private _handleBulkArea = (item) => { - const area = item.value; - this._bulkAddArea(area); + private _handleBulkArea = (ev: CustomEvent<{ item: HaDropdownItem }>) => { + const value = ev.detail.item.value; + if (value === "area_create") { + this._bulkCreateArea(); + return; + } + if (value === "area_none") { + this._bulkAddArea(null); + return; + } + if (value?.startsWith("area_")) { + this._bulkAddArea(value.substring(5)); + } }; - private async _bulkAddArea(area: string) { + private async _bulkAddArea(area: string | null) { const promises: Promise[] = []; this._selected.forEach((entityId) => { promises.push( @@ -1336,6 +1244,132 @@ ${rejected }); }; + private _renderCategoryItems = (slot = "") => + html`${this._categories?.map( + (category) => + html` + ${category.icon + ? html`` + : html``} + ${category.name} + ` + )} + + ${this.hass.localize( + "ui.panel.config.automation.picker.bulk_actions.no_category" + )} + + + + ${this.hass.localize("ui.panel.config.category.editor.add")} + `; + + private _renderLabelItems = (slot = "") => + html`${this._labels?.map((label) => { + const color = label.color ? computeCssColor(label.color) : undefined; + const selected = this._selected.every((entityId) => + this.hass.entities[entityId]?.labels.includes(label.label_id) + ); + const partial = + !selected && + this._selected.some((entityId) => + this.hass.entities[entityId]?.labels.includes(label.label_id) + ); + return html` + + + ${label.icon + ? html`` + : nothing} + ${label.name} + + `; + })} + + + ${this.hass.localize("ui.panel.config.labels.add_label")} + `; + + private _renderAreaItems = (slot = "") => + html`${Object.values(this.hass.areas).map( + (area) => + html` + ${area.icon + ? html`` + : html``} + ${area.name} + ` + )} + + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.no_area" + )} + + + + ${this.hass.localize( + "ui.panel.config.devices.picker.bulk_actions.add_area" + )} + `; + + private _handleBulkAction = (ev) => { + const item = ev.detail.item; + const value = item.value; + + if (!value) { + return; + } + + if (value.startsWith("category_")) { + if (value === "category_create") { + this._bulkCreateCategory(); + } else if (value === "category_none") { + this._bulkAddCategory(null); + } else { + this._bulkAddCategory(value.substring(9)); + } + return; + } + + if (value.startsWith("label_")) { + if (value === "label_create") { + this._bulkCreateLabel(); + } else { + const action = item.action; + this._bulkLabel(value.substring(6), action); + } + return; + } + + if (value.startsWith("area_")) { + if (value === "area_create") { + this._bulkCreateArea(); + } else if (value === "area_none") { + this._bulkAddArea(null); + } else { + this._bulkAddArea(value.substring(5)); + } + } + }; + private _handleSortingChanged(ev: CustomEvent) { this._activeSorting = ev.detail; } @@ -1383,7 +1417,7 @@ ${rejected ha-assist-chip { --ha-assist-chip-container-shape: 10px; } - ha-md-button-menu ha-assist-chip { + ha-dropdown ha-assist-chip { --md-assist-chip-trailing-space: 8px; } ha-label { diff --git a/src/panels/developer-tools/statistics/developer-tools-statistics.ts b/src/panels/developer-tools/statistics/developer-tools-statistics.ts index 636100e7a0..e6362353a9 100644 --- a/src/panels/developer-tools/statistics/developer-tools-statistics.ts +++ b/src/panels/developer-tools/statistics/developer-tools-statistics.ts @@ -28,10 +28,11 @@ import type { SortingDirection, } from "../../../components/data-table/ha-data-table"; import { showDataTableSettingsDialog } from "../../../components/data-table/show-dialog-data-table-settings"; +import "@home-assistant/webawesome/dist/components/divider/divider"; import "../../../components/ha-button"; import "../../../components/ha-dialog"; -import "../../../components/ha-md-button-menu"; -import "../../../components/ha-md-divider"; +import "../../../components/ha-dropdown"; +import "../../../components/ha-dropdown-item"; import type { HaMdMenu } from "../../../components/ha-md-menu"; import "../../../components/ha-md-menu-item"; import "../../../components/search-input-outlined"; @@ -325,7 +326,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) { "ui.components.subpage-data-table.exit_selection_mode" )} > - + - -
- ${localize("ui.components.subpage-data-table.select_all")} -
-
- -
- ${localize( - "ui.panel.developer-tools.tabs.statistics.data_table.select_all_issues" - )} -
-
- -
- ${localize( - "ui.components.subpage-data-table.select_none" - )} -
-
- - -
- ${localize( - "ui.components.subpage-data-table.exit_selection_mode" - )} -
-
-
+ + ${localize("ui.components.subpage-data-table.select_all")} + + + ${localize( + "ui.panel.developer-tools.tabs.statistics.data_table.select_all_issues" + )} + + + ${localize("ui.components.subpage-data-table.select_none")} + + + + ${localize( + "ui.components.subpage-data-table.exit_selection_mode" + )} + +

${localize("ui.components.subpage-data-table.selected", { selected: this._selected.length, @@ -814,7 +793,7 @@ class HaPanelDevStatistics extends KeyboardShortcutMixin(LitElement) { #sort-by-anchor, #group-by-anchor, - ha-md-button-menu ha-assist-chip { + ha-dropdown ha-assist-chip { --md-assist-chip-trailing-space: 8px; } `,