diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 81029851a0..78902bc018 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -6,6 +6,7 @@ import type { DataZoomComponentOption } from "echarts/components"; import type { EChartsType } from "echarts/core"; import type { ECElementEvent, + SetOptionOpts, XAXisOption, YAXisOption, } from "echarts/types/dist/shared"; @@ -83,19 +84,19 @@ export class HaChartBase extends LitElement { this._listeners.push( listenMediaQuery("(prefers-reduced-motion)", (matches) => { - this._reducedMotion = matches; - this.chart?.setOption({ animation: !this._reducedMotion }); + if (this._reducedMotion !== matches) { + this._reducedMotion = matches; + this.chart?.setOption({ animation: !this._reducedMotion }); + } }) ); // Add keyboard event listeners const handleKeyDown = (ev: KeyboardEvent) => { - if ((isMac && ev.metaKey) || (!isMac && ev.ctrlKey)) { + if ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) { this._modifierPressed = true; if (!this.options?.dataZoom) { - this.chart?.setOption({ - dataZoom: this._getDataZoomConfig(), - }); + this.chart?.setOption({ dataZoom: this._getDataZoomConfig() }); } } }; @@ -104,9 +105,7 @@ export class HaChartBase extends LitElement { if ((isMac && ev.key === "Meta") || (!isMac && ev.key === "Control")) { this._modifierPressed = false; if (!this.options?.dataZoom) { - this.chart?.setOption({ - dataZoom: this._getDataZoomConfig(), - }); + this.chart?.setOption({ dataZoom: this._getDataZoomConfig() }); } } }; @@ -124,27 +123,26 @@ export class HaChartBase extends LitElement { } public willUpdate(changedProps: PropertyValues): void { - super.willUpdate(changedProps); - - if (!this.hasUpdated || !this.chart) { + if (!this.chart) { return; } if (changedProps.has("_themes")) { this._setupChart(); return; } + let chartOptions: ECOption = {}; + const chartUpdateParams: SetOptionOpts = { lazyUpdate: true }; if (changedProps.has("data")) { - this.chart.setOption( - { series: this.data }, - { lazyUpdate: true, replaceMerge: ["series"] } - ); + chartOptions.series = this.data; + chartUpdateParams.replaceMerge = ["series"]; } - if (changedProps.has("options") || changedProps.has("_isZoomed")) { - this.chart.setOption(this._createOptions(), { - lazyUpdate: true, - // if we replace the whole object, it will reset the dataZoom - replaceMerge: ["grid"], - }); + if (changedProps.has("options")) { + chartOptions = { ...chartOptions, ...this._createOptions() }; + } else if (this._isTouchDevice && changedProps.has("_isZoomed")) { + chartOptions.dataZoom = this._getDataZoomConfig(); + } + if (Object.keys(chartOptions).length > 0) { + this.chart.setOption(chartOptions, chartUpdateParams); } } @@ -158,7 +156,6 @@ export class HaChartBase extends LitElement { style=${styleMap({ height: this.height ?? `${this._getDefaultHeight()}px`, })} - @wheel=${this._handleWheel} >
${this._isZoomed @@ -240,8 +237,8 @@ export class HaChartBase extends LitElement { type: "inside", orient: "horizontal", filterMode: "none", - moveOnMouseMove: this._isZoomed, - preventDefaultMouseMove: this._isZoomed, + moveOnMouseMove: !this._isTouchDevice || this._isZoomed, + preventDefaultMouseMove: !this._isTouchDevice || this._isZoomed, zoomLock: !this._isTouchDevice && !this._modifierPressed, }; } @@ -514,23 +511,6 @@ export class HaChartBase extends LitElement { private _handleZoomReset() { this.chart?.dispatchAction({ type: "dataZoom", start: 0, end: 100 }); - this._modifierPressed = false; - } - - private _handleWheel(e: WheelEvent) { - // if the window is not focused, we don't receive the keydown events but scroll still works - if (!this.options?.dataZoom) { - const modifierPressed = (isMac && e.metaKey) || (!isMac && e.ctrlKey); - if (modifierPressed) { - e.preventDefault(); - } - if (modifierPressed !== this._modifierPressed) { - this._modifierPressed = modifierPressed; - this.chart?.setOption({ - dataZoom: this._getDataZoomConfig(), - }); - } - } } static styles = css` diff --git a/src/components/chart/state-history-chart-line.ts b/src/components/chart/state-history-chart-line.ts index 76e73a89e7..f7e60f7804 100644 --- a/src/components/chart/state-history-chart-line.ts +++ b/src/components/chart/state-history-chart-line.ts @@ -75,6 +75,8 @@ export class StateHistoryChartLine extends LitElement { @state() private _yWidth = 25; + @state() private _visualMap?: VisualMapComponentOption[]; + private _chartTime: Date = new Date(); protected render() { @@ -92,7 +94,7 @@ export class StateHistoryChartLine extends LitElement { `; } - private _renderTooltip(params: any) { + private _renderTooltip = (params: any) => { const time = params[0].axisValue; const title = formatDateTimeWithSeconds( @@ -115,7 +117,7 @@ export class StateHistoryChartLine extends LitElement { return; } // If the datapoint is not found, we need to find the last datapoint before the current time - let lastData; + let lastData: any; const data = dataset.data || []; for (let i = data.length - 1; i >= 0; i--) { const point = data[i]; @@ -175,7 +177,7 @@ export class StateHistoryChartLine extends LitElement { }) .join("
") ); - } + }; private _datasetHidden(ev: CustomEvent) { this._hiddenStats.add(ev.detail.name); @@ -208,8 +210,8 @@ export class StateHistoryChartLine extends LitElement { changedProps.has("minYAxis") || changedProps.has("maxYAxis") || changedProps.has("fitYData") || - changedProps.has("_chartData") || changedProps.has("paddingYAxis") || + changedProps.has("_visualMap") || changedProps.has("_yWidth") ) { const rtl = computeRTL(this.hass); @@ -280,37 +282,11 @@ export class StateHistoryChartLine extends LitElement { right: rtl ? Math.max(this.paddingYAxis, this._yWidth) : 1, bottom: 30, }, - visualMap: this._chartData - .map((_, seriesIndex) => { - const dataIndex = this._datasetToDataIndex[seriesIndex]; - const data = this.data[dataIndex]; - if (!data.statistics || data.statistics.length === 0) { - return false; - } - // render stat data with a slightly transparent line - const firstStateTS = - data.states[0]?.last_changed ?? this.endTime.getTime(); - return { - show: false, - seriesIndex, - dimension: 0, - pieces: [ - { - max: firstStateTS - 0.01, - colorAlpha: 0.5, - }, - { - min: firstStateTS, - colorAlpha: 1, - }, - ], - }; - }) - .filter(Boolean) as VisualMapComponentOption[], + visualMap: this._visualMap, tooltip: { trigger: "axis", appendTo: document.body, - formatter: this._renderTooltip.bind(this), + formatter: this._renderTooltip, }, }; } @@ -725,6 +701,33 @@ export class StateHistoryChartLine extends LitElement { this._chartData = datasets; this._entityIds = entityIds; this._datasetToDataIndex = datasetToDataIndex; + const visualMap: VisualMapComponentOption[] = []; + this._chartData.forEach((_, seriesIndex) => { + const dataIndex = this._datasetToDataIndex[seriesIndex]; + const data = this.data[dataIndex]; + if (!data.statistics || data.statistics.length === 0) { + return; + } + // render stat data with a slightly transparent line + const firstStateTS = + data.states[0]?.last_changed ?? this.endTime.getTime(); + visualMap.push({ + show: false, + seriesIndex, + dimension: 0, + pieces: [ + { + max: firstStateTS - 0.01, + colorAlpha: 0.5, + }, + { + min: firstStateTS, + colorAlpha: 1, + }, + ], + }); + }); + this._visualMap = visualMap.length > 0 ? visualMap : undefined; } private _clampYAxis(value?: number | ((values: any) => number)) {