mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-21 16:26:43 +00:00
Virtualize data tabel (#5066)
* WIP * Fixes and implement further * Give more space to table on mobile * Remove unused deps * Update ha-config-lovelace-dashboards.ts * Add auto-height * Console.bye hihi * lint
This commit is contained in:
parent
1599dc9e16
commit
5a2649a65b
@ -19,8 +19,6 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@material/chips": "^5.0.0",
|
"@material/chips": "^5.0.0",
|
||||||
"@material/data-table": "^5.0.0",
|
|
||||||
"@material/mwc-base": "^0.13.0",
|
|
||||||
"@material/mwc-button": "^0.13.0",
|
"@material/mwc-button": "^0.13.0",
|
||||||
"@material/mwc-checkbox": "^0.13.0",
|
"@material/mwc-checkbox": "^0.13.0",
|
||||||
"@material/mwc-dialog": "^0.13.0",
|
"@material/mwc-dialog": "^0.13.0",
|
||||||
|
@ -1,27 +1,21 @@
|
|||||||
import { repeat } from "lit-html/directives/repeat";
|
|
||||||
import deepClone from "deep-clone-simple";
|
import deepClone from "deep-clone-simple";
|
||||||
|
|
||||||
import {
|
|
||||||
MDCDataTableAdapter,
|
|
||||||
MDCDataTableFoundation,
|
|
||||||
} from "@material/data-table";
|
|
||||||
|
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
|
|
||||||
|
import { scroll } from "lit-virtualizer";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
html,
|
html,
|
||||||
query,
|
query,
|
||||||
queryAll,
|
|
||||||
CSSResult,
|
CSSResult,
|
||||||
css,
|
css,
|
||||||
customElement,
|
customElement,
|
||||||
property,
|
property,
|
||||||
TemplateResult,
|
TemplateResult,
|
||||||
PropertyValues,
|
PropertyValues,
|
||||||
|
LitElement,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
|
|
||||||
import { BaseElement } from "@material/mwc-base/base-element";
|
|
||||||
|
|
||||||
// eslint-disable-next-line import/no-webpack-loader-syntax
|
// eslint-disable-next-line import/no-webpack-loader-syntax
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
// tslint:disable-next-line: no-implicit-dependencies
|
// tslint:disable-next-line: no-implicit-dependencies
|
||||||
@ -35,6 +29,8 @@ import { HaCheckbox } from "../ha-checkbox";
|
|||||||
import { fireEvent } from "../../common/dom/fire_event";
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
import { nextRender } from "../../common/util/render-status";
|
import { nextRender } from "../../common/util/render-status";
|
||||||
import { debounce } from "../../common/util/debounce";
|
import { debounce } from "../../common/util/debounce";
|
||||||
|
import { styleMap } from "lit-html/directives/style-map";
|
||||||
|
import { ifDefined } from "lit-html/directives/if-defined";
|
||||||
|
|
||||||
declare global {
|
declare global {
|
||||||
// for fire event
|
// for fire event
|
||||||
@ -50,8 +46,7 @@ export interface RowClickedEvent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectionChangedEvent {
|
export interface SelectionChangedEvent {
|
||||||
id: string;
|
value: string[];
|
||||||
selected: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SortingChangedEvent {
|
export interface SortingChangedEvent {
|
||||||
@ -76,6 +71,8 @@ export interface DataTableColumnData extends DataTableSortColumnData {
|
|||||||
title: string;
|
title: string;
|
||||||
type?: "numeric" | "icon";
|
type?: "numeric" | "icon";
|
||||||
template?: <T>(data: any, row: T) => TemplateResult | string;
|
template?: <T>(data: any, row: T) => TemplateResult | string;
|
||||||
|
width?: string;
|
||||||
|
grows?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DataTableRowData {
|
export interface DataTableRowData {
|
||||||
@ -84,26 +81,23 @@ export interface DataTableRowData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@customElement("ha-data-table")
|
@customElement("ha-data-table")
|
||||||
export class HaDataTable extends BaseElement {
|
export class HaDataTable extends LitElement {
|
||||||
@property({ type: Object }) public columns: DataTableColumnContainer = {};
|
@property({ type: Object }) public columns: DataTableColumnContainer = {};
|
||||||
@property({ type: Array }) public data: DataTableRowData[] = [];
|
@property({ type: Array }) public data: DataTableRowData[] = [];
|
||||||
@property({ type: Boolean }) public selectable = false;
|
@property({ type: Boolean }) public selectable = false;
|
||||||
|
@property({ type: Boolean, attribute: "auto-height" })
|
||||||
|
public autoHeight = false;
|
||||||
@property({ type: String }) public id = "id";
|
@property({ type: String }) public id = "id";
|
||||||
@property({ type: String }) public filter = "";
|
@property({ type: String }) public filter = "";
|
||||||
protected mdcFoundation!: MDCDataTableFoundation;
|
|
||||||
protected readonly mdcFoundationClass = MDCDataTableFoundation;
|
|
||||||
@query(".mdc-data-table") protected mdcRoot!: HTMLElement;
|
|
||||||
@queryAll(".mdc-data-table__row") protected rowElements!: HTMLElement[];
|
|
||||||
@property({ type: Boolean }) private _filterable = false;
|
@property({ type: Boolean }) private _filterable = false;
|
||||||
@property({ type: Boolean }) private _headerChecked = false;
|
|
||||||
@property({ type: Boolean }) private _headerIndeterminate = false;
|
|
||||||
@property({ type: Array }) private _checkedRows: string[] = [];
|
|
||||||
@property({ type: String }) private _filter = "";
|
@property({ type: String }) private _filter = "";
|
||||||
@property({ type: String }) private _sortColumn?: string;
|
@property({ type: String }) private _sortColumn?: string;
|
||||||
@property({ type: String }) private _sortDirection: SortingDirection = null;
|
@property({ type: String }) private _sortDirection: SortingDirection = null;
|
||||||
@property({ type: Array }) private _filteredData: DataTableRowData[] = [];
|
@property({ type: Array }) private _filteredData: DataTableRowData[] = [];
|
||||||
@query("slot[name='header']") private _header!: HTMLSlotElement;
|
@query("slot[name='header']") private _header!: HTMLSlotElement;
|
||||||
@query(".scroller") private _scroller!: HTMLDivElement;
|
@query(".mdc-data-table__table") private _table!: HTMLDivElement;
|
||||||
|
private _checkableRowsCount?: number;
|
||||||
|
private _checkedRows: string[] = [];
|
||||||
private _sortColumns: {
|
private _sortColumns: {
|
||||||
[key: string]: DataTableSortColumnData;
|
[key: string]: DataTableSortColumnData;
|
||||||
} = {};
|
} = {};
|
||||||
@ -114,18 +108,17 @@ export class HaDataTable extends BaseElement {
|
|||||||
(value: string) => {
|
(value: string) => {
|
||||||
this._filter = value;
|
this._filter = value;
|
||||||
},
|
},
|
||||||
200,
|
100,
|
||||||
false
|
false
|
||||||
);
|
);
|
||||||
|
|
||||||
public clearSelection(): void {
|
public clearSelection(): void {
|
||||||
this._headerChecked = false;
|
this._checkedRows = [];
|
||||||
this._headerIndeterminate = false;
|
this._checkedRowsChanged();
|
||||||
this.mdcFoundation.handleHeaderRowCheckboxChange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected firstUpdated() {
|
protected firstUpdated(properties: PropertyValues) {
|
||||||
super.firstUpdated();
|
super.firstUpdated(properties);
|
||||||
this._worker = sortFilterWorker();
|
this._worker = sortFilterWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,6 +152,12 @@ export class HaDataTable extends BaseElement {
|
|||||||
this._debounceSearch(this.filter);
|
this._debounceSearch(this.filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (properties.has("data")) {
|
||||||
|
this._checkableRowsCount = this.data.filter(
|
||||||
|
(row) => row.selectable !== false
|
||||||
|
).length;
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
properties.has("data") ||
|
properties.has("data") ||
|
||||||
properties.has("columns") ||
|
properties.has("columns") ||
|
||||||
@ -173,7 +172,7 @@ export class HaDataTable extends BaseElement {
|
|||||||
protected render() {
|
protected render() {
|
||||||
return html`
|
return html`
|
||||||
<div class="mdc-data-table">
|
<div class="mdc-data-table">
|
||||||
<slot name="header" @slotchange=${this._calcScrollHeight}>
|
<slot name="header" @slotchange=${this._calcTableHeight}>
|
||||||
${this._filterable
|
${this._filterable
|
||||||
? html`
|
? html`
|
||||||
<div class="table-header">
|
<div class="table-header">
|
||||||
@ -184,168 +183,151 @@ export class HaDataTable extends BaseElement {
|
|||||||
`
|
`
|
||||||
: ""}
|
: ""}
|
||||||
</slot>
|
</slot>
|
||||||
<div class="scroller">
|
<div
|
||||||
<table class="mdc-data-table__table">
|
class="mdc-data-table__table ${classMap({
|
||||||
<thead>
|
"auto-height": this.autoHeight,
|
||||||
<tr class="mdc-data-table__header-row">
|
})}"
|
||||||
${this.selectable
|
style=${styleMap({
|
||||||
? html`
|
height: this.autoHeight
|
||||||
<th
|
? `${this._filteredData.length * 52 + 56}px`
|
||||||
class="mdc-data-table__header-cell mdc-data-table__header-cell--checkbox"
|
: `calc(100% - ${this._header?.clientHeight}px)`,
|
||||||
role="columnheader"
|
})}
|
||||||
scope="col"
|
>
|
||||||
>
|
<div class="mdc-data-table__header-row">
|
||||||
<ha-checkbox
|
${this.selectable
|
||||||
class="mdc-data-table__row-checkbox"
|
? html`
|
||||||
@change=${this._handleHeaderRowCheckboxChange}
|
<div
|
||||||
.indeterminate=${this._headerIndeterminate}
|
class="mdc-data-table__header-cell mdc-data-table__header-cell--checkbox"
|
||||||
.checked=${this._headerChecked}
|
role="columnheader"
|
||||||
>
|
scope="col"
|
||||||
</ha-checkbox>
|
|
||||||
</th>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${Object.entries(this.columns).map((columnEntry) => {
|
|
||||||
const [key, column] = columnEntry;
|
|
||||||
const sorted = key === this._sortColumn;
|
|
||||||
const classes = {
|
|
||||||
"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),
|
|
||||||
};
|
|
||||||
return html`
|
|
||||||
<th
|
|
||||||
class="mdc-data-table__header-cell ${classMap(classes)}"
|
|
||||||
role="columnheader"
|
|
||||||
scope="col"
|
|
||||||
@click=${this._handleHeaderClick}
|
|
||||||
data-column-id="${key}"
|
|
||||||
>
|
|
||||||
${column.sortable
|
|
||||||
? html`
|
|
||||||
<ha-icon
|
|
||||||
.icon=${sorted && this._sortDirection === "desc"
|
|
||||||
? "hass:arrow-down"
|
|
||||||
: "hass:arrow-up"}
|
|
||||||
></ha-icon>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
<span>${column.title}</span>
|
|
||||||
</th>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody class="mdc-data-table__content">
|
|
||||||
${repeat(
|
|
||||||
this._filteredData!,
|
|
||||||
(row: DataTableRowData) => row[this.id],
|
|
||||||
(row: DataTableRowData) => html`
|
|
||||||
<tr
|
|
||||||
data-row-id="${row[this.id]}"
|
|
||||||
@click=${this._handleRowClick}
|
|
||||||
class="mdc-data-table__row"
|
|
||||||
.selectable=${row.selectable !== false}
|
|
||||||
>
|
>
|
||||||
${this.selectable
|
<ha-checkbox
|
||||||
? html`
|
class="mdc-data-table__row-checkbox"
|
||||||
<td
|
@change=${this._handleHeaderRowCheckboxClick}
|
||||||
class="mdc-data-table__cell mdc-data-table__cell--checkbox"
|
.indeterminate=${this._checkedRows.length &&
|
||||||
>
|
this._checkedRows.length !== this._checkableRowsCount}
|
||||||
<ha-checkbox
|
.checked=${this._checkedRows.length ===
|
||||||
class="mdc-data-table__row-checkbox"
|
this._checkableRowsCount}
|
||||||
@change=${this._handleRowCheckboxChange}
|
>
|
||||||
.disabled=${row.selectable === false}
|
</ha-checkbox>
|
||||||
.checked=${this._checkedRows.includes(
|
</div>
|
||||||
String(row[this.id])
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
</ha-checkbox>
|
|
||||||
</td>
|
|
||||||
`
|
|
||||||
: ""}
|
|
||||||
${Object.entries(this.columns).map((columnEntry) => {
|
|
||||||
const [key, column] = columnEntry;
|
|
||||||
return html`
|
|
||||||
<td
|
|
||||||
class="mdc-data-table__cell ${classMap({
|
|
||||||
"mdc-data-table__cell--numeric": Boolean(
|
|
||||||
column.type && column.type === "numeric"
|
|
||||||
),
|
|
||||||
"mdc-data-table__cell--icon": Boolean(
|
|
||||||
column.type && column.type === "icon"
|
|
||||||
),
|
|
||||||
})}"
|
|
||||||
>
|
|
||||||
${column.template
|
|
||||||
? column.template(row[key], row)
|
|
||||||
: row[key]}
|
|
||||||
</td>
|
|
||||||
`;
|
|
||||||
})}
|
|
||||||
</tr>
|
|
||||||
`
|
`
|
||||||
)}
|
: ""}
|
||||||
</tbody>
|
${Object.entries(this.columns).map((columnEntry) => {
|
||||||
</table>
|
const [key, column] = columnEntry;
|
||||||
|
const sorted = key === this._sortColumn;
|
||||||
|
const classes = {
|
||||||
|
"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),
|
||||||
|
grows: Boolean(column.grows),
|
||||||
|
};
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="mdc-data-table__header-cell ${classMap(classes)}"
|
||||||
|
style=${column.width
|
||||||
|
? styleMap({
|
||||||
|
[column.grows ? "minWidth" : "width"]: String(
|
||||||
|
column.width
|
||||||
|
),
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
|
role="columnheader"
|
||||||
|
scope="col"
|
||||||
|
@click=${this._handleHeaderClick}
|
||||||
|
.columnId=${key}
|
||||||
|
>
|
||||||
|
${column.sortable
|
||||||
|
? html`
|
||||||
|
<ha-icon
|
||||||
|
.icon=${sorted && this._sortDirection === "desc"
|
||||||
|
? "hass:arrow-down"
|
||||||
|
: "hass:arrow-up"}
|
||||||
|
></ha-icon>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
<span>${column.title}</span>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
<div class="mdc-data-table__content scroller">
|
||||||
|
${scroll({
|
||||||
|
items: this._filteredData,
|
||||||
|
renderItem: (row: DataTableRowData) => html`
|
||||||
|
<div
|
||||||
|
.rowId="${row[this.id]}"
|
||||||
|
@click=${this._handleRowClick}
|
||||||
|
class="mdc-data-table__row ${classMap({
|
||||||
|
"mdc-data-table__row--selected": this._checkedRows.includes(
|
||||||
|
String(row[this.id])
|
||||||
|
),
|
||||||
|
})}"
|
||||||
|
aria-selected=${ifDefined(
|
||||||
|
this._checkedRows.includes(String(row[this.id]))
|
||||||
|
? true
|
||||||
|
: undefined
|
||||||
|
)}
|
||||||
|
.selectable=${row.selectable !== false}
|
||||||
|
>
|
||||||
|
${this.selectable
|
||||||
|
? html`
|
||||||
|
<div
|
||||||
|
class="mdc-data-table__cell mdc-data-table__cell--checkbox"
|
||||||
|
>
|
||||||
|
<ha-checkbox
|
||||||
|
class="mdc-data-table__row-checkbox"
|
||||||
|
@change=${this._handleRowCheckboxClick}
|
||||||
|
.disabled=${row.selectable === false}
|
||||||
|
.checked=${this._checkedRows.includes(
|
||||||
|
String(row[this.id])
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
</ha-checkbox>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
${Object.entries(this.columns).map((columnEntry) => {
|
||||||
|
const [key, column] = columnEntry;
|
||||||
|
return html`
|
||||||
|
<div
|
||||||
|
class="mdc-data-table__cell ${classMap({
|
||||||
|
"mdc-data-table__cell--numeric": Boolean(
|
||||||
|
column.type && column.type === "numeric"
|
||||||
|
),
|
||||||
|
"mdc-data-table__cell--icon": Boolean(
|
||||||
|
column.type && column.type === "icon"
|
||||||
|
),
|
||||||
|
grows: Boolean(column.grows),
|
||||||
|
})}"
|
||||||
|
style=${column.width
|
||||||
|
? styleMap({
|
||||||
|
[column.grows ? "minWidth" : "width"]: String(
|
||||||
|
column.width
|
||||||
|
),
|
||||||
|
})
|
||||||
|
: ""}
|
||||||
|
>
|
||||||
|
${column.template
|
||||||
|
? column.template(row[key], row)
|
||||||
|
: row[key]}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createAdapter(): MDCDataTableAdapter {
|
|
||||||
return {
|
|
||||||
addClassAtRowIndex: (rowIndex: number, cssClasses: string) => {
|
|
||||||
if (!(this.rowElements[rowIndex] as any).selectable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.rowElements[rowIndex].classList.add(cssClasses);
|
|
||||||
},
|
|
||||||
getRowCount: () => this.rowElements.length,
|
|
||||||
getRowElements: () => this.rowElements,
|
|
||||||
getRowIdAtIndex: (rowIndex: number) => this._getRowIdAtIndex(rowIndex),
|
|
||||||
getRowIndexByChildElement: (el: Element) =>
|
|
||||||
Array.prototype.indexOf.call(this.rowElements, el.closest("tr")),
|
|
||||||
getSelectedRowCount: () => this._checkedRows.length,
|
|
||||||
isCheckboxAtRowIndexChecked: (rowIndex: number) =>
|
|
||||||
this._checkedRows.includes(this._getRowIdAtIndex(rowIndex)),
|
|
||||||
isHeaderRowCheckboxChecked: () => this._headerChecked,
|
|
||||||
isRowsSelectable: () => this.selectable,
|
|
||||||
notifyRowSelectionChanged: () => undefined,
|
|
||||||
notifySelectedAll: () => undefined,
|
|
||||||
notifyUnselectedAll: () => undefined,
|
|
||||||
registerHeaderRowCheckbox: () => undefined,
|
|
||||||
registerRowCheckboxes: () => undefined,
|
|
||||||
removeClassAtRowIndex: (rowIndex: number, cssClasses: string) => {
|
|
||||||
this.rowElements[rowIndex].classList.remove(cssClasses);
|
|
||||||
},
|
|
||||||
setAttributeAtRowIndex: (
|
|
||||||
rowIndex: number,
|
|
||||||
attr: string,
|
|
||||||
value: string
|
|
||||||
) => {
|
|
||||||
this.rowElements[rowIndex].setAttribute(attr, value);
|
|
||||||
},
|
|
||||||
setHeaderRowCheckboxChecked: (checked: boolean) => {
|
|
||||||
this._headerChecked = checked;
|
|
||||||
},
|
|
||||||
setHeaderRowCheckboxIndeterminate: (indeterminate: boolean) => {
|
|
||||||
this._headerIndeterminate = indeterminate;
|
|
||||||
},
|
|
||||||
setRowCheckboxCheckedAtIndex: (rowIndex: number, checked: boolean) => {
|
|
||||||
if (!(this.rowElements[rowIndex] as any).selectable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._setRowChecked(this._getRowIdAtIndex(rowIndex), checked);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private async _filterData() {
|
private async _filterData() {
|
||||||
const startTime = new Date().getTime();
|
const startTime = new Date().getTime();
|
||||||
this.curRequest++;
|
this.curRequest++;
|
||||||
@ -373,14 +355,10 @@ export class HaDataTable extends BaseElement {
|
|||||||
this._filteredData = data;
|
this._filteredData = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _getRowIdAtIndex(rowIndex: number): string {
|
|
||||||
return this.rowElements[rowIndex].getAttribute("data-row-id")!;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _handleHeaderClick(ev: Event) {
|
private _handleHeaderClick(ev: Event) {
|
||||||
const columnId = (ev.target as HTMLElement)
|
const columnId = ((ev.target as HTMLElement).closest(
|
||||||
.closest("th")!
|
".mdc-data-table__header-cell"
|
||||||
.getAttribute("data-column-id")!;
|
) as any).columnId;
|
||||||
if (!this.columns[columnId].sortable) {
|
if (!this.columns[columnId].sortable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -400,19 +378,32 @@ export class HaDataTable extends BaseElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleHeaderRowCheckboxChange(ev: Event) {
|
private _handleHeaderRowCheckboxClick(ev: Event) {
|
||||||
const checkbox = ev.target as HaCheckbox;
|
const checkbox = ev.target as HaCheckbox;
|
||||||
this._headerChecked = checkbox.checked;
|
if (checkbox.checked) {
|
||||||
this._headerIndeterminate = checkbox.indeterminate;
|
this._checkedRows = this._filteredData
|
||||||
this.mdcFoundation.handleHeaderRowCheckboxChange();
|
.filter((data) => data.selectable !== false)
|
||||||
|
.map((data) => data[this.id]);
|
||||||
|
this._checkedRowsChanged();
|
||||||
|
} else {
|
||||||
|
this._checkedRows = [];
|
||||||
|
this._checkedRowsChanged();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleRowCheckboxChange(ev: Event) {
|
private _handleRowCheckboxClick(ev: Event) {
|
||||||
const checkbox = ev.target as HaCheckbox;
|
const checkbox = ev.target as HaCheckbox;
|
||||||
const rowId = checkbox.closest("tr")!.getAttribute("data-row-id");
|
const rowId = (checkbox.closest(".mdc-data-table__row") as any).rowId;
|
||||||
|
|
||||||
this._setRowChecked(rowId!, checkbox.checked);
|
if (checkbox.checked) {
|
||||||
this.mdcFoundation.handleRowCheckboxChange(ev);
|
if (this._checkedRows.includes(rowId)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._checkedRows = [...this._checkedRows, rowId];
|
||||||
|
} else {
|
||||||
|
this._checkedRows = this._checkedRows.filter((row) => row !== rowId);
|
||||||
|
}
|
||||||
|
this._checkedRowsChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleRowClick(ev: Event) {
|
private _handleRowClick(ev: Event) {
|
||||||
@ -420,26 +411,15 @@ export class HaDataTable extends BaseElement {
|
|||||||
if (target.tagName === "HA-CHECKBOX") {
|
if (target.tagName === "HA-CHECKBOX") {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rowId = target.closest("tr")!.getAttribute("data-row-id")!;
|
const rowId = (target.closest(".mdc-data-table__row") as any).rowId;
|
||||||
fireEvent(this, "row-click", { id: rowId }, { bubbles: false });
|
fireEvent(this, "row-click", { id: rowId }, { bubbles: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
private _setRowChecked(rowId: string, checked: boolean) {
|
private _checkedRowsChanged() {
|
||||||
if (checked) {
|
// force scroller to update, change it's items
|
||||||
if (this._checkedRows.includes(rowId)) {
|
this._filteredData = [...this._filteredData];
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._checkedRows = [...this._checkedRows, rowId];
|
|
||||||
} else {
|
|
||||||
const index = this._checkedRows.indexOf(rowId);
|
|
||||||
if (index === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._checkedRows.splice(index, 1);
|
|
||||||
}
|
|
||||||
fireEvent(this, "selection-changed", {
|
fireEvent(this, "selection-changed", {
|
||||||
id: rowId,
|
value: this._checkedRows,
|
||||||
selected: checked,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -447,15 +427,20 @@ export class HaDataTable extends BaseElement {
|
|||||||
this._debounceSearch(ev.detail.value);
|
this._debounceSearch(ev.detail.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _calcScrollHeight() {
|
private async _calcTableHeight() {
|
||||||
|
if (this.autoHeight) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
await this.updateComplete;
|
await this.updateComplete;
|
||||||
this._scroller.style.height = `calc(100% - ${this._header.clientHeight}px)`;
|
this._table.style.height = `calc(100% - ${this._header.clientHeight}px)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
static get styles(): CSSResult {
|
static get styles(): CSSResult {
|
||||||
return css`
|
return css`
|
||||||
/* default mdc styles, colors changed, without checkbox styles */
|
/* default mdc styles, colors changed, without checkbox styles */
|
||||||
|
:host {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
.mdc-data-table__content {
|
.mdc-data-table__content {
|
||||||
font-family: Roboto, sans-serif;
|
font-family: Roboto, sans-serif;
|
||||||
-moz-osx-font-smoothing: grayscale;
|
-moz-osx-font-smoothing: grayscale;
|
||||||
@ -477,7 +462,7 @@ export class HaDataTable extends BaseElement {
|
|||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
overflow-x: auto;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__row--selected {
|
.mdc-data-table__row--selected {
|
||||||
@ -485,12 +470,13 @@ export class HaDataTable extends BaseElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__row {
|
.mdc-data-table__row {
|
||||||
border-top-color: rgba(var(--rgb-primary-text-color), 0.12);
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 52px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__row {
|
.mdc-data-table__row ~ .mdc-data-table__row {
|
||||||
border-top-width: 1px;
|
border-top: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
|
||||||
border-top-style: solid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__row:not(.mdc-data-table__row--selected):hover {
|
.mdc-data-table__row:not(.mdc-data-table__row--selected):hover {
|
||||||
@ -507,16 +493,24 @@ export class HaDataTable extends BaseElement {
|
|||||||
|
|
||||||
.mdc-data-table__header-row {
|
.mdc-data-table__header-row {
|
||||||
height: 56px;
|
height: 56px;
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
border-bottom: 1px solid rgba(var(--rgb-primary-text-color), 0.12);
|
||||||
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__row {
|
.mdc-data-table__header-row::-webkit-scrollbar {
|
||||||
height: 52px;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__cell,
|
.mdc-data-table__cell,
|
||||||
.mdc-data-table__header-cell {
|
.mdc-data-table__header-cell {
|
||||||
padding-right: 16px;
|
padding-right: 16px;
|
||||||
padding-left: 16px;
|
padding-left: 16px;
|
||||||
|
align-self: center;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__header-cell--checkbox,
|
.mdc-data-table__header-cell--checkbox,
|
||||||
@ -538,10 +532,10 @@ export class HaDataTable extends BaseElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__table {
|
.mdc-data-table__table {
|
||||||
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
border: 0;
|
border: 0;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__cell {
|
.mdc-data-table__cell {
|
||||||
@ -568,12 +562,20 @@ export class HaDataTable extends BaseElement {
|
|||||||
.mdc-data-table__cell--icon {
|
.mdc-data-table__cell--icon {
|
||||||
color: var(--secondary-text-color);
|
color: var(--secondary-text-color);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mdc-data-table__header-cell--icon,
|
||||||
|
.mdc-data-table__cell--icon {
|
||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mdc-data-table__header-cell--icon {
|
.mdc-data-table__header-cell.mdc-data-table__header-cell--icon {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:hover,
|
||||||
|
.mdc-data-table__header-cell.sortable.mdc-data-table__header-cell--icon:not(.not-sorted) {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
.mdc-data-table__cell--icon:first-child ha-icon {
|
.mdc-data-table__cell--icon:first-child ha-icon {
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
@ -604,6 +606,10 @@ export class HaDataTable extends BaseElement {
|
|||||||
.mdc-data-table__header-cell--numeric {
|
.mdc-data-table__header-cell--numeric {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.mdc-data-table__header-cell--numeric.sortable:hover,
|
||||||
|
.mdc-data-table__header-cell--numeric.sortable:not(.not-sorted) {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
[dir="rtl"] .mdc-data-table__header-cell--numeric,
|
[dir="rtl"] .mdc-data-table__header-cell--numeric,
|
||||||
.mdc-data-table__header-cell--numeric[dir="rtl"] {
|
.mdc-data-table__header-cell--numeric[dir="rtl"] {
|
||||||
/* @noflip */
|
/* @noflip */
|
||||||
@ -634,27 +640,21 @@ export class HaDataTable extends BaseElement {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.mdc-data-table__header-cell > * {
|
.mdc-data-table__header-cell > * {
|
||||||
transition: left 0.2s ease 0s;
|
transition: left 0.2s ease;
|
||||||
}
|
}
|
||||||
.mdc-data-table__header-cell ha-icon {
|
.mdc-data-table__header-cell ha-icon {
|
||||||
top: 15px;
|
top: -3px;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
}
|
}
|
||||||
.mdc-data-table__header-cell.not-sorted ha-icon {
|
.mdc-data-table__header-cell.not-sorted ha-icon {
|
||||||
left: -20px;
|
left: -20px;
|
||||||
}
|
}
|
||||||
.mdc-data-table__header-cell:not(.not-sorted) span,
|
.mdc-data-table__header-cell.sortable:not(.not-sorted) span,
|
||||||
.mdc-data-table__header-cell.not-sorted:hover span {
|
.mdc-data-table__header-cell.sortable.not-sorted:hover span {
|
||||||
left: 24px;
|
left: 24px;
|
||||||
}
|
}
|
||||||
.mdc-data-table__header-cell.mdc-data-table__header-cell--numeric:not(.not-sorted)
|
.mdc-data-table__header-cell.sortable:not(.not-sorted) ha-icon,
|
||||||
span,
|
.mdc-data-table__header-cell.sortable:hover.not-sorted ha-icon {
|
||||||
.mdc-data-table__header-cell.mdc-data-table__header-cell--numeric.not-sorted:hover
|
|
||||||
span {
|
|
||||||
left: 12px;
|
|
||||||
}
|
|
||||||
.mdc-data-table__header-cell:not(.not-sorted) ha-icon,
|
|
||||||
.mdc-data-table__header-cell:hover.not-sorted ha-icon {
|
|
||||||
left: 12px;
|
left: 12px;
|
||||||
}
|
}
|
||||||
.table-header {
|
.table-header {
|
||||||
@ -664,14 +664,24 @@ export class HaDataTable extends BaseElement {
|
|||||||
position: relative;
|
position: relative;
|
||||||
top: 2px;
|
top: 2px;
|
||||||
}
|
}
|
||||||
.scroller {
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
slot[name="header"] {
|
slot[name="header"] {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
.secondary {
|
.center {
|
||||||
color: var(--secondary-text-color);
|
text-align: center;
|
||||||
|
}
|
||||||
|
.scroller {
|
||||||
|
display: flex;
|
||||||
|
position: relative;
|
||||||
|
contain: strict;
|
||||||
|
height: calc(100% - 57px);
|
||||||
|
}
|
||||||
|
.mdc-data-table__table:not(.auto-height) .scroller {
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
.grows {
|
||||||
|
flex-grow: 1;
|
||||||
|
flex-shrink: 1;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ import {
|
|||||||
RowClickedEvent,
|
RowClickedEvent,
|
||||||
} from "../../../components/data-table/ha-data-table";
|
} from "../../../components/data-table/ha-data-table";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
@customElement("ha-config-devices-dashboard")
|
@customElement("ha-config-devices-dashboard")
|
||||||
export class HaConfigDeviceDashboard extends LitElement {
|
export class HaConfigDeviceDashboard extends LitElement {
|
||||||
@ -127,6 +128,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
template: (name, device: DataTableRowData) => {
|
template: (name, device: DataTableRowData) => {
|
||||||
const battery = device.battery_entity
|
const battery = device.battery_entity
|
||||||
? this.hass.states[device.battery_entity]
|
? this.hass.states[device.battery_entity]
|
||||||
@ -155,6 +157,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
grows: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
},
|
},
|
||||||
manufacturer: {
|
manufacturer: {
|
||||||
@ -163,6 +166,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -170,6 +174,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
area: {
|
area: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -177,6 +182,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
integration: {
|
integration: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -184,6 +190,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
battery_entity: {
|
battery_entity: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -191,6 +198,7 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
type: "numeric",
|
type: "numeric",
|
||||||
|
width: "60px",
|
||||||
template: (batteryEntity: string) => {
|
template: (batteryEntity: string) => {
|
||||||
const battery = batteryEntity
|
const battery = batteryEntity
|
||||||
? this.hass.states[batteryEntity]
|
? this.hass.states[batteryEntity]
|
||||||
@ -247,8 +255,8 @@ export class HaConfigDeviceDashboard extends LitElement {
|
|||||||
return batteryEntity ? batteryEntity.entity_id : undefined;
|
return batteryEntity ? batteryEntity.entity_id : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleRowClicked(ev: CustomEvent) {
|
private _handleRowClicked(ev: HASSDomEvent<RowClickedEvent>) {
|
||||||
const deviceId = (ev.detail as RowClickedEvent).id;
|
const deviceId = ev.detail.id;
|
||||||
navigate(this, `/config/devices/device/${deviceId}`);
|
navigate(this, `/config/devices/device/${deviceId}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,6 +134,7 @@ export class HaDevicesDataTable extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
template: (name, device: DataTableRowData) => {
|
template: (name, device: DataTableRowData) => {
|
||||||
const battery = device.battery_entity
|
const battery = device.battery_entity
|
||||||
? this.hass.states[device.battery_entity]
|
? this.hass.states[device.battery_entity]
|
||||||
@ -163,6 +164,7 @@ export class HaDevicesDataTable extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
},
|
},
|
||||||
manufacturer: {
|
manufacturer: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -170,6 +172,7 @@ export class HaDevicesDataTable extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -177,6 +180,7 @@ export class HaDevicesDataTable extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
area: {
|
area: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -184,6 +188,7 @@ export class HaDevicesDataTable extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
integration: {
|
integration: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -191,6 +196,7 @@ export class HaDevicesDataTable extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
battery_entity: {
|
battery_entity: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -198,6 +204,7 @@ export class HaDevicesDataTable extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
type: "numeric",
|
type: "numeric",
|
||||||
|
width: "60px",
|
||||||
template: (batteryEntity: string) => {
|
template: (batteryEntity: string) => {
|
||||||
const battery = batteryEntity
|
const battery = batteryEntity
|
||||||
? this.hass.states[batteryEntity]
|
? this.hass.states[batteryEntity]
|
||||||
|
@ -49,6 +49,7 @@ import { classMap } from "lit-html/directives/class-map";
|
|||||||
import { computeStateName } from "../../../common/entity/compute_state_name";
|
import { computeStateName } from "../../../common/entity/compute_state_name";
|
||||||
// tslint:disable-next-line: no-duplicate-imports
|
// tslint:disable-next-line: no-duplicate-imports
|
||||||
import { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-data-table";
|
import { HaTabsSubpageDataTable } from "../../../layouts/hass-tabs-subpage-data-table";
|
||||||
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
export interface StateEntity extends EntityRegistryEntry {
|
export interface StateEntity extends EntityRegistryEntry {
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
@ -96,6 +97,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -106,6 +108,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
type: "icon",
|
type: "icon",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "55px",
|
||||||
template: (_status, entity: any) =>
|
template: (_status, entity: any) =>
|
||||||
entity.unavailable || entity.disabled_by || entity.readonly
|
entity.unavailable || entity.disabled_by || entity.readonly
|
||||||
? html`
|
? html`
|
||||||
@ -166,6 +169,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "20%",
|
||||||
};
|
};
|
||||||
columns.platform = {
|
columns.platform = {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -173,6 +177,7 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "20%",
|
||||||
template: (platform) =>
|
template: (platform) =>
|
||||||
this.hass.localize(`component.${platform}.config.title`) || platform,
|
this.hass.localize(`component.${platform}.config.title`) || platform,
|
||||||
};
|
};
|
||||||
@ -467,16 +472,10 @@ export class HaConfigEntities extends SubscribeMixin(LitElement) {
|
|||||||
this._filter = ev.detail.value;
|
this._filter = ev.detail.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleSelectionChanged(ev: CustomEvent): void {
|
private _handleSelectionChanged(
|
||||||
const changedSelection = ev.detail as SelectionChangedEvent;
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
const entity = changedSelection.id;
|
): void {
|
||||||
if (changedSelection.selected) {
|
this._selectedEntities = ev.detail.value;
|
||||||
this._selectedEntities = [...this._selectedEntities, entity];
|
|
||||||
} else {
|
|
||||||
this._selectedEntities = this._selectedEntities.filter(
|
|
||||||
(entityId) => entityId !== entity
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _enableSelected() {
|
private _enableSelected() {
|
||||||
|
@ -39,8 +39,8 @@ export class HaConfigHelpers extends LitElement {
|
|||||||
@property() private _stateItems: HassEntity[] = [];
|
@property() private _stateItems: HassEntity[] = [];
|
||||||
|
|
||||||
private _columns = memoize(
|
private _columns = memoize(
|
||||||
(_language): DataTableColumnContainer => {
|
(narrow, _language): DataTableColumnContainer => {
|
||||||
return {
|
const columns: DataTableColumnContainer = {
|
||||||
icon: {
|
icon: {
|
||||||
title: "",
|
title: "",
|
||||||
type: "icon",
|
type: "icon",
|
||||||
@ -54,28 +54,45 @@ export class HaConfigHelpers extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
grows: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
template: (name, item: any) =>
|
template: (name, item: any) =>
|
||||||
html`
|
html`
|
||||||
${name}
|
${name}
|
||||||
<div style="color: var(--secondary-text-color)">
|
${narrow
|
||||||
${item.entity_id}
|
? html`
|
||||||
</div>
|
<div class="secondary">
|
||||||
`,
|
${item.entity_id}
|
||||||
},
|
</div>
|
||||||
type: {
|
`
|
||||||
title: this.hass.localize(
|
: ""}
|
||||||
"ui.panel.config.helpers.picker.headers.type"
|
|
||||||
),
|
|
||||||
sortable: true,
|
|
||||||
filterable: true,
|
|
||||||
template: (type) =>
|
|
||||||
html`
|
|
||||||
${this.hass.localize(`ui.panel.config.helpers.types.${type}`) ||
|
|
||||||
type}
|
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
if (!narrow) {
|
||||||
|
columns.entity_id = {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.helpers.picker.headers.entity_id"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
filterable: true,
|
||||||
|
width: "30%",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
columns.type = {
|
||||||
|
title: this.hass.localize(
|
||||||
|
"ui.panel.config.helpers.picker.headers.type"
|
||||||
|
),
|
||||||
|
sortable: true,
|
||||||
|
width: "30%",
|
||||||
|
filterable: true,
|
||||||
|
template: (type) =>
|
||||||
|
html`
|
||||||
|
${this.hass.localize(`ui.panel.config.helpers.types.${type}`) ||
|
||||||
|
type}
|
||||||
|
`,
|
||||||
|
};
|
||||||
|
return columns;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -106,7 +123,7 @@ export class HaConfigHelpers extends LitElement {
|
|||||||
back-path="/config"
|
back-path="/config"
|
||||||
.route=${this.route}
|
.route=${this.route}
|
||||||
.tabs=${configSections.automation}
|
.tabs=${configSections.automation}
|
||||||
.columns=${this._columns(this.hass.language)}
|
.columns=${this._columns(this.narrow, this.hass.language)}
|
||||||
.data=${this._getItems(this._stateItems)}
|
.data=${this._getItems(this._stateItems)}
|
||||||
@row-click=${this._openEditDialog}
|
@row-click=${this._openEditDialog}
|
||||||
>
|
>
|
||||||
|
@ -194,12 +194,14 @@ class HaConfigEntryPage extends LitElement {
|
|||||||
return css`
|
return css`
|
||||||
.content {
|
.content {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
p {
|
p {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
ha-devices-data-table {
|
ha-devices-data-table {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
template: (title, dashboard: any) => {
|
template: (title, dashboard: any) => {
|
||||||
const titleTemplate = html`
|
const titleTemplate = html`
|
||||||
${title}
|
${title}
|
||||||
@ -101,6 +102,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
template: (mode) =>
|
template: (mode) =>
|
||||||
html`
|
html`
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
@ -113,6 +115,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
"ui.panel.config.lovelace.dashboards.picker.headers.filename"
|
"ui.panel.config.lovelace.dashboards.picker.headers.filename"
|
||||||
),
|
),
|
||||||
|
width: "15%",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
};
|
};
|
||||||
@ -123,6 +126,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
type: "icon",
|
type: "icon",
|
||||||
|
width: "100px",
|
||||||
template: (requireAdmin: boolean) =>
|
template: (requireAdmin: boolean) =>
|
||||||
requireAdmin
|
requireAdmin
|
||||||
? html`
|
? html`
|
||||||
@ -137,6 +141,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
"ui.panel.config.lovelace.dashboards.picker.headers.sidebar"
|
"ui.panel.config.lovelace.dashboards.picker.headers.sidebar"
|
||||||
),
|
),
|
||||||
type: "icon",
|
type: "icon",
|
||||||
|
width: "100px",
|
||||||
template: (sidebar) =>
|
template: (sidebar) =>
|
||||||
sidebar
|
sidebar
|
||||||
? html`
|
? html`
|
||||||
@ -151,6 +156,7 @@ export class HaConfigLovelaceDashboards extends LitElement {
|
|||||||
columns.url_path = {
|
columns.url_path = {
|
||||||
title: "",
|
title: "",
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "75px",
|
||||||
template: (urlPath) =>
|
template: (urlPath) =>
|
||||||
narrow
|
narrow
|
||||||
? html`
|
? html`
|
||||||
|
@ -57,6 +57,7 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
},
|
},
|
||||||
type: {
|
type: {
|
||||||
title: this.hass.localize(
|
title: this.hass.localize(
|
||||||
@ -64,6 +65,7 @@ export class HaConfigLovelaceRescources extends LitElement {
|
|||||||
),
|
),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "30%",
|
||||||
template: (type) =>
|
template: (type) =>
|
||||||
html`
|
html`
|
||||||
${this.hass.localize(
|
${this.hass.localize(
|
||||||
|
@ -25,6 +25,7 @@ import { PolymerChangedEvent } from "../../../polymer-types";
|
|||||||
import "@polymer/paper-spinner/paper-spinner";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
import { PaperInputElement } from "@polymer/paper-input/paper-input";
|
||||||
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
@customElement("zha-add-group-page")
|
@customElement("zha-add-group-page")
|
||||||
export class ZHAAddGroupPage extends LitElement {
|
export class ZHAAddGroupPage extends LitElement {
|
||||||
@ -82,7 +83,6 @@ export class ZHAAddGroupPage extends LitElement {
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
selectable
|
selectable
|
||||||
@selection-changed=${this._handleAddSelectionChanged}
|
@selection-changed=${this._handleAddSelectionChanged}
|
||||||
class="table"
|
|
||||||
>
|
>
|
||||||
</zha-devices-data-table>
|
</zha-devices-data-table>
|
||||||
|
|
||||||
@ -114,21 +114,10 @@ export class ZHAAddGroupPage extends LitElement {
|
|||||||
this.devices = await fetchGroupableDevices(this.hass!);
|
this.devices = await fetchGroupableDevices(this.hass!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleAddSelectionChanged(ev: CustomEvent): void {
|
private _handleAddSelectionChanged(
|
||||||
const changedSelection = ev.detail as SelectionChangedEvent;
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
const entity = changedSelection.id;
|
): void {
|
||||||
if (
|
this._selectedDevicesToAdd = ev.detail.value;
|
||||||
changedSelection.selected &&
|
|
||||||
!this._selectedDevicesToAdd.includes(entity)
|
|
||||||
) {
|
|
||||||
this._selectedDevicesToAdd.push(entity);
|
|
||||||
} else {
|
|
||||||
const index = this._selectedDevicesToAdd.indexOf(entity);
|
|
||||||
if (index !== -1) {
|
|
||||||
this._selectedDevicesToAdd.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._selectedDevicesToAdd = [...this._selectedDevicesToAdd];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _createGroup(): Promise<void> {
|
private async _createGroup(): Promise<void> {
|
||||||
@ -168,11 +157,6 @@ export class ZHAAddGroupPage extends LitElement {
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
|
||||||
height: 400px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
ha-config-section *:last-child {
|
ha-config-section *:last-child {
|
||||||
padding-bottom: 24px;
|
padding-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ export class ZHAClustersDataTable extends LitElement {
|
|||||||
title: "Name",
|
title: "Name",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
@ -56,6 +57,7 @@ export class ZHAClustersDataTable extends LitElement {
|
|||||||
title: "Name",
|
title: "Name",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
},
|
},
|
||||||
id: {
|
id: {
|
||||||
title: "ID",
|
title: "ID",
|
||||||
@ -65,10 +67,12 @@ export class ZHAClustersDataTable extends LitElement {
|
|||||||
`;
|
`;
|
||||||
},
|
},
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
endpoint_id: {
|
endpoint_id: {
|
||||||
title: "Endpoint ID",
|
title: "Endpoint ID",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -80,6 +84,7 @@ export class ZHAClustersDataTable extends LitElement {
|
|||||||
.data=${this._clusters(this.clusters)}
|
.data=${this._clusters(this.clusters)}
|
||||||
.id=${"cluster_id"}
|
.id=${"cluster_id"}
|
||||||
selectable
|
selectable
|
||||||
|
auto-height
|
||||||
></ha-data-table>
|
></ha-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,7 @@ class ZHAConfigDashboard extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
@ -71,16 +72,19 @@ class ZHAConfigDashboard extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
},
|
},
|
||||||
nwk: {
|
nwk: {
|
||||||
title: "Nwk",
|
title: "Nwk",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
},
|
},
|
||||||
ieee: {
|
ieee: {
|
||||||
title: "IEEE",
|
title: "IEEE",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "25%",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -139,6 +143,7 @@ class ZHAConfigDashboard extends LitElement {
|
|||||||
.data=${this._memoizeDevices(this._devices)}
|
.data=${this._memoizeDevices(this._devices)}
|
||||||
@row-click=${this._handleDeviceClicked}
|
@row-click=${this._handleDeviceClicked}
|
||||||
.id=${"ieee"}
|
.id=${"ieee"}
|
||||||
|
auto-height
|
||||||
></ha-data-table>
|
></ha-data-table>
|
||||||
</ha-card>
|
</ha-card>
|
||||||
</ha-config-section>
|
</ha-config-section>
|
||||||
|
@ -53,6 +53,7 @@ export class ZHADevicesDataTable extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
template: (name) => html`
|
template: (name) => html`
|
||||||
<div @click=${this._handleClicked} style="cursor: pointer;">
|
<div @click=${this._handleClicked} style="cursor: pointer;">
|
||||||
${name}
|
${name}
|
||||||
@ -66,6 +67,7 @@ export class ZHADevicesDataTable extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
template: (name) => html`
|
template: (name) => html`
|
||||||
<div @click=${this._handleClicked} style="cursor: pointer;">
|
<div @click=${this._handleClicked} style="cursor: pointer;">
|
||||||
${name}
|
${name}
|
||||||
@ -76,11 +78,13 @@ export class ZHADevicesDataTable extends LitElement {
|
|||||||
title: "Manufacturer",
|
title: "Manufacturer",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "20%",
|
||||||
},
|
},
|
||||||
model: {
|
model: {
|
||||||
title: "Model",
|
title: "Model",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "20%",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -91,6 +95,7 @@ export class ZHADevicesDataTable extends LitElement {
|
|||||||
.columns=${this._columns(this.narrow)}
|
.columns=${this._columns(this.narrow)}
|
||||||
.data=${this._devices(this.devices)}
|
.data=${this._devices(this.devices)}
|
||||||
.selectable=${this.selectable}
|
.selectable=${this.selectable}
|
||||||
|
auto-height
|
||||||
></ha-data-table>
|
></ha-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import { HomeAssistant } from "../../../types";
|
|||||||
import { ItemSelectedEvent } from "./types";
|
import { ItemSelectedEvent } from "./types";
|
||||||
import "@polymer/paper-item/paper-item";
|
import "@polymer/paper-item/paper-item";
|
||||||
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
|
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
|
||||||
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
@customElement("zha-group-binding-control")
|
@customElement("zha-group-binding-control")
|
||||||
export class ZHAGroupBindingControl extends LitElement {
|
export class ZHAGroupBindingControl extends LitElement {
|
||||||
@ -200,21 +201,11 @@ export class ZHAGroupBindingControl extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleClusterSelectionChanged(event: CustomEvent): void {
|
private _handleClusterSelectionChanged(
|
||||||
const changedSelection = event.detail as SelectionChangedEvent;
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
const clusterId = changedSelection.id;
|
): void {
|
||||||
if (
|
this._selectedClusters = ev.detail.value;
|
||||||
changedSelection.selected &&
|
|
||||||
!this._selectedClusters.includes(clusterId)
|
|
||||||
) {
|
|
||||||
this._selectedClusters.push(clusterId);
|
|
||||||
} else {
|
|
||||||
const index = this._selectedClusters.indexOf(clusterId);
|
|
||||||
if (index !== -1) {
|
|
||||||
this._selectedClusters.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._selectedClusters = [...this._selectedClusters];
|
|
||||||
this._clustersToBind = [];
|
this._clustersToBind = [];
|
||||||
for (const clusterIndex of this._selectedClusters) {
|
for (const clusterIndex of this._selectedClusters) {
|
||||||
const selectedCluster = this._clusters.find((cluster) => {
|
const selectedCluster = this._clusters.find((cluster) => {
|
||||||
|
@ -31,6 +31,7 @@ import "@polymer/paper-icon-button/paper-icon-button";
|
|||||||
import "@polymer/paper-spinner/paper-spinner";
|
import "@polymer/paper-spinner/paper-spinner";
|
||||||
import "@material/mwc-button";
|
import "@material/mwc-button";
|
||||||
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
|
import { SelectionChangedEvent } from "../../../components/data-table/ha-data-table";
|
||||||
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
@customElement("zha-group-page")
|
@customElement("zha-group-page")
|
||||||
export class ZHAGroupPage extends LitElement {
|
export class ZHAGroupPage extends LitElement {
|
||||||
@ -145,7 +146,6 @@ export class ZHAGroupPage extends LitElement {
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
selectable
|
selectable
|
||||||
@selection-changed=${this._handleRemoveSelectionChanged}
|
@selection-changed=${this._handleRemoveSelectionChanged}
|
||||||
class="table"
|
|
||||||
>
|
>
|
||||||
</zha-devices-data-table>
|
</zha-devices-data-table>
|
||||||
|
|
||||||
@ -180,7 +180,6 @@ export class ZHAGroupPage extends LitElement {
|
|||||||
.narrow=${this.narrow}
|
.narrow=${this.narrow}
|
||||||
selectable
|
selectable
|
||||||
@selection-changed=${this._handleAddSelectionChanged}
|
@selection-changed=${this._handleAddSelectionChanged}
|
||||||
class="table"
|
|
||||||
>
|
>
|
||||||
</zha-devices-data-table>
|
</zha-devices-data-table>
|
||||||
|
|
||||||
@ -223,38 +222,16 @@ export class ZHAGroupPage extends LitElement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleAddSelectionChanged(ev: CustomEvent): void {
|
private _handleAddSelectionChanged(
|
||||||
const changedSelection = ev.detail as SelectionChangedEvent;
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
const entity = changedSelection.id;
|
): void {
|
||||||
if (
|
this._selectedDevicesToAdd = ev.detail.value;
|
||||||
changedSelection.selected &&
|
|
||||||
!this._selectedDevicesToAdd.includes(entity)
|
|
||||||
) {
|
|
||||||
this._selectedDevicesToAdd.push(entity);
|
|
||||||
} else {
|
|
||||||
const index = this._selectedDevicesToAdd.indexOf(entity);
|
|
||||||
if (index !== -1) {
|
|
||||||
this._selectedDevicesToAdd.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._selectedDevicesToAdd = [...this._selectedDevicesToAdd];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleRemoveSelectionChanged(ev: CustomEvent): void {
|
private _handleRemoveSelectionChanged(
|
||||||
const changedSelection = ev.detail as SelectionChangedEvent;
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
const entity = changedSelection.id;
|
): void {
|
||||||
if (
|
this._selectedDevicesToRemove = ev.detail.value;
|
||||||
changedSelection.selected &&
|
|
||||||
!this._selectedDevicesToRemove.includes(entity)
|
|
||||||
) {
|
|
||||||
this._selectedDevicesToRemove.push(entity);
|
|
||||||
} else {
|
|
||||||
const index = this._selectedDevicesToRemove.indexOf(entity);
|
|
||||||
if (index !== -1) {
|
|
||||||
this._selectedDevicesToRemove.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._selectedDevicesToRemove = [...this._selectedDevicesToRemove];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _addMembersToGroup(): Promise<void> {
|
private async _addMembersToGroup(): Promise<void> {
|
||||||
@ -309,11 +286,6 @@ export class ZHAGroupPage extends LitElement {
|
|||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
|
||||||
height: 200px;
|
|
||||||
overflow: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
mwc-button paper-spinner {
|
mwc-button paper-spinner {
|
||||||
width: 14px;
|
width: 14px;
|
||||||
height: 14px;
|
height: 14px;
|
||||||
|
@ -19,6 +19,7 @@ import "@polymer/paper-spinner/paper-spinner";
|
|||||||
import "@polymer/paper-icon-button/paper-icon-button";
|
import "@polymer/paper-icon-button/paper-icon-button";
|
||||||
import { navigate } from "../../../common/navigate";
|
import { navigate } from "../../../common/navigate";
|
||||||
import "../../../layouts/hass-subpage";
|
import "../../../layouts/hass-subpage";
|
||||||
|
import { HASSDomEvent } from "../../../common/dom/fire_event";
|
||||||
|
|
||||||
@customElement("zha-groups-dashboard")
|
@customElement("zha-groups-dashboard")
|
||||||
export class ZHAGroupsDashboard extends LitElement {
|
export class ZHAGroupsDashboard extends LitElement {
|
||||||
@ -102,21 +103,12 @@ export class ZHAGroupsDashboard extends LitElement {
|
|||||||
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
|
this._groups = (await fetchGroups(this.hass!)).sort(sortZHAGroups);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleRemoveSelectionChanged(ev: CustomEvent): void {
|
private _handleRemoveSelectionChanged(
|
||||||
const changedSelection = ev.detail as SelectionChangedEvent;
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
const groupId = Number(changedSelection.id);
|
): void {
|
||||||
if (
|
this._selectedGroupsToRemove = ev.detail.value.map((value) =>
|
||||||
changedSelection.selected &&
|
Number(value)
|
||||||
!this._selectedGroupsToRemove.includes(groupId)
|
);
|
||||||
) {
|
|
||||||
this._selectedGroupsToRemove.push(groupId);
|
|
||||||
} else {
|
|
||||||
const index = this._selectedGroupsToRemove.indexOf(groupId);
|
|
||||||
if (index !== -1) {
|
|
||||||
this._selectedGroupsToRemove.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._selectedGroupsToRemove = [...this._selectedGroupsToRemove];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async _removeGroup(): Promise<void> {
|
private async _removeGroup(): Promise<void> {
|
||||||
|
@ -52,6 +52,7 @@ export class ZHAGroupsDataTable extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
template: (name) => html`
|
template: (name) => html`
|
||||||
<div @click=${this._handleRowClicked} style="cursor: pointer;">
|
<div @click=${this._handleRowClicked} style="cursor: pointer;">
|
||||||
${name}
|
${name}
|
||||||
@ -65,6 +66,7 @@ export class ZHAGroupsDataTable extends LitElement {
|
|||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
|
grows: true,
|
||||||
template: (name) => html`
|
template: (name) => html`
|
||||||
<div @click=${this._handleRowClicked} style="cursor: pointer;">
|
<div @click=${this._handleRowClicked} style="cursor: pointer;">
|
||||||
${name}
|
${name}
|
||||||
@ -73,6 +75,8 @@ export class ZHAGroupsDataTable extends LitElement {
|
|||||||
},
|
},
|
||||||
group_id: {
|
group_id: {
|
||||||
title: this.hass.localize("ui.panel.config.zha.groups.group_id"),
|
title: this.hass.localize("ui.panel.config.zha.groups.group_id"),
|
||||||
|
type: "numeric",
|
||||||
|
width: "15%",
|
||||||
template: (groupId: number) => {
|
template: (groupId: number) => {
|
||||||
return html`
|
return html`
|
||||||
${formatAsPaddedHex(groupId)}
|
${formatAsPaddedHex(groupId)}
|
||||||
@ -82,6 +86,8 @@ export class ZHAGroupsDataTable extends LitElement {
|
|||||||
},
|
},
|
||||||
members: {
|
members: {
|
||||||
title: this.hass.localize("ui.panel.config.zha.groups.members"),
|
title: this.hass.localize("ui.panel.config.zha.groups.members"),
|
||||||
|
type: "numeric",
|
||||||
|
width: "15%",
|
||||||
template: (members: ZHADevice[]) => {
|
template: (members: ZHADevice[]) => {
|
||||||
return html`
|
return html`
|
||||||
${members.length}
|
${members.length}
|
||||||
@ -98,6 +104,7 @@ export class ZHAGroupsDataTable extends LitElement {
|
|||||||
.columns=${this._columns(this.narrow)}
|
.columns=${this._columns(this.narrow)}
|
||||||
.data=${this._groups(this.groups)}
|
.data=${this._groups(this.groups)}
|
||||||
.selectable=${this.selectable}
|
.selectable=${this.selectable}
|
||||||
|
auto-height
|
||||||
></ha-data-table>
|
></ha-data-table>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,7 @@ import { computeUnusedEntities } from "../../common/compute-unused-entities";
|
|||||||
import { HomeAssistant } from "../../../../types";
|
import { HomeAssistant } from "../../../../types";
|
||||||
import { Lovelace } from "../../types";
|
import { Lovelace } from "../../types";
|
||||||
import { LovelaceConfig } from "../../../../data/lovelace";
|
import { LovelaceConfig } from "../../../../data/lovelace";
|
||||||
import { fireEvent } from "../../../../common/dom/fire_event";
|
import { fireEvent, HASSDomEvent } from "../../../../common/dom/fire_event";
|
||||||
import { addEntitiesToLovelaceView } from "../add-entities-to-view";
|
import { addEntitiesToLovelaceView } from "../add-entities-to-view";
|
||||||
|
|
||||||
@customElement("hui-unused-entities")
|
@customElement("hui-unused-entities")
|
||||||
@ -55,19 +55,33 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
|
|
||||||
private _columns = memoizeOne((narrow: boolean) => {
|
private _columns = memoizeOne((narrow: boolean) => {
|
||||||
const columns: DataTableColumnContainer = {
|
const columns: DataTableColumnContainer = {
|
||||||
entity: {
|
icon: {
|
||||||
|
title: "",
|
||||||
|
type: "icon",
|
||||||
|
template: (_icon, entity: any) => html`
|
||||||
|
<state-badge
|
||||||
|
@click=${this._handleEntityClicked}
|
||||||
|
.hass=${this.hass!}
|
||||||
|
.stateObj=${entity.stateObj}
|
||||||
|
></state-badge>
|
||||||
|
`,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity"),
|
title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity"),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
filterKey: "friendly_name",
|
grows: true,
|
||||||
direction: "asc",
|
direction: "asc",
|
||||||
template: (stateObj) => html`
|
template: (name, entity: any) => html`
|
||||||
<div @click=${this._handleEntityClicked} style="cursor: pointer;">
|
<div @click=${this._handleEntityClicked} style="cursor: pointer;">
|
||||||
<state-badge
|
${name}
|
||||||
.hass=${this.hass!}
|
${narrow
|
||||||
.stateObj=${stateObj}
|
? html`
|
||||||
></state-badge>
|
<div class="secondary">
|
||||||
${stateObj.friendly_name}
|
${entity.stateObj.entity_id}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
</div>
|
</div>
|
||||||
`,
|
`,
|
||||||
},
|
},
|
||||||
@ -81,11 +95,13 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity_id"),
|
title: this.hass!.localize("ui.panel.lovelace.unused_entities.entity_id"),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "30%",
|
||||||
};
|
};
|
||||||
columns.domain = {
|
columns.domain = {
|
||||||
title: this.hass!.localize("ui.panel.lovelace.unused_entities.domain"),
|
title: this.hass!.localize("ui.panel.lovelace.unused_entities.domain"),
|
||||||
sortable: true,
|
sortable: true,
|
||||||
filterable: true,
|
filterable: true,
|
||||||
|
width: "15%",
|
||||||
};
|
};
|
||||||
columns.last_changed = {
|
columns.last_changed = {
|
||||||
title: this.hass!.localize(
|
title: this.hass!.localize(
|
||||||
@ -93,6 +109,7 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
),
|
),
|
||||||
type: "numeric",
|
type: "numeric",
|
||||||
sortable: true,
|
sortable: true,
|
||||||
|
width: "15%",
|
||||||
template: (lastChanged: string) => html`
|
template: (lastChanged: string) => html`
|
||||||
<ha-relative-time
|
<ha-relative-time
|
||||||
.hass=${this.hass!}
|
.hass=${this.hass!}
|
||||||
@ -122,34 +139,37 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<ha-card
|
${!this.narrow
|
||||||
header="${this.hass.localize(
|
? html`
|
||||||
"ui.panel.lovelace.unused_entities.title"
|
<ha-card
|
||||||
)}"
|
header="${this.hass.localize(
|
||||||
>
|
"ui.panel.lovelace.unused_entities.title"
|
||||||
<div class="card-content">
|
)}"
|
||||||
${this.hass.localize(
|
>
|
||||||
"ui.panel.lovelace.unused_entities.available_entities"
|
<div class="card-content">
|
||||||
)}
|
${this.hass.localize(
|
||||||
${this.lovelace.mode === "storage"
|
"ui.panel.lovelace.unused_entities.available_entities"
|
||||||
? html`
|
|
||||||
<br />${this.hass.localize(
|
|
||||||
"ui.panel.lovelace.unused_entities.select_to_add"
|
|
||||||
)}
|
)}
|
||||||
`
|
${this.lovelace.mode === "storage"
|
||||||
: ""}
|
? html`
|
||||||
</div>
|
<br />${this.hass.localize(
|
||||||
</ha-card>
|
"ui.panel.lovelace.unused_entities.select_to_add"
|
||||||
|
)}
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
|
</div>
|
||||||
|
</ha-card>
|
||||||
|
`
|
||||||
|
: ""}
|
||||||
<ha-data-table
|
<ha-data-table
|
||||||
.columns=${this._columns(this.narrow!)}
|
.columns=${this._columns(this.narrow!)}
|
||||||
.data=${this._unusedEntities.map((entity) => {
|
.data=${this._unusedEntities.map((entity) => {
|
||||||
const stateObj = this.hass!.states[entity];
|
const stateObj = this.hass!.states[entity];
|
||||||
return {
|
return {
|
||||||
|
icon: "",
|
||||||
entity_id: entity,
|
entity_id: entity,
|
||||||
entity: {
|
stateObj,
|
||||||
...stateObj,
|
name: computeStateName(stateObj),
|
||||||
friendly_name: computeStateName(stateObj),
|
|
||||||
},
|
|
||||||
domain: computeDomain(entity),
|
domain: computeDomain(entity),
|
||||||
last_changed: stateObj!.last_changed,
|
last_changed: stateObj!.last_changed,
|
||||||
};
|
};
|
||||||
@ -178,23 +198,16 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
this._unusedEntities = computeUnusedEntities(this.hass, this._config!);
|
this._unusedEntities = computeUnusedEntities(this.hass, this._config!);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleSelectionChanged(ev: CustomEvent): void {
|
private _handleSelectionChanged(
|
||||||
const changedSelection = ev.detail as SelectionChangedEvent;
|
ev: HASSDomEvent<SelectionChangedEvent>
|
||||||
const entity = changedSelection.id;
|
): void {
|
||||||
if (changedSelection.selected) {
|
this._selectedEntities = ev.detail.value;
|
||||||
this._selectedEntities.push(entity);
|
|
||||||
} else {
|
|
||||||
const index = this._selectedEntities.indexOf(entity);
|
|
||||||
if (index !== -1) {
|
|
||||||
this._selectedEntities.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _handleEntityClicked(ev: Event) {
|
private _handleEntityClicked(ev: Event) {
|
||||||
const entityId = (ev.target as HTMLElement)
|
const entityId = ((ev.target as HTMLElement).closest(
|
||||||
.closest("tr")!
|
".mdc-data-table__row"
|
||||||
.getAttribute("data-row-id")!;
|
) as any).rowId;
|
||||||
fireEvent(this, "hass-more-info", {
|
fireEvent(this, "hass-more-info", {
|
||||||
entityId,
|
entityId,
|
||||||
});
|
});
|
||||||
@ -214,20 +227,27 @@ export class HuiUnusedEntities extends LitElement {
|
|||||||
return css`
|
return css`
|
||||||
:host {
|
:host {
|
||||||
background: var(--lovelace-background);
|
background: var(--lovelace-background);
|
||||||
padding: 16px;
|
display: flex;
|
||||||
box-sizing: border-box;
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
ha-card {
|
||||||
|
--ha-card-box-shadow: none;
|
||||||
|
--ha-card-border-radius: 0;
|
||||||
|
}
|
||||||
|
ha-data-table {
|
||||||
|
--data-table-border-width: 0;
|
||||||
|
flex-grow: 1;
|
||||||
|
margin-top: -20px;
|
||||||
}
|
}
|
||||||
ha-fab {
|
ha-fab {
|
||||||
position: sticky;
|
position: absolute;
|
||||||
float: right;
|
right: 16px;
|
||||||
bottom: 16px;
|
bottom: 16px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
ha-fab.rtl {
|
ha-fab.rtl {
|
||||||
float: left;
|
left: 16px;
|
||||||
}
|
right: auto;
|
||||||
ha-card {
|
|
||||||
margin-bottom: 16px;
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import {
|
|||||||
CSSResult,
|
CSSResult,
|
||||||
css,
|
css,
|
||||||
property,
|
property,
|
||||||
|
query,
|
||||||
} from "lit-element";
|
} from "lit-element";
|
||||||
import { classMap } from "lit-html/directives/class-map";
|
import { classMap } from "lit-html/directives/class-map";
|
||||||
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
import "@polymer/app-layout/app-header-layout/app-header-layout";
|
||||||
@ -60,6 +61,7 @@ class HUIRoot extends LitElement {
|
|||||||
@property() public route?: { path: string; prefix: string };
|
@property() public route?: { path: string; prefix: string };
|
||||||
@property() private _routeData?: { view: string };
|
@property() private _routeData?: { view: string };
|
||||||
@property() private _curView?: number | "hass-unused-entities";
|
@property() private _curView?: number | "hass-unused-entities";
|
||||||
|
@query("ha-app-layout") private _appLayout!: HTMLElement;
|
||||||
private _viewCache?: { [viewId: string]: HUIView };
|
private _viewCache?: { [viewId: string]: HUIView };
|
||||||
|
|
||||||
private _debouncedConfigChanged: () => void;
|
private _debouncedConfigChanged: () => void;
|
||||||
@ -344,7 +346,8 @@ class HUIRoot extends LitElement {
|
|||||||
}
|
}
|
||||||
</app-header>
|
</app-header>
|
||||||
<div id='view' class="${classMap({
|
<div id='view' class="${classMap({
|
||||||
"tabs-hidden": this.lovelace!.config.views.length < 2,
|
"tabs-hidden":
|
||||||
|
!this._editMode && this.lovelace!.config.views.length < 2,
|
||||||
})}" @ll-rebuild='${this._debouncedConfigChanged}'></div>
|
})}" @ll-rebuild='${this._debouncedConfigChanged}'></div>
|
||||||
</app-header-layout>
|
</app-header-layout>
|
||||||
`;
|
`;
|
||||||
@ -468,7 +471,7 @@ class HUIRoot extends LitElement {
|
|||||||
|
|
||||||
if (changedProperties.has("route")) {
|
if (changedProperties.has("route")) {
|
||||||
const views = this.config.views;
|
const views = this.config.views;
|
||||||
if (this.route!.path === "" && views) {
|
if (this.route!.path === "" && views.length) {
|
||||||
navigate(this, `${this.route!.prefix}/${views[0].path || 0}`, true);
|
navigate(this, `${this.route!.prefix}/${views[0].path || 0}`, true);
|
||||||
newSelectView = 0;
|
newSelectView = 0;
|
||||||
} else if (this._routeData!.view === "hass-unused-entities") {
|
} else if (this._routeData!.view === "hass-unused-entities") {
|
||||||
@ -495,8 +498,6 @@ class HUIRoot extends LitElement {
|
|||||||
if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) {
|
if (!oldLovelace || oldLovelace.config !== this.lovelace!.config) {
|
||||||
// On config change, recreate the current view from scratch.
|
// On config change, recreate the current view from scratch.
|
||||||
force = true;
|
force = true;
|
||||||
// Recalculate to see if we need to adjust content area for tab bar
|
|
||||||
fireEvent(this, "iron-resize");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!oldLovelace || oldLovelace.editMode !== this.lovelace!.editMode) {
|
if (!oldLovelace || oldLovelace.editMode !== this.lovelace!.editMode) {
|
||||||
@ -506,13 +507,11 @@ class HUIRoot extends LitElement {
|
|||||||
this._routeData!.view === "hass-unused-entities"
|
this._routeData!.view === "hass-unused-entities"
|
||||||
) {
|
) {
|
||||||
const views = this.config && this.config.views;
|
const views = this.config && this.config.views;
|
||||||
navigate(this, `${this.route?.prefix}/${views[0].path || 0}`);
|
navigate(this, `${this.route?.prefix}/${views[0]?.path || 0}`);
|
||||||
newSelectView = 0;
|
newSelectView = 0;
|
||||||
}
|
}
|
||||||
// On edit mode change, recreate the current view from scratch
|
// On edit mode change, recreate the current view from scratch
|
||||||
force = true;
|
force = true;
|
||||||
// Recalculate to see if we need to adjust content area for tab bar
|
|
||||||
fireEvent(this, "iron-resize");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -578,14 +577,14 @@ class HUIRoot extends LitElement {
|
|||||||
}
|
}
|
||||||
this.lovelace!.setEditMode(true);
|
this.lovelace!.setEditMode(true);
|
||||||
if (this.config.views.length < 2) {
|
if (this.config.views.length < 2) {
|
||||||
fireEvent(this, "iron-resize");
|
this.updateComplete.then(() => fireEvent(this._appLayout, "iron-resize"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _editModeDisable(): void {
|
private _editModeDisable(): void {
|
||||||
this.lovelace!.setEditMode(false);
|
this.lovelace!.setEditMode(false);
|
||||||
if (this.config.views.length < 2) {
|
if (this.config.views.length < 2) {
|
||||||
fireEvent(this, "iron-resize");
|
this.updateComplete.then(() => fireEvent(this._appLayout, "iron-resize"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,6 +811,7 @@
|
|||||||
"picker": {
|
"picker": {
|
||||||
"headers": {
|
"headers": {
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
|
"entity_id": "Entity ID",
|
||||||
"type": "Type",
|
"type": "Type",
|
||||||
"editable": "Editable"
|
"editable": "Editable"
|
||||||
},
|
},
|
||||||
|
18
yarn.lock
18
yarn.lock
@ -1515,24 +1515,6 @@
|
|||||||
"@material/typography" "^5.0.0"
|
"@material/typography" "^5.0.0"
|
||||||
tslib "^1.9.3"
|
tslib "^1.9.3"
|
||||||
|
|
||||||
"@material/data-table@^5.0.0":
|
|
||||||
version "5.0.0"
|
|
||||||
resolved "https://registry.yarnpkg.com/@material/data-table/-/data-table-5.0.0.tgz#441f4bde8f4206273cbc304baf26d003cac4ea8d"
|
|
||||||
integrity sha512-h7GHVGwStqeBigkWrr+5/VWX6iqCG1eXWoD/my7YfBZzOOcJ3xvSfF+jNPchK0bRPSzX06jhaNRvGOmu3HCAzg==
|
|
||||||
dependencies:
|
|
||||||
"@material/animation" "^5.0.0"
|
|
||||||
"@material/base" "^5.0.0"
|
|
||||||
"@material/checkbox" "^5.0.0"
|
|
||||||
"@material/density" "^5.0.0"
|
|
||||||
"@material/dom" "^5.0.0"
|
|
||||||
"@material/elevation" "^5.0.0"
|
|
||||||
"@material/feature-targeting" "^5.0.0"
|
|
||||||
"@material/rtl" "^5.0.0"
|
|
||||||
"@material/shape" "^5.0.0"
|
|
||||||
"@material/theme" "^5.0.0"
|
|
||||||
"@material/typography" "^5.0.0"
|
|
||||||
tslib "^1.10.0"
|
|
||||||
|
|
||||||
"@material/density@^5.0.0":
|
"@material/density@^5.0.0":
|
||||||
version "5.0.0"
|
version "5.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/@material/density/-/density-5.0.0.tgz#643d9bd1a5d89b3985d48fd1d6572f73d05fb2e9"
|
resolved "https://registry.yarnpkg.com/@material/density/-/density-5.0.0.tgz#643d9bd1a5d89b3985d48fd1d6572f73d05fb2e9"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user