mirror of
https://github.com/home-assistant/frontend.git
synced 2025-07-22 16:56:35 +00:00
Add zoom & pan to charts (#23183)
Co-authored-by: Bram Kragten <mail@bramkragten.nl>
This commit is contained in:
parent
f4bf999ae2
commit
784b7e4d04
@ -102,6 +102,7 @@
|
|||||||
"app-datepicker": "5.1.1",
|
"app-datepicker": "5.1.1",
|
||||||
"barcode-detector": "2.3.1",
|
"barcode-detector": "2.3.1",
|
||||||
"chart.js": "4.4.7",
|
"chart.js": "4.4.7",
|
||||||
|
"chartjs-plugin-zoom": "2.2.0",
|
||||||
"color-name": "2.0.0",
|
"color-name": "2.0.0",
|
||||||
"comlink": "4.4.2",
|
"comlink": "4.4.2",
|
||||||
"core-js": "3.39.0",
|
"core-js": "3.39.0",
|
||||||
|
@ -14,6 +14,7 @@ import { fireEvent } from "../../common/dom/fire_event";
|
|||||||
import { clamp } from "../../common/number/clamp";
|
import { clamp } from "../../common/number/clamp";
|
||||||
import type { HomeAssistant } from "../../types";
|
import type { HomeAssistant } from "../../types";
|
||||||
import { debounce } from "../../common/util/debounce";
|
import { debounce } from "../../common/util/debounce";
|
||||||
|
import { isMac } from "../../util/is_mac";
|
||||||
|
|
||||||
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
export const MIN_TIME_BETWEEN_UPDATES = 60 * 5 * 1000;
|
||||||
|
|
||||||
@ -64,6 +65,8 @@ export class HaChartBase extends LitElement {
|
|||||||
|
|
||||||
@state() private _hiddenDatasets: Set<number> = new Set();
|
@state() private _hiddenDatasets: Set<number> = new Set();
|
||||||
|
|
||||||
|
@state() private _showZoomHint = false;
|
||||||
|
|
||||||
private _paddingUpdateCount = 0;
|
private _paddingUpdateCount = 0;
|
||||||
|
|
||||||
private _paddingUpdateLock = false;
|
private _paddingUpdateLock = false;
|
||||||
@ -201,7 +204,9 @@ export class HaChartBase extends LitElement {
|
|||||||
}
|
}
|
||||||
this.chart.data = this.data;
|
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.options = this._createOptions();
|
||||||
}
|
}
|
||||||
this.chart.update("none");
|
this.chart.update("none");
|
||||||
@ -249,7 +254,7 @@ export class HaChartBase extends LitElement {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="chartContainer"
|
class="chart-container"
|
||||||
style=${styleMap({
|
style=${styleMap({
|
||||||
height: `${
|
height: `${
|
||||||
this.height ?? this._chartHeight ?? this.clientWidth / 2
|
this.height ?? this._chartHeight ?? this.clientWidth / 2
|
||||||
@ -259,8 +264,22 @@ export class HaChartBase extends LitElement {
|
|||||||
"padding-inline-start": `${this._paddingYAxisInternal}px`,
|
"padding-inline-start": `${this._paddingYAxisInternal}px`,
|
||||||
"padding-inline-end": 0,
|
"padding-inline-end": 0,
|
||||||
})}
|
})}
|
||||||
|
@wheel=${this._handleChartScroll}
|
||||||
>
|
>
|
||||||
<canvas></canvas>
|
<canvas></canvas>
|
||||||
|
<div
|
||||||
|
class="zoom-hint ${classMap({
|
||||||
|
visible: this._showZoomHint,
|
||||||
|
})}"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
${isMac
|
||||||
|
? this.hass.localize(
|
||||||
|
"ui.components.history_charts.zoom_hint_mac"
|
||||||
|
)
|
||||||
|
: this.hass.localize("ui.components.history_charts.zoom_hint")}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
${this._tooltip
|
${this._tooltip
|
||||||
? html`<div
|
? html`<div
|
||||||
class="chartTooltip ${classMap({
|
class="chartTooltip ${classMap({
|
||||||
@ -343,7 +362,8 @@ export class HaChartBase extends LitElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createOptions() {
|
private _createOptions(): ChartOptions {
|
||||||
|
const modifierKey = isMac ? "meta" : "ctrl";
|
||||||
return {
|
return {
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
...this.options,
|
...this.options,
|
||||||
@ -358,6 +378,36 @@ export class HaChartBase extends LitElement {
|
|||||||
...this.options?.plugins?.legend,
|
...this.options?.plugins?.legend,
|
||||||
display: false,
|
display: false,
|
||||||
},
|
},
|
||||||
|
zoom: {
|
||||||
|
...this.options?.plugins?.zoom,
|
||||||
|
pan: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
pinch: {
|
||||||
|
enabled: true,
|
||||||
|
},
|
||||||
|
drag: {
|
||||||
|
enabled: true,
|
||||||
|
modifierKey,
|
||||||
|
},
|
||||||
|
wheel: {
|
||||||
|
enabled: true,
|
||||||
|
modifierKey,
|
||||||
|
},
|
||||||
|
mode: "x",
|
||||||
|
},
|
||||||
|
limits: {
|
||||||
|
x: {
|
||||||
|
min: "original",
|
||||||
|
max: "original",
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
min: "original",
|
||||||
|
max: "original",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -382,6 +432,16 @@ export class HaChartBase extends LitElement {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _handleChartScroll(ev: MouseEvent) {
|
||||||
|
const modifier = isMac ? "metaKey" : "ctrlKey";
|
||||||
|
if (!ev[modifier] && !this._showZoomHint) {
|
||||||
|
this._showZoomHint = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this._showZoomHint = false;
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private _legendClick(ev) {
|
private _legendClick(ev) {
|
||||||
if (!this.chart) {
|
if (!this.chart) {
|
||||||
return;
|
return;
|
||||||
@ -450,6 +510,9 @@ export class HaChartBase extends LitElement {
|
|||||||
height: 0;
|
height: 0;
|
||||||
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
.chart-container {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
canvas {
|
canvas {
|
||||||
max-height: var(--chart-max-height, 400px);
|
max-height: var(--chart-max-height, 400px);
|
||||||
}
|
}
|
||||||
@ -539,6 +602,31 @@ export class HaChartBase extends LitElement {
|
|||||||
font-weight: 300;
|
font-weight: 300;
|
||||||
word-break: break-all;
|
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);
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import ZoomPlugin from "chartjs-plugin-zoom";
|
||||||
import {
|
import {
|
||||||
LineController,
|
LineController,
|
||||||
TimeScale,
|
TimeScale,
|
||||||
@ -35,5 +36,6 @@ Chart.register(
|
|||||||
TextBarElement,
|
TextBarElement,
|
||||||
TimelineController,
|
TimelineController,
|
||||||
CategoryScale,
|
CategoryScale,
|
||||||
LogarithmicScale
|
LogarithmicScale,
|
||||||
|
ZoomPlugin
|
||||||
);
|
);
|
||||||
|
@ -828,7 +828,9 @@
|
|||||||
"error": "Unable to load history",
|
"error": "Unable to load history",
|
||||||
"duration": "Duration",
|
"duration": "Duration",
|
||||||
"source_history": "Source: History",
|
"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": {
|
"map": {
|
||||||
"error": "Unable to load map"
|
"error": "Unable to load map"
|
||||||
|
1
src/util/is_mac.ts
Normal file
1
src/util/is_mac.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const isMac = /Mac/i.test(navigator.userAgent);
|
28
yarn.lock
28
yarn.lock
@ -4594,10 +4594,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/hammerjs@npm:^2.0.36":
|
"@types/hammerjs@npm:^2.0.36, @types/hammerjs@npm:^2.0.45":
|
||||||
version: 2.0.45
|
version: 2.0.46
|
||||||
resolution: "@types/hammerjs@npm:2.0.45"
|
resolution: "@types/hammerjs@npm:2.0.46"
|
||||||
checksum: 10/8d7f8791789853a9461f6445e625f18922a823a61042161dde5513f4a2c15ecd6361fa6f9b457ce13bfb6b518489b892fedb9e2cebb4420523cb45f1cbb4ee88
|
checksum: 10/1b6502d668f45ca49fb488c01f7938d3aa75e989d70c64801c8feded7d659ca1a118f745c1b604d220efe344c93231767d5cc68c05e00e069c14539b6143cfd9
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -6426,6 +6426,18 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"check-error@npm:^2.1.1":
|
||||||
version: 2.1.1
|
version: 2.1.1
|
||||||
resolution: "check-error@npm:2.1.1"
|
resolution: "check-error@npm:2.1.1"
|
||||||
@ -9028,6 +9040,13 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
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":
|
"handle-thing@npm:^2.0.0":
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
resolution: "handle-thing@npm:2.0.1"
|
resolution: "handle-thing@npm:2.0.1"
|
||||||
@ -9238,6 +9257,7 @@ __metadata:
|
|||||||
barcode-detector: "npm:2.3.1"
|
barcode-detector: "npm:2.3.1"
|
||||||
browserslist-useragent-regexp: "npm:4.1.3"
|
browserslist-useragent-regexp: "npm:4.1.3"
|
||||||
chart.js: "npm:4.4.7"
|
chart.js: "npm:4.4.7"
|
||||||
|
chartjs-plugin-zoom: "npm:2.2.0"
|
||||||
color-name: "npm:2.0.0"
|
color-name: "npm:2.0.0"
|
||||||
comlink: "npm:4.4.2"
|
comlink: "npm:4.4.2"
|
||||||
core-js: "npm:3.39.0"
|
core-js: "npm:3.39.0"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user