From 39d4b8e2155e0b691d07f38fc0c2419ae7f09d65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9r=C3=B4me=20Wiedemann?= Date: Sat, 23 Jan 2021 20:33:32 +0000 Subject: [PATCH] feat: Add spinner while data is loading --- src/apexcharts-card.ts | 34 +++++++++++++++++++------- src/styles.ts | 55 ++++++++++++++++++++++++++++++++++++++++++ src/types-config-ti.ts | 35 +++++++++++++++------------ src/types-config.ts | 3 +++ 4 files changed, 102 insertions(+), 25 deletions(-) diff --git a/src/apexcharts-card.ts b/src/apexcharts-card.ts index da8cb84..a908cfa 100644 --- a/src/apexcharts-card.ts +++ b/src/apexcharts-card.ts @@ -1,9 +1,10 @@ import { LitElement, html, customElement, property, TemplateResult, CSSResult, PropertyValues } from 'lit-element'; +import { ClassInfo, classMap } from 'lit-html/directives/class-map'; import { ChartCardConfig, EntityEntryCache } from './types'; import { HomeAssistant } from 'custom-card-helpers'; import localForage from 'localforage'; import * as pjson from '../package.json'; -import { computeName, computeUom, decompress, getMilli, log } from './utils'; +import { computeName, computeUom, decompress, getMilli, log, mergeDeep } from './utils'; import ApexCharts from 'apexcharts'; import { styles } from './styles'; import { HassEntity } from 'home-assistant-js-websocket'; @@ -49,7 +50,7 @@ class ChartsCard extends LitElement { private _loaded = false; - private _updating = false; + @property() private _updating = false; private _graphs: (GraphEntry | undefined)[] | undefined; @@ -103,13 +104,16 @@ class ChartsCard extends LitElement { const { ChartCardExternalConfig } = createCheckers(exportedTypeSuite); ChartCardExternalConfig.check(config); - this._config = { - hours_to_show: 24, - cache: true, - useCompress: false, - header: { display: true }, - ...JSON.parse(JSON.stringify(config)), - }; + this._config = mergeDeep( + { + hours_to_show: 24, + cache: true, + useCompress: false, + show: { loading: true }, + header: { display: true }, + }, + JSON.parse(JSON.stringify(config)), + ); if (this._config) { this._graphs = this._config.series.map((serie, index) => { @@ -140,8 +144,20 @@ class ChartsCard extends LitElement { return this.renderWarnings(); } + const spinnerClass: ClassInfo = { + 'lds-ring': this._config.show?.loading && this._updating ? true : false, + }; + return html` +
+
+
+
+
+
+
+
${this._config.header?.display ? this._renderHeader() : html``}
diff --git a/src/styles.ts b/src/styles.ts index 554aebe..f7b328c 100644 --- a/src/styles.ts +++ b/src/styles.ts @@ -7,6 +7,7 @@ export const styles: CSSResult = css` ha-card { overflow: hidden; + position: relative; } .wrapper { @@ -704,4 +705,58 @@ export const styles: CSSResult = css` width: 200%; height: 200%; } + + /* spinner */ + #spinner-wrapper { + position: absolute; + top: 5px; + right: 5px; + height: 20px; + width: 20px; + opacity: 0.5; + } + + #spinner { + position: relative; + } + + .lds-ring, + .lds-ring div { + box-sizing: border-box; + } + .lds-ring { + display: inline-block; + position: relative; + width: 20px; + height: 20px; + } + .lds-ring div { + box-sizing: border-box; + display: block; + position: absolute; + width: 16px; + height: 16px; + margin: 2px; + border: 2px solid var(--primary-text-color); + border-radius: 50%; + animation: lds-ring 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite; + border-color: var(--primary-text-color) transparent transparent transparent; + } + .lds-ring div:nth-child(1) { + animation-delay: -0.45s; + } + .lds-ring div:nth-child(2) { + animation-delay: -0.3s; + } + .lds-ring div:nth-child(3) { + animation-delay: -0.15s; + } + @keyframes lds-ring { + 0% { + transform: rotate(0deg); + } + 100% { + transform: rotate(360deg); + } + } `; diff --git a/src/types-config-ti.ts b/src/types-config-ti.ts index 055fda4..89b3edd 100644 --- a/src/types-config-ti.ts +++ b/src/types-config-ti.ts @@ -1,31 +1,34 @@ /** * This module was automatically generated by `ts-interface-builder` */ -import * as t from 'ts-interface-checker'; +import * as t from "ts-interface-checker"; // tslint:disable:object-literal-key-quotes export const ChartCardExternalConfig = t.iface([], { - type: t.lit('custom:apexcharts-card'), - series: t.array('ChartCardSeriesExternalConfig'), - hours_to_show: t.opt('number'), - cache: t.opt('boolean'), - stacked: t.opt('boolean'), - layout: t.opt('string'), - apex_config: t.opt('any'), - header: t.opt('ChartCardHeaderExternalConfig'), + "type": t.lit('custom:apexcharts-card'), + "series": t.array("ChartCardSeriesExternalConfig"), + "hours_to_show": t.opt("number"), + "show": t.opt(t.iface([], { + "loading": t.opt("boolean"), + })), + "cache": t.opt("boolean"), + "stacked": t.opt("boolean"), + "layout": t.opt("string"), + "apex_config": t.opt("any"), + "header": t.opt("ChartCardHeaderExternalConfig"), }); export const ChartCardSeriesExternalConfig = t.iface([], { - entity: 'string', - name: t.opt('string'), - type: t.opt(t.union(t.lit('line'), t.lit('bar'), t.lit('area'))), - curve: t.opt(t.union(t.lit('smooth'), t.lit('straight'), t.lit('stepline'))), - extend_to_end: t.opt('boolean'), - unit: t.opt('string'), + "entity": "string", + "name": t.opt("string"), + "type": t.opt(t.union(t.lit('line'), t.lit('bar'), t.lit('area'))), + "curve": t.opt(t.union(t.lit('smooth'), t.lit('straight'), t.lit('stepline'))), + "extend_to_end": t.opt("boolean"), + "unit": t.opt("string"), }); export const ChartCardHeaderExternalConfig = t.iface([], { - display: t.opt('boolean'), + "display": t.opt("boolean"), }); const exportedTypeSuite: t.ITypeSuite = { diff --git a/src/types-config.ts b/src/types-config.ts index debc52d..91797b1 100644 --- a/src/types-config.ts +++ b/src/types-config.ts @@ -2,6 +2,9 @@ export interface ChartCardExternalConfig { type: 'custom:apexcharts-card'; series: ChartCardSeriesExternalConfig[]; hours_to_show?: number; + show?: { + loading?: boolean; + }; cache?: boolean; stacked?: boolean; layout?: string;