diff --git a/package.json b/package.json index 5fae641fdeac..58949958f9c4 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "app-datepicker": "5.1.1", "barcode-detector": "2.3.1", "chart.js": "4.4.7", + "chartjs-plugin-zoom": "2.2.0", "color-name": "2.0.0", "comlink": "4.4.2", "core-js": "3.39.0", diff --git a/src/components/chart/ha-chart-base.ts b/src/components/chart/ha-chart-base.ts index 69cbac9a7b6f..06f7a52f36c0 100644 --- a/src/components/chart/ha-chart-base.ts +++ b/src/components/chart/ha-chart-base.ts @@ -14,6 +14,7 @@ import { fireEvent } from "../../common/dom/fire_event"; import { clamp } from "../../common/number/clamp"; import type { HomeAssistant } from "../../types"; import { debounce } from "../../common/util/debounce"; +import { isMac } from "../../util/is_mac"; export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000; @@ -64,6 +65,8 @@ export class HaChartBase extends LitElement { @state() private _hiddenDatasets: Set = new Set(); + @state() private _showZoomHint = false; + private _paddingUpdateCount = 0; private _paddingUpdateLock = false; @@ -201,7 +204,9 @@ export class HaChartBase extends LitElement { } this.chart.data = this.data; } - if (changedProps.has("options")) { + if (changedProps.has("options") && !this.chart.isZoomedOrPanned()) { + // this resets the chart zoom because min/max scales changed + // so we only do it if the user is not zooming or panning this.chart.options = this._createOptions(); } this.chart.update("none"); @@ -249,7 +254,7 @@ export class HaChartBase extends LitElement { })} >
+
+
+ ${isMac + ? this.hass.localize( + "ui.components.history_charts.zoom_hint_mac" + ) + : this.hass.localize("ui.components.history_charts.zoom_hint")} +
+
${this._tooltip ? html`
{ + this._showZoomHint = false; + }, 1000); + } + } + private _legendClick(ev) { if (!this.chart) { return; @@ -450,6 +510,9 @@ export class HaChartBase extends LitElement { height: 0; transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1); } + .chart-container { + position: relative; + } canvas { max-height: var(--chart-max-height, 400px); } @@ -539,6 +602,31 @@ export class HaChartBase extends LitElement { font-weight: 300; word-break: break-all; } + .zoom-hint { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: opacity 500ms cubic-bezier(0.4, 0, 0.2, 1); + pointer-events: none; + } + .zoom-hint.visible { + opacity: 1; + } + .zoom-hint > div { + color: white; + font-size: 1.5em; + font-weight: 500; + padding: 8px; + border-radius: 8px; + background: rgba(0, 0, 0, 0.3); + box-shadow: 0 0 32px 32px rgba(0, 0, 0, 0.3); + } `; } } diff --git a/src/resources/chartjs.ts b/src/resources/chartjs.ts index bf0e2494d7c3..b7b74fed9952 100644 --- a/src/resources/chartjs.ts +++ b/src/resources/chartjs.ts @@ -1,3 +1,4 @@ +import ZoomPlugin from "chartjs-plugin-zoom"; import { LineController, TimeScale, @@ -35,5 +36,6 @@ Chart.register( TextBarElement, TimelineController, CategoryScale, - LogarithmicScale + LogarithmicScale, + ZoomPlugin ); diff --git a/src/translations/en.json b/src/translations/en.json index 13bc5c84de9b..675e0ef796bb 100644 --- a/src/translations/en.json +++ b/src/translations/en.json @@ -828,7 +828,9 @@ "error": "Unable to load history", "duration": "Duration", "source_history": "Source: History", - "source_stats": "Source: Long term statistics" + "source_stats": "Source: Long term statistics", + "zoom_hint": "Use ctrl + scroll to zoom in/out", + "zoom_hint_mac": "Use ⌘ + scroll to zoom in/out" }, "map": { "error": "Unable to load map" diff --git a/src/util/is_mac.ts b/src/util/is_mac.ts new file mode 100644 index 000000000000..0a6c2f52124d --- /dev/null +++ b/src/util/is_mac.ts @@ -0,0 +1 @@ +export const isMac = /Mac/i.test(navigator.userAgent); diff --git a/yarn.lock b/yarn.lock index 903fb48b23fb..c189770c12c0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4585,10 +4585,10 @@ __metadata: languageName: node linkType: hard -"@types/hammerjs@npm:^2.0.36": - version: 2.0.45 - resolution: "@types/hammerjs@npm:2.0.45" - checksum: 10/8d7f8791789853a9461f6445e625f18922a823a61042161dde5513f4a2c15ecd6361fa6f9b457ce13bfb6b518489b892fedb9e2cebb4420523cb45f1cbb4ee88 +"@types/hammerjs@npm:^2.0.36, @types/hammerjs@npm:^2.0.45": + version: 2.0.46 + resolution: "@types/hammerjs@npm:2.0.46" + checksum: 10/1b6502d668f45ca49fb488c01f7938d3aa75e989d70c64801c8feded7d659ca1a118f745c1b604d220efe344c93231767d5cc68c05e00e069c14539b6143cfd9 languageName: node linkType: hard @@ -6417,6 +6417,18 @@ __metadata: languageName: node linkType: hard +"chartjs-plugin-zoom@npm:2.2.0": + version: 2.2.0 + resolution: "chartjs-plugin-zoom@npm:2.2.0" + dependencies: + "@types/hammerjs": "npm:^2.0.45" + hammerjs: "npm:^2.0.8" + peerDependencies: + chart.js: ">=3.2.0" + checksum: 10/4a549b1b21ed5433f9ba67038d6176ed545b2881521e12d6b8024cd2ab08fb008c36fe388ab2ac7ee2ac334bf44a8d785703570388fa0e0b4c22c18602536f9c + languageName: node + linkType: hard + "check-error@npm:^2.1.1": version: 2.1.1 resolution: "check-error@npm:2.1.1" @@ -9019,6 +9031,13 @@ __metadata: languageName: node linkType: hard +"hammerjs@npm:^2.0.8": + version: 2.0.8 + resolution: "hammerjs@npm:2.0.8" + checksum: 10/9155d056f252ef35e8ca258dbb5ee2c9d8794f6805d083da7d1d9763d185e3e149459ecc2b36ccce584e3cd5f099fd9fa55056e3bcc7724046390f2e5ae25815 + languageName: node + linkType: hard + "handle-thing@npm:^2.0.0": version: 2.0.1 resolution: "handle-thing@npm:2.0.1" @@ -9229,6 +9248,7 @@ __metadata: barcode-detector: "npm:2.3.1" browserslist-useragent-regexp: "npm:4.1.3" chart.js: "npm:4.4.7" + chartjs-plugin-zoom: "npm:2.2.0" color-name: "npm:2.0.0" comlink: "npm:4.4.2" core-js: "npm:3.39.0"