Draw custom graphs tooltips so we are not limited to the canvas size when many apps are selected

This commit is contained in:
Johannes Zellner
2025-07-21 18:56:02 +02:00
parent cc3dc1636b
commit 44ae48baf5
2 changed files with 61 additions and 10 deletions

View File

@@ -4,7 +4,7 @@
import moment from 'moment-timezone';
import { onMounted, onUnmounted, useTemplateRef, watch } from 'vue';
import Chart from 'chart.js/auto';
import { prettyDecimalSize } from '@cloudron/pankow/utils';
import { prettyDecimalSize, prettyLongDate, prettyShortDate } from '@cloudron/pankow/utils';
import annotationPlugin from 'chartjs-plugin-annotation';
Chart.register(annotationPlugin);
@@ -12,6 +12,7 @@ Chart.register(annotationPlugin);
const LIVE_REFRESH_INTERVAL_MSECS = 1000; // for realtime graphs, the time (x axis) advances at this pace
const graphNode = useTemplateRef('graphNode');
const tooltipElem = useTemplateRef('tooltipElem');
let graph = null;
let liveRefreshIntervalId = null;
@@ -50,11 +51,40 @@ function createGraphOptions({ yscale, period, highMark }) {
position: 'bottom' // not used, hidden since color code is shown in tooltip
},
tooltip: {
enabled: false,
external: function(context) {
const tooltipModel = context.tooltip;
if (tooltipModel.opacity === 0) {
tooltipElem.value.style.opacity = 0;
return;
}
// Set Text
if (tooltipModel.body) {
const titleLines = tooltipModel.title || [];
const bodyLines = tooltipModel.body.map(item => item.lines);
let innerHtml = `<div class="graphs-tooltip-title">${titleLines[0]}</div>`;
bodyLines.forEach(function(body, i) {
const colors = tooltipModel.labelColors[i];
innerHtml += `<div style="color: ${colors.borderColor}" class="graphs-tooltip-item">${body}</div>`;
});
tooltipElem.value.innerHTML = innerHtml;
}
tooltipElem.value.style.opacity = 1;
tooltipElem.value.style.position = 'absolute';
tooltipElem.value.style.left = tooltipModel.caretX + 'px';
tooltipElem.value.style.top = 0 + 'px';
tooltipElem.value.style.height = '100%';
},
callbacks: {
title: (tooltipItem) => moment(tooltipItem[0].raw.x).format(period.tooltipFormat),
title: (tooltipItem) => period.tooltipFormat === 'long' ? prettyLongDate(tooltipItem[0].raw.x) : prettyShortDate(tooltipItem[0].raw.x),
label: (tooltipItem) => {
const datasetLabel = tooltipItem.chart.data.datasets[tooltipItem.datasetIndex].label;
return `${datasetLabel}: ${yscale.ticks.callback(tooltipItem.raw.y)}`;
return `<span style="font-family: fixed">${yscale.ticks.callback(tooltipItem.raw.y)}</span>: ${datasetLabel}`;
}
}
},
@@ -260,6 +290,7 @@ defineExpose({
<label>{{ title }} <span class="pull-right text-small">{{ subtext }}</span></label>
<div class="graph">
<canvas ref="graphNode"></canvas>
<div ref="tooltipElem" class="graphs-tooltip"></div>
</div>
</div>
</template>
@@ -277,3 +308,23 @@ defineExpose({
}
</style>
<style>
.graphs-tooltip {
white-space: nowrap;
border-left: 1px var(--pankow-color-primary) solid;
padding-left: 10px;
pointer-events: none;
backdrop-filter: blur(1px);
}
.graphs-tooltip-title {
font-weight: bold;
}
.graphs-tooltip-item {
padding: 2px 10px;
}
</style>