From 90526ac56325593e9556cbe90924b82a94da047a Mon Sep 17 00:00:00 2001 From: Bram Kragten Date: Fri, 11 Oct 2019 14:55:45 +0200 Subject: [PATCH] Migrate entity registry to data-table (#3965) * Migrate entity registry to data-table * icons * Styling * Review comments * fix not selector * typos + move columns out of class * Localize + comments * Fucked up the rebase --- src/components/data-table/ha-data-table.ts | 51 +++-- .../data-table/sort_filter_worker.ts | 26 +-- .../config/devices/ha-devices-data-table.ts | 8 +- .../ha-config-entity-registry.ts | 175 ++++++++++-------- .../unused-entities/hui-unused-entities.ts | 4 +- src/translations/en.json | 8 +- 6 files changed, 158 insertions(+), 114 deletions(-) diff --git a/src/components/data-table/ha-data-table.ts b/src/components/data-table/ha-data-table.ts index afa109088e..d009bd8c77 100644 --- a/src/components/data-table/ha-data-table.ts +++ b/src/components/data-table/ha-data-table.ts @@ -59,31 +59,31 @@ export interface SortingChangedEvent { export type SortingDirection = "desc" | "asc" | null; -export interface DataTabelColumnContainer { - [key: string]: DataTabelColumnData; +export interface DataTableColumnContainer { + [key: string]: DataTableColumnData; } -export interface DataTabelSortColumnData { +export interface DataTableSortColumnData { sortable?: boolean; filterable?: boolean; filterKey?: string; direction?: SortingDirection; } -export interface DataTabelColumnData extends DataTabelSortColumnData { +export interface DataTableColumnData extends DataTableSortColumnData { title: string; - type?: "numeric"; - template?: (data: any, row: DataTabelRowData) => TemplateResult; + type?: "numeric" | "icon"; + template?: (data: any, row: T) => TemplateResult; } -export interface DataTabelRowData { +export interface DataTableRowData { [key: string]: any; } @customElement("ha-data-table") export class HaDataTable extends BaseElement { - @property({ type: Object }) public columns: DataTabelColumnContainer = {}; - @property({ type: Array }) public data: DataTabelRowData[] = []; + @property({ type: Object }) public columns: DataTableColumnContainer = {}; + @property({ type: Array }) public data: DataTableRowData[] = []; @property({ type: Boolean }) public selectable = false; @property({ type: String }) public id = "id"; protected mdcFoundation!: MDCDataTableFoundation; @@ -98,9 +98,9 @@ export class HaDataTable extends BaseElement { @property({ type: String }) private _filter = ""; @property({ type: String }) private _sortColumn?: string; @property({ type: String }) private _sortDirection: SortingDirection = null; - @property({ type: Array }) private _filteredData: DataTabelRowData[] = []; + @property({ type: Array }) private _filteredData: DataTableRowData[] = []; private _sortColumns: { - [key: string]: DataTabelSortColumnData; + [key: string]: DataTableSortColumnData; } = {}; private curRequest = 0; private _worker: any | undefined; @@ -134,8 +134,8 @@ export class HaDataTable extends BaseElement { } } - const clonedColumns: DataTabelColumnContainer = deepClone(this.columns); - Object.values(clonedColumns).forEach((column: DataTabelColumnData) => { + const clonedColumns: DataTableColumnContainer = deepClone(this.columns); + Object.values(clonedColumns).forEach((column: DataTableColumnData) => { delete column.title; delete column.type; delete column.template; @@ -190,9 +190,12 @@ export class HaDataTable extends BaseElement { const [key, column] = columnEntry; const sorted = key === this._sortColumn; const classes = { - "mdc-data-table__cell--numeric": Boolean( + "mdc-data-table__header-cell--numeric": Boolean( column.type && column.type === "numeric" ), + "mdc-data-table__header-cell--icon": Boolean( + column.type && column.type === "icon" + ), sortable: Boolean(column.sortable), "not-sorted": Boolean(column.sortable && !sorted), }; @@ -222,8 +225,8 @@ export class HaDataTable extends BaseElement { ${repeat( this._filteredData!, - (row: DataTabelRowData) => row[this.id], - (row: DataTabelRowData) => html` + (row: DataTableRowData) => row[this.id], + (row: DataTableRowData) => html` ${column.template @@ -516,6 +522,11 @@ export class HaDataTable extends BaseElement { text-align: left; } + .mdc-data-table__cell--icon { + color: var(--secondary-text-color); + text-align: center; + } + .mdc-data-table__header-cell { font-family: Roboto, sans-serif; -moz-osx-font-smoothing: grayscale; @@ -543,6 +554,10 @@ export class HaDataTable extends BaseElement { text-align: left; } + .mdc-data-table__header-cell--icon { + text-align: center; + } + /* custom from here */ .mdc-data-table { @@ -554,7 +569,7 @@ export class HaDataTable extends BaseElement { .mdc-data-table__header-cell.sortable { cursor: pointer; } - .mdc-data-table__header-cell.not-sorted:not(.mdc-data-table__cell--numeric) + .mdc-data-table__header-cell.not-sorted:not(.mdc-data-table__header-cell--numeric):not(.mdc-data-table__header-cell--icon) span { position: relative; left: -24px; @@ -565,7 +580,7 @@ export class HaDataTable extends BaseElement { .mdc-data-table__header-cell.not-sorted ha-icon { left: -36px; } - .mdc-data-table__header-cell.not-sorted:not(.mdc-data-table__cell--numeric):hover + .mdc-data-table__header-cell.not-sorted:not(.mdc-data-table__header-cell--numeric):not(.mdc-data-table__header-cell--icon):hover span { left: 0px; } diff --git a/src/components/data-table/sort_filter_worker.ts b/src/components/data-table/sort_filter_worker.ts index 47e36007b9..bd4eb831d3 100644 --- a/src/components/data-table/sort_filter_worker.ts +++ b/src/components/data-table/sort_filter_worker.ts @@ -1,7 +1,7 @@ import { - DataTabelColumnContainer, - DataTabelColumnData, - DataTabelRowData, + DataTableColumnContainer, + DataTableColumnData, + DataTableRowData, SortingDirection, } from "./ha-data-table"; @@ -9,8 +9,8 @@ import memoizeOne from "memoize-one"; export const filterSortData = memoizeOne( async ( - data: DataTabelRowData[], - columns: DataTabelColumnContainer, + data: DataTableRowData[], + columns: DataTableColumnContainer, filter: string, direction: SortingDirection, sortColumn?: string @@ -27,8 +27,8 @@ export const filterSortData = memoizeOne( const _memFilterData = memoizeOne( async ( - data: DataTabelRowData[], - columns: DataTabelColumnContainer, + data: DataTableRowData[], + columns: DataTableColumnContainer, filter: string ) => { if (!filter) { @@ -40,8 +40,8 @@ const _memFilterData = memoizeOne( const _memSortData = memoizeOne( ( - data: DataTabelRowData[], - columns: DataTabelColumnContainer, + data: DataTableRowData[], + columns: DataTableColumnContainer, direction: SortingDirection, sortColumn: string ) => { @@ -50,8 +50,8 @@ const _memSortData = memoizeOne( ); export const filterData = ( - data: DataTabelRowData[], - columns: DataTabelColumnContainer, + data: DataTableRowData[], + columns: DataTableColumnContainer, filter: string ) => data.filter((row) => { @@ -71,8 +71,8 @@ export const filterData = ( }); export const sortData = ( - data: DataTabelRowData[], - column: DataTabelColumnData, + data: DataTableRowData[], + column: DataTableColumnData, direction: SortingDirection, sortColumn: string ) => diff --git a/src/panels/config/devices/ha-devices-data-table.ts b/src/panels/config/devices/ha-devices-data-table.ts index d21e31aaa8..32ba9d881c 100644 --- a/src/panels/config/devices/ha-devices-data-table.ts +++ b/src/panels/config/devices/ha-devices-data-table.ts @@ -13,9 +13,9 @@ import { import { HomeAssistant } from "../../../types"; // tslint:disable-next-line import { - DataTabelColumnContainer, + DataTableColumnContainer, RowClickedEvent, - DataTabelRowData, + DataTableRowData, } from "../../../components/data-table/ha-data-table"; // tslint:disable-next-line import { DeviceRegistryEntry } from "../../../data/device_registry"; @@ -127,7 +127,7 @@ export class HaDevicesDataTable extends LitElement { ); private _columns = memoizeOne( - (narrow: boolean): DataTabelColumnContainer => + (narrow: boolean): DataTableColumnContainer => narrow ? { name: { @@ -136,7 +136,7 @@ export class HaDevicesDataTable extends LitElement { filterKey: "name", filterable: true, direction: "asc", - template: (name, device: DataTabelRowData) => { + template: (name, device: DataTableRowData) => { const battery = device.battery_entity ? this.hass.states[device.battery_entity] : undefined; diff --git a/src/panels/config/entity_registry/ha-config-entity-registry.ts b/src/panels/config/entity_registry/ha-config-entity-registry.ts index e720e6e319..b0424d4511 100644 --- a/src/panels/config/entity_registry/ha-config-entity-registry.ts +++ b/src/panels/config/entity_registry/ha-config-entity-registry.ts @@ -6,9 +6,6 @@ import { CSSResult, property, } from "lit-element"; -import "@polymer/paper-item/paper-icon-item"; -import "@polymer/paper-item/paper-item"; -import "@polymer/paper-item/paper-item-body"; import { HomeAssistant } from "../../../types"; import { @@ -18,7 +15,7 @@ import { } from "../../../data/entity_registry"; import "../../../layouts/hass-subpage"; import "../../../layouts/hass-loading-screen"; -import "../../../components/ha-card"; +import "../../../components/data-table/ha-data-table"; import "../../../components/ha-icon"; import "../../../components/ha-switch"; import { domainIcon } from "../../../common/entity/domain_icon"; @@ -30,11 +27,14 @@ import { loadEntityRegistryDetailDialog, } from "./show-dialog-entity-registry-detail"; import { UnsubscribeFunc } from "home-assistant-js-websocket"; -import { compare } from "../../../common/string/compare"; -import { classMap } from "lit-html/directives/class-map"; // tslint:disable-next-line import { HaSwitch } from "../../../components/ha-switch"; import memoize from "memoize-one"; +// tslint:disable-next-line +import { + DataTableColumnContainer, + RowClickedEvent, +} from "../../../components/data-table/ha-data-table"; class HaConfigEntityRegistry extends LitElement { @property() public hass!: HomeAssistant; @@ -43,11 +43,76 @@ class HaConfigEntityRegistry extends LitElement { @property() private _showDisabled = false; private _unsubEntities?: UnsubscribeFunc; + private _columns = memoize( + (_language): DataTableColumnContainer => { + return { + icon: { + title: "", + type: "icon", + template: (icon) => html` + + `, + }, + name: { + title: this.hass.localize( + "ui.panel.config.entity_registry.picker.headers.name" + ), + sortable: true, + filterable: true, + direction: "asc", + }, + entity_id: { + title: this.hass.localize( + "ui.panel.config.entity_registry.picker.headers.entity_id" + ), + sortable: true, + filterable: true, + }, + platform: { + title: this.hass.localize( + "ui.panel.config.entity_registry.picker.headers.integration" + ), + sortable: true, + filterable: true, + template: (platform) => + html` + ${this.hass.localize(`component.${platform}.config.title`) || + platform} + `, + }, + disabled_by: { + title: this.hass.localize( + "ui.panel.config.entity_registry.picker.headers.enabled" + ), + type: "icon", + template: (disabledBy) => html` + + `, + }, + }; + } + ); + private _filteredEntities = memoize( (entities: EntityRegistryEntry[], showDisabled: boolean) => - showDisabled + (showDisabled ? entities : entities.filter((entity) => !Boolean(entity.disabled_by)) + ).map((entry) => { + const state = this.hass!.states[entry.entity_id]; + return { + ...entry, + icon: state + ? stateIcon(state) + : domainIcon(computeDomain(entry.entity_id)), + name: + computeEntityRegistryName(this.hass!, entry) || + this.hass!.localize("state.default.unavailable"), + }; + }) ); public disconnectedCallback() { @@ -89,56 +154,21 @@ class HaConfigEntityRegistry extends LitElement { "ui.panel.config.entity_registry.picker.integrations_page" )} - - - - ${this.hass.localize( - "ui.panel.config.entity_registry.picker.show_disabled" - )}${this.hass.localize( + "ui.panel.config.entity_registry.picker.show_disabled" + )} - ${this._filteredEntities(this._entities, this._showDisabled).map( - (entry) => { - const state = this.hass!.states[entry.entity_id]; - return html` - - - -
- ${computeEntityRegistryName(this.hass!, entry) || - `(${this.hass!.localize( - "state.default.unavailable" - )})`} -
-
- ${entry.entity_id} -
-
-
- ${entry.platform} - ${entry.disabled_by - ? html` -
(disabled) - ` - : ""} -
-
- `; - } - )} -
+ + + `; @@ -155,9 +185,7 @@ class HaConfigEntityRegistry extends LitElement { this._unsubEntities = subscribeEntityRegistry( this.hass.connection, (entities) => { - this._entities = entities.sort((ent1, ent2) => - compare(ent1.entity_id, ent2.entity_id) - ); + this._entities = entities; } ); } @@ -167,8 +195,14 @@ class HaConfigEntityRegistry extends LitElement { this._showDisabled = (ev.target as HaSwitch).checked; } - private _openEditEntry(ev: MouseEvent): void { - const entry = (ev.currentTarget! as any).entry; + private _openEditEntry(ev: CustomEvent): void { + const entryId = (ev.detail as RowClickedEvent).id; + const entry = this._entities!.find( + (entity) => entity.entity_id === entryId + ); + if (!entry) { + return; + } showEntityRegistryDetailDialog(this, { entry, }); @@ -179,23 +213,12 @@ class HaConfigEntityRegistry extends LitElement { a { color: var(--primary-color); } - ha-card { + ha-data-table { margin-bottom: 24px; - direction: ltr; + margin-top: 0px; } - paper-icon-item { - cursor: pointer; - color: var(--primary-text-color); - } - ha-icon { - margin-left: 8px; - } - .platform { - text-align: right; - margin: 0 0 0 8px; - } - .disabled-entry { - color: var(--secondary-text-color); + ha-switch { + margin-top: 16px; } `; } diff --git a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts index ebe06225dc..10e4b21293 100644 --- a/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts +++ b/src/panels/lovelace/editor/unused-entities/hui-unused-entities.ts @@ -22,7 +22,7 @@ import "../../../../components/data-table/ha-data-table"; // tslint:disable-next-line import { SelectionChangedEvent, - DataTabelColumnContainer, + DataTableColumnContainer, } from "../../../../components/data-table/ha-data-table"; import { computeStateName } from "../../../../common/entity/compute_state_name"; @@ -55,7 +55,7 @@ export class HuiUnusedEntities extends LitElement { } private _columns = memoizeOne((narrow: boolean) => { - const columns: DataTabelColumnContainer = { + const columns: DataTableColumnContainer = { entity: { title: "Entity", sortable: true, diff --git a/src/translations/en.json b/src/translations/en.json index 857ba13728..e0cc3bf6d1 100755 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -1110,7 +1110,13 @@ "introduction": "Home Assistant keeps a registry of every entity it has ever seen that can be uniquely identified. Each of these entities will have an entity ID assigned which will be reserved for just this entity.", "introduction2": "Use the entity registry to override the name, change the entity ID or remove the entry from Home Assistant. Note, removing the entity registry entry won't remove the entity. To do that, follow the link below and remove it from the integrations page.", "integrations_page": "Integrations page", - "show_disabled": "Show disabled entities" + "show_disabled": "Show disabled entities", + "headers": { + "name": "Name", + "entity_id": "Entity ID", + "integration": "Integration", + "enabled": "Enabled" + } }, "editor": { "unavailable": "This entity is not currently available.",