@perses-dev/timeseries-chart-plugin 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__mf/js/{983.7cddb989.js → 983.b0368f8f.js} +2 -2
- package/__mf/js/{TimeSeriesChart.2074a1a8.js → TimeSeriesChart.33363c77.js} +3 -3
- package/__mf/js/async/171.598de6a9.js +7 -0
- package/__mf/js/async/223.def4d0a9.js +1 -0
- package/__mf/js/async/281.a2a87c37.js +2 -0
- package/__mf/js/async/359.18a264e7.js +2 -0
- package/__mf/js/async/359.18a264e7.js.LICENSE.txt +15 -0
- package/__mf/js/async/409.b4cabab7.js +1 -0
- package/__mf/js/async/476.45fca8d3.js +1 -0
- package/__mf/js/async/{754.a8adddbc.js → 755.f76e3538.js} +2 -2
- package/__mf/js/async/845.6d99c354.js +73 -0
- package/__mf/js/async/879.24def15a.js +2 -0
- package/__mf/js/async/{511.754bc334.js.LICENSE.txt → 879.24def15a.js.LICENSE.txt} +1 -17
- package/__mf/js/async/{110.e82fda83.js → 964.6dfbf211.js} +2 -2
- package/__mf/js/async/__federation_expose_TimeSeriesChart.1e16af77.js +1 -0
- package/__mf/js/main.beef92ec.js +1 -0
- package/lib/TimeSeriesChartBase.d.ts +22 -0
- package/lib/TimeSeriesChartBase.d.ts.map +1 -0
- package/lib/TimeSeriesChartBase.js +389 -0
- package/lib/TimeSeriesChartBase.js.map +1 -0
- package/lib/TimeSeriesChartPanel.d.ts.map +1 -1
- package/lib/TimeSeriesChartPanel.js +3 -2
- package/lib/TimeSeriesChartPanel.js.map +1 -1
- package/lib/cjs/TimeSeriesChartBase.js +402 -0
- package/lib/cjs/TimeSeriesChartPanel.js +2 -1
- package/lib/cjs/index.js +13 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +13 -0
- package/lib/index.js.map +1 -1
- package/mf-manifest.json +39 -35
- package/mf-stats.json +44 -37
- package/package.json +4 -5
- package/__mf/js/async/162.5d97bc19.js +0 -73
- package/__mf/js/async/357.397150dd.js +0 -29
- package/__mf/js/async/511.754bc334.js +0 -2
- package/__mf/js/async/610.df049245.js +0 -1
- package/__mf/js/async/964.8be59ce0.js +0 -1
- package/__mf/js/async/996.7bede0b2.js +0 -2
- package/__mf/js/async/__federation_expose_TimeSeriesChart.a02bcd07.js +0 -1
- package/__mf/js/main.6227cfef.js +0 -1
- /package/__mf/js/async/{357.397150dd.js.LICENSE.txt → 171.598de6a9.js.LICENSE.txt} +0 -0
- /package/__mf/js/async/{996.7bede0b2.js.LICENSE.txt → 281.a2a87c37.js.LICENSE.txt} +0 -0
- /package/__mf/js/async/{162.5d97bc19.js.LICENSE.txt → 845.6d99c354.js.LICENSE.txt} +0 -0
- /package/__mf/js/async/{110.e82fda83.js.LICENSE.txt → 964.6dfbf211.js.LICENSE.txt} +0 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
// Copyright 2025 The Perses Authors
|
|
2
|
+
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
3
|
+
// you may not use this file except in compliance with the License.
|
|
4
|
+
// You may obtain a copy of the License at
|
|
5
|
+
//
|
|
6
|
+
// http://www.apache.org/licenses/LICENSE-2.0
|
|
7
|
+
//
|
|
8
|
+
// Unless required by applicable law or agreed to in writing, software
|
|
9
|
+
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
10
|
+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
11
|
+
// See the License for the specific language governing permissions and
|
|
12
|
+
// limitations under the License.
|
|
13
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
14
|
+
import { forwardRef, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
|
15
|
+
import { Box } from '@mui/material';
|
|
16
|
+
import merge from 'lodash/merge';
|
|
17
|
+
import isEqual from 'lodash/isEqual';
|
|
18
|
+
import { toZonedTime } from 'date-fns-tz';
|
|
19
|
+
import { getCommonTimeScale } from '@perses-dev/core';
|
|
20
|
+
import { use } from 'echarts/core';
|
|
21
|
+
import { LineChart as EChartsLineChart, BarChart as EChartsBarChart } from 'echarts/charts';
|
|
22
|
+
import { GridComponent, DatasetComponent, DataZoomComponent, MarkAreaComponent, MarkLineComponent, MarkPointComponent, TitleComponent, ToolboxComponent, TooltipComponent } from 'echarts/components';
|
|
23
|
+
import { CanvasRenderer } from 'echarts/renderers';
|
|
24
|
+
import { clearHighlightedSeries, DEFAULT_PINNED_CROSSHAIR, DEFAULT_TOOLTIP_CONFIG, EChart, enableDataZoom, getClosestTimestamp, getFormattedAxis, getFormattedAxisLabel, getPointInGrid, restoreChart, TimeChartTooltip, useChartsContext, useTimeZone } from '@perses-dev/components';
|
|
25
|
+
use([
|
|
26
|
+
EChartsLineChart,
|
|
27
|
+
EChartsBarChart,
|
|
28
|
+
GridComponent,
|
|
29
|
+
DatasetComponent,
|
|
30
|
+
DataZoomComponent,
|
|
31
|
+
MarkAreaComponent,
|
|
32
|
+
MarkLineComponent,
|
|
33
|
+
MarkPointComponent,
|
|
34
|
+
TitleComponent,
|
|
35
|
+
ToolboxComponent,
|
|
36
|
+
TooltipComponent,
|
|
37
|
+
CanvasRenderer
|
|
38
|
+
]);
|
|
39
|
+
export const TimeSeriesChartBase = /*#__PURE__*/ forwardRef(function TimeChart({ height, data, seriesMapping, timeScale: timeScaleProp, yAxis, format, grid, isStackedBar = false, tooltipConfig = DEFAULT_TOOLTIP_CONFIG, noDataVariant = 'message', syncGroup, onDataZoom, onDoubleClick, __experimentalEChartsOptionsOverride }, ref) {
|
|
40
|
+
const { chartsTheme, enablePinning, lastTooltipPinnedCoords, setLastTooltipPinnedCoords } = useChartsContext();
|
|
41
|
+
const isPinningEnabled = tooltipConfig.enablePinning && enablePinning;
|
|
42
|
+
const chartRef = useRef();
|
|
43
|
+
const [showTooltip, setShowTooltip] = useState(true);
|
|
44
|
+
const [tooltipPinnedCoords, setTooltipPinnedCoords] = useState(null);
|
|
45
|
+
const [pinnedCrosshair, setPinnedCrosshair] = useState(null);
|
|
46
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
47
|
+
const [startX, setStartX] = useState(0);
|
|
48
|
+
const { timeZone } = useTimeZone();
|
|
49
|
+
let timeScale;
|
|
50
|
+
if (timeScaleProp === undefined) {
|
|
51
|
+
const commonTimeScale = getCommonTimeScale(data);
|
|
52
|
+
if (commonTimeScale === undefined) {
|
|
53
|
+
// set default to past 5 years
|
|
54
|
+
const today = new Date();
|
|
55
|
+
const pastDate = new Date(today);
|
|
56
|
+
pastDate.setFullYear(today.getFullYear() - 5);
|
|
57
|
+
const todayMs = today.getTime();
|
|
58
|
+
const pastDateMs = pastDate.getTime();
|
|
59
|
+
timeScale = {
|
|
60
|
+
startMs: pastDateMs,
|
|
61
|
+
endMs: todayMs,
|
|
62
|
+
stepMs: 1,
|
|
63
|
+
rangeMs: todayMs - pastDateMs
|
|
64
|
+
};
|
|
65
|
+
} else {
|
|
66
|
+
timeScale = commonTimeScale;
|
|
67
|
+
}
|
|
68
|
+
} else {
|
|
69
|
+
timeScale = timeScaleProp;
|
|
70
|
+
}
|
|
71
|
+
useImperativeHandle(ref, ()=>{
|
|
72
|
+
return {
|
|
73
|
+
highlightSeries ({ name }) {
|
|
74
|
+
if (!chartRef.current) {
|
|
75
|
+
// when chart undef, do not highlight series when hovering over legend
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
chartRef.current.dispatchAction({
|
|
79
|
+
type: 'highlight',
|
|
80
|
+
seriesId: name
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
clearHighlightedSeries: ()=>{
|
|
84
|
+
if (!chartRef.current) {
|
|
85
|
+
// when chart undef, do not clear highlight series
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
clearHighlightedSeries(chartRef.current);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}, []);
|
|
92
|
+
const handleEvents = useMemo(()=>{
|
|
93
|
+
return {
|
|
94
|
+
datazoom: (params)=>{
|
|
95
|
+
if (onDataZoom === undefined) {
|
|
96
|
+
setTimeout(()=>{
|
|
97
|
+
// workaround so unpin happens after click event
|
|
98
|
+
setTooltipPinnedCoords(null);
|
|
99
|
+
}, 10);
|
|
100
|
+
}
|
|
101
|
+
if (onDataZoom === undefined || params.batch[0] === undefined) return;
|
|
102
|
+
const xAxisStartValue = params.batch[0].startValue;
|
|
103
|
+
const xAxisEndValue = params.batch[0].endValue;
|
|
104
|
+
if (xAxisStartValue !== undefined && xAxisEndValue !== undefined) {
|
|
105
|
+
const zoomEvent = {
|
|
106
|
+
start: xAxisStartValue,
|
|
107
|
+
end: xAxisEndValue
|
|
108
|
+
};
|
|
109
|
+
onDataZoom(zoomEvent);
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
finished: ()=>{
|
|
113
|
+
if (chartRef.current !== undefined) {
|
|
114
|
+
enableDataZoom(chartRef.current);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
}, [
|
|
119
|
+
onDataZoom,
|
|
120
|
+
setTooltipPinnedCoords
|
|
121
|
+
]);
|
|
122
|
+
const { noDataOption } = chartsTheme;
|
|
123
|
+
const option = useMemo(()=>{
|
|
124
|
+
// The "chart" `noDataVariant` is only used when the `timeSeries` is an
|
|
125
|
+
// empty array because a `null` value will throw an error.
|
|
126
|
+
if (data === null || data.length === 0 && noDataVariant === 'message') return noDataOption;
|
|
127
|
+
// Utilizes ECharts dataset so raw data is separate from series option style properties
|
|
128
|
+
// https://apache.github.io/echarts-handbook/en/concepts/dataset/
|
|
129
|
+
const dataset = [];
|
|
130
|
+
const isLocalTimeZone = timeZone === 'local';
|
|
131
|
+
data.map((d, index)=>{
|
|
132
|
+
const values = d.values.map(([timestamp, value])=>{
|
|
133
|
+
const val = value === null ? '-' : value; // echarts use '-' to represent null data
|
|
134
|
+
return [
|
|
135
|
+
isLocalTimeZone ? timestamp : toZonedTime(timestamp, timeZone),
|
|
136
|
+
val
|
|
137
|
+
];
|
|
138
|
+
});
|
|
139
|
+
dataset.push({
|
|
140
|
+
id: index,
|
|
141
|
+
source: [
|
|
142
|
+
...values
|
|
143
|
+
],
|
|
144
|
+
dimensions: [
|
|
145
|
+
'time',
|
|
146
|
+
'value'
|
|
147
|
+
]
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
const updatedSeriesMapping = enablePinning && pinnedCrosshair !== null ? [
|
|
151
|
+
...seriesMapping,
|
|
152
|
+
pinnedCrosshair
|
|
153
|
+
] : seriesMapping;
|
|
154
|
+
const option = {
|
|
155
|
+
dataset: dataset,
|
|
156
|
+
series: updatedSeriesMapping,
|
|
157
|
+
xAxis: {
|
|
158
|
+
type: 'time',
|
|
159
|
+
min: isLocalTimeZone ? timeScale.startMs : toZonedTime(timeScale.startMs, timeZone),
|
|
160
|
+
max: isLocalTimeZone ? timeScale.endMs : toZonedTime(timeScale.endMs, timeZone),
|
|
161
|
+
axisLabel: {
|
|
162
|
+
hideOverlap: true,
|
|
163
|
+
formatter: getFormattedAxisLabel(timeScale.rangeMs ?? 0)
|
|
164
|
+
},
|
|
165
|
+
axisPointer: {
|
|
166
|
+
snap: false
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
yAxis: getFormattedAxis(yAxis, format),
|
|
170
|
+
animation: false,
|
|
171
|
+
tooltip: {
|
|
172
|
+
show: true,
|
|
173
|
+
// ECharts tooltip content hidden by default since we use custom tooltip instead.
|
|
174
|
+
// Stacked bar uses ECharts tooltip so subgroup data shows correctly.
|
|
175
|
+
showContent: isStackedBar,
|
|
176
|
+
trigger: isStackedBar ? 'item' : 'axis',
|
|
177
|
+
appendToBody: isStackedBar
|
|
178
|
+
},
|
|
179
|
+
// https://echarts.apache.org/en/option.html#axisPointer
|
|
180
|
+
axisPointer: {
|
|
181
|
+
type: 'line',
|
|
182
|
+
z: 0,
|
|
183
|
+
triggerEmphasis: false,
|
|
184
|
+
triggerTooltip: false,
|
|
185
|
+
snap: false
|
|
186
|
+
},
|
|
187
|
+
toolbox: {
|
|
188
|
+
feature: {
|
|
189
|
+
dataZoom: {
|
|
190
|
+
icon: null,
|
|
191
|
+
yAxisIndex: 'none'
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
grid
|
|
196
|
+
};
|
|
197
|
+
if (__experimentalEChartsOptionsOverride) {
|
|
198
|
+
return __experimentalEChartsOptionsOverride(option);
|
|
199
|
+
}
|
|
200
|
+
return option;
|
|
201
|
+
}, [
|
|
202
|
+
data,
|
|
203
|
+
seriesMapping,
|
|
204
|
+
timeScale,
|
|
205
|
+
yAxis,
|
|
206
|
+
format,
|
|
207
|
+
grid,
|
|
208
|
+
noDataOption,
|
|
209
|
+
__experimentalEChartsOptionsOverride,
|
|
210
|
+
noDataVariant,
|
|
211
|
+
timeZone,
|
|
212
|
+
isStackedBar,
|
|
213
|
+
enablePinning,
|
|
214
|
+
pinnedCrosshair
|
|
215
|
+
]);
|
|
216
|
+
// Update adjacent charts so tooltip is unpinned when current chart is clicked.
|
|
217
|
+
useEffect(()=>{
|
|
218
|
+
// Only allow pinning one tooltip at a time, subsequent tooltip click unpins previous.
|
|
219
|
+
// Multiple tooltips can only be pinned if Ctrl or Cmd key is pressed while clicking.
|
|
220
|
+
const multipleTooltipsPinned = tooltipPinnedCoords !== null && lastTooltipPinnedCoords !== null;
|
|
221
|
+
if (multipleTooltipsPinned) {
|
|
222
|
+
if (!isEqual(lastTooltipPinnedCoords, tooltipPinnedCoords)) {
|
|
223
|
+
setTooltipPinnedCoords(null);
|
|
224
|
+
if (tooltipPinnedCoords !== null && pinnedCrosshair !== null) {
|
|
225
|
+
setPinnedCrosshair(null);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
// tooltipPinnedCoords CANNOT be in dep array or tooltip pinning breaks in the current chart's onClick
|
|
230
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
231
|
+
}, [
|
|
232
|
+
lastTooltipPinnedCoords,
|
|
233
|
+
seriesMapping
|
|
234
|
+
]);
|
|
235
|
+
return /*#__PURE__*/ _jsxs(Box, {
|
|
236
|
+
style: {
|
|
237
|
+
height
|
|
238
|
+
},
|
|
239
|
+
// onContextMenu={(e) => {
|
|
240
|
+
// // TODO: confirm tooltip pinning works correctly on Windows, should e.preventDefault() be added here
|
|
241
|
+
// e.preventDefault(); // Prevent the default behaviour when right clicked
|
|
242
|
+
// }}
|
|
243
|
+
onClick: (e)=>{
|
|
244
|
+
// Allows user to opt-in to multi tooltip pinning when Ctrl or Cmd key held down
|
|
245
|
+
const isControlKeyPressed = e.ctrlKey || e.metaKey;
|
|
246
|
+
if (isControlKeyPressed) {
|
|
247
|
+
e.preventDefault();
|
|
248
|
+
}
|
|
249
|
+
// Determine where on chart canvas to plot pinned crosshair as markLine.
|
|
250
|
+
const pointInGrid = getPointInGrid(e.nativeEvent.offsetX, e.nativeEvent.offsetY, chartRef.current);
|
|
251
|
+
if (pointInGrid === null) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
// Pin and unpin when clicking on chart canvas but not tooltip text.
|
|
255
|
+
if (isPinningEnabled && e.target instanceof HTMLCanvasElement) {
|
|
256
|
+
// Pin tooltip and update shared charts context to remember these coordinates.
|
|
257
|
+
const pinnedPos = {
|
|
258
|
+
page: {
|
|
259
|
+
x: e.pageX,
|
|
260
|
+
y: e.pageY
|
|
261
|
+
},
|
|
262
|
+
client: {
|
|
263
|
+
x: e.clientX,
|
|
264
|
+
y: e.clientY
|
|
265
|
+
},
|
|
266
|
+
plotCanvas: {
|
|
267
|
+
x: e.nativeEvent.offsetX,
|
|
268
|
+
y: e.nativeEvent.offsetY
|
|
269
|
+
},
|
|
270
|
+
target: e.target
|
|
271
|
+
};
|
|
272
|
+
setTooltipPinnedCoords((current)=>{
|
|
273
|
+
if (current === null) {
|
|
274
|
+
return pinnedPos;
|
|
275
|
+
} else {
|
|
276
|
+
setPinnedCrosshair(null);
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
setPinnedCrosshair((current)=>{
|
|
281
|
+
// Only add pinned crosshair line series when there is not one already in seriesMapping.
|
|
282
|
+
if (current === null) {
|
|
283
|
+
const cursorX = pointInGrid[0];
|
|
284
|
+
// Only need to loop through first dataset source since getCommonTimeScale ensures xAxis timestamps are consistent
|
|
285
|
+
const firstTimeSeriesValues = data[0]?.values;
|
|
286
|
+
const closestTimestamp = getClosestTimestamp(firstTimeSeriesValues, cursorX);
|
|
287
|
+
// Crosshair snaps to nearest timestamp since cursor may be slightly to left or right
|
|
288
|
+
const pinnedCrosshair = merge({}, DEFAULT_PINNED_CROSSHAIR, {
|
|
289
|
+
markLine: {
|
|
290
|
+
data: [
|
|
291
|
+
{
|
|
292
|
+
xAxis: closestTimestamp
|
|
293
|
+
}
|
|
294
|
+
]
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
return pinnedCrosshair;
|
|
298
|
+
} else {
|
|
299
|
+
// Clear previously set pinned crosshair
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
if (!isControlKeyPressed) {
|
|
304
|
+
setLastTooltipPinnedCoords(pinnedPos);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
onMouseDown: (e)=>{
|
|
309
|
+
const { clientX } = e;
|
|
310
|
+
setIsDragging(true);
|
|
311
|
+
setStartX(clientX);
|
|
312
|
+
},
|
|
313
|
+
onMouseMove: (e)=>{
|
|
314
|
+
// Allow clicking inside tooltip to copy labels.
|
|
315
|
+
if (!(e.target instanceof HTMLCanvasElement)) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const { clientX } = e;
|
|
319
|
+
if (isDragging) {
|
|
320
|
+
const deltaX = clientX - startX;
|
|
321
|
+
if (deltaX > 0) {
|
|
322
|
+
// Hide tooltip when user drags to zoom.
|
|
323
|
+
setShowTooltip(false);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
onMouseUp: ()=>{
|
|
328
|
+
setIsDragging(false);
|
|
329
|
+
setStartX(0);
|
|
330
|
+
setShowTooltip(true);
|
|
331
|
+
},
|
|
332
|
+
onMouseLeave: ()=>{
|
|
333
|
+
if (tooltipPinnedCoords === null) {
|
|
334
|
+
setShowTooltip(false);
|
|
335
|
+
}
|
|
336
|
+
if (chartRef.current !== undefined) {
|
|
337
|
+
clearHighlightedSeries(chartRef.current);
|
|
338
|
+
}
|
|
339
|
+
},
|
|
340
|
+
onMouseEnter: ()=>{
|
|
341
|
+
setShowTooltip(true);
|
|
342
|
+
if (chartRef.current !== undefined) {
|
|
343
|
+
enableDataZoom(chartRef.current);
|
|
344
|
+
}
|
|
345
|
+
},
|
|
346
|
+
onDoubleClick: (e)=>{
|
|
347
|
+
setTooltipPinnedCoords(null);
|
|
348
|
+
// either dispatch ECharts restore action to return to orig state or allow consumer to define behavior
|
|
349
|
+
if (onDoubleClick === undefined) {
|
|
350
|
+
if (chartRef.current !== undefined) {
|
|
351
|
+
restoreChart(chartRef.current);
|
|
352
|
+
}
|
|
353
|
+
} else {
|
|
354
|
+
onDoubleClick(e);
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
children: [
|
|
358
|
+
showTooltip === true && option.tooltip?.showContent === false && tooltipConfig.hidden !== true && /*#__PURE__*/ _jsx(TimeChartTooltip, {
|
|
359
|
+
containerId: chartsTheme.tooltipPortalContainerId,
|
|
360
|
+
chartRef: chartRef,
|
|
361
|
+
data: data,
|
|
362
|
+
seriesMapping: seriesMapping,
|
|
363
|
+
wrapLabels: tooltipConfig.wrapLabels,
|
|
364
|
+
enablePinning: isPinningEnabled,
|
|
365
|
+
pinnedPos: tooltipPinnedCoords,
|
|
366
|
+
format: format,
|
|
367
|
+
onUnpinClick: ()=>{
|
|
368
|
+
// Unpins tooltip when clicking Pin icon in TooltipHeader.
|
|
369
|
+
setTooltipPinnedCoords(null);
|
|
370
|
+
// Clear previously set pinned crosshair.
|
|
371
|
+
setPinnedCrosshair(null);
|
|
372
|
+
}
|
|
373
|
+
}),
|
|
374
|
+
/*#__PURE__*/ _jsx(EChart, {
|
|
375
|
+
sx: {
|
|
376
|
+
width: '100%',
|
|
377
|
+
height: '100%'
|
|
378
|
+
},
|
|
379
|
+
option: option,
|
|
380
|
+
theme: chartsTheme.echartsTheme,
|
|
381
|
+
onEvents: handleEvents,
|
|
382
|
+
_instance: chartRef,
|
|
383
|
+
syncGroup: syncGroup
|
|
384
|
+
})
|
|
385
|
+
]
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
//# sourceMappingURL=TimeSeriesChartBase.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/TimeSeriesChartBase.tsx"],"sourcesContent":["// Copyright 2025 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { forwardRef, MouseEvent, useEffect, useImperativeHandle, useMemo, useRef, useState } from 'react';\nimport { Box } from '@mui/material';\nimport merge from 'lodash/merge';\nimport isEqual from 'lodash/isEqual';\nimport { toZonedTime } from 'date-fns-tz';\nimport { getCommonTimeScale, TimeScale, FormatOptions, TimeSeries } from '@perses-dev/core';\nimport type {\n EChartsCoreOption,\n GridComponentOption,\n LineSeriesOption,\n YAXisComponentOption,\n TooltipComponentOption,\n} from 'echarts';\nimport { ECharts as EChartsInstance, use } from 'echarts/core';\nimport { LineChart as EChartsLineChart, BarChart as EChartsBarChart } from 'echarts/charts';\nimport {\n GridComponent,\n DatasetComponent,\n DataZoomComponent,\n MarkAreaComponent,\n MarkLineComponent,\n MarkPointComponent,\n TitleComponent,\n ToolboxComponent,\n TooltipComponent,\n} from 'echarts/components';\nimport { CanvasRenderer } from 'echarts/renderers';\nimport {\n ChartInstance,\n ChartInstanceFocusOpts,\n clearHighlightedSeries,\n CursorCoordinates,\n DEFAULT_PINNED_CROSSHAIR,\n DEFAULT_TOOLTIP_CONFIG,\n EChart,\n enableDataZoom,\n getClosestTimestamp,\n getFormattedAxis,\n getFormattedAxisLabel,\n getPointInGrid,\n OnEventsType,\n restoreChart,\n TimeChartSeriesMapping,\n TimeChartTooltip,\n TooltipConfig,\n useChartsContext,\n useTimeZone,\n ZoomEventData,\n} from '@perses-dev/components';\nimport { DatasetOption } from 'echarts/types/dist/shared';\n\nuse([\n EChartsLineChart,\n EChartsBarChart,\n GridComponent,\n DatasetComponent,\n DataZoomComponent,\n MarkAreaComponent,\n MarkLineComponent,\n MarkPointComponent,\n TitleComponent,\n ToolboxComponent,\n TooltipComponent,\n CanvasRenderer,\n]);\n\nexport interface TimeChartProps {\n height: number;\n data: TimeSeries[];\n seriesMapping: TimeChartSeriesMapping;\n timeScale?: TimeScale;\n yAxis?: YAXisComponentOption;\n format?: FormatOptions;\n grid?: GridComponentOption;\n tooltipConfig?: TooltipConfig;\n noDataVariant?: 'chart' | 'message';\n syncGroup?: string;\n isStackedBar?: boolean;\n onDataZoom?: (e: ZoomEventData) => void;\n onDoubleClick?: (e: MouseEvent) => void;\n __experimentalEChartsOptionsOverride?: (options: EChartsCoreOption) => EChartsCoreOption;\n}\n\nexport const TimeSeriesChartBase = forwardRef<ChartInstance, TimeChartProps>(function TimeChart(\n {\n height,\n data,\n seriesMapping,\n timeScale: timeScaleProp,\n yAxis,\n format,\n grid,\n isStackedBar = false,\n tooltipConfig = DEFAULT_TOOLTIP_CONFIG,\n noDataVariant = 'message',\n syncGroup,\n onDataZoom,\n onDoubleClick,\n __experimentalEChartsOptionsOverride,\n },\n ref\n) {\n const { chartsTheme, enablePinning, lastTooltipPinnedCoords, setLastTooltipPinnedCoords } = useChartsContext();\n const isPinningEnabled = tooltipConfig.enablePinning && enablePinning;\n const chartRef = useRef<EChartsInstance>();\n const [showTooltip, setShowTooltip] = useState<boolean>(true);\n const [tooltipPinnedCoords, setTooltipPinnedCoords] = useState<CursorCoordinates | null>(null);\n const [pinnedCrosshair, setPinnedCrosshair] = useState<LineSeriesOption | null>(null);\n const [isDragging, setIsDragging] = useState(false);\n const [startX, setStartX] = useState(0);\n const { timeZone } = useTimeZone();\n let timeScale: TimeScale;\n if (timeScaleProp === undefined) {\n const commonTimeScale = getCommonTimeScale(data);\n if (commonTimeScale === undefined) {\n // set default to past 5 years\n const today = new Date();\n const pastDate = new Date(today);\n pastDate.setFullYear(today.getFullYear() - 5);\n const todayMs = today.getTime();\n const pastDateMs = pastDate.getTime();\n timeScale = { startMs: pastDateMs, endMs: todayMs, stepMs: 1, rangeMs: todayMs - pastDateMs };\n } else {\n timeScale = commonTimeScale;\n }\n } else {\n timeScale = timeScaleProp;\n }\n\n useImperativeHandle(ref, () => {\n return {\n highlightSeries({ name }: ChartInstanceFocusOpts): void {\n if (!chartRef.current) {\n // when chart undef, do not highlight series when hovering over legend\n return;\n }\n\n chartRef.current.dispatchAction({ type: 'highlight', seriesId: name });\n },\n clearHighlightedSeries: (): void => {\n if (!chartRef.current) {\n // when chart undef, do not clear highlight series\n return;\n }\n clearHighlightedSeries(chartRef.current);\n },\n };\n }, []);\n\n const handleEvents: OnEventsType<LineSeriesOption['data'] | unknown> = useMemo(() => {\n return {\n datazoom: (params): void => {\n if (onDataZoom === undefined) {\n setTimeout(() => {\n // workaround so unpin happens after click event\n setTooltipPinnedCoords(null);\n }, 10);\n }\n if (onDataZoom === undefined || params.batch[0] === undefined) return;\n const xAxisStartValue = params.batch[0].startValue;\n const xAxisEndValue = params.batch[0].endValue;\n if (xAxisStartValue !== undefined && xAxisEndValue !== undefined) {\n const zoomEvent: ZoomEventData = {\n start: xAxisStartValue,\n end: xAxisEndValue,\n };\n onDataZoom(zoomEvent);\n }\n },\n finished: (): void => {\n if (chartRef.current !== undefined) {\n enableDataZoom(chartRef.current);\n }\n },\n };\n }, [onDataZoom, setTooltipPinnedCoords]);\n\n const { noDataOption } = chartsTheme;\n\n const option: EChartsCoreOption = useMemo(() => {\n // The \"chart\" `noDataVariant` is only used when the `timeSeries` is an\n // empty array because a `null` value will throw an error.\n if (data === null || (data.length === 0 && noDataVariant === 'message')) return noDataOption;\n\n // Utilizes ECharts dataset so raw data is separate from series option style properties\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/\n const dataset: DatasetOption[] = [];\n const isLocalTimeZone = timeZone === 'local';\n data.map((d, index) => {\n const values = d.values.map(([timestamp, value]) => {\n const val: string | number = value === null ? '-' : value; // echarts use '-' to represent null data\n return [isLocalTimeZone ? timestamp : toZonedTime(timestamp, timeZone), val];\n });\n dataset.push({ id: index, source: [...values], dimensions: ['time', 'value'] });\n });\n\n const updatedSeriesMapping =\n enablePinning && pinnedCrosshair !== null ? [...seriesMapping, pinnedCrosshair] : seriesMapping;\n\n const option: EChartsCoreOption = {\n dataset: dataset,\n series: updatedSeriesMapping,\n xAxis: {\n type: 'time',\n min: isLocalTimeZone ? timeScale.startMs : toZonedTime(timeScale.startMs, timeZone),\n max: isLocalTimeZone ? timeScale.endMs : toZonedTime(timeScale.endMs, timeZone),\n axisLabel: {\n hideOverlap: true,\n formatter: getFormattedAxisLabel(timeScale.rangeMs ?? 0),\n },\n axisPointer: {\n snap: false, // important so shared crosshair does not lag\n },\n },\n yAxis: getFormattedAxis(yAxis, format),\n animation: false,\n tooltip: {\n show: true,\n // ECharts tooltip content hidden by default since we use custom tooltip instead.\n // Stacked bar uses ECharts tooltip so subgroup data shows correctly.\n showContent: isStackedBar,\n trigger: isStackedBar ? 'item' : 'axis',\n appendToBody: isStackedBar,\n },\n // https://echarts.apache.org/en/option.html#axisPointer\n axisPointer: {\n type: 'line',\n z: 0, // ensure point symbol shows on top of dashed line\n triggerEmphasis: false, // https://github.com/apache/echarts/issues/18495\n triggerTooltip: false,\n snap: false, // xAxis.axisPointer.snap takes priority\n },\n toolbox: {\n feature: {\n dataZoom: {\n icon: null, // https://stackoverflow.com/a/67684076/17575201\n yAxisIndex: 'none',\n },\n },\n },\n grid,\n };\n\n if (__experimentalEChartsOptionsOverride) {\n return __experimentalEChartsOptionsOverride(option);\n }\n\n return option;\n }, [\n data,\n seriesMapping,\n timeScale,\n yAxis,\n format,\n grid,\n noDataOption,\n __experimentalEChartsOptionsOverride,\n noDataVariant,\n timeZone,\n isStackedBar,\n enablePinning,\n pinnedCrosshair,\n ]);\n\n // Update adjacent charts so tooltip is unpinned when current chart is clicked.\n useEffect(() => {\n // Only allow pinning one tooltip at a time, subsequent tooltip click unpins previous.\n // Multiple tooltips can only be pinned if Ctrl or Cmd key is pressed while clicking.\n const multipleTooltipsPinned = tooltipPinnedCoords !== null && lastTooltipPinnedCoords !== null;\n if (multipleTooltipsPinned) {\n if (!isEqual(lastTooltipPinnedCoords, tooltipPinnedCoords)) {\n setTooltipPinnedCoords(null);\n if (tooltipPinnedCoords !== null && pinnedCrosshair !== null) {\n setPinnedCrosshair(null);\n }\n }\n }\n // tooltipPinnedCoords CANNOT be in dep array or tooltip pinning breaks in the current chart's onClick\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [lastTooltipPinnedCoords, seriesMapping]);\n\n return (\n <Box\n style={{ height }}\n // onContextMenu={(e) => {\n // // TODO: confirm tooltip pinning works correctly on Windows, should e.preventDefault() be added here\n // e.preventDefault(); // Prevent the default behaviour when right clicked\n // }}\n onClick={(e) => {\n // Allows user to opt-in to multi tooltip pinning when Ctrl or Cmd key held down\n const isControlKeyPressed = e.ctrlKey || e.metaKey;\n if (isControlKeyPressed) {\n e.preventDefault();\n }\n\n // Determine where on chart canvas to plot pinned crosshair as markLine.\n const pointInGrid = getPointInGrid(e.nativeEvent.offsetX, e.nativeEvent.offsetY, chartRef.current);\n if (pointInGrid === null) {\n return;\n }\n\n // Pin and unpin when clicking on chart canvas but not tooltip text.\n if (isPinningEnabled && e.target instanceof HTMLCanvasElement) {\n // Pin tooltip and update shared charts context to remember these coordinates.\n const pinnedPos: CursorCoordinates = {\n page: {\n x: e.pageX,\n y: e.pageY,\n },\n client: {\n x: e.clientX,\n y: e.clientY,\n },\n plotCanvas: {\n x: e.nativeEvent.offsetX,\n y: e.nativeEvent.offsetY,\n },\n target: e.target,\n };\n\n setTooltipPinnedCoords((current) => {\n if (current === null) {\n return pinnedPos;\n } else {\n setPinnedCrosshair(null);\n return null;\n }\n });\n\n setPinnedCrosshair((current) => {\n // Only add pinned crosshair line series when there is not one already in seriesMapping.\n if (current === null) {\n const cursorX = pointInGrid[0];\n\n // Only need to loop through first dataset source since getCommonTimeScale ensures xAxis timestamps are consistent\n const firstTimeSeriesValues = data[0]?.values;\n const closestTimestamp = getClosestTimestamp(firstTimeSeriesValues, cursorX);\n\n // Crosshair snaps to nearest timestamp since cursor may be slightly to left or right\n const pinnedCrosshair = merge({}, DEFAULT_PINNED_CROSSHAIR, {\n markLine: {\n data: [\n {\n xAxis: closestTimestamp,\n },\n ],\n },\n } as LineSeriesOption);\n return pinnedCrosshair;\n } else {\n // Clear previously set pinned crosshair\n return null;\n }\n });\n\n if (!isControlKeyPressed) {\n setLastTooltipPinnedCoords(pinnedPos);\n }\n }\n }}\n onMouseDown={(e) => {\n const { clientX } = e;\n setIsDragging(true);\n setStartX(clientX);\n }}\n onMouseMove={(e) => {\n // Allow clicking inside tooltip to copy labels.\n if (!(e.target instanceof HTMLCanvasElement)) {\n return;\n }\n const { clientX } = e;\n if (isDragging) {\n const deltaX = clientX - startX;\n if (deltaX > 0) {\n // Hide tooltip when user drags to zoom.\n setShowTooltip(false);\n }\n }\n }}\n onMouseUp={() => {\n setIsDragging(false);\n setStartX(0);\n setShowTooltip(true);\n }}\n onMouseLeave={() => {\n if (tooltipPinnedCoords === null) {\n setShowTooltip(false);\n }\n if (chartRef.current !== undefined) {\n clearHighlightedSeries(chartRef.current);\n }\n }}\n onMouseEnter={() => {\n setShowTooltip(true);\n if (chartRef.current !== undefined) {\n enableDataZoom(chartRef.current);\n }\n }}\n onDoubleClick={(e) => {\n setTooltipPinnedCoords(null);\n // either dispatch ECharts restore action to return to orig state or allow consumer to define behavior\n if (onDoubleClick === undefined) {\n if (chartRef.current !== undefined) {\n restoreChart(chartRef.current);\n }\n } else {\n onDoubleClick(e);\n }\n }}\n >\n {/* Allows overrides prop to hide custom tooltip and use the ECharts option.tooltip instead */}\n {showTooltip === true &&\n (option.tooltip as TooltipComponentOption)?.showContent === false &&\n tooltipConfig.hidden !== true && (\n <TimeChartTooltip\n containerId={chartsTheme.tooltipPortalContainerId}\n chartRef={chartRef}\n data={data}\n seriesMapping={seriesMapping}\n wrapLabels={tooltipConfig.wrapLabels}\n enablePinning={isPinningEnabled}\n pinnedPos={tooltipPinnedCoords}\n format={format}\n onUnpinClick={() => {\n // Unpins tooltip when clicking Pin icon in TooltipHeader.\n setTooltipPinnedCoords(null);\n // Clear previously set pinned crosshair.\n setPinnedCrosshair(null);\n }}\n />\n )}\n <EChart\n sx={{\n width: '100%',\n height: '100%',\n }}\n option={option}\n theme={chartsTheme.echartsTheme}\n onEvents={handleEvents}\n _instance={chartRef}\n syncGroup={syncGroup}\n />\n </Box>\n );\n});\n"],"names":["forwardRef","useEffect","useImperativeHandle","useMemo","useRef","useState","Box","merge","isEqual","toZonedTime","getCommonTimeScale","use","LineChart","EChartsLineChart","BarChart","EChartsBarChart","GridComponent","DatasetComponent","DataZoomComponent","MarkAreaComponent","MarkLineComponent","MarkPointComponent","TitleComponent","ToolboxComponent","TooltipComponent","CanvasRenderer","clearHighlightedSeries","DEFAULT_PINNED_CROSSHAIR","DEFAULT_TOOLTIP_CONFIG","EChart","enableDataZoom","getClosestTimestamp","getFormattedAxis","getFormattedAxisLabel","getPointInGrid","restoreChart","TimeChartTooltip","useChartsContext","useTimeZone","TimeSeriesChartBase","TimeChart","height","data","seriesMapping","timeScale","timeScaleProp","yAxis","format","grid","isStackedBar","tooltipConfig","noDataVariant","syncGroup","onDataZoom","onDoubleClick","__experimentalEChartsOptionsOverride","ref","chartsTheme","enablePinning","lastTooltipPinnedCoords","setLastTooltipPinnedCoords","isPinningEnabled","chartRef","showTooltip","setShowTooltip","tooltipPinnedCoords","setTooltipPinnedCoords","pinnedCrosshair","setPinnedCrosshair","isDragging","setIsDragging","startX","setStartX","timeZone","undefined","commonTimeScale","today","Date","pastDate","setFullYear","getFullYear","todayMs","getTime","pastDateMs","startMs","endMs","stepMs","rangeMs","highlightSeries","name","current","dispatchAction","type","seriesId","handleEvents","datazoom","params","setTimeout","batch","xAxisStartValue","startValue","xAxisEndValue","endValue","zoomEvent","start","end","finished","noDataOption","option","length","dataset","isLocalTimeZone","map","d","index","values","timestamp","value","val","push","id","source","dimensions","updatedSeriesMapping","series","xAxis","min","max","axisLabel","hideOverlap","formatter","axisPointer","snap","animation","tooltip","show","showContent","trigger","appendToBody","z","triggerEmphasis","triggerTooltip","toolbox","feature","dataZoom","icon","yAxisIndex","multipleTooltipsPinned","style","onClick","e","isControlKeyPressed","ctrlKey","metaKey","preventDefault","pointInGrid","nativeEvent","offsetX","offsetY","target","HTMLCanvasElement","pinnedPos","page","x","pageX","y","pageY","client","clientX","clientY","plotCanvas","cursorX","firstTimeSeriesValues","closestTimestamp","markLine","onMouseDown","onMouseMove","deltaX","onMouseUp","onMouseLeave","onMouseEnter","hidden","containerId","tooltipPortalContainerId","wrapLabels","onUnpinClick","sx","width","theme","echartsTheme","onEvents","_instance"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TimeSeriesChartPanel.d.ts","sourceRoot":"","sources":["../../src/TimeSeriesChartPanel.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAA6B,MAAM,OAAO,CAAC;AAIhE,OAAO,EAQL,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,UAAU,EAIX,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"TimeSeriesChartPanel.d.ts","sourceRoot":"","sources":["../../src/TimeSeriesChartPanel.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAE,YAAY,EAA6B,MAAM,OAAO,CAAC;AAIhE,OAAO,EAQL,cAAc,EACf,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAEL,UAAU,EAIX,MAAM,2BAA2B,CAAC;AAgBnC,OAAO,EACL,sBAAsB,EAKvB,MAAM,2BAA2B,CAAC;AAWnC,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,sBAAsB,EAAE,cAAc,CAAC,CAAC;AAWtF,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GAAG,IAAI,CAqVrF"}
|
|
@@ -16,10 +16,11 @@ import { Box, useTheme } from '@mui/material';
|
|
|
16
16
|
import merge from 'lodash/merge';
|
|
17
17
|
import { getTimeSeriesValues, DEFAULT_LEGEND, getCalculations, formatValue } from '@perses-dev/core';
|
|
18
18
|
import { LEGEND_VALUE_CONFIG, useTimeRange, validateLegendSpec, legendValues } from '@perses-dev/plugin-system';
|
|
19
|
-
import { YAxisLabel, useChartsTheme, ContentWithLegend, useId,
|
|
19
|
+
import { YAxisLabel, useChartsTheme, ContentWithLegend, useId, DEFAULT_TOOLTIP_CONFIG } from '@perses-dev/components';
|
|
20
20
|
import { DEFAULT_FORMAT, DEFAULT_VISUAL, THRESHOLD_PLOT_INTERVAL } from './time-series-chart-model';
|
|
21
21
|
import { getTimeSeries, getCommonTimeScaleForQueries, convertPanelYAxis, getThresholdSeries, convertPercentThreshold } from './utils/data-transform';
|
|
22
22
|
import { getSeriesColor } from './utils/palette-gen';
|
|
23
|
+
import { TimeSeriesChartBase } from './TimeSeriesChartBase';
|
|
23
24
|
// Using an "ALL" value to handle the case on first loading the chart where we
|
|
24
25
|
// want to select all, but do not want all of the legend items to be visually highlighted.
|
|
25
26
|
// This helps us differentiate those cases more clearly instead of inferring it
|
|
@@ -317,7 +318,7 @@ export function TimeSeriesChartPanel(props) {
|
|
|
317
318
|
name: yAxis.label,
|
|
318
319
|
height: height
|
|
319
320
|
}),
|
|
320
|
-
/*#__PURE__*/ _jsx(
|
|
321
|
+
/*#__PURE__*/ _jsx(TimeSeriesChartBase, {
|
|
321
322
|
ref: chartRef,
|
|
322
323
|
height: height,
|
|
323
324
|
data: timeChartData,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/TimeSeriesChartPanel.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { ReactElement, useMemo, useRef, useState } from 'react';\nimport { Box, useTheme } from '@mui/material';\nimport type { GridComponentOption } from 'echarts';\nimport merge from 'lodash/merge';\nimport {\n getTimeSeriesValues,\n DEFAULT_LEGEND,\n getCalculations,\n formatValue,\n StepOptions,\n TimeSeries,\n TimeSeriesValueTuple,\n TimeSeriesData,\n} from '@perses-dev/core';\nimport {\n LEGEND_VALUE_CONFIG,\n PanelProps,\n useTimeRange,\n validateLegendSpec,\n legendValues,\n} from '@perses-dev/plugin-system';\nimport {\n ChartInstance,\n YAxisLabel,\n ZoomEventData,\n useChartsTheme,\n SelectedLegendItemState,\n ContentWithLegend,\n TableColumnConfig,\n LegendItem,\n LegendProps,\n useId,\n TimeChart,\n TimeChartSeriesMapping,\n TooltipConfig,\n DEFAULT_TOOLTIP_CONFIG,\n} from '@perses-dev/components';\nimport {\n TimeSeriesChartOptions,\n DEFAULT_FORMAT,\n DEFAULT_VISUAL,\n THRESHOLD_PLOT_INTERVAL,\n QuerySettingsOptions,\n} from './time-series-chart-model';\nimport {\n getTimeSeries,\n getCommonTimeScaleForQueries,\n convertPanelYAxis,\n getThresholdSeries,\n convertPercentThreshold,\n} from './utils/data-transform';\nimport { getSeriesColor } from './utils/palette-gen';\n\nexport type TimeSeriesChartProps = PanelProps<TimeSeriesChartOptions, TimeSeriesData>;\n\n// Using an \"ALL\" value to handle the case on first loading the chart where we\n// want to select all, but do not want all of the legend items to be visually highlighted.\n// This helps us differentiate those cases more clearly instead of inferring it\n// based on the state of the data. This also helps us avoid some coding\n// complexity around initializing a full record for the initial load that would\n// currently require significantly more refactoring of this component.\n// TODO: simplify this if we switch the list-based legend UI to use checkboxes,\n// where we *would* want to visually select all items in this case.\n\nexport function TimeSeriesChartPanel(props: TimeSeriesChartProps): ReactElement | null {\n const {\n spec: { thresholds, yAxis, tooltip, querySettings: querySettingsList },\n contentDimensions,\n queryResults,\n } = props;\n const chartsTheme = useChartsTheme();\n const muiTheme = useTheme();\n const chartId = useId('time-series-panel');\n\n const chartRef = useRef<ChartInstance>(null);\n\n // ECharts theme comes from ChartsProvider, more info: https://echarts.apache.org/en/option.html#color\n // Colors are manually applied since our legend and tooltip are built custom with React.\n const categoricalPalette = chartsTheme.echartsTheme.color;\n\n // TODO: consider refactoring how the layout/spacing/alignment are calculated\n // the next time significant changes are made to the time series panel (e.g.\n // when making improvements to the legend to more closely match designs).\n // This may also want to include moving some of this logic down to the shared,\n // embeddable components.\n const contentPadding = chartsTheme.container.padding.default;\n const adjustedContentDimensions: typeof contentDimensions = contentDimensions\n ? {\n width: contentDimensions.width - contentPadding * 2,\n height: contentDimensions.height - contentPadding * 2,\n }\n : undefined;\n\n // populate default 'position' and other future properties\n const legend = useMemo(() => {\n return props.spec.legend && validateLegendSpec(props.spec.legend)\n ? merge({}, DEFAULT_LEGEND, props.spec.legend)\n : undefined;\n }, [props.spec.legend]);\n\n // TODO: add support for y_axis_alt.format\n const format = props.spec.yAxis?.format ?? DEFAULT_FORMAT;\n\n // ensures there are fallbacks for unset properties since most\n // users should not need to customize visual display\n const visual = useMemo(() => {\n return merge({}, DEFAULT_VISUAL, props.spec.visual);\n }, [props.spec.visual]);\n\n // convert Perses dashboard format to be ECharts compatible\n const echartsYAxis = useMemo(() => {\n return convertPanelYAxis(yAxis);\n }, [yAxis]);\n\n const [selectedLegendItems, setSelectedLegendItems] = useState<SelectedLegendItemState>('ALL');\n const [legendSorting, setLegendSorting] = useState<NonNullable<LegendProps['tableProps']>['sorting']>();\n\n const { setTimeRange } = useTimeRange();\n\n // Populate series data based on query results\n const { timeScale, timeChartData, timeSeriesMapping, legendItems } = useMemo(() => {\n const timeScale = getCommonTimeScaleForQueries(queryResults);\n if (timeScale === undefined) {\n return {\n timeChartData: [],\n timeSeriesMapping: [],\n };\n }\n\n const legendItems: LegendItem[] = [];\n\n // Utilizes ECharts dataset so raw data is separate from series option style properties\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/\n const timeChartData: TimeSeries[] = [];\n const timeSeriesMapping: TimeChartSeriesMapping = [];\n\n // Index is counted across multiple queries which ensures the categorical color palette does not reset for every query\n let seriesIndex = 0;\n\n // Mapping of each set of query results to be ECharts option compatible\n // TODO: Look into performance optimizations and moving parts of mapping to the lower level chart\n for (let queryIndex = 0; queryIndex < queryResults.length; queryIndex++) {\n const result = queryResults[queryIndex];\n\n // Retrieve querySettings for this query, if exists.\n // queries & querySettings indices do not necessarily match, so we have to check the tail value of the $ref attribute\n let querySettings: QuerySettingsOptions | undefined;\n for (const item of querySettingsList ?? []) {\n if (item.queryIndex === queryIndex) {\n querySettings = item;\n // We don't break the loop here just in case there are multiple querySettings defined for the\n // same queryIndex, because in that case we want the last one to take precedence.\n }\n }\n\n if (result) {\n for (let i = 0; i < result.data.series.length; i++) {\n const timeSeries: TimeSeries | undefined = result.data.series[i];\n if (timeSeries === undefined) {\n return { timeChartData: [], timeSeriesMapping: [], legendItems: [] };\n }\n\n // Format is determined by seriesNameFormat in query spec\n const formattedSeriesName = timeSeries.formattedName ?? timeSeries.name;\n\n // Color is used for line, tooltip, and legend\n const seriesColor = getSeriesColor({\n // ECharts type for color is not always an array but it is always an array in ChartsProvider\n categoricalPalette: categoricalPalette as string[],\n visual,\n muiPrimaryColor: muiTheme.palette.primary.main,\n seriesName: formattedSeriesName,\n seriesIndex,\n querySettings: querySettings,\n queryHasMultipleResults: (queryResults[queryIndex]?.data?.series?.length ?? 0) > 1,\n });\n\n // We add a unique id for the chart to disambiguate items across charts\n // when there are multiple on the page.\n const seriesId = chartId + timeSeries.name + seriesIndex;\n\n const legendCalculations = legend?.values ? getCalculations(timeSeries.values, legend.values) : undefined;\n\n // When we initially load the chart, we want to show all series, but\n // DO NOT want to visualy highlight all the items in the legend.\n const isSelectAll = selectedLegendItems === 'ALL';\n const isSelected = !isSelectAll && !!selectedLegendItems[seriesId];\n const showTimeSeries = isSelected || isSelectAll;\n\n if (showTimeSeries) {\n // Use timeChartData.length to ensure the data that is passed into the tooltip accounts for\n // which legend items are selected. This must happen before timeChartData.push to avoid an\n // off-by-one error, seriesIndex cannot be used since it's needed to cycle through palette\n const datasetIndex = timeChartData.length;\n\n // Each series is stored as a separate dataset source.\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/#how-to-reference-several-datasets\n timeSeriesMapping.push(\n getTimeSeries(seriesId, datasetIndex, formattedSeriesName, visual, timeScale, seriesColor)\n );\n\n timeChartData.push({\n name: formattedSeriesName,\n values: getTimeSeriesValues(timeSeries, timeScale),\n });\n }\n\n if (legend && legendItems) {\n legendItems.push({\n id: seriesId, // Avoids duplicate key console errors when there are duplicate series names\n label: formattedSeriesName,\n color: seriesColor,\n data: legendCalculations,\n });\n }\n\n // Used for repeating colors in Categorical palette\n seriesIndex++;\n }\n }\n }\n\n if (thresholds && thresholds.steps) {\n // Convert how thresholds are defined in the panel spec to valid ECharts 'line' series.\n // These are styled with predefined colors and a dashed style to look different than series from query results.\n // Regular series are used instead of markLines since thresholds currently show in our React TimeSeriesTooltip.\n const thresholdsColors = chartsTheme.thresholds;\n const defaultThresholdColor = thresholds.defaultColor ?? thresholdsColors.defaultColor;\n thresholds.steps.forEach((step: StepOptions, index: number) => {\n const stepPaletteColor = thresholdsColors.palette[index] ?? defaultThresholdColor;\n const thresholdLineColor = step.color ?? stepPaletteColor;\n const stepOption: StepOptions = {\n color: thresholdLineColor,\n value:\n // yAxis is passed here since it corresponds to dashboard JSON instead of the already converted ECharts yAxis\n thresholds.mode === 'percent'\n ? convertPercentThreshold(step.value, timeChartData, yAxis?.max, yAxis?.min)\n : step.value,\n };\n const thresholdName = step.name ?? `Threshold ${index + 1}`;\n\n // Generates array of [time, step.value] where time ranges from timescale.startMs to timescale.endMs with an interval of 15s\n const thresholdTimeValueTuple: TimeSeriesValueTuple[] = [];\n let currentTimestamp = timeScale.startMs;\n while (currentTimestamp <= timeScale.endMs) {\n thresholdTimeValueTuple.push([currentTimestamp, stepOption.value]);\n // Used to plot fake thresholds datapoints so correct nearby threshold series shows in tooltip without flicker\n currentTimestamp += 1000 * THRESHOLD_PLOT_INTERVAL;\n }\n\n timeChartData.push({\n name: thresholdName,\n values: thresholdTimeValueTuple,\n });\n timeSeriesMapping.push(getThresholdSeries(thresholdName, stepOption, seriesIndex));\n seriesIndex++;\n });\n }\n\n return {\n timeScale,\n timeChartData,\n timeSeriesMapping,\n legendItems,\n };\n }, [\n queryResults,\n thresholds,\n selectedLegendItems,\n legend,\n visual,\n querySettingsList,\n yAxis?.max,\n yAxis?.min,\n categoricalPalette,\n chartId,\n chartsTheme.thresholds,\n muiTheme.palette.primary.main,\n ]);\n\n // Translate the legend values into columns for the table legend.\n const legendColumns = useMemo(() => {\n if (!legend?.values) {\n return [];\n }\n\n // Iterating the predefined list of possible values to retain a specific\n // intended order of values.\n return legendValues.reduce(\n (columns, legendValue) => {\n const legendConfig = LEGEND_VALUE_CONFIG[legendValue];\n\n if (legendConfig && legend?.values?.includes(legendValue)) {\n columns.push({\n accessorKey: `data.${legendValue}`,\n header: legendConfig.label,\n headerDescription: legendConfig.description,\n // Intentionally hardcoding a column width to start based on discussions\n // with design around keeping this simple to start. This may need\n // revisiting in the future to handle edge cases with very large values.\n width: 72,\n align: 'right',\n cell: ({ getValue }) => {\n const cellValue = getValue();\n return typeof cellValue === 'number' && format ? formatValue(cellValue, format) : cellValue;\n },\n cellDescription: true,\n enableSorting: true,\n });\n }\n\n return columns;\n },\n [] as Array<TableColumnConfig<LegendItem>>\n );\n }, [legend?.values, format]);\n\n if (adjustedContentDimensions === undefined) {\n return null;\n }\n\n // override default spacing, see: https://echarts.apache.org/en/option.html#grid\n const gridLeft = yAxis && yAxis.label ? 30 : 20;\n const gridOverrides: GridComponentOption = {\n left: !echartsYAxis.show ? 0 : gridLeft,\n right: 20,\n bottom: 0,\n };\n\n const handleDataZoom = (event: ZoomEventData): void => {\n // TODO: add ECharts transition animation on zoom\n setTimeRange({ start: new Date(event.start), end: new Date(event.end) });\n };\n\n // Used to opt in to ECharts trigger item which show subgroup data accurately\n const isStackedBar = visual.display === 'bar' && visual.stack === 'all';\n\n // Turn on tooltip pinning by default but opt out for stacked bar or if explicitly set in tooltip panel spec\n let enablePinning = true;\n if (isStackedBar) {\n enablePinning = false;\n } else if (tooltip?.enablePinning !== undefined) {\n enablePinning = tooltip.enablePinning;\n }\n const tooltipConfig: TooltipConfig = {\n ...DEFAULT_TOOLTIP_CONFIG,\n enablePinning,\n };\n\n return (\n <Box sx={{ padding: `${contentPadding}px` }}>\n <ContentWithLegend\n width={adjustedContentDimensions.width}\n height={adjustedContentDimensions.height}\n // Making this small enough that the medium size doesn't get\n // responsive-handling-ed away when in the panel options editor.\n minChildrenHeight={50}\n legendSize={legend?.size}\n legendProps={\n legend && {\n options: legend,\n data: legendItems || [],\n selectedItems: selectedLegendItems,\n onSelectedItemsChange: setSelectedLegendItems,\n tableProps: {\n columns: legendColumns,\n sorting: legendSorting,\n onSortingChange: setLegendSorting,\n },\n onItemMouseOver: (e, { id }): void => {\n chartRef.current?.highlightSeries({ name: id });\n },\n onItemMouseOut: (): void => {\n chartRef.current?.clearHighlightedSeries();\n },\n }\n }\n >\n {({ height, width }) => {\n return (\n <Box style={{ height, width }}>\n {yAxis && yAxis.show && yAxis.label && <YAxisLabel name={yAxis.label} height={height} />}\n <TimeChart\n ref={chartRef}\n height={height}\n data={timeChartData}\n seriesMapping={timeSeriesMapping}\n timeScale={timeScale}\n yAxis={echartsYAxis}\n format={format}\n grid={gridOverrides}\n isStackedBar={isStackedBar}\n tooltipConfig={tooltipConfig}\n syncGroup=\"default-panel-group\" // TODO: make configurable from dashboard settings and per panel-group overrides\n onDataZoom={handleDataZoom}\n // Show an empty chart when there is no data because the user unselected all items in\n // the legend. Otherwise, show a \"no data\" message.\n noDataVariant={!timeChartData.length && legendItems && legendItems.length > 0 ? 'chart' : 'message'}\n />\n </Box>\n );\n }}\n </ContentWithLegend>\n </Box>\n );\n}\n"],"names":["useMemo","useRef","useState","Box","useTheme","merge","getTimeSeriesValues","DEFAULT_LEGEND","getCalculations","formatValue","LEGEND_VALUE_CONFIG","useTimeRange","validateLegendSpec","legendValues","YAxisLabel","useChartsTheme","ContentWithLegend","useId","TimeChart","DEFAULT_TOOLTIP_CONFIG","DEFAULT_FORMAT","DEFAULT_VISUAL","THRESHOLD_PLOT_INTERVAL","getTimeSeries","getCommonTimeScaleForQueries","convertPanelYAxis","getThresholdSeries","convertPercentThreshold","getSeriesColor","TimeSeriesChartPanel","props","spec","thresholds","yAxis","tooltip","querySettings","querySettingsList","contentDimensions","queryResults","chartsTheme","muiTheme","chartId","chartRef","categoricalPalette","echartsTheme","color","contentPadding","container","padding","default","adjustedContentDimensions","width","height","undefined","legend","format","visual","echartsYAxis","selectedLegendItems","setSelectedLegendItems","legendSorting","setLegendSorting","setTimeRange","timeScale","timeChartData","timeSeriesMapping","legendItems","seriesIndex","queryIndex","length","result","item","i","data","series","timeSeries","formattedSeriesName","formattedName","name","seriesColor","muiPrimaryColor","palette","primary","main","seriesName","queryHasMultipleResults","seriesId","legendCalculations","values","isSelectAll","isSelected","showTimeSeries","datasetIndex","push","id","label","steps","thresholdsColors","defaultThresholdColor","defaultColor","forEach","step","index","stepPaletteColor","thresholdLineColor","stepOption","value","mode","max","min","thresholdName","thresholdTimeValueTuple","currentTimestamp","startMs","endMs","legendColumns","reduce","columns","legendValue","legendConfig","includes","accessorKey","header","headerDescription","description","align","cell","getValue","cellValue","cellDescription","enableSorting","gridLeft","gridOverrides","left","show","right","bottom","handleDataZoom","event","start","Date","end","isStackedBar","display","stack","enablePinning","tooltipConfig","sx","minChildrenHeight","legendSize","size","legendProps","options","selectedItems","onSelectedItemsChange","tableProps","sorting","onSortingChange","onItemMouseOver","e","current","highlightSeries","onItemMouseOut","clearHighlightedSeries","style","ref","seriesMapping","grid","syncGroup","onDataZoom","noDataVariant"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/TimeSeriesChartPanel.tsx"],"sourcesContent":["// Copyright 2023 The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { ReactElement, useMemo, useRef, useState } from 'react';\nimport { Box, useTheme } from '@mui/material';\nimport type { GridComponentOption } from 'echarts';\nimport merge from 'lodash/merge';\nimport {\n getTimeSeriesValues,\n DEFAULT_LEGEND,\n getCalculations,\n formatValue,\n StepOptions,\n TimeSeries,\n TimeSeriesValueTuple,\n TimeSeriesData,\n} from '@perses-dev/core';\nimport {\n LEGEND_VALUE_CONFIG,\n PanelProps,\n useTimeRange,\n validateLegendSpec,\n legendValues,\n} from '@perses-dev/plugin-system';\nimport {\n ChartInstance,\n YAxisLabel,\n ZoomEventData,\n useChartsTheme,\n SelectedLegendItemState,\n ContentWithLegend,\n TableColumnConfig,\n LegendItem,\n LegendProps,\n useId,\n TooltipConfig,\n DEFAULT_TOOLTIP_CONFIG,\n TimeChartSeriesMapping,\n} from '@perses-dev/components';\nimport {\n TimeSeriesChartOptions,\n DEFAULT_FORMAT,\n DEFAULT_VISUAL,\n THRESHOLD_PLOT_INTERVAL,\n QuerySettingsOptions,\n} from './time-series-chart-model';\nimport {\n getTimeSeries,\n getCommonTimeScaleForQueries,\n convertPanelYAxis,\n getThresholdSeries,\n convertPercentThreshold,\n} from './utils/data-transform';\nimport { getSeriesColor } from './utils/palette-gen';\nimport { TimeSeriesChartBase } from './TimeSeriesChartBase';\n\nexport type TimeSeriesChartProps = PanelProps<TimeSeriesChartOptions, TimeSeriesData>;\n\n// Using an \"ALL\" value to handle the case on first loading the chart where we\n// want to select all, but do not want all of the legend items to be visually highlighted.\n// This helps us differentiate those cases more clearly instead of inferring it\n// based on the state of the data. This also helps us avoid some coding\n// complexity around initializing a full record for the initial load that would\n// currently require significantly more refactoring of this component.\n// TODO: simplify this if we switch the list-based legend UI to use checkboxes,\n// where we *would* want to visually select all items in this case.\n\nexport function TimeSeriesChartPanel(props: TimeSeriesChartProps): ReactElement | null {\n const {\n spec: { thresholds, yAxis, tooltip, querySettings: querySettingsList },\n contentDimensions,\n queryResults,\n } = props;\n const chartsTheme = useChartsTheme();\n const muiTheme = useTheme();\n const chartId = useId('time-series-panel');\n\n const chartRef = useRef<ChartInstance>(null);\n\n // ECharts theme comes from ChartsProvider, more info: https://echarts.apache.org/en/option.html#color\n // Colors are manually applied since our legend and tooltip are built custom with React.\n const categoricalPalette = chartsTheme.echartsTheme.color;\n\n // TODO: consider refactoring how the layout/spacing/alignment are calculated\n // the next time significant changes are made to the time series panel (e.g.\n // when making improvements to the legend to more closely match designs).\n // This may also want to include moving some of this logic down to the shared,\n // embeddable components.\n const contentPadding = chartsTheme.container.padding.default;\n const adjustedContentDimensions: typeof contentDimensions = contentDimensions\n ? {\n width: contentDimensions.width - contentPadding * 2,\n height: contentDimensions.height - contentPadding * 2,\n }\n : undefined;\n\n // populate default 'position' and other future properties\n const legend = useMemo(() => {\n return props.spec.legend && validateLegendSpec(props.spec.legend)\n ? merge({}, DEFAULT_LEGEND, props.spec.legend)\n : undefined;\n }, [props.spec.legend]);\n\n // TODO: add support for y_axis_alt.format\n const format = props.spec.yAxis?.format ?? DEFAULT_FORMAT;\n\n // ensures there are fallbacks for unset properties since most\n // users should not need to customize visual display\n const visual = useMemo(() => {\n return merge({}, DEFAULT_VISUAL, props.spec.visual);\n }, [props.spec.visual]);\n\n // convert Perses dashboard format to be ECharts compatible\n const echartsYAxis = useMemo(() => {\n return convertPanelYAxis(yAxis);\n }, [yAxis]);\n\n const [selectedLegendItems, setSelectedLegendItems] = useState<SelectedLegendItemState>('ALL');\n const [legendSorting, setLegendSorting] = useState<NonNullable<LegendProps['tableProps']>['sorting']>();\n\n const { setTimeRange } = useTimeRange();\n\n // Populate series data based on query results\n const { timeScale, timeChartData, timeSeriesMapping, legendItems } = useMemo(() => {\n const timeScale = getCommonTimeScaleForQueries(queryResults);\n if (timeScale === undefined) {\n return {\n timeChartData: [],\n timeSeriesMapping: [],\n };\n }\n\n const legendItems: LegendItem[] = [];\n\n // Utilizes ECharts dataset so raw data is separate from series option style properties\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/\n const timeChartData: TimeSeries[] = [];\n const timeSeriesMapping: TimeChartSeriesMapping = [];\n\n // Index is counted across multiple queries which ensures the categorical color palette does not reset for every query\n let seriesIndex = 0;\n\n // Mapping of each set of query results to be ECharts option compatible\n // TODO: Look into performance optimizations and moving parts of mapping to the lower level chart\n for (let queryIndex = 0; queryIndex < queryResults.length; queryIndex++) {\n const result = queryResults[queryIndex];\n\n // Retrieve querySettings for this query, if exists.\n // queries & querySettings indices do not necessarily match, so we have to check the tail value of the $ref attribute\n let querySettings: QuerySettingsOptions | undefined;\n for (const item of querySettingsList ?? []) {\n if (item.queryIndex === queryIndex) {\n querySettings = item;\n // We don't break the loop here just in case there are multiple querySettings defined for the\n // same queryIndex, because in that case we want the last one to take precedence.\n }\n }\n\n if (result) {\n for (let i = 0; i < result.data.series.length; i++) {\n const timeSeries: TimeSeries | undefined = result.data.series[i];\n if (timeSeries === undefined) {\n return { timeChartData: [], timeSeriesMapping: [], legendItems: [] };\n }\n\n // Format is determined by seriesNameFormat in query spec\n const formattedSeriesName = timeSeries.formattedName ?? timeSeries.name;\n\n // Color is used for line, tooltip, and legend\n const seriesColor = getSeriesColor({\n // ECharts type for color is not always an array but it is always an array in ChartsProvider\n categoricalPalette: categoricalPalette as string[],\n visual,\n muiPrimaryColor: muiTheme.palette.primary.main,\n seriesName: formattedSeriesName,\n seriesIndex,\n querySettings: querySettings,\n queryHasMultipleResults: (queryResults[queryIndex]?.data?.series?.length ?? 0) > 1,\n });\n\n // We add a unique id for the chart to disambiguate items across charts\n // when there are multiple on the page.\n const seriesId = chartId + timeSeries.name + seriesIndex;\n\n const legendCalculations = legend?.values ? getCalculations(timeSeries.values, legend.values) : undefined;\n\n // When we initially load the chart, we want to show all series, but\n // DO NOT want to visualy highlight all the items in the legend.\n const isSelectAll = selectedLegendItems === 'ALL';\n const isSelected = !isSelectAll && !!selectedLegendItems[seriesId];\n const showTimeSeries = isSelected || isSelectAll;\n\n if (showTimeSeries) {\n // Use timeChartData.length to ensure the data that is passed into the tooltip accounts for\n // which legend items are selected. This must happen before timeChartData.push to avoid an\n // off-by-one error, seriesIndex cannot be used since it's needed to cycle through palette\n const datasetIndex = timeChartData.length;\n\n // Each series is stored as a separate dataset source.\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/#how-to-reference-several-datasets\n timeSeriesMapping.push(\n getTimeSeries(seriesId, datasetIndex, formattedSeriesName, visual, timeScale, seriesColor)\n );\n\n timeChartData.push({\n name: formattedSeriesName,\n values: getTimeSeriesValues(timeSeries, timeScale),\n });\n }\n\n if (legend && legendItems) {\n legendItems.push({\n id: seriesId, // Avoids duplicate key console errors when there are duplicate series names\n label: formattedSeriesName,\n color: seriesColor,\n data: legendCalculations,\n });\n }\n\n // Used for repeating colors in Categorical palette\n seriesIndex++;\n }\n }\n }\n\n if (thresholds && thresholds.steps) {\n // Convert how thresholds are defined in the panel spec to valid ECharts 'line' series.\n // These are styled with predefined colors and a dashed style to look different than series from query results.\n // Regular series are used instead of markLines since thresholds currently show in our React TimeSeriesTooltip.\n const thresholdsColors = chartsTheme.thresholds;\n const defaultThresholdColor = thresholds.defaultColor ?? thresholdsColors.defaultColor;\n thresholds.steps.forEach((step: StepOptions, index: number) => {\n const stepPaletteColor = thresholdsColors.palette[index] ?? defaultThresholdColor;\n const thresholdLineColor = step.color ?? stepPaletteColor;\n const stepOption: StepOptions = {\n color: thresholdLineColor,\n value:\n // yAxis is passed here since it corresponds to dashboard JSON instead of the already converted ECharts yAxis\n thresholds.mode === 'percent'\n ? convertPercentThreshold(step.value, timeChartData, yAxis?.max, yAxis?.min)\n : step.value,\n };\n const thresholdName = step.name ?? `Threshold ${index + 1}`;\n\n // Generates array of [time, step.value] where time ranges from timescale.startMs to timescale.endMs with an interval of 15s\n const thresholdTimeValueTuple: TimeSeriesValueTuple[] = [];\n let currentTimestamp = timeScale.startMs;\n while (currentTimestamp <= timeScale.endMs) {\n thresholdTimeValueTuple.push([currentTimestamp, stepOption.value]);\n // Used to plot fake thresholds datapoints so correct nearby threshold series shows in tooltip without flicker\n currentTimestamp += 1000 * THRESHOLD_PLOT_INTERVAL;\n }\n\n timeChartData.push({\n name: thresholdName,\n values: thresholdTimeValueTuple,\n });\n timeSeriesMapping.push(getThresholdSeries(thresholdName, stepOption, seriesIndex));\n seriesIndex++;\n });\n }\n\n return {\n timeScale,\n timeChartData,\n timeSeriesMapping,\n legendItems,\n };\n }, [\n queryResults,\n thresholds,\n selectedLegendItems,\n legend,\n visual,\n querySettingsList,\n yAxis?.max,\n yAxis?.min,\n categoricalPalette,\n chartId,\n chartsTheme.thresholds,\n muiTheme.palette.primary.main,\n ]);\n\n // Translate the legend values into columns for the table legend.\n const legendColumns = useMemo(() => {\n if (!legend?.values) {\n return [];\n }\n\n // Iterating the predefined list of possible values to retain a specific\n // intended order of values.\n return legendValues.reduce(\n (columns, legendValue) => {\n const legendConfig = LEGEND_VALUE_CONFIG[legendValue];\n\n if (legendConfig && legend?.values?.includes(legendValue)) {\n columns.push({\n accessorKey: `data.${legendValue}`,\n header: legendConfig.label,\n headerDescription: legendConfig.description,\n // Intentionally hardcoding a column width to start based on discussions\n // with design around keeping this simple to start. This may need\n // revisiting in the future to handle edge cases with very large values.\n width: 72,\n align: 'right',\n cell: ({ getValue }) => {\n const cellValue = getValue();\n return typeof cellValue === 'number' && format ? formatValue(cellValue, format) : cellValue;\n },\n cellDescription: true,\n enableSorting: true,\n });\n }\n\n return columns;\n },\n [] as Array<TableColumnConfig<LegendItem>>\n );\n }, [legend?.values, format]);\n\n if (adjustedContentDimensions === undefined) {\n return null;\n }\n\n // override default spacing, see: https://echarts.apache.org/en/option.html#grid\n const gridLeft = yAxis && yAxis.label ? 30 : 20;\n const gridOverrides: GridComponentOption = {\n left: !echartsYAxis.show ? 0 : gridLeft,\n right: 20,\n bottom: 0,\n };\n\n const handleDataZoom = (event: ZoomEventData): void => {\n // TODO: add ECharts transition animation on zoom\n setTimeRange({ start: new Date(event.start), end: new Date(event.end) });\n };\n\n // Used to opt in to ECharts trigger item which show subgroup data accurately\n const isStackedBar = visual.display === 'bar' && visual.stack === 'all';\n\n // Turn on tooltip pinning by default but opt out for stacked bar or if explicitly set in tooltip panel spec\n let enablePinning = true;\n if (isStackedBar) {\n enablePinning = false;\n } else if (tooltip?.enablePinning !== undefined) {\n enablePinning = tooltip.enablePinning;\n }\n const tooltipConfig: TooltipConfig = {\n ...DEFAULT_TOOLTIP_CONFIG,\n enablePinning,\n };\n\n return (\n <Box sx={{ padding: `${contentPadding}px` }}>\n <ContentWithLegend\n width={adjustedContentDimensions.width}\n height={adjustedContentDimensions.height}\n // Making this small enough that the medium size doesn't get\n // responsive-handling-ed away when in the panel options editor.\n minChildrenHeight={50}\n legendSize={legend?.size}\n legendProps={\n legend && {\n options: legend,\n data: legendItems || [],\n selectedItems: selectedLegendItems,\n onSelectedItemsChange: setSelectedLegendItems,\n tableProps: {\n columns: legendColumns,\n sorting: legendSorting,\n onSortingChange: setLegendSorting,\n },\n onItemMouseOver: (e, { id }): void => {\n chartRef.current?.highlightSeries({ name: id });\n },\n onItemMouseOut: (): void => {\n chartRef.current?.clearHighlightedSeries();\n },\n }\n }\n >\n {({ height, width }) => {\n return (\n <Box style={{ height, width }}>\n {yAxis && yAxis.show && yAxis.label && <YAxisLabel name={yAxis.label} height={height} />}\n <TimeSeriesChartBase\n ref={chartRef}\n height={height}\n data={timeChartData}\n seriesMapping={timeSeriesMapping}\n timeScale={timeScale}\n yAxis={echartsYAxis}\n format={format}\n grid={gridOverrides}\n isStackedBar={isStackedBar}\n tooltipConfig={tooltipConfig}\n syncGroup=\"default-panel-group\" // TODO: make configurable from dashboard settings and per panel-group overrides\n onDataZoom={handleDataZoom}\n // Show an empty chart when there is no data because the user unselected all items in\n // the legend. Otherwise, show a \"no data\" message.\n noDataVariant={!timeChartData.length && legendItems && legendItems.length > 0 ? 'chart' : 'message'}\n />\n </Box>\n );\n }}\n </ContentWithLegend>\n </Box>\n );\n}\n"],"names":["useMemo","useRef","useState","Box","useTheme","merge","getTimeSeriesValues","DEFAULT_LEGEND","getCalculations","formatValue","LEGEND_VALUE_CONFIG","useTimeRange","validateLegendSpec","legendValues","YAxisLabel","useChartsTheme","ContentWithLegend","useId","DEFAULT_TOOLTIP_CONFIG","DEFAULT_FORMAT","DEFAULT_VISUAL","THRESHOLD_PLOT_INTERVAL","getTimeSeries","getCommonTimeScaleForQueries","convertPanelYAxis","getThresholdSeries","convertPercentThreshold","getSeriesColor","TimeSeriesChartBase","TimeSeriesChartPanel","props","spec","thresholds","yAxis","tooltip","querySettings","querySettingsList","contentDimensions","queryResults","chartsTheme","muiTheme","chartId","chartRef","categoricalPalette","echartsTheme","color","contentPadding","container","padding","default","adjustedContentDimensions","width","height","undefined","legend","format","visual","echartsYAxis","selectedLegendItems","setSelectedLegendItems","legendSorting","setLegendSorting","setTimeRange","timeScale","timeChartData","timeSeriesMapping","legendItems","seriesIndex","queryIndex","length","result","item","i","data","series","timeSeries","formattedSeriesName","formattedName","name","seriesColor","muiPrimaryColor","palette","primary","main","seriesName","queryHasMultipleResults","seriesId","legendCalculations","values","isSelectAll","isSelected","showTimeSeries","datasetIndex","push","id","label","steps","thresholdsColors","defaultThresholdColor","defaultColor","forEach","step","index","stepPaletteColor","thresholdLineColor","stepOption","value","mode","max","min","thresholdName","thresholdTimeValueTuple","currentTimestamp","startMs","endMs","legendColumns","reduce","columns","legendValue","legendConfig","includes","accessorKey","header","headerDescription","description","align","cell","getValue","cellValue","cellDescription","enableSorting","gridLeft","gridOverrides","left","show","right","bottom","handleDataZoom","event","start","Date","end","isStackedBar","display","stack","enablePinning","tooltipConfig","sx","minChildrenHeight","legendSize","size","legendProps","options","selectedItems","onSelectedItemsChange","tableProps","sorting","onSortingChange","onItemMouseOver","e","current","highlightSeries","onItemMouseOut","clearHighlightedSeries","style","ref","seriesMapping","grid","syncGroup","onDataZoom","noDataVariant"],"mappings":"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"}
|