mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2025-01-19 23:09:18 +01:00
vmui: graph fixes (#1982)
* fix: remove disabling custom step when zooming * feat: add a dynamic calc of the width of the graph * fix: add validate y-axis limits * fix: correct axis limits for value 0 * fix: change logic create time series * fix: change types for tooltip * fix: correct points on the line * fix: change the logic for set graph width * fix: stop checking the period when auto-refresh is enabled * app/vmselect/vmui: `make vmui-update` Co-authored-by: Aliaksandr Valialkin <valyala@victoriametrics.com>
This commit is contained in:
parent
871528fedb
commit
718c352946
@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"files": {
|
"files": {
|
||||||
"main.css": "./static/css/main.a33903a8.css",
|
"main.css": "./static/css/main.a33903a8.css",
|
||||||
"main.js": "./static/js/main.4305bd17.js",
|
"main.js": "./static/js/main.23f635e5.js",
|
||||||
"static/js/27.85f0e2b0.chunk.js": "./static/js/27.85f0e2b0.chunk.js",
|
"static/js/27.85f0e2b0.chunk.js": "./static/js/27.85f0e2b0.chunk.js",
|
||||||
"index.html": "./index.html"
|
"index.html": "./index.html"
|
||||||
},
|
},
|
||||||
"entrypoints": [
|
"entrypoints": [
|
||||||
"static/css/main.a33903a8.css",
|
"static/css/main.a33903a8.css",
|
||||||
"static/js/main.4305bd17.js"
|
"static/js/main.23f635e5.js"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1 +1 @@
|
|||||||
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script defer="defer" src="./static/js/main.4305bd17.js"></script><link href="./static/css/main.a33903a8.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="icon" href="./favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1"/><meta name="theme-color" content="#000000"/><meta name="description" content="VM-UI is a metric explorer for Victoria Metrics"/><link rel="apple-touch-icon" href="./apple-touch-icon.png"/><link rel="icon" type="image/png" sizes="32x32" href="./favicon-32x32.png"><link rel="manifest" href="./manifest.json"/><title>VM UI</title><link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/><script defer="defer" src="./static/js/main.23f635e5.js"></script><link href="./static/css/main.a33903a8.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
|
2
app/vmselect/vmui/static/js/main.23f635e5.js
Normal file
2
app/vmselect/vmui/static/js/main.23f635e5.js
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -15,6 +15,7 @@ const AxesLimitsConfigurator: FC = () => {
|
|||||||
const onChangeLimit = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, axis: string, index: number) => {
|
const onChangeLimit = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, axis: string, index: number) => {
|
||||||
const newLimits = yaxis.limits.range;
|
const newLimits = yaxis.limits.range;
|
||||||
newLimits[axis][index] = +e.target.value;
|
newLimits[axis][index] = +e.target.value;
|
||||||
|
if (newLimits[axis][0] === newLimits[axis][1] || newLimits[axis][0] > newLimits[axis][1]) return;
|
||||||
graphDispatch({type: "SET_YAXIS_LIMITS", payload: newLimits});
|
graphDispatch({type: "SET_YAXIS_LIMITS", payload: newLimits});
|
||||||
};
|
};
|
||||||
const debouncedOnChangeLimit = useCallback(debounce(onChangeLimit, 500), [yaxis.limits.range]);
|
const debouncedOnChangeLimit = useCallback(debounce(onChangeLimit, 500), [yaxis.limits.range]);
|
||||||
|
@ -9,7 +9,7 @@ const StepConfigurator: FC = () => {
|
|||||||
const {customStep} = useGraphState();
|
const {customStep} = useGraphState();
|
||||||
const graphDispatch = useGraphDispatch();
|
const graphDispatch = useGraphDispatch();
|
||||||
const [error, setError] = useState(false);
|
const [error, setError] = useState(false);
|
||||||
const {time: {period: {step}, duration}} = useAppState();
|
const {time: {period: {step}}} = useAppState();
|
||||||
|
|
||||||
const onChangeStep = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
const onChangeStep = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||||
const value = +e.target.value;
|
const value = +e.target.value;
|
||||||
@ -28,10 +28,6 @@ const StepConfigurator: FC = () => {
|
|||||||
graphDispatch({type: "TOGGLE_CUSTOM_STEP"});
|
graphDispatch({type: "TOGGLE_CUSTOM_STEP"});
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (customStep.enable) onChangeEnableStep();
|
|
||||||
}, [duration]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!customStep.enable) graphDispatch({type: "SET_CUSTOM_STEP", payload: step || 1});
|
if (!customStep.enable) graphDispatch({type: "SET_CUSTOM_STEP", payload: step || 1});
|
||||||
}, [step]);
|
}, [step]);
|
||||||
|
@ -18,7 +18,7 @@ export const useFetchQuery = (): {
|
|||||||
liveData?: InstantMetricResult[],
|
liveData?: InstantMetricResult[],
|
||||||
error?: ErrorTypes | string,
|
error?: ErrorTypes | string,
|
||||||
} => {
|
} => {
|
||||||
const {query, displayType, serverUrl, time: {period}, queryControls: {nocache}} = useAppState();
|
const {query, displayType, serverUrl, time: {period}, queryControls: {nocache, autoRefresh}} = useAppState();
|
||||||
|
|
||||||
const {basicData, bearerData, authMethod} = useAuthState();
|
const {basicData, bearerData, authMethod} = useAuthState();
|
||||||
const {customStep} = useGraphState();
|
const {customStep} = useGraphState();
|
||||||
@ -37,7 +37,7 @@ export const useFetchQuery = (): {
|
|||||||
}, [error]);
|
}, [error]);
|
||||||
|
|
||||||
const needUpdateData = useMemo(() => {
|
const needUpdateData = useMemo(() => {
|
||||||
if (!prevPeriod) return true;
|
if (!prevPeriod || autoRefresh) return true;
|
||||||
const duration = (prevPeriod.end - prevPeriod.start) / 3;
|
const duration = (prevPeriod.end - prevPeriod.start) / 3;
|
||||||
const factorLimit = duration / (period.end - period.start) >= 0.7;
|
const factorLimit = duration / (period.end - period.start) >= 0.7;
|
||||||
const maxLimit = period.end > (prevPeriod.end + duration);
|
const maxLimit = period.end > (prevPeriod.end + duration);
|
||||||
|
@ -16,7 +16,7 @@ const HomeLayout: FC = () => {
|
|||||||
const {isLoading, liveData, graphData, error} = useFetchQuery();
|
const {isLoading, liveData, graphData, error} = useFetchQuery();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<Box id="homeLayout">
|
||||||
<AppBar position="static">
|
<AppBar position="static">
|
||||||
<Toolbar>
|
<Toolbar>
|
||||||
<Box display="flex">
|
<Box display="flex">
|
||||||
@ -78,7 +78,7 @@ const HomeLayout: FC = () => {
|
|||||||
</Box>}
|
</Box>}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
</>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
import React, {FC, useEffect, useState} from "react";
|
import React, {FC, useEffect, useMemo, useState} from "react";
|
||||||
import {MetricResult} from "../../../api/types";
|
import {MetricResult} from "../../../api/types";
|
||||||
import LineChart from "../../LineChart/LineChart";
|
import LineChart from "../../LineChart/LineChart";
|
||||||
import {AlignedData as uPlotData, Series as uPlotSeries} from "uplot";
|
import {AlignedData as uPlotData, Series as uPlotSeries} from "uplot";
|
||||||
import Legend from "../../Legend/Legend";
|
import Legend from "../../Legend/Legend";
|
||||||
import {useGraphDispatch} from "../../../state/graph/GraphStateContext";
|
import {useGraphDispatch, useGraphState} from "../../../state/graph/GraphStateContext";
|
||||||
import {getHideSeries, getLegendItem, getSeriesItem} from "../../../utils/uplot/series";
|
import {getHideSeries, getLegendItem, getSeriesItem} from "../../../utils/uplot/series";
|
||||||
import {getLimitsYAxis, getTimeSeries} from "../../../utils/uplot/axes";
|
import {getLimitsYAxis, getTimeSeries} from "../../../utils/uplot/axes";
|
||||||
import {LegendItem} from "../../../utils/uplot/types";
|
import {LegendItem} from "../../../utils/uplot/types";
|
||||||
import {AxisRange} from "../../../state/graph/reducer";
|
|
||||||
import GraphSettings from "../Configurator/Graph/GraphSettings";
|
import GraphSettings from "../Configurator/Graph/GraphSettings";
|
||||||
|
import {useAppState} from "../../../state/common/StateContext";
|
||||||
|
|
||||||
export interface GraphViewProps {
|
export interface GraphViewProps {
|
||||||
data?: MetricResult[];
|
data?: MetricResult[];
|
||||||
@ -16,16 +16,17 @@ export interface GraphViewProps {
|
|||||||
|
|
||||||
const GraphView: FC<GraphViewProps> = ({data = []}) => {
|
const GraphView: FC<GraphViewProps> = ({data = []}) => {
|
||||||
const graphDispatch = useGraphDispatch();
|
const graphDispatch = useGraphDispatch();
|
||||||
|
const {time: {period}} = useAppState();
|
||||||
|
const { customStep } = useGraphState();
|
||||||
|
const currentStep = useMemo(() => customStep.enable ? customStep.value : period.step || 1, [period.step, customStep]);
|
||||||
|
|
||||||
const [dataChart, setDataChart] = useState<uPlotData>([[]]);
|
const [dataChart, setDataChart] = useState<uPlotData>([[]]);
|
||||||
const [series, setSeries] = useState<uPlotSeries[]>([]);
|
const [series, setSeries] = useState<uPlotSeries[]>([]);
|
||||||
const [legend, setLegend] = useState<LegendItem[]>([]);
|
const [legend, setLegend] = useState<LegendItem[]>([]);
|
||||||
const [hideSeries, setHideSeries] = useState<string[]>([]);
|
const [hideSeries, setHideSeries] = useState<string[]>([]);
|
||||||
const [valuesLimit, setValuesLimit] = useState<AxisRange>({"1": [0, 1]});
|
|
||||||
|
|
||||||
const setLimitsYaxis = (values: {[key: string]: number[]}) => {
|
const setLimitsYaxis = (values: {[key: string]: number[]}) => {
|
||||||
const limits = getLimitsYAxis(values);
|
const limits = getLimitsYAxis(values);
|
||||||
setValuesLimit(limits);
|
|
||||||
graphDispatch({type: "SET_YAXIS_LIMITS", payload: limits});
|
graphDispatch({type: "SET_YAXIS_LIMITS", payload: limits});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -50,9 +51,12 @@ const GraphView: FC<GraphViewProps> = ({data = []}) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const timeSeries = getTimeSeries(tempTimes);
|
const timeSeries = getTimeSeries(tempTimes, currentStep, period);
|
||||||
setDataChart([timeSeries, ...data.map(d => {
|
setDataChart([timeSeries, ...data.map(d => {
|
||||||
return new Array(timeSeries.length).fill(1).map((v, i) => d.values[i] ? +d.values[i][1] : null);
|
return timeSeries.map(t => {
|
||||||
|
const value = d.values.find(v => v[0] === t);
|
||||||
|
return value ? +value[1] : null;
|
||||||
|
});
|
||||||
})] as uPlotData);
|
})] as uPlotData);
|
||||||
setLimitsYaxis(tempValues);
|
setLimitsYaxis(tempValues);
|
||||||
|
|
||||||
@ -79,7 +83,7 @@ const GraphView: FC<GraphViewProps> = ({data = []}) => {
|
|||||||
{(data.length > 0)
|
{(data.length > 0)
|
||||||
? <div>
|
? <div>
|
||||||
<GraphSettings/>
|
<GraphSettings/>
|
||||||
<LineChart data={dataChart} series={series} metrics={data} limits={valuesLimit}/>
|
<LineChart data={dataChart} series={series} metrics={data}/>
|
||||||
<Legend labels={legend} onChange={onChangeLegend}/>
|
<Legend labels={legend} onChange={onChangeLegend}/>
|
||||||
</div>
|
</div>
|
||||||
: <div style={{textAlign: "center"}}>No data to show</div>}
|
: <div style={{textAlign: "center"}}>No data to show</div>}
|
||||||
|
@ -11,29 +11,28 @@ import {limitsDurations} from "../../utils/time";
|
|||||||
import throttle from "lodash.throttle";
|
import throttle from "lodash.throttle";
|
||||||
import "uplot/dist/uPlot.min.css";
|
import "uplot/dist/uPlot.min.css";
|
||||||
import "./tooltip.css";
|
import "./tooltip.css";
|
||||||
import {AxisRange} from "../../state/graph/reducer";
|
import useResize from "../../hooks/useResize";
|
||||||
|
|
||||||
export interface LineChartProps {
|
export interface LineChartProps {
|
||||||
metrics: MetricResult[];
|
metrics: MetricResult[];
|
||||||
data: uPlotData;
|
data: uPlotData;
|
||||||
series: uPlotSeries[];
|
series: uPlotSeries[];
|
||||||
limits: AxisRange;
|
|
||||||
}
|
}
|
||||||
enum typeChartUpdate {xRange = "xRange", yRange = "yRange", data = "data"}
|
enum typeChartUpdate {xRange = "xRange", yRange = "yRange", data = "data"}
|
||||||
|
|
||||||
const LineChart: FC<LineChartProps> = ({data, series, metrics = [], limits}) => {
|
const LineChart: FC<LineChartProps> = ({data, series, metrics = []}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const {time: {period}} = useAppState();
|
const {time: {period}} = useAppState();
|
||||||
const {yaxis} = useGraphState();
|
const {yaxis} = useGraphState();
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
|
||||||
const uPlotRef = useRef<HTMLDivElement>(null);
|
const uPlotRef = useRef<HTMLDivElement>(null);
|
||||||
const [isPanning, setPanning] = useState(false);
|
const [isPanning, setPanning] = useState(false);
|
||||||
const [xRange, setXRange] = useState({min: period.start, max: period.end});
|
const [xRange, setXRange] = useState({min: period.start, max: period.end});
|
||||||
const [uPlotInst, setUPlotInst] = useState<uPlot>();
|
const [uPlotInst, setUPlotInst] = useState<uPlot>();
|
||||||
|
const layoutSize = useResize(document.getElementById("homeLayout"));
|
||||||
|
|
||||||
const tooltip = document.createElement("div");
|
const tooltip = document.createElement("div");
|
||||||
tooltip.className = "u-tooltip";
|
tooltip.className = "u-tooltip";
|
||||||
const tooltipIdx = {seriesIdx: 1, dataIdx: 0};
|
const tooltipIdx: {seriesIdx: number | null, dataIdx: number | undefined} = {seriesIdx: null, dataIdx: undefined};
|
||||||
const tooltipOffset = {left: 0, top: 0};
|
const tooltipOffset = {left: 0, top: 0};
|
||||||
|
|
||||||
const setScale = ({min, max}: { min: number, max: number }): void => {
|
const setScale = ({min, max}: { min: number, max: number }): void => {
|
||||||
@ -73,22 +72,22 @@ const LineChart: FC<LineChartProps> = ({data, series, metrics = [], limits}) =>
|
|||||||
const setCursor = (u: uPlot) => {
|
const setCursor = (u: uPlot) => {
|
||||||
if (tooltipIdx.dataIdx === u.cursor.idx) return;
|
if (tooltipIdx.dataIdx === u.cursor.idx) return;
|
||||||
tooltipIdx.dataIdx = u.cursor.idx || 0;
|
tooltipIdx.dataIdx = u.cursor.idx || 0;
|
||||||
if (tooltipIdx.seriesIdx && tooltipIdx.dataIdx) {
|
if (tooltipIdx.seriesIdx !== null && tooltipIdx.dataIdx !== undefined) {
|
||||||
setTooltip({u, tooltipIdx, metrics, series, tooltip, tooltipOffset});
|
setTooltip({u, tooltipIdx, metrics, series, tooltip, tooltipOffset});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const seriesFocus = (u: uPlot, sidx: (number | null)) => {
|
const seriesFocus = (u: uPlot, sidx: (number | null)) => {
|
||||||
if (tooltipIdx.seriesIdx === sidx) return;
|
if (tooltipIdx.seriesIdx === sidx) return;
|
||||||
tooltipIdx.seriesIdx = sidx || 0;
|
tooltipIdx.seriesIdx = sidx;
|
||||||
sidx && tooltipIdx.dataIdx
|
sidx && tooltipIdx.dataIdx !== undefined
|
||||||
? setTooltip({u, tooltipIdx, metrics, series, tooltip, tooltipOffset})
|
? setTooltip({u, tooltipIdx, metrics, series, tooltip, tooltipOffset})
|
||||||
: tooltip.style.display = "none";
|
: tooltip.style.display = "none";
|
||||||
};
|
};
|
||||||
const getRangeX = (): Range.MinMax => [xRange.min, xRange.max];
|
const getRangeX = (): Range.MinMax => [xRange.min, xRange.max];
|
||||||
const getRangeY = (u: uPlot, min = 0, max = 1, axis: string): Range.MinMax => {
|
const getRangeY = (u: uPlot, min = 0, max = 1, axis: string): Range.MinMax => {
|
||||||
if (yaxis.limits.enable) return yaxis.limits.range[axis];
|
if (yaxis.limits.enable) return yaxis.limits.range[axis];
|
||||||
return min && max ? [min - (min * 0.05), max + (max * 0.05)] : limits[axis];
|
return min && max ? [min - (min * 0.25), max + (max * 0.25)] : [-1, 1];
|
||||||
};
|
};
|
||||||
|
|
||||||
const getScales = (): Scales => {
|
const getScales = (): Scales => {
|
||||||
@ -104,7 +103,7 @@ const LineChart: FC<LineChartProps> = ({data, series, metrics = [], limits}) =>
|
|||||||
series,
|
series,
|
||||||
axes: getAxes(series),
|
axes: getAxes(series),
|
||||||
scales: {...getScales()},
|
scales: {...getScales()},
|
||||||
width: containerRef.current ? containerRef.current.offsetWidth : 400,
|
width: layoutSize.width ? layoutSize.width - 64 : 400,
|
||||||
plugins: [{hooks: {ready: onReadyChart, setCursor, setSeries: seriesFocus}}],
|
plugins: [{hooks: {ready: onReadyChart, setCursor, setSeries: seriesFocus}}],
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -135,13 +134,13 @@ const LineChart: FC<LineChartProps> = ({data, series, metrics = [], limits}) =>
|
|||||||
setUPlotInst(u);
|
setUPlotInst(u);
|
||||||
setXRange({min: period.start, max: period.end});
|
setXRange({min: period.start, max: period.end});
|
||||||
return u.destroy;
|
return u.destroy;
|
||||||
}, [uPlotRef.current, series]);
|
}, [uPlotRef.current, series, layoutSize]);
|
||||||
|
|
||||||
useEffect(() => updateChart(typeChartUpdate.data), [data]);
|
useEffect(() => updateChart(typeChartUpdate.data), [data]);
|
||||||
useEffect(() => updateChart(typeChartUpdate.xRange), [xRange]);
|
useEffect(() => updateChart(typeChartUpdate.xRange), [xRange]);
|
||||||
useEffect(() => updateChart(typeChartUpdate.yRange), [yaxis]);
|
useEffect(() => updateChart(typeChartUpdate.yRange), [yaxis]);
|
||||||
|
|
||||||
return <div ref={containerRef} style={{pointerEvents: isPanning ? "none" : "auto", height: "500px"}}>
|
return <div style={{pointerEvents: isPanning ? "none" : "auto", height: "500px"}}>
|
||||||
<div ref={uPlotRef}/>
|
<div ref={uPlotRef}/>
|
||||||
</div>;
|
</div>;
|
||||||
};
|
};
|
||||||
|
23
app/vmui/packages/vmui/src/hooks/useResize.ts
Normal file
23
app/vmui/packages/vmui/src/hooks/useResize.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
const useResize = (node: HTMLElement | null): {width: number, height: number} => {
|
||||||
|
const [windowSize, setWindowSize] = useState({
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
});
|
||||||
|
useEffect(() => {
|
||||||
|
if (!node) return;
|
||||||
|
const handleResize = () => {
|
||||||
|
setWindowSize({
|
||||||
|
width: node.offsetWidth,
|
||||||
|
height: node.offsetHeight,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
handleResize();
|
||||||
|
return () => window.removeEventListener("resize", handleResize);
|
||||||
|
}, []);
|
||||||
|
return windowSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useResize;
|
@ -21,7 +21,7 @@ export interface GraphState {
|
|||||||
|
|
||||||
export type GraphAction =
|
export type GraphAction =
|
||||||
| { type: "TOGGLE_ENABLE_YAXIS_LIMITS" }
|
| { type: "TOGGLE_ENABLE_YAXIS_LIMITS" }
|
||||||
| { type: "SET_YAXIS_LIMITS", payload: { [key: string]: [number, number] } }
|
| { type: "SET_YAXIS_LIMITS", payload: AxisRange }
|
||||||
| { type: "TOGGLE_CUSTOM_STEP" }
|
| { type: "TOGGLE_CUSTOM_STEP" }
|
||||||
| { type: "SET_CUSTOM_STEP", payload: number}
|
| { type: "SET_CUSTOM_STEP", payload: number}
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import {getMaxFromArray, getMinFromArray} from "../math";
|
|||||||
import {roundTimeSeconds} from "../time";
|
import {roundTimeSeconds} from "../time";
|
||||||
import {AxisRange} from "../../state/graph/reducer";
|
import {AxisRange} from "../../state/graph/reducer";
|
||||||
import {formatTicks} from "./helpers";
|
import {formatTicks} from "./helpers";
|
||||||
|
import {TimeParams} from "../../types";
|
||||||
|
|
||||||
export const getAxes = (series: Series[]): Axis[] => Array.from(new Set(series.map(s => s.scale))).map(a => {
|
export const getAxes = (series: Series[]): Axis[] => Array.from(new Set(series.map(s => s.scale))).map(a => {
|
||||||
const axis = {scale: a, show: true, font: "10px Arial", values: formatTicks};
|
const axis = {scale: a, show: true, font: "10px Arial", values: formatTicks};
|
||||||
@ -11,11 +12,11 @@ export const getAxes = (series: Series[]): Axis[] => Array.from(new Set(series.m
|
|||||||
return axis;
|
return axis;
|
||||||
});
|
});
|
||||||
|
|
||||||
export const getTimeSeries = (times: number[]): number[] => {
|
export const getTimeSeries = (times: number[], defaultStep: number, period: TimeParams): number[] => {
|
||||||
const allTimes = Array.from(new Set(times)).sort((a, b) => a - b);
|
const allTimes = Array.from(new Set(times)).sort((a, b) => a - b);
|
||||||
const step = getMinFromArray(allTimes.map((t, i) => allTimes[i + 1] - t));
|
const length = Math.ceil((period.end - period.start)/defaultStep);
|
||||||
const startTime = allTimes[0] || 0;
|
const startTime = allTimes[0] || 0;
|
||||||
return new Array(allTimes.length).fill(startTime).map((d, i) => roundTimeSeconds(d + (step * i)));
|
return new Array(length*2).fill(startTime).map((d, i) => roundTimeSeconds(d + (defaultStep * i)));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getLimitsYAxis = (values: { [key: string]: number[] }): AxisRange => {
|
export const getLimitsYAxis = (values: { [key: string]: number[] }): AxisRange => {
|
||||||
@ -24,7 +25,7 @@ export const getLimitsYAxis = (values: { [key: string]: number[] }): AxisRange =
|
|||||||
const numbers = values[key];
|
const numbers = values[key];
|
||||||
const min = getMinFromArray(numbers);
|
const min = getMinFromArray(numbers);
|
||||||
const max = getMaxFromArray(numbers);
|
const max = getMaxFromArray(numbers);
|
||||||
result[key] = [min - (min * 0.05), max + (max * 0.05)];
|
result[key] = min && max ? [min - (min * 0.25), max + (max * 0.25)] : [-1, 1];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
@ -15,6 +15,10 @@ export const defaultOptions = {
|
|||||||
focus: {
|
focus: {
|
||||||
prox: 30
|
prox: 30
|
||||||
},
|
},
|
||||||
|
points: {
|
||||||
|
size: 5.6,
|
||||||
|
width: 1.4
|
||||||
|
},
|
||||||
bind: {
|
bind: {
|
||||||
mouseup: (): null => null,
|
mouseup: (): null => null,
|
||||||
mousedown: (): null => null,
|
mousedown: (): null => null,
|
||||||
|
@ -10,10 +10,14 @@ export const getSeriesItem = (d: MetricResult, hideSeries: string[]): Series =>
|
|||||||
return {
|
return {
|
||||||
label,
|
label,
|
||||||
dash: getDashLine(d.group),
|
dash: getDashLine(d.group),
|
||||||
width: 1.5,
|
width: 1.4,
|
||||||
stroke: getColorLine(d.group, label),
|
stroke: getColorLine(d.group, label),
|
||||||
show: !includesHideSeries(label, d.group, hideSeries),
|
show: !includesHideSeries(label, d.group, hideSeries),
|
||||||
scale: String(d.group)
|
scale: String(d.group),
|
||||||
|
points: {
|
||||||
|
size: 4.2,
|
||||||
|
width: 1.4
|
||||||
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ import {getColorLine} from "./helpers";
|
|||||||
|
|
||||||
export const setTooltip = ({u, tooltipIdx, metrics, series, tooltip, tooltipOffset}: SetupTooltip): void => {
|
export const setTooltip = ({u, tooltipIdx, metrics, series, tooltip, tooltipOffset}: SetupTooltip): void => {
|
||||||
const {seriesIdx, dataIdx} = tooltipIdx;
|
const {seriesIdx, dataIdx} = tooltipIdx;
|
||||||
|
if (seriesIdx === null || dataIdx === undefined) return;
|
||||||
const dataSeries = u.data[seriesIdx][dataIdx];
|
const dataSeries = u.data[seriesIdx][dataIdx];
|
||||||
const dataTime = u.data[0][dataIdx];
|
const dataTime = u.data[0][dataIdx];
|
||||||
const metric = metrics[seriesIdx - 1]?.metric || {};
|
const metric = metrics[seriesIdx - 1]?.metric || {};
|
||||||
|
@ -11,8 +11,8 @@ export interface SetupTooltip {
|
|||||||
top: number
|
top: number
|
||||||
},
|
},
|
||||||
tooltipIdx: {
|
tooltipIdx: {
|
||||||
seriesIdx: number,
|
seriesIdx: number | null,
|
||||||
dataIdx: number
|
dataIdx: number | undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user