mirror of
https://github.com/coollabsio/coolify.git
synced 2026-10-28 09:34:36 +08:00
feat(ui): sync metric chart tooltips across charts on hover
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1527917523
commit
407ac50439
4 files changed
+49
-2
No files matched your search
@@ -138,6 +138,48 @@
|
||||
root.style.setProperty('--theme-accent-foreground', window.themeAccentForeground(localStorage.themeColor));
|
||||
document.querySelector('meta[name=theme-color]')?.setAttribute('content', isDark ? '#101010' : '#ffffff');
|
||||
};
|
||||
// Shows the tooltip at the same time on every chart of a group while one of them is hovered.
|
||||
// ApexCharts' own chart.group needs equal widths and equal x ranges, so it does not
|
||||
// fit side-by-side cards. Instead, replay the hover as a mouse event at the matching x.
|
||||
// Charts can join a group from separate scripts; charts removed from the page drop out.
|
||||
window.chartSyncGroups ??= {};
|
||||
window.syncChartTooltips = (group, charts) => {
|
||||
const grid = chart => chart.el.querySelector('.apexcharts-grid')?.getBoundingClientRect();
|
||||
const hoverArea = chart => chart.el.querySelector('.apexcharts-svg');
|
||||
const hide = chart => hoverArea(chart)?.dispatchEvent(new MouseEvent('mouseout', { clientX: 0, clientY: -1 }));
|
||||
const peers = source => window.chartSyncGroups[group].filter(chart => chart !== source && chart.el.isConnected);
|
||||
|
||||
window.chartSyncGroups[group] = [
|
||||
...(window.chartSyncGroups[group] ?? []).filter(chart => chart.el.isConnected),
|
||||
...charts,
|
||||
];
|
||||
|
||||
charts.forEach(source => {
|
||||
source.el.addEventListener('mousemove', event => {
|
||||
// Replayed events are untrusted; skip them so charts do not echo each other.
|
||||
const rect = grid(source);
|
||||
const { minX, maxX } = source.w.globals;
|
||||
if (!event.isTrusted || !rect?.width || !(maxX > minX)) {
|
||||
return;
|
||||
}
|
||||
const timestamp = minX + (event.clientX - rect.left) / rect.width * (maxX - minX);
|
||||
|
||||
peers(source).forEach(chart => {
|
||||
const target = grid(chart);
|
||||
const { minX: min, maxX: max } = chart.w.globals;
|
||||
if (!target?.width || !(max > min) || timestamp < min || timestamp > max) {
|
||||
hide(chart);
|
||||
return;
|
||||
}
|
||||
hoverArea(chart)?.dispatchEvent(new MouseEvent('mousemove', {
|
||||
clientX: target.left + (timestamp - min) / (max - min) * target.width,
|
||||
clientY: target.top + target.height / 2,
|
||||
}));
|
||||
});
|
||||
});
|
||||
source.el.addEventListener('mouseleave', () => peers(source).forEach(hide));
|
||||
});
|
||||
};
|
||||
// Alpine state for the theme controls on the Appearance page (x-data="themeControls()").
|
||||
window.themeControls = () => ({
|
||||
theme: localStorage.getItem('theme') === 'purple' ? 'custom' : (localStorage.getItem('theme') || 'dark'),
|
||||
|
||||
@@ -433,6 +433,7 @@ $serverListboxOptions = array_merge(
|
||||
chart.render();
|
||||
charts[key] = chart;
|
||||
}
|
||||
window.syncChartTooltips('{!! $chartId !!}', Object.values(charts));
|
||||
|
||||
const renderLegends = specs => {
|
||||
for (const [key, spec] of Object.entries(specs)) {
|
||||
|
||||
@@ -170,6 +170,7 @@
|
||||
});
|
||||
|
||||
cpuChart.render();
|
||||
window.syncChartTooltips('{!! $chartId !!}', [cpuChart]);
|
||||
|
||||
Livewire.on('refreshChartData-{!! $chartId !!}-cpu', chartData => {
|
||||
checkTheme();
|
||||
@@ -326,6 +327,7 @@
|
||||
});
|
||||
|
||||
memoryChart.render();
|
||||
window.syncChartTooltips('{!! $chartId !!}', [memoryChart]);
|
||||
|
||||
Livewire.on('refreshChartData-{!! $chartId !!}-memory', chartData => {
|
||||
checkTheme();
|
||||
|
||||
@@ -220,11 +220,12 @@
|
||||
dataPointIndex,
|
||||
w
|
||||
}) => {
|
||||
const value = series[seriesIndex][dataPointIndex];
|
||||
const timestamp = w.globals.seriesX[seriesIndex][dataPointIndex];
|
||||
const values = series.map((values, index) => values[dataPointIndex] === undefined ? '' :
|
||||
`<div class="apexcharts-tooltip-custom-value">${w.globals.seriesNames?.[index] ?? name}: <span class="apexcharts-tooltip-value-bold">${formatter(values[dataPointIndex])}</span></div>`);
|
||||
|
||||
return `<div class="apexcharts-tooltip-custom">
|
||||
<div class="apexcharts-tooltip-custom-value">${w.globals.seriesNames?.[seriesIndex] ?? name}: <span class="apexcharts-tooltip-value-bold">${formatter(value)}</span></div>
|
||||
${values.join('')}
|
||||
<div class="apexcharts-tooltip-custom-title">Your time: ${formatLocalTimestamp(timestamp)}</div>
|
||||
<div class="apexcharts-tooltip-custom-title">UTC: ${formatUtcTimestamp(timestamp)}</div>
|
||||
</div>`;
|
||||
@@ -268,6 +269,7 @@
|
||||
diskChart.render();
|
||||
networkChart.render();
|
||||
loadChart.render();
|
||||
window.syncChartTooltips('{!! $chartId !!}-metrics', [cpuChart, memoryChart, networkChart, loadChart, diskChart]);
|
||||
});
|
||||
|
||||
Livewire.on('refreshChartData-{!! $chartId !!}-metrics', chartData => {
|
||||
|
||||
Reference in new issue
Block a user