Performance improvements (#352)
* Optimize Sidebar performance * Optimize Dialog
This commit is contained in:
parent
25228fba25
commit
caf6a359b4
|
@ -111,19 +111,8 @@ const Dialog = ({
|
||||||
const isDataReady = Object.keys(tune.constants).length && Object.keys(config.constants).length;
|
const isDataReady = Object.keys(tune.constants).length && Object.keys(config.constants).length;
|
||||||
const { storageSet } = useStorage();
|
const { storageSet } = useStorage();
|
||||||
const { findConstantOnPage } = useConfig(config);
|
const { findConstantOnPage } = useConfig(config);
|
||||||
const [canvasWidth, setCanvasWidth] = useState(0);
|
const [panelsComponents, setPanelsComponents] = useState<any[]>([]);
|
||||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const calculateCanvasWidth = useCallback(() => {
|
|
||||||
setCanvasWidth((containerRef.current?.clientWidth || 0) - 20);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
storageSet('lastDialog', url);
|
|
||||||
calculateCanvasWidth();
|
|
||||||
window.addEventListener('resize', calculateCanvasWidth);
|
|
||||||
|
|
||||||
return () => window.removeEventListener('resize', calculateCanvasWidth);
|
|
||||||
}, [calculateCanvasWidth, storageSet, url, ui.sidebarCollapsed]);
|
|
||||||
|
|
||||||
const renderHelp = (link?: string) => (link &&
|
const renderHelp = (link?: string) => (link &&
|
||||||
<Popover
|
<Popover
|
||||||
|
@ -142,7 +131,7 @@ const Dialog = ({
|
||||||
</Popover>
|
</Popover>
|
||||||
);
|
);
|
||||||
|
|
||||||
const renderCurve = (curve: CurveType) => {
|
const renderCurve = useCallback((curve: CurveType) => {
|
||||||
const x = tune.constants[curve.xBins[0]];
|
const x = tune.constants[curve.xBins[0]];
|
||||||
const y = tune.constants[curve.yBins[0]];
|
const y = tune.constants[curve.yBins[0]];
|
||||||
const xConstant = findConstantOnPage(curve.xBins[0]) as ScalarConstantType;
|
const xConstant = findConstantOnPage(curve.xBins[0]) as ScalarConstantType;
|
||||||
|
@ -150,7 +139,6 @@ const Dialog = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Curve
|
<Curve
|
||||||
width={canvasWidth}
|
|
||||||
key={curve.yBins[0]}
|
key={curve.yBins[0]}
|
||||||
disabled={false} // TODO: evaluate condition
|
disabled={false} // TODO: evaluate condition
|
||||||
help={config.help[curve.yBins[0]]}
|
help={config.help[curve.yBins[0]]}
|
||||||
|
@ -162,9 +150,9 @@ const Dialog = ({
|
||||||
yData={parseXy(y.value as string)}
|
yData={parseXy(y.value as string)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
};
|
}, [config.help, findConstantOnPage, tune.constants]);
|
||||||
|
|
||||||
const renderTable = (table: TableType | RenderedPanel) => {
|
const renderTable = useCallback((table: TableType | RenderedPanel) => {
|
||||||
const x = tune.constants[table.xBins[0]];
|
const x = tune.constants[table.xBins[0]];
|
||||||
const y = tune.constants[table.yBins[0]];
|
const y = tune.constants[table.yBins[0]];
|
||||||
const z = tune.constants[table.zBins[0]];
|
const z = tune.constants[table.zBins[0]];
|
||||||
|
@ -181,45 +169,7 @@ const Dialog = ({
|
||||||
yUnits={y.units as string}
|
yUnits={y.units as string}
|
||||||
/>
|
/>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
}, [tune.constants]);
|
||||||
|
|
||||||
if (!isDataReady) {
|
|
||||||
return skeleton;
|
|
||||||
}
|
|
||||||
|
|
||||||
const dialogConfig = config.dialogs[name];
|
|
||||||
const curveConfig = config.curves[name];
|
|
||||||
const tableConfig = config.tables[name];
|
|
||||||
|
|
||||||
// standalone dialog / page
|
|
||||||
if (!dialogConfig) {
|
|
||||||
if (curveConfig) {
|
|
||||||
return (
|
|
||||||
<div ref={containerRef} style={containerStyle}>
|
|
||||||
<Divider>{curveConfig.title}</Divider>
|
|
||||||
{renderCurve(curveConfig)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tableConfig) {
|
|
||||||
return (
|
|
||||||
<div ref={containerRef} style={containerStyle}>
|
|
||||||
{renderHelp(tableConfig.help)}
|
|
||||||
<Divider>{tableConfig.title}</Divider>
|
|
||||||
{renderTable(tableConfig)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Result
|
|
||||||
status="warning"
|
|
||||||
title="Dialog not found"
|
|
||||||
style={{ marginTop: 50 }}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const calculateSpan = (type: PanelTypes, dialogsCount: number) => {
|
const calculateSpan = (type: PanelTypes, dialogsCount: number) => {
|
||||||
let xxl = 24;
|
let xxl = 24;
|
||||||
|
@ -283,8 +233,9 @@ const Dialog = ({
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: refactor this
|
if (config.dialogs) {
|
||||||
resolveDialogs(config.dialogs, name);
|
resolveDialogs(config.dialogs, name);
|
||||||
|
}
|
||||||
|
|
||||||
// remove dummy dialogs and flatten to array
|
// remove dummy dialogs and flatten to array
|
||||||
const panels = Object.keys(resolvedDialogs).map((dialogName: string): RenderedPanel => {
|
const panels = Object.keys(resolvedDialogs).map((dialogName: string): RenderedPanel => {
|
||||||
|
@ -324,7 +275,7 @@ const Dialog = ({
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const panelsComponents = () => panels.map((panel: RenderedPanel) => {
|
const generatePanelsComponents = useCallback(() => panels.map((panel: RenderedPanel) => {
|
||||||
if (panel.type === PanelTypes.FIELDS && panel.fields.length === 0) {
|
if (panel.type === PanelTypes.FIELDS && panel.fields.length === 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -401,7 +352,54 @@ const Dialog = ({
|
||||||
{panel.type === PanelTypes.TABLE && renderTable(panel)}
|
{panel.type === PanelTypes.TABLE && renderTable(panel)}
|
||||||
</Col>
|
</Col>
|
||||||
);
|
);
|
||||||
});
|
}), [config, findConstantOnPage, panels, renderCurve, renderTable, tune.constants]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
storageSet('lastDialog', url);
|
||||||
|
|
||||||
|
if (isDataReady) {
|
||||||
|
setPanelsComponents(generatePanelsComponents());
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [isDataReady, url, ui.sidebarCollapsed]);
|
||||||
|
|
||||||
|
if (!isDataReady) {
|
||||||
|
return skeleton;
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogConfig = config.dialogs[name];
|
||||||
|
const curveConfig = config.curves[name];
|
||||||
|
const tableConfig = config.tables[name];
|
||||||
|
|
||||||
|
// standalone dialog / page
|
||||||
|
if (!dialogConfig) {
|
||||||
|
if (curveConfig) {
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} style={containerStyle}>
|
||||||
|
<Divider>{curveConfig.title}</Divider>
|
||||||
|
{renderCurve(curveConfig)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tableConfig) {
|
||||||
|
return (
|
||||||
|
<div ref={containerRef} style={containerStyle}>
|
||||||
|
{renderHelp(tableConfig.help)}
|
||||||
|
<Divider>{tableConfig.title}</Divider>
|
||||||
|
{renderTable(tableConfig)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Result
|
||||||
|
status="warning"
|
||||||
|
title="Dialog not found"
|
||||||
|
style={{ marginTop: 50 }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef} style={containerStyle}>
|
<div ref={containerRef} style={containerStyle}>
|
||||||
|
@ -411,7 +409,7 @@ const Dialog = ({
|
||||||
wrapperCol={{ span: 10 }}
|
wrapperCol={{ span: 10 }}
|
||||||
>
|
>
|
||||||
<Row gutter={20}>
|
<Row gutter={20}>
|
||||||
{isDataReady && panelsComponents()}
|
{panelsComponents}
|
||||||
</Row>
|
</Row>
|
||||||
<Form.Item>
|
<Form.Item>
|
||||||
{burnButton}
|
{burnButton}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import {
|
import {
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import UplotReact from 'uplot-react';
|
import UplotReact from 'uplot-react';
|
||||||
|
@ -15,7 +16,6 @@ import Table from './Curve/Table';
|
||||||
const { useBreakpoint } = Grid;
|
const { useBreakpoint } = Grid;
|
||||||
|
|
||||||
const Curve = ({
|
const Curve = ({
|
||||||
width,
|
|
||||||
xLabel,
|
xLabel,
|
||||||
yLabel,
|
yLabel,
|
||||||
xData,
|
xData,
|
||||||
|
@ -25,7 +25,6 @@ const Curve = ({
|
||||||
xUnits = '',
|
xUnits = '',
|
||||||
yUnits = '',
|
yUnits = '',
|
||||||
}: {
|
}: {
|
||||||
width: number,
|
|
||||||
xLabel: string,
|
xLabel: string,
|
||||||
yLabel: string,
|
yLabel: string,
|
||||||
xData: number[],
|
xData: number[],
|
||||||
|
@ -35,6 +34,7 @@ const Curve = ({
|
||||||
xUnits?: string,
|
xUnits?: string,
|
||||||
yUnits?: string,
|
yUnits?: string,
|
||||||
}) => {
|
}) => {
|
||||||
|
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||||
const { sm } = useBreakpoint();
|
const { sm } = useBreakpoint();
|
||||||
const [options, setOptions] = useState<uPlot.Options>();
|
const [options, setOptions] = useState<uPlot.Options>();
|
||||||
const [plotData, setPlotData] = useState<uPlot.AlignedData>();
|
const [plotData, setPlotData] = useState<uPlot.AlignedData>();
|
||||||
|
@ -42,7 +42,7 @@ const Curve = ({
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setPlotData([xData, yData]);
|
setPlotData([xData, yData]);
|
||||||
setOptions({
|
setOptions({
|
||||||
width,
|
width: containerRef.current?.clientWidth || 0,
|
||||||
height: 350,
|
height: 350,
|
||||||
scales: {
|
scales: {
|
||||||
x: { time: false },
|
x: { time: false },
|
||||||
|
@ -76,7 +76,7 @@ const Curve = ({
|
||||||
points: { size: 9 },
|
points: { size: 9 },
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}, [width, xData, xLabel, xUnits, yData, yLabel, yUnits, sm]);
|
}, [xData, xLabel, xUnits, yData, yLabel, yUnits, sm]);
|
||||||
|
|
||||||
if (!sm) {
|
if (!sm) {
|
||||||
return <LandscapeNotice />;
|
return <LandscapeNotice />;
|
||||||
|
@ -88,15 +88,17 @@ const Curve = ({
|
||||||
<Typography.Text type="secondary">{help}</Typography.Text>
|
<Typography.Text type="secondary">{help}</Typography.Text>
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
<UplotReact options={options!} data={plotData!} />
|
<UplotReact options={options!} data={plotData!} />
|
||||||
<Table
|
<div ref={containerRef}>
|
||||||
xLabel={xLabel}
|
<Table
|
||||||
yLabel={yLabel}
|
xLabel={xLabel}
|
||||||
xData={xData}
|
yLabel={yLabel}
|
||||||
yData={yData}
|
xData={xData}
|
||||||
disabled={disabled}
|
yData={yData}
|
||||||
xUnits={xUnits}
|
disabled={disabled}
|
||||||
yUnits={yUnits}
|
xUnits={xUnits}
|
||||||
/>
|
yUnits={yUnits}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -35,6 +35,7 @@ import {
|
||||||
OutputChannel,
|
OutputChannel,
|
||||||
Logs,
|
Logs,
|
||||||
DatalogEntry,
|
DatalogEntry,
|
||||||
|
Tune as TuneType,
|
||||||
} from '@speedy-tuner/types';
|
} from '@speedy-tuner/types';
|
||||||
import { loadLogs } from '../utils/api';
|
import { loadLogs } from '../utils/api';
|
||||||
import LogCanvas from './Log/LogCanvas';
|
import LogCanvas from './Log/LogCanvas';
|
||||||
|
@ -44,24 +45,38 @@ import {
|
||||||
msToTime,
|
msToTime,
|
||||||
} from '../utils/number';
|
} from '../utils/number';
|
||||||
import useConfig from '../hooks/useConfig';
|
import useConfig from '../hooks/useConfig';
|
||||||
|
import {
|
||||||
|
isExpression,
|
||||||
|
stripExpression,
|
||||||
|
} from '../utils/tune/expression';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
const { Content } = Layout;
|
const { Content } = Layout;
|
||||||
const { Step } = Steps;
|
const { Step } = Steps;
|
||||||
const edgeUnknown = 'Unknown';
|
const edgeUnknown = 'Unknown';
|
||||||
|
const margin = 30;
|
||||||
|
const sidebarWidth = 250;
|
||||||
|
const minCanvasHeightInner = 600;
|
||||||
|
|
||||||
const mapStateToProps = (state: AppState) => ({
|
const mapStateToProps = (state: AppState) => ({
|
||||||
ui: state.ui,
|
ui: state.ui,
|
||||||
|
tune: state.tune,
|
||||||
status: state.status,
|
status: state.status,
|
||||||
config: state.config,
|
config: state.config,
|
||||||
loadedLogs: state.logs,
|
loadedLogs: state.logs,
|
||||||
});
|
});
|
||||||
|
|
||||||
const margin = 30;
|
const Log = ({
|
||||||
const sidebarWidth = 250;
|
ui,
|
||||||
const minCanvasHeightInner = 600;
|
config,
|
||||||
|
tune,
|
||||||
const Log = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loadedLogs: Logs }) => {
|
loadedLogs,
|
||||||
|
}: {
|
||||||
|
ui: UIState,
|
||||||
|
tune: TuneType,
|
||||||
|
config: Config,
|
||||||
|
loadedLogs: Logs,
|
||||||
|
}) => {
|
||||||
const { lg } = useBreakpoint();
|
const { lg } = useBreakpoint();
|
||||||
const { Sider } = Layout;
|
const { Sider } = Layout;
|
||||||
const [progress, setProgress] = useState(0);
|
const [progress, setProgress] = useState(0);
|
||||||
|
@ -215,8 +230,7 @@ const Log = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loadedLo
|
||||||
{fields.map((field) => (
|
{fields.map((field) => (
|
||||||
<Row key={field.name}>
|
<Row key={field.name}>
|
||||||
<Checkbox key={field.name} value={field.name}>
|
<Checkbox key={field.name} value={field.name}>
|
||||||
{field.label}
|
{isExpression(field.label) ? stripExpression(field.label) : field.label}
|
||||||
{/* {field.units && ` (${field.units})`} */}
|
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</Row>
|
</Row>
|
||||||
))}
|
))}
|
||||||
|
@ -230,8 +244,7 @@ const Log = ({ ui, config, loadedLogs }: { ui: UIState, config: Config, loadedLo
|
||||||
{fields.map((field) => (
|
{fields.map((field) => (
|
||||||
<Row key={field.name}>
|
<Row key={field.name}>
|
||||||
<Checkbox key={field.name} value={field.name}>
|
<Checkbox key={field.name} value={field.name}>
|
||||||
{field.label}
|
{isExpression(field.label) ? stripExpression(field.label) : field.label}
|
||||||
{/* {field.units && ` (${field.units})`} */}
|
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</Row>
|
</Row>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -9,7 +9,11 @@ import {
|
||||||
Link,
|
Link,
|
||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
import PerfectScrollbar from 'react-perfect-scrollbar';
|
import PerfectScrollbar from 'react-perfect-scrollbar';
|
||||||
import { useCallback } from 'react';
|
import {
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
Config as ConfigType,
|
Config as ConfigType,
|
||||||
Menus as MenusType,
|
Menus as MenusType,
|
||||||
|
@ -79,9 +83,10 @@ const SideBar = ({
|
||||||
category: main,
|
category: main,
|
||||||
dialog: sub,
|
dialog: sub,
|
||||||
}), []);
|
}), []);
|
||||||
|
const [menus, setMenus] = useState<any[]>([]);
|
||||||
|
|
||||||
const menusList = useCallback((menus: MenusType) => (
|
const menusList = useCallback((types: MenusType) => (
|
||||||
Object.keys(menus).map((menuName: string) => {
|
Object.keys(types).map((menuName: string) => {
|
||||||
if (SKIP_MENUS.includes(menuName)) {
|
if (SKIP_MENUS.includes(menuName)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -90,9 +95,9 @@ const SideBar = ({
|
||||||
<SubMenu
|
<SubMenu
|
||||||
key={`/${menuName}`}
|
key={`/${menuName}`}
|
||||||
icon={<Icon name={menuName} />}
|
icon={<Icon name={menuName} />}
|
||||||
title={menus[menuName].title}
|
title={types[menuName].title}
|
||||||
>
|
>
|
||||||
{Object.keys(menus[menuName].subMenus).map((subMenuName: string) => {
|
{Object.keys(types[menuName].subMenus).map((subMenuName: string) => {
|
||||||
if (subMenuName === 'std_separator') {
|
if (subMenuName === 'std_separator') {
|
||||||
return <Menu.Divider key={buildLink(menuName, subMenuName)} />;
|
return <Menu.Divider key={buildLink(menuName, subMenuName)} />;
|
||||||
}
|
}
|
||||||
|
@ -101,10 +106,9 @@ const SideBar = ({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const subMenu = menus[menuName].subMenus[subMenuName];
|
const subMenu = types[menuName].subMenus[subMenuName];
|
||||||
let enabled = true;
|
let enabled = true;
|
||||||
|
|
||||||
// TODO: optimize this!
|
|
||||||
if (subMenu.condition) {
|
if (subMenu.condition) {
|
||||||
enabled = checkCondition(subMenu.condition);
|
enabled = checkCondition(subMenu.condition);
|
||||||
}
|
}
|
||||||
|
@ -124,6 +128,12 @@ const SideBar = ({
|
||||||
})
|
})
|
||||||
), [buildLink, checkCondition]);
|
), [buildLink, checkCondition]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (Object.keys(tune.constants).length) {
|
||||||
|
setMenus(menusList(config.menus));
|
||||||
|
}
|
||||||
|
}, [config.menus, menusList, tune.constants]);
|
||||||
|
|
||||||
if (!isConfigReady) {
|
if (!isConfigReady) {
|
||||||
return (
|
return (
|
||||||
<Sider {...siderProps} className="app-sidebar" >
|
<Sider {...siderProps} className="app-sidebar" >
|
||||||
|
@ -148,7 +158,7 @@ const SideBar = ({
|
||||||
style={{ height: '100%' }}
|
style={{ height: '100%' }}
|
||||||
key={matchedPath.url}
|
key={matchedPath.url}
|
||||||
>
|
>
|
||||||
{Object.keys(tune.constants).length && menusList(config.menus)}
|
{menus}
|
||||||
</Menu>
|
</Menu>
|
||||||
</PerfectScrollbar>
|
</PerfectScrollbar>
|
||||||
</Sider>
|
</Sider>
|
||||||
|
|
Loading…
Reference in New Issue