mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-27 19:26:36 +00:00
Add label filter for helper page (#20281)
* Label filter for helper page * Clean up debugging label
This commit is contained in:
parent
85f80ff863
commit
88c59c5c13
@ -1,8 +1,17 @@
|
|||||||
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
import "@lrnwebcomponents/simple-tooltip/simple-tooltip";
|
||||||
import { mdiAlertCircle, mdiPencilOff, mdiPlus } from "@mdi/js";
|
import { mdiAlertCircle, mdiPencilOff, mdiPlus } from "@mdi/js";
|
||||||
import { HassEntity } from "home-assistant-js-websocket";
|
import { HassEntity } from "home-assistant-js-websocket";
|
||||||
import { LitElement, PropertyValues, TemplateResult, html } from "lit";
|
import {
|
||||||
|
CSSResultGroup,
|
||||||
|
LitElement,
|
||||||
|
PropertyValues,
|
||||||
|
TemplateResult,
|
||||||
|
css,
|
||||||
|
html,
|
||||||
|
nothing,
|
||||||
|
} from "lit";
|
||||||
import { customElement, property, state } from "lit/decorators";
|
import { customElement, property, state } from "lit/decorators";
|
||||||
|
import { consume } from "@lit-labs/context";
|
||||||
import memoizeOne from "memoize-one";
|
import memoizeOne from "memoize-one";
|
||||||
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
import { computeStateDomain } from "../../../common/entity/compute_state_domain";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
@ -15,6 +24,7 @@ import {
|
|||||||
DataTableColumnContainer,
|
DataTableColumnContainer,
|
||||||
RowClickedEvent,
|
RowClickedEvent,
|
||||||
} from "../../../components/data-table/ha-data-table";
|
} from "../../../components/data-table/ha-data-table";
|
||||||
|
import "../../../components/data-table/ha-data-table-labels";
|
||||||
import "../../../components/ha-fab";
|
import "../../../components/ha-fab";
|
||||||
import "../../../components/ha-icon";
|
import "../../../components/ha-icon";
|
||||||
import "../../../components/ha-state-icon";
|
import "../../../components/ha-state-icon";
|
||||||
@ -44,6 +54,13 @@ import { configSections } from "../ha-panel-config";
|
|||||||
import "../integrations/ha-integration-overflow-menu";
|
import "../integrations/ha-integration-overflow-menu";
|
||||||
import { isHelperDomain } from "./const";
|
import { isHelperDomain } from "./const";
|
||||||
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
|
import { showHelperDetailDialog } from "./show-dialog-helper-detail";
|
||||||
|
import {
|
||||||
|
LabelRegistryEntry,
|
||||||
|
subscribeLabelRegistry,
|
||||||
|
} from "../../../data/label_registry";
|
||||||
|
import { fullEntitiesContext } from "../../../data/context";
|
||||||
|
import "../../../components/ha-filter-labels";
|
||||||
|
import { haStyle } from "../../../resources/styles";
|
||||||
|
|
||||||
type HelperItem = {
|
type HelperItem = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -54,6 +71,7 @@ type HelperItem = {
|
|||||||
type: string;
|
type: string;
|
||||||
configEntry?: ConfigEntry;
|
configEntry?: ConfigEntry;
|
||||||
entity?: HassEntity;
|
entity?: HassEntity;
|
||||||
|
label_entries: LabelRegistryEntry[];
|
||||||
};
|
};
|
||||||
|
|
||||||
// This groups items by a key but only returns last entry per key.
|
// This groups items by a key but only returns last entry per key.
|
||||||
@ -93,6 +111,24 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
|
|
||||||
@state() private _configEntries?: Record<string, ConfigEntry>;
|
@state() private _configEntries?: Record<string, ConfigEntry>;
|
||||||
|
|
||||||
|
@state() private _activeFilters?: string[];
|
||||||
|
|
||||||
|
@state() private _filters: Record<
|
||||||
|
string,
|
||||||
|
{ value: string[] | undefined; items: Set<string> | undefined }
|
||||||
|
> = {};
|
||||||
|
|
||||||
|
@state() private _expandedFilter?: string;
|
||||||
|
|
||||||
|
@state()
|
||||||
|
_labels!: LabelRegistryEntry[];
|
||||||
|
|
||||||
|
@state()
|
||||||
|
@consume({ context: fullEntitiesContext, subscribe: true })
|
||||||
|
_entityReg!: EntityRegistryEntry[];
|
||||||
|
|
||||||
|
@state() private _filteredStateItems?: string[] | null;
|
||||||
|
|
||||||
public hassSubscribe() {
|
public hassSubscribe() {
|
||||||
return [
|
return [
|
||||||
subscribeConfigEntries(
|
subscribeConfigEntries(
|
||||||
@ -117,6 +153,9 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
subscribeEntityRegistry(this.hass.connection!, (entries) => {
|
subscribeEntityRegistry(this.hass.connection!, (entries) => {
|
||||||
this._entityEntries = groupByOne(entries, (entry) => entry.entity_id);
|
this._entityEntries = groupByOne(entries, (entry) => entry.entity_id);
|
||||||
}),
|
}),
|
||||||
|
subscribeLabelRegistry(this.hass.connection, (labels) => {
|
||||||
|
this._labels = labels;
|
||||||
|
}),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,10 +185,17 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
grows: true,
|
grows: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
template: (helper) => html`
|
template: (helper) => html`
|
||||||
${helper.name}
|
<div style="font-size: 14px;">${helper.name}</div>
|
||||||
${narrow
|
${narrow
|
||||||
? html`<div class="secondary">${helper.entity_id}</div> `
|
? html`<div class="secondary">${helper.entity_id}</div> `
|
||||||
: ""}
|
: nothing}
|
||||||
|
${helper.label_entries.length
|
||||||
|
? html`
|
||||||
|
<ha-data-table-labels
|
||||||
|
.labels=${helper.label_entries}
|
||||||
|
></ha-data-table-labels>
|
||||||
|
`
|
||||||
|
: nothing}
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -201,8 +247,15 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
localize: LocalizeFunc,
|
localize: LocalizeFunc,
|
||||||
stateItems: HassEntity[],
|
stateItems: HassEntity[],
|
||||||
entityEntries: Record<string, EntityRegistryEntry>,
|
entityEntries: Record<string, EntityRegistryEntry>,
|
||||||
configEntries: Record<string, ConfigEntry>
|
configEntries: Record<string, ConfigEntry>,
|
||||||
|
entityReg: EntityRegistryEntry[],
|
||||||
|
labelReg?: LabelRegistryEntry[],
|
||||||
|
filteredStateItems?: string[] | null
|
||||||
): HelperItem[] => {
|
): HelperItem[] => {
|
||||||
|
if (filteredStateItems === null) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
const configEntriesCopy = { ...configEntries };
|
const configEntriesCopy = { ...configEntries };
|
||||||
|
|
||||||
const states = stateItems.map((entityState) => {
|
const states = stateItems.map((entityState) => {
|
||||||
@ -241,14 +294,29 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
entity: undefined,
|
entity: undefined,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return [...states, ...entries].map((item) => ({
|
return [...states, ...entries]
|
||||||
...item,
|
.filter((item) =>
|
||||||
localized_type: item.configEntry
|
filteredStateItems
|
||||||
? domainToName(localize, item.type)
|
? filteredStateItems?.includes(item.entity_id)
|
||||||
: localize(
|
: true
|
||||||
`ui.panel.config.helpers.types.${item.type}` as LocalizeKeys
|
)
|
||||||
) || item.type,
|
.map((item) => {
|
||||||
}));
|
const entityRegEntry = entityReg.find(
|
||||||
|
(reg) => reg.entity_id === item.entity_id
|
||||||
|
);
|
||||||
|
const labels = labelReg && entityRegEntry?.labels;
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
localized_type: item.configEntry
|
||||||
|
? domainToName(localize, item.type)
|
||||||
|
: localize(
|
||||||
|
`ui.panel.config.helpers.types.${item.type}` as LocalizeKeys
|
||||||
|
) || item.type,
|
||||||
|
label_entries: (labels || []).map(
|
||||||
|
(lbl) => labelReg!.find((label) => label.label_id === lbl)!
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -269,20 +337,40 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
back-path="/config"
|
back-path="/config"
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.devices}
|
.tabs=${configSections.devices}
|
||||||
|
hasFilters
|
||||||
|
.filters=${Object.values(this._filters).filter(
|
||||||
|
(filter) => filter.value?.length
|
||||||
|
).length}
|
||||||
.columns=${this._columns(this.narrow, this.hass.localize)}
|
.columns=${this._columns(this.narrow, this.hass.localize)}
|
||||||
.data=${this._getItems(
|
.data=${this._getItems(
|
||||||
this.hass.localize,
|
this.hass.localize,
|
||||||
this._stateItems,
|
this._stateItems,
|
||||||
this._entityEntries,
|
this._entityEntries,
|
||||||
this._configEntries
|
this._configEntries,
|
||||||
|
this._entityReg,
|
||||||
|
this._labels,
|
||||||
|
this._filteredStateItems
|
||||||
)}
|
)}
|
||||||
|
.activeFilters=${this._activeFilters}
|
||||||
|
@clear-filter=${this._clearFilter}
|
||||||
@row-click=${this._openEditDialog}
|
@row-click=${this._openEditDialog}
|
||||||
hasFab
|
hasFab
|
||||||
clickable
|
clickable
|
||||||
.noDataText=${this.hass.localize(
|
.noDataText=${this.hass.localize(
|
||||||
"ui.panel.config.helpers.picker.no_helpers"
|
"ui.panel.config.helpers.picker.no_helpers"
|
||||||
)}
|
)}
|
||||||
|
class=${this.narrow ? "narrow" : ""}
|
||||||
>
|
>
|
||||||
|
<ha-filter-labels
|
||||||
|
.hass=${this.hass}
|
||||||
|
.value=${this._filters["ha-filter-labels"]?.value}
|
||||||
|
@data-table-filter-changed=${this._filterChanged}
|
||||||
|
slot="filter-pane"
|
||||||
|
.expanded=${this._expandedFilter === "ha-filter-labels"}
|
||||||
|
.narrow=${this.narrow}
|
||||||
|
@expanded-changed=${this._filterExpanded}
|
||||||
|
></ha-filter-labels>
|
||||||
|
|
||||||
<ha-integration-overflow-menu
|
<ha-integration-overflow-menu
|
||||||
.hass=${this.hass}
|
.hass=${this.hass}
|
||||||
slot="toolbar-icon"
|
slot="toolbar-icon"
|
||||||
@ -293,7 +381,7 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
"ui.panel.config.helpers.picker.create_helper"
|
"ui.panel.config.helpers.picker.create_helper"
|
||||||
)}
|
)}
|
||||||
extended
|
extended
|
||||||
@click=${this._createHelpler}
|
@click=${this._createHelper}
|
||||||
>
|
>
|
||||||
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
<ha-svg-icon slot="icon" .path=${mdiPlus}></ha-svg-icon>
|
||||||
</ha-fab>
|
</ha-fab>
|
||||||
@ -301,6 +389,63 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _filterExpanded(ev) {
|
||||||
|
if (ev.detail.expanded) {
|
||||||
|
this._expandedFilter = ev.target.localName;
|
||||||
|
} else if (this._expandedFilter === ev.target.localName) {
|
||||||
|
this._expandedFilter = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private _filterChanged(ev) {
|
||||||
|
const type = ev.target.localName;
|
||||||
|
this._filters[type] = ev.detail;
|
||||||
|
this._applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
private _applyFilters() {
|
||||||
|
const filters = Object.entries(this._filters);
|
||||||
|
let items: Set<string> | undefined;
|
||||||
|
for (const [key, filter] of filters) {
|
||||||
|
if (filter.items) {
|
||||||
|
if (!items) {
|
||||||
|
items = filter.items;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
items =
|
||||||
|
"intersection" in items
|
||||||
|
? // @ts-ignore
|
||||||
|
items.intersection(filter.items)
|
||||||
|
: new Set([...items].filter((x) => filter.items!.has(x)));
|
||||||
|
}
|
||||||
|
if (key === "ha-filter-labels" && filter.value?.length) {
|
||||||
|
const labelItems: Set<string> = new Set();
|
||||||
|
this._stateItems
|
||||||
|
.filter((stateItem) =>
|
||||||
|
this._entityReg
|
||||||
|
.find((reg) => reg.entity_id === stateItem.entity_id)
|
||||||
|
?.labels.some((lbl) => filter.value!.includes(lbl))
|
||||||
|
)
|
||||||
|
.forEach((stateItem) => labelItems.add(stateItem.entity_id));
|
||||||
|
if (!items) {
|
||||||
|
items = labelItems;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
items =
|
||||||
|
"intersection" in items
|
||||||
|
? // @ts-ignore
|
||||||
|
items.intersection(labelItems)
|
||||||
|
: new Set([...items].filter((x) => labelItems!.has(x)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._filteredStateItems = items ? [...items] : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _clearFilter() {
|
||||||
|
this._filters = {};
|
||||||
|
this._applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
protected firstUpdated(changedProps: PropertyValues) {
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
super.firstUpdated(changedProps);
|
super.firstUpdated(changedProps);
|
||||||
if (this.route.path === "/add") {
|
if (this.route.path === "/add") {
|
||||||
@ -418,9 +563,23 @@ export class HaConfigHelpers extends SubscribeMixin(LitElement) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createHelpler() {
|
private _createHelper() {
|
||||||
showHelperDetailDialog(this, {});
|
showHelperDetailDialog(this, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResultGroup {
|
||||||
|
return [
|
||||||
|
haStyle,
|
||||||
|
css`
|
||||||
|
hass-tabs-subpage-data-table {
|
||||||
|
--data-table-row-height: 60px;
|
||||||
|
}
|
||||||
|
hass-tabs-subpage-data-table.narrow {
|
||||||
|
--data-table-row-height: 72px;
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user