diff --git a/src/components/trace/ha-timeline.ts b/src/components/trace/ha-timeline.ts index caaa32e066..b717bdee7d 100644 --- a/src/components/trace/ha-timeline.ts +++ b/src/components/trace/ha-timeline.ts @@ -1,5 +1,14 @@ import { mdiCircleOutline } from "@mdi/js"; -import { LitElement, customElement, html, css, property } from "lit-element"; +import { + LitElement, + customElement, + html, + css, + property, + TemplateResult, + internalProperty, +} from "lit-element"; +import { buttonLinkStyle } from "../../resources/styles"; import "../ha-svg-icon"; @customElement("ha-timeline") @@ -8,53 +17,82 @@ class HaTimeline extends LitElement { @property({ type: String }) public icon?: string; + @property({ attribute: false }) public moreItems?: TemplateResult[]; + + @internalProperty() private _showMore = false; + protected render() { return html`
${this.lastItem ? "" : html`
`}
-
+
+ + ${!this.moreItems + ? "" + : html` +
+ ${this._showMore || + // If there is only 1 item hidden behind "show more", just show it + // instead of showing the more info link. We're not animals. + this.moreItems.length === 1 + ? this.moreItems + : html` + + `} +
+ `} +
`; } + private _handleShowMore() { + this._showMore = true; + } + static get styles() { - return css` - :host { - display: flex; - flex-direction: row; - } - :host(:not([lastItem])) { - min-height: 50px; - } - .timeline-start { - display: flex; - flex-direction: column; - align-items: center; - margin-right: 4px; - } - ha-svg-icon { - color: var( - --timeline-ball-color, - var(--timeline-color, var(--secondary-text-color)) - ); - } - .line { - flex: 1; - width: 2px; - background-color: var( - --timeline-line-color, - var(--timeline-color, var(--secondary-text-color)) - ); - margin: 4px 0; - } - .content { - margin-top: 2px; - } - :host(:not([lastItem])) .content { - padding-bottom: 16px; - } - `; + return [ + css` + :host { + display: flex; + flex-direction: row; + } + :host(:not([lastItem])) { + min-height: 50px; + } + .timeline-start { + display: flex; + flex-direction: column; + align-items: center; + margin-right: 4px; + } + ha-svg-icon { + color: var( + --timeline-ball-color, + var(--timeline-color, var(--secondary-text-color)) + ); + } + .line { + flex: 1; + width: 2px; + background-color: var( + --timeline-line-color, + var(--timeline-color, var(--secondary-text-color)) + ); + margin: 4px 0; + } + .content { + margin-top: 2px; + } + :host(:not([lastItem])) .content { + padding-bottom: 16px; + } + `, + buttonLinkStyle, + ]; } } diff --git a/src/components/trace/hat-trace.ts b/src/components/trace/hat-trace.ts index 05e990aef0..9ea68eef3e 100644 --- a/src/components/trace/hat-trace.ts +++ b/src/components/trace/hat-trace.ts @@ -27,6 +27,8 @@ import { LogbookEntry } from "../../data/logbook"; const pathToName = (path: string) => path.split("/").join(" "); +const LOGBOOK_ENTRIES_BEFORE_FOLD = 2; + @customElement("hat-trace") export class HaAutomationTracer extends LitElement { @property({ attribute: false }) public hass!: HomeAssistant; @@ -75,6 +77,8 @@ export class HaAutomationTracer extends LitElement { let logbookIndex = 0; let actionTraceIndex = 0; + let groupedLogbookItems: LogbookEntry[] = []; + while ( logbookIndex < this.logbookEntries.length && actionTraceIndex < actionTraces.length @@ -98,21 +102,26 @@ export class HaAutomationTracer extends LitElement { new Date(logbookItem.when) > new Date(actionTrace[1][0].timestamp) ) { actionTraceIndex++; + if (groupedLogbookItems.length > 0) { + entries.push(this._renderLogbookEntries(groupedLogbookItems)); + groupedLogbookItems = []; + } entries.push(this._renderActionTrace(...actionTrace)); } else { logbookIndex++; - entries.push(this._renderLogbookEntry(logbookItem)); + groupedLogbookItems.push(logbookItem); } } - // Append all leftover items while (logbookIndex < this.logbookEntries.length) { - entries.push( - this._renderLogbookEntry(this.logbookEntries[logbookIndex]) - ); + groupedLogbookItems.push(this.logbookEntries[logbookIndex]); logbookIndex++; } + if (groupedLogbookItems.length > 0) { + entries.push(this._renderLogbookEntries(groupedLogbookItems)); + } + while (actionTraceIndex < actionTraces.length) { entries.push( this._renderActionTrace(...actionTraces[actionTraceIndex]) @@ -151,10 +160,35 @@ export class HaAutomationTracer extends LitElement { return html`${entries}`; } - private _renderLogbookEntry(entry: LogbookEntry) { + private _renderLogbookEntryHelper(entry: LogbookEntry) { + return html`${entry.name} (${entry.entity_id}) turned ${entry.state}
`; + } + + private _renderLogbookEntries(entries: LogbookEntry[]) { + const parts: TemplateResult[] = []; + + let i; + + for ( + i = 0; + i < Math.min(entries.length, LOGBOOK_ENTRIES_BEFORE_FOLD); + i++ + ) { + parts.push(this._renderLogbookEntryHelper(entries[i])); + } + + let moreItems: TemplateResult[] | undefined; + + if (i < entries.length) { + moreItems = []; + for (i = 0; i < entries.length; i++) { + moreItems.push(this._renderLogbookEntryHelper(entries[i])); + } + } + return html` - - ${entry.name} (${entry.entity_id}) turned ${entry.state} + + ${parts} `; } diff --git a/src/resources/styles.ts b/src/resources/styles.ts index e6d70ac0e4..89f0463042 100644 --- a/src/resources/styles.ts +++ b/src/resources/styles.ts @@ -94,6 +94,19 @@ export const derivedStyles = { "mdc-dialog-scroll-divider-color": "var(--divider-color)", }; +export const buttonLinkStyle = css` + button.link { + background: none; + color: inherit; + border: none; + padding: 0; + font: inherit; + text-align: left; + text-decoration: underline; + cursor: pointer; + } +`; + export const haStyle = css` :host { font-family: var(--paper-font-body1_-_font-family); @@ -180,16 +193,7 @@ export const haStyle = css` --mdc-theme-primary: var(--error-color); } - button.link { - background: none; - color: inherit; - border: none; - padding: 0; - font: inherit; - text-align: left; - text-decoration: underline; - cursor: pointer; - } + ${buttonLinkStyle} .card-actions a { text-decoration: none;