Use uplot for Dashboard graphs to reduce CPU usage. #5794

This commit is contained in:
Aditya Toshniwal
2023-02-06 15:55:02 +05:30
committed by GitHub
parent f3bb4776e4
commit 4a3bcfa202
14 changed files with 230 additions and 126 deletions

View File

@@ -17,3 +17,6 @@
@import 'node_modules/react-checkbox-tree/lib/react-checkbox-tree.css';
@import 'node_modules/@simonwep/pickr/dist/themes/monolith.min.css';
@import 'node_modules/uplot/dist/uPlot.min.css';

View File

@@ -21,6 +21,7 @@ import getDarkTheme from './dark';
import getHightContrastTheme from './high_contrast';
import { CssBaseline } from '@material-ui/core';
import pickrOverride from './overrides/pickr.override';
import uplotOverride from './overrides/uplot.override';
/* Common settings across all themes */
let basicSettings = createMuiTheme();
@@ -313,6 +314,7 @@ function getFinalTheme(baseTheme) {
padding: 0,
},
...pickrOverride(baseTheme),
...uplotOverride(baseTheme),
},
},
MuiOutlinedInput: {

View File

@@ -0,0 +1,32 @@
/////////////////////////////////////////////////////////////
//
// pgAdmin 4 - PostgreSQL Tools
//
// Copyright (C) 2013 - 2023, The pgAdmin Development Team
// This software is released under the PostgreSQL Licence
//
//////////////////////////////////////////////////////////////
export default function uplotOverride(theme) {
return {
'.uplot': {
'& .u-legend': {
display: 'none',
}
},
'.uplot-tooltip': {
position: 'absolute',
fontSize: '0.9em',
padding: '4px 8px',
borderRadius: theme.shape.borderRadius,
color: theme.palette.background.default,
backgroundColor: theme.palette.text.primary,
'& .uplot-tooltip-label': {
display: 'flex',
gap: '4px',
alignItems: 'center',
}
}
};
}

View File

@@ -0,0 +1,124 @@
import React, { useRef, useMemo } from 'react';
import UplotReact from 'uplot-react';
import { useResizeDetector } from 'react-resize-detector';
import gettext from 'sources/gettext';
import PropTypes from 'prop-types';
function tooltipPlugin(refreshRate) {
let tooltipTopOffset = -20;
let tooltipLeftOffset = 10;
let tooltip;
function showTooltip() {
if(!tooltip) {
tooltip = document.createElement('div');
tooltip.className = 'uplot-tooltip';
tooltip.style.display = 'block';
document.body.appendChild(tooltip);
}
}
function hideTooltip() {
tooltip?.remove();
tooltip = null;
}
function setTooltip(u) {
if(u.cursor.top <= 0) {
hideTooltip();
return;
}
showTooltip();
let tooltipHtml=`<div>${(u.data[1].length-1-parseInt(u.legend.values[0]['_'])) * refreshRate + gettext(' seconds ago')}</div>`;
for(let i=1; i<u.series.length; i++) {
tooltipHtml += `<div class="uplot-tooltip-label"><div style="height:12px; width:12px; background-color:${u.series[i].stroke()}"></div> ${u.series[i].label}: ${u.legend.values[i]['_']}</div>`;
}
tooltip.innerHTML = tooltipHtml;
let overBBox = u.over.getBoundingClientRect();
let tooltipBBox = tooltip.getBoundingClientRect();
let left = (tooltipLeftOffset + u.cursor.left + overBBox.left);
/* Should not outside the graph right */
if((left+tooltipBBox.width) > overBBox.right) {
left = left - tooltipBBox.width - tooltipLeftOffset*2;
}
tooltip.style.left = left + 'px';
tooltip.style.top = (tooltipTopOffset + u.cursor.top + overBBox.top) + 'px';
}
return {
hooks: {
setCursor: [
u => {
setTooltip(u);
}
],
}
};
}
export default function StreamingChart({xRange=75, data, options}) {
const chartRef = useRef();
const { width, height, ref:containerRef } = useResizeDetector();
const defaultOptions = useMemo(()=>({
title: '',
width: width,
height: height,
padding: [10, 0, 10, 0],
focus: {
alpha: 0.3,
},
cursor: {
drag: {
setScale: false,
}
},
series: [
{},
...data.datasets?.map((datum)=>({
label: datum.label,
stroke: datum.borderColor,
width: options.lineBorderWidth ?? 1,
points: { show: options.showDataPoints ?? false, size: datum.pointHitRadius*2 }
}))
],
scales: {
x: {
time: false,
}
},
axes: [
{
show: false,
},
],
plugins: options.showTooltip ? [tooltipPlugin(data.refreshRate)] : [],
}), [data.refreshRate, data?.datasets?.length, width, height, options]);
const initialState = [
Array.from(new Array(xRange).keys()),
...data.datasets?.map((d)=>{
let ret = [...d.data];
ret.reverse();
return ret;
}),
];
chartRef.current?.setScale('x', {min: data.datasets[0]?.data?.length-xRange, max: data.datasets[0]?.data?.length-1});
return (
<div ref={containerRef} style={{width: '100%', height: '100%'}}>
<UplotReact target={containerRef.current} options={defaultOptions} data={initialState} onCreate={(obj)=>chartRef.current=obj} />
</div>
);
}
const propTypeData = PropTypes.shape({
datasets: PropTypes.array,
refreshRate: PropTypes.number.isRequired,
});
StreamingChart.propTypes = {
xRange: PropTypes.number.isRequired,
data: propTypeData.isRequired,
options: PropTypes.object,
};