mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-28 11:46:42 +00:00
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
This commit is contained in:
parent
6f7ea03e35
commit
90526ac563
@ -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?: <T>(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 {
|
||||
<tbody class="mdc-data-table__content">
|
||||
${repeat(
|
||||
this._filteredData!,
|
||||
(row: DataTabelRowData) => row[this.id],
|
||||
(row: DataTabelRowData) => html`
|
||||
(row: DataTableRowData) => row[this.id],
|
||||
(row: DataTableRowData) => html`
|
||||
<tr
|
||||
data-row-id="${row[this.id]}"
|
||||
@click=${this._handleRowClick}
|
||||
@ -251,6 +254,9 @@ export class HaDataTable extends BaseElement {
|
||||
"mdc-data-table__cell--numeric": Boolean(
|
||||
column.type && column.type === "numeric"
|
||||
),
|
||||
"mdc-data-table__cell--icon": Boolean(
|
||||
column.type && column.type === "icon"
|
||||
),
|
||||
})}"
|
||||
>
|
||||
${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;
|
||||
}
|
||||
|
@ -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
|
||||
) =>
|
||||
|
@ -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;
|
||||
|
@ -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`
|
||||
<ha-icon slot="item-icon" .icon=${icon}></ha-icon>
|
||||
`,
|
||||
},
|
||||
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`
|
||||
<ha-icon
|
||||
slot="item-icon"
|
||||
.icon=${disabledBy ? "hass:cancel" : "hass:check-circle"}
|
||||
></ha-icon>
|
||||
`,
|
||||
},
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
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"
|
||||
)}
|
||||
</a>
|
||||
</span>
|
||||
<ha-card>
|
||||
<paper-item>
|
||||
<ha-switch
|
||||
?checked=${this._showDisabled}
|
||||
@change=${this._showDisabledChanged}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.entity_registry.picker.show_disabled"
|
||||
)}</ha-switch
|
||||
></paper-item
|
||||
<ha-switch
|
||||
?checked=${this._showDisabled}
|
||||
@change=${this._showDisabledChanged}
|
||||
>${this.hass.localize(
|
||||
"ui.panel.config.entity_registry.picker.show_disabled"
|
||||
)}</ha-switch
|
||||
>
|
||||
${this._filteredEntities(this._entities, this._showDisabled).map(
|
||||
(entry) => {
|
||||
const state = this.hass!.states[entry.entity_id];
|
||||
return html`
|
||||
<paper-icon-item
|
||||
@click=${this._openEditEntry}
|
||||
.entry=${entry}
|
||||
class=${classMap({ "disabled-entry": !!entry.disabled_by })}
|
||||
>
|
||||
<ha-icon
|
||||
slot="item-icon"
|
||||
.icon=${state
|
||||
? stateIcon(state)
|
||||
: domainIcon(computeDomain(entry.entity_id))}
|
||||
></ha-icon>
|
||||
<paper-item-body two-line>
|
||||
<div class="name">
|
||||
${computeEntityRegistryName(this.hass!, entry) ||
|
||||
`(${this.hass!.localize(
|
||||
"state.default.unavailable"
|
||||
)})`}
|
||||
</div>
|
||||
<div class="secondary entity-id">
|
||||
${entry.entity_id}
|
||||
</div>
|
||||
</paper-item-body>
|
||||
<div class="platform">
|
||||
${entry.platform}
|
||||
${entry.disabled_by
|
||||
? html`
|
||||
<br />(disabled)
|
||||
`
|
||||
: ""}
|
||||
</div>
|
||||
</paper-icon-item>
|
||||
`;
|
||||
}
|
||||
)}
|
||||
</ha-card>
|
||||
</span>
|
||||
<ha-data-table
|
||||
.columns=${this._columns(this.hass.language)}
|
||||
.data=${this._filteredEntities(this._entities, this._showDisabled)}
|
||||
@row-click=${this._openEditEntry}
|
||||
id="entity_id"
|
||||
>
|
||||
</ha-data-table>
|
||||
</ha-config-section>
|
||||
</hass-subpage>
|
||||
`;
|
||||
@ -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;
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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.",
|
||||
|
Loading…
x
Reference in New Issue
Block a user