mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-22 16:56:35 +00:00
Virtualize logbook (#4450)
* Virtualize logbook * Clean * Update ha-logbook.ts
This commit is contained in:
parent
adce40de56
commit
58ad949bc8
@ -89,6 +89,7 @@
|
|||||||
"leaflet": "^1.4.0",
|
"leaflet": "^1.4.0",
|
||||||
"lit-element": "^2.2.1",
|
"lit-element": "^2.2.1",
|
||||||
"lit-html": "^1.1.0",
|
"lit-html": "^1.1.0",
|
||||||
|
"lit-virtualizer": "^0.4.2",
|
||||||
"marked": "^0.6.1",
|
"marked": "^0.6.1",
|
||||||
"mdn-polyfills": "^5.16.0",
|
"mdn-polyfills": "^5.16.0",
|
||||||
"memoize-one": "^5.0.2",
|
"memoize-one": "^5.0.2",
|
||||||
|
7
src/data/logbook.ts
Normal file
7
src/data/logbook.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export interface LogbookEntry {
|
||||||
|
when: string;
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
entity_id?: string;
|
||||||
|
domain: string;
|
||||||
|
}
|
@ -1,137 +0,0 @@
|
|||||||
import "@polymer/iron-flex-layout/iron-flex-layout-classes";
|
|
||||||
import "@polymer/iron-icon/iron-icon";
|
|
||||||
import { html } from "@polymer/polymer/lib/utils/html-tag";
|
|
||||||
import { PolymerElement } from "@polymer/polymer/polymer-element";
|
|
||||||
|
|
||||||
import formatTime from "../../common/datetime/format_time";
|
|
||||||
import formatDate from "../../common/datetime/format_date";
|
|
||||||
import { EventsMixin } from "../../mixins/events-mixin";
|
|
||||||
import LocalizeMixin from "../../mixins/localize-mixin";
|
|
||||||
import { domainIcon } from "../../common/entity/domain_icon";
|
|
||||||
import { computeRTL } from "../../common/util/compute_rtl";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* @appliesMixin EventsMixin
|
|
||||||
*/
|
|
||||||
class HaLogbook extends LocalizeMixin(EventsMixin(PolymerElement)) {
|
|
||||||
static get template() {
|
|
||||||
return html`
|
|
||||||
<style include="iron-flex"></style>
|
|
||||||
<style>
|
|
||||||
:host {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([rtl]) {
|
|
||||||
direction: ltr;
|
|
||||||
}
|
|
||||||
|
|
||||||
.entry {
|
|
||||||
@apply --paper-font-body1;
|
|
||||||
line-height: 2em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.time {
|
|
||||||
width: 55px;
|
|
||||||
font-size: 0.8em;
|
|
||||||
color: var(--secondary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
:host([rtl]) .date {
|
|
||||||
direction: rtl;
|
|
||||||
}
|
|
||||||
|
|
||||||
iron-icon {
|
|
||||||
margin: 0 8px 0 16px;
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.message {
|
|
||||||
color: var(--primary-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: var(--primary-color);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<template is="dom-if" if="[[!entries.length]]">
|
|
||||||
[[localize('ui.panel.logbook.entries_not_found')]]
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<template is="dom-repeat" items="[[entries]]">
|
|
||||||
<template is="dom-if" if="{{_needHeader(entries.*, index)}}">
|
|
||||||
<h4 class="date">[[_formatDate(item.when)]]</h4>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<div class="horizontal layout entry">
|
|
||||||
<div class="time">[[_formatTime(item.when)]]</div>
|
|
||||||
<iron-icon icon="[[_computeIcon(item.domain)]]"></iron-icon>
|
|
||||||
<div class="message" flex="">
|
|
||||||
<template is="dom-if" if="[[!item.entity_id]]">
|
|
||||||
<span class="name">[[item.name]]</span>
|
|
||||||
</template>
|
|
||||||
<template is="dom-if" if="[[item.entity_id]]">
|
|
||||||
<a href="#" on-click="entityClicked" class="name"
|
|
||||||
>[[item.name]]</a
|
|
||||||
>
|
|
||||||
</template>
|
|
||||||
<span> </span> <span>[[item.message]]</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
static get properties() {
|
|
||||||
return {
|
|
||||||
hass: {
|
|
||||||
type: Object,
|
|
||||||
},
|
|
||||||
|
|
||||||
entries: {
|
|
||||||
type: Array,
|
|
||||||
value: [],
|
|
||||||
},
|
|
||||||
rtl: {
|
|
||||||
type: Boolean,
|
|
||||||
reflectToAttribute: true,
|
|
||||||
computed: "_computeRTL(hass)",
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
_formatTime(date) {
|
|
||||||
return formatTime(new Date(date), this.hass.language);
|
|
||||||
}
|
|
||||||
|
|
||||||
_formatDate(date) {
|
|
||||||
return formatDate(new Date(date), this.hass.language);
|
|
||||||
}
|
|
||||||
|
|
||||||
_needHeader(change, index) {
|
|
||||||
if (!index) return true;
|
|
||||||
const current = this.get("when", change.base[index]);
|
|
||||||
const previous = this.get("when", change.base[index - 1]);
|
|
||||||
return (
|
|
||||||
current &&
|
|
||||||
previous &&
|
|
||||||
new Date(current).toDateString() !== new Date(previous).toDateString()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeIcon(domain) {
|
|
||||||
return domainIcon(domain);
|
|
||||||
}
|
|
||||||
|
|
||||||
_computeRTL(hass) {
|
|
||||||
return computeRTL(hass);
|
|
||||||
}
|
|
||||||
|
|
||||||
entityClicked(ev) {
|
|
||||||
ev.preventDefault();
|
|
||||||
this.fire("hass-more-info", { entityId: ev.model.item.entity_id });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
customElements.define("ha-logbook", HaLogbook);
|
|
156
src/panels/logbook/ha-logbook.ts
Normal file
156
src/panels/logbook/ha-logbook.ts
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
import "@polymer/iron-icon/iron-icon";
|
||||||
|
|
||||||
|
import formatTime from "../../common/datetime/format_time";
|
||||||
|
import formatDate from "../../common/datetime/format_date";
|
||||||
|
import { domainIcon } from "../../common/entity/domain_icon";
|
||||||
|
import { computeRTL } from "../../common/util/compute_rtl";
|
||||||
|
import {
|
||||||
|
LitElement,
|
||||||
|
html,
|
||||||
|
property,
|
||||||
|
TemplateResult,
|
||||||
|
CSSResult,
|
||||||
|
css,
|
||||||
|
PropertyValues,
|
||||||
|
} from "lit-element";
|
||||||
|
import { HomeAssistant } from "../../types";
|
||||||
|
import { fireEvent } from "../../common/dom/fire_event";
|
||||||
|
import "lit-virtualizer";
|
||||||
|
import { LogbookEntry } from "../../data/logbook";
|
||||||
|
|
||||||
|
class HaLogbook extends LitElement {
|
||||||
|
@property() public hass!: HomeAssistant;
|
||||||
|
@property() public entries: LogbookEntry[] = [];
|
||||||
|
@property({ attribute: "rtl", type: Boolean, reflect: true })
|
||||||
|
// @ts-ignore
|
||||||
|
private _rtl = false;
|
||||||
|
|
||||||
|
protected updated(changedProps: PropertyValues) {
|
||||||
|
super.updated(changedProps);
|
||||||
|
if (!changedProps.has("hass")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const oldHass = changedProps.get("hass") as HomeAssistant | undefined;
|
||||||
|
if (oldHass && oldHass.language !== this.hass.language) {
|
||||||
|
this._rtl = computeRTL(this.hass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected firstUpdated(changedProps: PropertyValues) {
|
||||||
|
super.firstUpdated(changedProps);
|
||||||
|
this._rtl = computeRTL(this.hass);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected render(): TemplateResult | void {
|
||||||
|
if (!this.entries?.length) {
|
||||||
|
return html`
|
||||||
|
${this.hass.localize("ui.panel.logbook.entries_not_found")}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<lit-virtualizer
|
||||||
|
.items=${this.entries}
|
||||||
|
.renderItem=${(item: LogbookEntry, index: number) =>
|
||||||
|
this._renderLogbookItem(item, index)}
|
||||||
|
style="height: 100%;"
|
||||||
|
></lit-virtualizer>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _renderLogbookItem(
|
||||||
|
item: LogbookEntry,
|
||||||
|
index: number
|
||||||
|
): TemplateResult {
|
||||||
|
const previous = this.entries[index - 1];
|
||||||
|
return html`
|
||||||
|
<div>
|
||||||
|
${index === 0 ||
|
||||||
|
(item?.when &&
|
||||||
|
previous?.when &&
|
||||||
|
new Date(item.when).toDateString() !==
|
||||||
|
new Date(previous.when).toDateString())
|
||||||
|
? html`
|
||||||
|
<h4 class="date">
|
||||||
|
${formatDate(new Date(item.when), this.hass.language)}
|
||||||
|
</h4>
|
||||||
|
`
|
||||||
|
: html``}
|
||||||
|
|
||||||
|
<div class="entry">
|
||||||
|
<div class="time">
|
||||||
|
${formatTime(new Date(item.when), this.hass.language)}
|
||||||
|
</div>
|
||||||
|
<iron-icon .icon="${domainIcon(item.domain)}"></iron-icon>
|
||||||
|
<div class="message">
|
||||||
|
${!item.entity_id
|
||||||
|
? html`
|
||||||
|
<span class="name">${item.name}</span>
|
||||||
|
`
|
||||||
|
: html`
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
@click=${this._entityClicked}
|
||||||
|
.entityId=${item.entity_id}
|
||||||
|
class="name"
|
||||||
|
>
|
||||||
|
${item.name}
|
||||||
|
</a>
|
||||||
|
`}
|
||||||
|
<span>${item.message}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private _entityClicked(ev: Event) {
|
||||||
|
ev.preventDefault();
|
||||||
|
fireEvent(this, "hass-more-info", {
|
||||||
|
entityId: (ev.target as any).entityId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static get styles(): CSSResult {
|
||||||
|
return css`
|
||||||
|
:host {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([rtl]) {
|
||||||
|
direction: ltr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.entry {
|
||||||
|
display: flex;
|
||||||
|
line-height: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time {
|
||||||
|
width: 55px;
|
||||||
|
font-size: 0.8em;
|
||||||
|
color: var(--secondary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([rtl]) .date {
|
||||||
|
direction: rtl;
|
||||||
|
}
|
||||||
|
|
||||||
|
iron-icon {
|
||||||
|
margin: 0 8px 0 16px;
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.message {
|
||||||
|
color: var(--primary-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: var(--primary-color);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("ha-logbook", HaLogbook);
|
@ -28,7 +28,15 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
|
|||||||
return html`
|
return html`
|
||||||
<style include="ha-style">
|
<style include="ha-style">
|
||||||
.content {
|
.content {
|
||||||
padding: 0 16px 16px;
|
padding: 0 16px 0 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
ha-logbook {
|
||||||
|
height: calc(100vh - 136px);
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([narrow]) ha-logbook {
|
||||||
|
height: calc(100vh - 198px);
|
||||||
}
|
}
|
||||||
|
|
||||||
paper-spinner {
|
paper-spinner {
|
||||||
@ -42,6 +50,15 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
|
|||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
:host([narrow]) .filters {
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
vaadin-date-picker {
|
vaadin-date-picker {
|
||||||
max-width: 200px;
|
max-width: 200px;
|
||||||
margin-right: 16px;
|
margin-right: 16px;
|
||||||
@ -65,10 +82,15 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
|
|||||||
|
|
||||||
ha-entity-picker {
|
ha-entity-picker {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 100%;
|
flex-grow: 1;
|
||||||
max-width: 400px;
|
max-width: 400px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:host([narrow]) ha-entity-picker {
|
||||||
|
max-width: none;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
[hidden] {
|
[hidden] {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
@ -106,7 +128,7 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
|
|||||||
alt="[[localize('ui.common.loading')]]"
|
alt="[[localize('ui.common.loading')]]"
|
||||||
></paper-spinner>
|
></paper-spinner>
|
||||||
|
|
||||||
<div class="flex layout horizontal wrap">
|
<div class="filters">
|
||||||
<vaadin-date-picker
|
<vaadin-date-picker
|
||||||
id="picker"
|
id="picker"
|
||||||
value="{{_currentDate}}"
|
value="{{_currentDate}}"
|
||||||
@ -158,7 +180,8 @@ class HaPanelLogbook extends LocalizeMixin(PolymerElement) {
|
|||||||
static get properties() {
|
static get properties() {
|
||||||
return {
|
return {
|
||||||
hass: Object,
|
hass: Object,
|
||||||
narrow: Boolean,
|
|
||||||
|
narrow: { type: Boolean, reflectToAttribute: true },
|
||||||
|
|
||||||
// ISO8601 formatted date string
|
// ISO8601 formatted date string
|
||||||
_currentDate: {
|
_currentDate: {
|
||||||
|
23
yarn.lock
23
yarn.lock
@ -6246,6 +6246,11 @@ etag@~1.8.1:
|
|||||||
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
|
||||||
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
|
||||||
|
|
||||||
|
event-target-shim@^5.0.1:
|
||||||
|
version "5.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789"
|
||||||
|
integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==
|
||||||
|
|
||||||
eventemitter3@3.1.0, eventemitter3@^3.0.0:
|
eventemitter3@3.1.0, eventemitter3@^3.0.0:
|
||||||
version "3.1.0"
|
version "3.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
|
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.0.tgz#090b4d6cdbd645ed10bf750d4b5407942d7ba163"
|
||||||
@ -8751,7 +8756,7 @@ listr@^0.14.2:
|
|||||||
p-map "^2.0.0"
|
p-map "^2.0.0"
|
||||||
rxjs "^6.3.3"
|
rxjs "^6.3.3"
|
||||||
|
|
||||||
lit-element@^2.2.1:
|
lit-element@^2.0.0, lit-element@^2.2.1:
|
||||||
version "2.2.1"
|
version "2.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.2.1.tgz#79c94d8cfdc2d73b245656e37991bd1e4811d96f"
|
resolved "https://registry.yarnpkg.com/lit-element/-/lit-element-2.2.1.tgz#79c94d8cfdc2d73b245656e37991bd1e4811d96f"
|
||||||
integrity sha512-ipDcgQ1EpW6Va2Z6dWm79jYdimVepO5GL0eYkZrFvdr0OD/1N260Q9DH+K5HXHFrRoC7dOg+ZpED2XE0TgGdXw==
|
integrity sha512-ipDcgQ1EpW6Va2Z6dWm79jYdimVepO5GL0eYkZrFvdr0OD/1N260Q9DH+K5HXHFrRoC7dOg+ZpED2XE0TgGdXw==
|
||||||
@ -8763,6 +8768,17 @@ lit-html@^1.0.0, lit-html@^1.1.0, lit-html@^1.1.2:
|
|||||||
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.1.2.tgz#2e3560a7075210243649c888ad738eaf0daa8374"
|
resolved "https://registry.yarnpkg.com/lit-html/-/lit-html-1.1.2.tgz#2e3560a7075210243649c888ad738eaf0daa8374"
|
||||||
integrity sha512-FFlUMKHKi+qG1x1iHNZ1hrtc/zHmfYTyrSvs3/wBTvaNtpZjOZGWzU7efGYVpgp6KvWeKF6ql9/KsCq6Z/mEDA==
|
integrity sha512-FFlUMKHKi+qG1x1iHNZ1hrtc/zHmfYTyrSvs3/wBTvaNtpZjOZGWzU7efGYVpgp6KvWeKF6ql9/KsCq6Z/mEDA==
|
||||||
|
|
||||||
|
lit-virtualizer@^0.4.2:
|
||||||
|
version "0.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/lit-virtualizer/-/lit-virtualizer-0.4.2.tgz#8c66e26c0e50cd8e3fc7f2dfeab932967b40050a"
|
||||||
|
integrity sha512-aytx/Qym8h7eIh3u17oT2FfgmhOixnk4IuJAOMIbA6E8szkbpnKUDSLDWlN9ihQyCb0eijV213P+4mlekOWKxA==
|
||||||
|
dependencies:
|
||||||
|
event-target-shim "^5.0.1"
|
||||||
|
lit-element "^2.0.0"
|
||||||
|
lit-html "^1.0.0"
|
||||||
|
resize-observer-polyfill "^1.5.1"
|
||||||
|
tslib "^1.10.0"
|
||||||
|
|
||||||
load-json-file@^1.0.0:
|
load-json-file@^1.0.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
||||||
@ -11622,6 +11638,11 @@ requires-port@^1.0.0:
|
|||||||
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
|
||||||
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
|
||||||
|
|
||||||
|
resize-observer-polyfill@^1.5.1:
|
||||||
|
version "1.5.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464"
|
||||||
|
integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==
|
||||||
|
|
||||||
resolve-cwd@^2.0.0:
|
resolve-cwd@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
|
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user