diff --git a/src/components/ha-filter-devices.ts b/src/components/ha-filter-devices.ts index a88b957ed775..66da797b1e53 100644 --- a/src/components/ha-filter-devices.ts +++ b/src/components/ha-filter-devices.ts @@ -69,7 +69,7 @@ export class HaFilterDevices extends LitElement { @value-changed=${this._handleSearchChange} > - + +
+ ${this.hass.localize( + "ui.panel.config.entities.picker.headers.domain" + )} + ${this.value?.length + ? html`
${this.value?.length}
+ ` + : nothing} +
+ ${this._shouldRender + ? html` + + + ${repeat( + this._domains(this.hass.states, this._filter), + (i) => i, + (domain) => + html` + + ${domainToName(this.hass.localize, domain)} + ` + )} + ` + : nothing} + + `; + } + + private _domains = memoizeOne((states, filter) => { + const domains = new Set(); + Object.keys(states).forEach((entityId) => { + domains.add(computeDomain(entityId)); + }); + return Array.from(domains) + .filter((domain) => !filter || domain.toLowerCase().includes(filter)) + .sort((a, b) => stringCompare(a, b, this.hass.locale.language)); + }); + + protected updated(changed) { + if (changed.has("expanded") && this.expanded) { + setTimeout(() => { + if (!this.expanded) return; + this.renderRoot.querySelector("mwc-list")!.style.height = + `${this.clientHeight - 49 - 32}px`; // 32px is the height of the search input + }, 300); + } + } + + private _expandedWillChange(ev) { + this._shouldRender = ev.detail.expanded; + } + + private _expandedChanged(ev) { + this.expanded = ev.detail.expanded; + } + + private _handleItemClick(ev) { + const listItem = ev.target.closest("ha-check-list-item"); + const value = listItem?.value; + if (!value) { + return; + } + if (this.value?.includes(value)) { + this.value = this.value?.filter((val) => val !== value); + } else { + this.value = [...(this.value || []), value]; + } + + listItem.selected = this.value.includes(value); + + fireEvent(this, "data-table-filter-changed", { + value: this.value, + items: undefined, + }); + } + + private _clearFilter(ev) { + ev.preventDefault(); + this.value = undefined; + fireEvent(this, "data-table-filter-changed", { + value: undefined, + items: undefined, + }); + } + + private _handleSearchChange(ev: CustomEvent) { + this._filter = ev.detail.value.toLowerCase(); + } + + static get styles(): CSSResultGroup { + return [ + haStyleScrollbar, + css` + :host { + border-bottom: 1px solid var(--divider-color); + } + :host([expanded]) { + flex: 1; + height: 0; + } + ha-expansion-panel { + --ha-card-border-radius: 0; + --expansion-panel-content-padding: 0; + } + .header { + display: flex; + align-items: center; + } + .header ha-icon-button { + margin-inline-start: auto; + margin-inline-end: 8px; + } + .badge { + display: inline-block; + margin-left: 8px; + margin-inline-start: 8px; + margin-inline-end: 0; + min-width: 16px; + box-sizing: border-box; + border-radius: 50%; + font-weight: 400; + font-size: 11px; + background-color: var(--primary-color); + line-height: 16px; + text-align: center; + padding: 0px 2px; + color: var(--text-primary-color); + } + search-input-outlined { + display: block; + padding: 0 8px; + } + `, + ]; + } +} + +declare global { + interface HTMLElementTagNameMap { + "ha-filter-domains": HaFilterDomains; + } +} diff --git a/src/components/ha-filter-entities.ts b/src/components/ha-filter-entities.ts index ee92c9d0c369..2f3f1f7b457f 100644 --- a/src/components/ha-filter-entities.ts +++ b/src/components/ha-filter-entities.ts @@ -71,7 +71,7 @@ export class HaFilterEntities extends LitElement { @value-changed=${this._handleSearchChange} > - + - + ${repeat( this._integrations(this._manifests, this._filter, this.value), (i) => i.domain, diff --git a/src/panels/config/entities/ha-config-entities.ts b/src/panels/config/entities/ha-config-entities.ts index 1e8d9a6db479..2260effc44ab 100644 --- a/src/panels/config/entities/ha-config-entities.ts +++ b/src/panels/config/entities/ha-config-entities.ts @@ -53,6 +53,7 @@ import "../../../components/ha-alert"; import "../../../components/ha-button-menu"; import "../../../components/ha-check-list-item"; import "../../../components/ha-filter-devices"; +import "../../../components/ha-filter-domains"; import "../../../components/ha-filter-floor-areas"; import "../../../components/ha-filter-integrations"; import "../../../components/ha-filter-labels"; @@ -443,6 +444,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) { entryIds.includes(entity.config_entry_id)) ); filter.value!.forEach((domain) => filteredDomains.add(domain)); + } else if (key === "ha-filter-domains" && filter.value?.length) { + filteredEntities = filteredEntities.filter((entity) => + filter.value?.includes(computeDomain(entity.entity_id)) + ); } else if (key === "ha-filter-labels" && filter.value?.length) { filteredEntities = filteredEntities.filter((entity) => entity.labels.some((lbl) => filter.value!.includes(lbl)) @@ -782,6 +787,15 @@ ${ .narrow=${this.narrow} @expanded-changed=${this._filterExpanded} > +