@perses-dev/timeseries-chart-plugin 0.12.0-beta.0 → 0.12.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.
Files changed (99) hide show
  1. package/__mf/js/{TimeSeriesChart.a84df9d1.js → TimeSeriesChart.e51aefbf.js} +3 -3
  2. package/__mf/js/async/{675.450939c3.js → 390.ad50daba.js} +7 -7
  3. package/__mf/js/async/392.550376f0.js +2 -0
  4. package/__mf/js/async/{724.5594af97.js → 489.8bb61ec9.js} +1 -1
  5. package/__mf/js/async/{437.aa3568c0.js → 544.4dd63985.js} +2 -2
  6. package/__mf/js/async/648.128f31b8.js +22 -0
  7. package/__mf/js/async/78.362ece9d.js +1 -0
  8. package/__mf/js/async/867.12a42f78.js +38 -0
  9. package/__mf/js/async/97.3f27a901.js +7 -0
  10. package/__mf/js/async/__federation_expose_TimeSeriesChart.8cacec73.js +3 -0
  11. package/__mf/js/async/lib-router.9730f5e9.js +2 -0
  12. package/__mf/js/async/{lib-router.54d80a0a.js.LICENSE.txt → lib-router.9730f5e9.js.LICENSE.txt} +3 -3
  13. package/__mf/js/{main.7d4d10e5.js → main.90e8ca42.js} +3 -3
  14. package/lib/CSVExportUtils.js +1 -1
  15. package/lib/CSVExportUtils.js.map +1 -1
  16. package/lib/GeneralSettingsEditor.js +1 -1
  17. package/lib/GeneralSettingsEditor.js.map +1 -1
  18. package/lib/QuerySettingsEditor.d.ts.map +1 -1
  19. package/lib/QuerySettingsEditor.js +46 -8
  20. package/lib/QuerySettingsEditor.js.map +1 -1
  21. package/lib/TimeSeriesChart.js +1 -1
  22. package/lib/TimeSeriesChart.js.map +1 -1
  23. package/lib/TimeSeriesChartBase.d.ts +5 -1
  24. package/lib/TimeSeriesChartBase.d.ts.map +1 -1
  25. package/lib/TimeSeriesChartBase.js +5 -3
  26. package/lib/TimeSeriesChartBase.js.map +1 -1
  27. package/lib/TimeSeriesChartPanel.d.ts.map +1 -1
  28. package/lib/TimeSeriesChartPanel.js +100 -15
  29. package/lib/TimeSeriesChartPanel.js.map +1 -1
  30. package/lib/TimeSeriesExportAction.d.ts.map +1 -1
  31. package/lib/TimeSeriesExportAction.js +7 -6
  32. package/lib/TimeSeriesExportAction.js.map +1 -1
  33. package/lib/VisualOptionsEditor.js +1 -1
  34. package/lib/VisualOptionsEditor.js.map +1 -1
  35. package/lib/YAxisOptionsEditor.d.ts.map +1 -1
  36. package/lib/YAxisOptionsEditor.js +23 -3
  37. package/lib/YAxisOptionsEditor.js.map +1 -1
  38. package/lib/bootstrap.js +1 -1
  39. package/lib/bootstrap.js.map +1 -1
  40. package/lib/cjs/CSVExportUtils.js +1 -1
  41. package/lib/cjs/GeneralSettingsEditor.js +1 -1
  42. package/lib/cjs/QuerySettingsEditor.js +45 -7
  43. package/lib/cjs/TimeSeriesChart.js +1 -1
  44. package/lib/cjs/TimeSeriesChartBase.js +5 -3
  45. package/lib/cjs/TimeSeriesChartPanel.js +99 -14
  46. package/lib/cjs/TimeSeriesExportAction.js +5 -4
  47. package/lib/cjs/VisualOptionsEditor.js +1 -1
  48. package/lib/cjs/YAxisOptionsEditor.js +21 -1
  49. package/lib/cjs/bootstrap.js +1 -1
  50. package/lib/cjs/env.d.js +1 -1
  51. package/lib/cjs/getPluginModule.js +12 -0
  52. package/lib/cjs/index-federation.js +1 -1
  53. package/lib/cjs/index.js +1 -1
  54. package/lib/cjs/setup-tests.js +1 -1
  55. package/lib/cjs/test/mock-query-results.js +1 -1
  56. package/lib/cjs/time-series-chart-model.js +30 -2
  57. package/lib/cjs/utils/data-transform.js +32 -13
  58. package/lib/cjs/utils/palette-gen.js +1 -1
  59. package/lib/cjs/utils/palette.js +1 -1
  60. package/lib/env.d.js +1 -1
  61. package/lib/env.d.js.map +1 -1
  62. package/lib/getPluginModule.d.ts.map +1 -1
  63. package/lib/getPluginModule.js +12 -0
  64. package/lib/getPluginModule.js.map +1 -1
  65. package/lib/index-federation.js +1 -1
  66. package/lib/index-federation.js.map +1 -1
  67. package/lib/index.js +1 -1
  68. package/lib/index.js.map +1 -1
  69. package/lib/setup-tests.js +1 -1
  70. package/lib/setup-tests.js.map +1 -1
  71. package/lib/test/mock-query-results.js +1 -1
  72. package/lib/test/mock-query-results.js.map +1 -1
  73. package/lib/time-series-chart-model.d.ts +15 -0
  74. package/lib/time-series-chart-model.d.ts.map +1 -1
  75. package/lib/time-series-chart-model.js +26 -2
  76. package/lib/time-series-chart-model.js.map +1 -1
  77. package/lib/utils/data-transform.d.ts +3 -2
  78. package/lib/utils/data-transform.d.ts.map +1 -1
  79. package/lib/utils/data-transform.js +34 -14
  80. package/lib/utils/data-transform.js.map +1 -1
  81. package/lib/utils/palette-gen.js +1 -1
  82. package/lib/utils/palette-gen.js.map +1 -1
  83. package/lib/utils/palette.js +1 -1
  84. package/lib/utils/palette.js.map +1 -1
  85. package/mf-manifest.json +18 -18
  86. package/mf-stats.json +18 -18
  87. package/package.json +4 -4
  88. package/__mf/js/async/197.e065f9da.js +0 -1
  89. package/__mf/js/async/252.eb86b477.js +0 -22
  90. package/__mf/js/async/392.fa5c2b2f.js +0 -2
  91. package/__mf/js/async/591.9d963c5f.js +0 -38
  92. package/__mf/js/async/743.d260286e.js +0 -7
  93. package/__mf/js/async/__federation_expose_TimeSeriesChart.b844f4c6.js +0 -3
  94. package/__mf/js/async/lib-router.54d80a0a.js +0 -2
  95. /package/__mf/js/async/{675.450939c3.js.LICENSE.txt → 390.ad50daba.js.LICENSE.txt} +0 -0
  96. /package/__mf/js/async/{392.fa5c2b2f.js.LICENSE.txt → 392.550376f0.js.LICENSE.txt} +0 -0
  97. /package/__mf/js/async/{437.aa3568c0.js.LICENSE.txt → 544.4dd63985.js.LICENSE.txt} +0 -0
  98. /package/__mf/js/async/{252.eb86b477.js.LICENSE.txt → 648.128f31b8.js.LICENSE.txt} +0 -0
  99. /package/__mf/js/async/{743.d260286e.js.LICENSE.txt → 97.3f27a901.js.LICENSE.txt} +0 -0
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -212,6 +212,23 @@ function QuerySettingsEditor(props) {
212
212
  qs.areaOpacity = undefined;
213
213
  });
214
214
  };
215
+ const addUnit = (i)=>{
216
+ updateQuerySettings(i, (qs)=>{
217
+ qs.format = {
218
+ unit: 'decimal'
219
+ };
220
+ });
221
+ };
222
+ const removeUnit = (i)=>{
223
+ updateQuerySettings(i, (qs)=>{
224
+ qs.format = undefined;
225
+ });
226
+ };
227
+ const handleUnitChange = (i, format)=>{
228
+ updateQuerySettings(i, (qs)=>{
229
+ qs.format = format;
230
+ });
231
+ };
215
232
  const queryCount = (0, _pluginsystem.useQueryCountContext)();
216
233
  // Compute the list of query indexes for which query settings are not already defined.
217
234
  // This is to avoid already-booked indexes to still be selectable in the dropdown(s)
@@ -270,7 +287,10 @@ function QuerySettingsEditor(props) {
270
287
  onAddLineStyle: ()=>addLineStyle(i),
271
288
  onRemoveLineStyle: ()=>removeLineStyle(i),
272
289
  onAddAreaOpacity: ()=>addAreaOpacity(i),
273
- onRemoveAreaOpacity: ()=>removeAreaOpacity(i)
290
+ onRemoveAreaOpacity: ()=>removeAreaOpacity(i),
291
+ onAddUnit: ()=>addUnit(i),
292
+ onRemoveUnit: ()=>removeUnit(i),
293
+ onUnitChange: (format)=>handleUnitChange(i, format)
274
294
  }, i)),
275
295
  queryCount > 0 && firstAvailableQueryIndex !== NO_INDEX_AVAILABLE && /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Button, {
276
296
  variant: "contained",
@@ -284,7 +304,7 @@ function QuerySettingsEditor(props) {
284
304
  ]
285
305
  });
286
306
  }
287
- function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue, lineStyle, areaOpacity }, availableQueryIndexes, onQueryIndexChange, onColorModeChange, onColorValueChange, onLineStyleChange, onAreaOpacityChange, onDelete, inputRef, onAddColor: onAddColor, onRemoveColor: onRemoveColor, onAddLineStyle, onRemoveLineStyle, onAddAreaOpacity, onRemoveAreaOpacity }) {
307
+ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue, lineStyle, areaOpacity, format }, availableQueryIndexes, onQueryIndexChange, onColorModeChange, onColorValueChange, onLineStyleChange, onAreaOpacityChange, onDelete, inputRef, onAddColor: onAddColor, onRemoveColor: onRemoveColor, onAddLineStyle, onRemoveLineStyle, onAddAreaOpacity, onRemoveAreaOpacity, onAddUnit, onRemoveUnit, onUnitChange }) {
288
308
  // current query index should also be selectable
289
309
  const selectableQueryIndexes = availableQueryIndexes.concat(queryIndex).sort((a, b)=>a - b);
290
310
  // State for dropdown menu
@@ -307,14 +327,21 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
307
327
  label: 'Opacity',
308
328
  action: onAddAreaOpacity
309
329
  });
330
+ if (format === undefined) options.push({
331
+ key: 'unit',
332
+ label: 'Unit',
333
+ action: onAddUnit
334
+ });
310
335
  return options;
311
336
  }, [
312
337
  colorMode,
313
338
  lineStyle,
314
339
  areaOpacity,
340
+ format,
315
341
  onAddColor,
316
342
  onAddLineStyle,
317
- onAddAreaOpacity
343
+ onAddAreaOpacity,
344
+ onAddUnit
318
345
  ]);
319
346
  const handleAddMenuClick = (event)=>{
320
347
  if (availableOptions.length === 1 && availableOptions[0]) {
@@ -333,7 +360,6 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
333
360
  handleMenuClose();
334
361
  };
335
362
  return /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Stack, {
336
- spacing: 2,
337
363
  sx: {
338
364
  borderBottom: '1px solid',
339
365
  borderColor: 'divider',
@@ -343,10 +369,9 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
343
369
  children: /*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.Stack, {
344
370
  direction: "row",
345
371
  alignItems: "center",
346
- spacing: 1,
347
372
  sx: {
348
373
  flexWrap: 'wrap',
349
- gap: 1
374
+ gap: 2
350
375
  },
351
376
  children: [
352
377
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.TextField, {
@@ -442,6 +467,19 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
442
467
  })
443
468
  ]
444
469
  }),
470
+ format !== undefined && /*#__PURE__*/ (0, _jsxruntime.jsx)(SettingsSection, {
471
+ label: "Unit",
472
+ onRemove: onRemoveUnit,
473
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Box, {
474
+ sx: {
475
+ minWidth: '180px'
476
+ },
477
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.UnitSelector, {
478
+ value: format,
479
+ onChange: onUnitChange
480
+ })
481
+ })
482
+ }),
445
483
  availableOptions.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
446
484
  children: [
447
485
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.IconButton, {
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -1,4 +1,4 @@
1
- // Copyright 2025 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -51,7 +51,7 @@ function _interop_require_default(obj) {
51
51
  _components.TooltipComponent,
52
52
  _renderers.CanvasRenderer
53
53
  ]);
54
- const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeChart({ height, data, seriesMapping, timeScale: timeScaleProp, yAxis, format, grid, isStackedBar = false, tooltipConfig = _components1.DEFAULT_TOOLTIP_CONFIG, noDataVariant = 'message', syncGroup, onDataZoom, onDoubleClick, __experimentalEChartsOptionsOverride }, ref) {
54
+ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeChart({ height, data, seriesMapping, timeScale: timeScaleProp, yAxis, format, seriesFormatMap, grid, isStackedBar = false, tooltipConfig = _components1.DEFAULT_TOOLTIP_CONFIG, noDataVariant = 'message', syncGroup, onDataZoom, onDoubleClick, __experimentalEChartsOptionsOverride }, ref) {
55
55
  const { chartsTheme, enablePinning, enableSyncGrouping, lastTooltipPinnedCoords, setLastTooltipPinnedCoords } = (0, _components1.useChartsContext)();
56
56
  const isPinningEnabled = tooltipConfig.enablePinning && enablePinning;
57
57
  const chartRef = (0, _react.useRef)();
@@ -181,7 +181,8 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
181
181
  snap: false
182
182
  }
183
183
  },
184
- yAxis: (0, _components1.getFormattedAxis)(yAxis, format),
184
+ // If yAxis is already an array (multiple Y axes), use it directly; otherwise use getFormattedAxis
185
+ yAxis: Array.isArray(yAxis) ? yAxis : (0, _components1.getFormattedAxis)(yAxis, format),
185
186
  animation: false,
186
187
  tooltip: {
187
188
  show: true,
@@ -379,6 +380,7 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
379
380
  enablePinning: isPinningEnabled,
380
381
  pinnedPos: tooltipPinnedCoords,
381
382
  format: format,
383
+ seriesFormatMap: seriesFormatMap,
382
384
  onUnpinClick: ()=>{
383
385
  // Unpins tooltip when clicking Pin icon in TooltipHeader.
384
386
  setTooltipPinnedCoords(null);
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -76,16 +76,47 @@ function TimeSeriesChartPanel(props) {
76
76
  }, [
77
77
  yAxis
78
78
  ]);
79
+ // Collect unique formats from query settings that differ from the base format
80
+ // These will create additional Y axes on the right side
81
+ const { additionalFormats, formatToYAxisIndex, seriesFormatMap } = (0, _react.useMemo)(()=>{
82
+ const baseUnit = format?.unit ?? 'decimal';
83
+ const additionalFormats = [];
84
+ const formatToYAxisIndex = new Map();
85
+ const seriesFormatMap = new Map();
86
+ // Index 0 is reserved for the base Y axis
87
+ formatToYAxisIndex.set(baseUnit, 0);
88
+ // Collect unique formats from query settings
89
+ for (const qs of querySettingsList ?? []){
90
+ if (qs.format?.unit && qs.format.unit !== baseUnit) {
91
+ const unitKey = qs.format.unit;
92
+ if (!formatToYAxisIndex.has(unitKey)) {
93
+ // Add new format - index is 1 + position in additionalFormats array
94
+ formatToYAxisIndex.set(unitKey, 1 + additionalFormats.length);
95
+ additionalFormats.push(qs.format);
96
+ }
97
+ }
98
+ }
99
+ return {
100
+ additionalFormats,
101
+ formatToYAxisIndex,
102
+ seriesFormatMap
103
+ };
104
+ }, [
105
+ format,
106
+ querySettingsList
107
+ ]);
79
108
  const [selectedLegendItems, setSelectedLegendItems] = (0, _react.useState)('ALL');
80
109
  const [legendSorting, setLegendSorting] = (0, _react.useState)();
81
110
  const { setTimeRange } = (0, _pluginsystem.useTimeRange)();
82
111
  // Populate series data based on query results
83
- const { timeScale, timeChartData, timeSeriesMapping, legendItems } = (0, _react.useMemo)(()=>{
112
+ const { timeScale, timeChartData, timeSeriesMapping, legendItems, seriesFormatMap: computedSeriesFormatMap, maxValuesByFormat } = (0, _react.useMemo)(()=>{
84
113
  const timeScale = (0, _datatransform.getCommonTimeScaleForQueries)(queryResults);
85
114
  if (timeScale === undefined) {
86
115
  return {
87
116
  timeChartData: [],
88
- timeSeriesMapping: []
117
+ timeSeriesMapping: [],
118
+ seriesFormatMap: new Map(),
119
+ maxValuesByFormat: new Map()
89
120
  };
90
121
  }
91
122
  const legendItems = [];
@@ -93,6 +124,8 @@ function TimeSeriesChartPanel(props) {
93
124
  // https://apache.github.io/echarts-handbook/en/concepts/dataset/
94
125
  const timeChartData = [];
95
126
  const timeSeriesMapping = [];
127
+ // Track max values for each format unit (used for dynamic Y axis offset calculation)
128
+ const maxValuesByFormat = new Map();
96
129
  // Index is counted across multiple queries which ensures the categorical color palette does not reset for every query
97
130
  let seriesIndex = 0;
98
131
  // Mapping of each set of query results to be ECharts option compatible
@@ -146,9 +179,25 @@ function TimeSeriesChartPanel(props) {
146
179
  // which legend items are selected. This must happen before timeChartData.push to avoid an
147
180
  // off-by-one error, seriesIndex cannot be used since it's needed to cycle through palette
148
181
  const datasetIndex = timeChartData.length;
182
+ // Determine yAxisIndex based on the query's format setting
183
+ const queryFormat = querySettings?.format;
184
+ const yAxisIndex = queryFormat?.unit ? formatToYAxisIndex.get(queryFormat.unit) ?? 0 : 0;
149
185
  // Each series is stored as a separate dataset source.
150
186
  // https://apache.github.io/echarts-handbook/en/concepts/dataset/#how-to-reference-several-datasets
151
- timeSeriesMapping.push((0, _datatransform.getTimeSeries)(seriesId, datasetIndex, formattedSeriesName, visual, timeScale, seriesColor, querySettings));
187
+ timeSeriesMapping.push((0, _datatransform.getTimeSeries)(seriesId, datasetIndex, formattedSeriesName, visual, timeScale, seriesColor, querySettings, yAxisIndex));
188
+ // Store the format for this series for tooltip formatting
189
+ if (queryFormat) {
190
+ seriesFormatMap.set(seriesId, queryFormat);
191
+ // Track max value for this format unit (used for dynamic Y axis offset calculation)
192
+ const unitKey = queryFormat.unit;
193
+ if (unitKey) {
194
+ const seriesMax = Math.max(...timeSeries.values.map((v)=>Math.abs(v[1] ?? 0)));
195
+ const currentMax = maxValuesByFormat.get(unitKey) ?? 0;
196
+ if (seriesMax > currentMax) {
197
+ maxValuesByFormat.set(unitKey, seriesMax);
198
+ }
199
+ }
200
+ }
152
201
  timeChartData.push({
153
202
  name: formattedSeriesName,
154
203
  values: (0, _core.getTimeSeriesValues)(timeSeries, timeScale)
@@ -205,7 +254,9 @@ function TimeSeriesChartPanel(props) {
205
254
  timeScale,
206
255
  timeChartData,
207
256
  timeSeriesMapping,
208
- legendItems
257
+ legendItems,
258
+ seriesFormatMap,
259
+ maxValuesByFormat
209
260
  };
210
261
  }, [
211
262
  queryResults,
@@ -219,7 +270,27 @@ function TimeSeriesChartPanel(props) {
219
270
  categoricalPalette,
220
271
  chartId,
221
272
  chartsTheme.thresholds,
222
- muiTheme.palette.primary.main
273
+ muiTheme.palette.primary.main,
274
+ formatToYAxisIndex,
275
+ seriesFormatMap
276
+ ]);
277
+ // Create multiple Y axes if there are additional formats
278
+ // Uses max values from data to compute dynamic offsets that adapt to label widths
279
+ const multipleYAxes = (0, _react.useMemo)(()=>{
280
+ if (additionalFormats.length === 0) {
281
+ return undefined; // Use single Y axis (default behavior)
282
+ }
283
+ // Build array of max values for each additional format (in order)
284
+ const maxValues = additionalFormats.map((fmt)=>{
285
+ const unitKey = fmt.unit;
286
+ return unitKey ? maxValuesByFormat?.get(unitKey) ?? 1000 : 1000;
287
+ });
288
+ return (0, _components.getFormattedMultipleYAxes)(echartsYAxis, format, additionalFormats, maxValues);
289
+ }, [
290
+ echartsYAxis,
291
+ format,
292
+ additionalFormats,
293
+ maxValuesByFormat
223
294
  ]);
224
295
  // Translate the legend values into columns for the table legend.
225
296
  const legendColumns = (0, _react.useMemo)(()=>{
@@ -254,16 +325,29 @@ function TimeSeriesChartPanel(props) {
254
325
  legend?.values,
255
326
  format
256
327
  ]);
328
+ const gridOverrides = (0, _react.useMemo)(()=>{
329
+ // When Y axes are hidden, disable containLabel to prevent auto-spacing, but add bottom padding for X axis
330
+ return echartsYAxis.show === false ? {
331
+ left: 0,
332
+ right: 0,
333
+ bottom: 30,
334
+ containLabel: false
335
+ } : {
336
+ left: yAxis && yAxis.label ? 30 : 20,
337
+ // With containLabel: true in theme, ECharts auto-reserves space for axis labels.
338
+ // For multiple right axes, add extra padding for the last axis labels that extend beyond the grid.
339
+ right: additionalFormats.length > 0 ? 10 : 20,
340
+ bottom: 0,
341
+ containLabel: true
342
+ };
343
+ }, [
344
+ echartsYAxis.show,
345
+ yAxis,
346
+ additionalFormats.length
347
+ ]);
257
348
  if (adjustedContentDimensions === undefined) {
258
349
  return null;
259
350
  }
260
- // override default spacing, see: https://echarts.apache.org/en/option.html#grid
261
- const gridLeft = yAxis && yAxis.label ? 30 : 20;
262
- const gridOverrides = {
263
- left: !echartsYAxis.show ? 0 : gridLeft,
264
- right: 20,
265
- bottom: 0
266
- };
267
351
  const handleDataZoom = (event)=>{
268
352
  // TODO: add ECharts transition animation on zoom
269
353
  setTimeRange({
@@ -331,8 +415,9 @@ function TimeSeriesChartPanel(props) {
331
415
  data: timeChartData,
332
416
  seriesMapping: timeSeriesMapping,
333
417
  timeScale: timeScale,
334
- yAxis: echartsYAxis,
418
+ yAxis: multipleYAxes ?? echartsYAxis,
335
419
  format: format,
420
+ seriesFormatMap: computedSeriesFormatMap,
336
421
  grid: gridOverrides,
337
422
  isStackedBar: isStackedBar,
338
423
  tooltipConfig: tooltipConfig,
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -21,9 +21,10 @@ Object.defineProperty(exports, "TimeSeriesExportAction", {
21
21
  }
22
22
  });
23
23
  const _jsxruntime = require("react/jsx-runtime");
24
- const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
25
24
  const _material = require("@mui/material");
25
+ const _components = require("@perses-dev/components");
26
26
  const _Download = /*#__PURE__*/ _interop_require_default(require("mdi-material-ui/Download"));
27
+ const _react = /*#__PURE__*/ _interop_require_wildcard(require("react"));
27
28
  const _CSVExportUtils = require("./CSVExportUtils");
28
29
  function _interop_require_default(obj) {
29
30
  return obj && obj.__esModule ? obj : {
@@ -109,8 +110,8 @@ const TimeSeriesExportAction = ({ queryResults, definition })=>{
109
110
  if (!canExport) {
110
111
  return null;
111
112
  }
112
- return /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Tooltip, {
113
- title: "Export as CSV",
113
+ return /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.InfoTooltip, {
114
+ description: "Export as CSV",
114
115
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.IconButton, {
115
116
  size: "small",
116
117
  onClick: handleExport,
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -25,6 +25,7 @@ const _material = require("@mui/material");
25
25
  const _components = require("@perses-dev/components");
26
26
  const _timeserieschartmodel = require("./time-series-chart-model");
27
27
  function YAxisOptionsEditor({ value, onChange }) {
28
+ const logBase = value.logBase ? _timeserieschartmodel.LOG_BASE_CONFIG[value.logBase] : undefined;
28
29
  return /*#__PURE__*/ (0, _jsxruntime.jsxs)(_components.OptionsEditorGroup, {
29
30
  title: "Y Axis",
30
31
  children: [
@@ -47,6 +48,25 @@ function YAxisOptionsEditor({ value, onChange }) {
47
48
  format: newFormat
48
49
  })
49
50
  }),
51
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.OptionsEditorControl, {
52
+ label: _timeserieschartmodel.Y_AXIS_CONFIG.logBase.label,
53
+ control: /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.SettingsAutocomplete, {
54
+ value: {
55
+ ...logBase,
56
+ id: logBase?.label ?? 'None'
57
+ },
58
+ options: _timeserieschartmodel.LOG_BASE_OPTIONS,
59
+ onChange: (__, newValue)=>{
60
+ const updatedValue = {
61
+ ...value,
62
+ logBase: newValue.log ?? undefined
63
+ };
64
+ onChange(updatedValue);
65
+ },
66
+ disabled: value === undefined,
67
+ disableClearable: true
68
+ })
69
+ }),
50
70
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.OptionsEditorControl, {
51
71
  label: _timeserieschartmodel.Y_AXIS_CONFIG.label.label,
52
72
  control: /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.TextField, {
@@ -1,4 +1,4 @@
1
- // Copyright 2024 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
package/lib/cjs/env.d.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright 2024 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -1,3 +1,15 @@
1
+ // Copyright 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.
1
13
  "use strict";
2
14
  Object.defineProperty(exports, "__esModule", {
3
15
  value: true
@@ -40,7 +40,7 @@ function _interop_require_wildcard(obj, nodeInterop) {
40
40
  }
41
41
  return newObj;
42
42
  }
43
- // Copyright 2024 The Perses Authors
43
+ // Copyright The Perses Authors
44
44
  // Licensed under the Apache License, Version 2.0 (the "License");
45
45
  // you may not use this file except in compliance with the License.
46
46
  // You may obtain a copy of the License at
package/lib/cjs/index.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright 2025 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -51,6 +51,12 @@ _export(exports, {
51
51
  get LINE_STYLE_CONFIG () {
52
52
  return LINE_STYLE_CONFIG;
53
53
  },
54
+ get LOG_BASE_CONFIG () {
55
+ return LOG_BASE_CONFIG;
56
+ },
57
+ get LOG_BASE_OPTIONS () {
58
+ return LOG_BASE_OPTIONS;
59
+ },
54
60
  get NEGATIVE_MIN_VALUE_MULTIPLIER () {
55
61
  return NEGATIVE_MIN_VALUE_MULTIPLIER;
56
62
  },
@@ -91,7 +97,8 @@ const DEFAULT_Y_AXIS = {
91
97
  label: '',
92
98
  format: DEFAULT_FORMAT,
93
99
  min: undefined,
94
- max: undefined
100
+ max: undefined,
101
+ logBase: undefined
95
102
  };
96
103
  const Y_AXIS_CONFIG = {
97
104
  show: {
@@ -108,6 +115,9 @@ const Y_AXIS_CONFIG = {
108
115
  },
109
116
  max: {
110
117
  label: 'Max'
118
+ },
119
+ logBase: {
120
+ label: 'Log Base'
111
121
  }
112
122
  };
113
123
  const DEFAULT_DISPLAY = 'line';
@@ -190,6 +200,24 @@ const OPACITY_CONFIG = {
190
200
  max: 1,
191
201
  step: 0.05
192
202
  };
203
+ const LOG_BASE_CONFIG = {
204
+ none: {
205
+ label: 'None',
206
+ log: undefined
207
+ },
208
+ '2': {
209
+ label: '2',
210
+ log: 2
211
+ },
212
+ '10': {
213
+ label: '10',
214
+ log: 10
215
+ }
216
+ };
217
+ const LOG_BASE_OPTIONS = Object.entries(LOG_BASE_CONFIG).map(([id, config])=>({
218
+ id: id,
219
+ ...config
220
+ }));
193
221
  const POSITIVE_MIN_VALUE_MULTIPLIER = 0.8;
194
222
  const NEGATIVE_MIN_VALUE_MULTIPLIER = 1.2;
195
223
  function createInitialTimeSeriesChartOptions() {
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -63,7 +63,7 @@ function getCommonTimeScaleForQueries(queries) {
63
63
  const seriesData = queries.map((query)=>query.data);
64
64
  return (0, _core.getCommonTimeScale)(seriesData);
65
65
  }
66
- function getTimeSeries(id, datasetIndex, formattedName, visual, timeScale, paletteColor, querySettings) {
66
+ function getTimeSeries(id, datasetIndex, formattedName, visual, timeScale, paletteColor, querySettings, yAxisIndex) {
67
67
  const lineWidth = visual.lineWidth ?? _timeserieschartmodel.DEFAULT_LINE_WIDTH;
68
68
  const pointRadius = visual.pointRadius ?? _timeserieschartmodel.DEFAULT_POINT_RADIUS;
69
69
  // Shows datapoint symbols when selected time range is roughly 15 minutes or less
@@ -81,6 +81,7 @@ function getTimeSeries(id, datasetIndex, formattedName, visual, timeScale, palet
81
81
  name: formattedName,
82
82
  color: paletteColor,
83
83
  stack: visual.stack === 'all' ? visual.stack : undefined,
84
+ yAxisIndex: yAxisIndex,
84
85
  label: {
85
86
  show: false
86
87
  }
@@ -95,6 +96,7 @@ function getTimeSeries(id, datasetIndex, formattedName, visual, timeScale, palet
95
96
  connectNulls: visual.connectNulls ?? _timeserieschartmodel.DEFAULT_CONNECT_NULLS,
96
97
  color: paletteColor,
97
98
  stack: visual.stack === 'all' ? visual.stack : undefined,
99
+ yAxisIndex: yAxisIndex,
98
100
  sampling: 'lttb',
99
101
  progressiveThreshold: _components.OPTIMIZED_MODE_SERIES_LIMIT,
100
102
  showSymbol: showPoints,
@@ -195,18 +197,21 @@ function findMax(data) {
195
197
  return max;
196
198
  }
197
199
  function convertPanelYAxis(inputAxis = {}) {
198
- const yAxis = {
199
- show: true,
200
- axisLabel: {
201
- show: inputAxis?.show ?? _timeserieschartmodel.DEFAULT_Y_AXIS.show
202
- },
203
- min: inputAxis?.min,
204
- max: inputAxis?.max
205
- };
206
- // Set the y-axis minimum relative to the data
207
- if (inputAxis?.min === undefined) {
200
+ // Determine the appropriate min value based on scale type and user input
201
+ let minValue;
202
+ if (inputAxis.logBase !== undefined) {
203
+ // For logarithmic scales without explicit min:
204
+ // Let ECharts auto-calculate the range based on data to avoid issues with
205
+ // function-based calculations which can result in improper ranges (e.g., 1-10)
206
+ minValue = undefined;
207
+ } else if (inputAxis?.min !== undefined) {
208
+ // User explicitly set a min value - use it for both linear and log scales
209
+ minValue = inputAxis.min;
210
+ } else {
211
+ // For linear scales without explicit min:
212
+ // Use dynamic calculation with padding for better visualization
208
213
  // https://echarts.apache.org/en/option.html#yAxis.min
209
- yAxis.min = (value)=>{
214
+ minValue = (value)=>{
210
215
  if (value.min >= 0 && value.min <= 1) {
211
216
  // Helps with PercentDecimal units, or datasets that return 0 or 1 booleans
212
217
  return 0;
@@ -220,6 +225,20 @@ function convertPanelYAxis(inputAxis = {}) {
220
225
  }
221
226
  };
222
227
  }
228
+ // Build the yAxis configuration
229
+ const yAxis = {
230
+ show: inputAxis?.show ?? _timeserieschartmodel.DEFAULT_Y_AXIS.show,
231
+ min: minValue,
232
+ max: inputAxis?.max
233
+ };
234
+ // Apply logarithmic scale settings if requested
235
+ if (inputAxis.logBase !== undefined) {
236
+ return {
237
+ ...yAxis,
238
+ type: 'log',
239
+ logBase: inputAxis.logBase
240
+ };
241
+ }
223
242
  return yAxis;
224
243
  }
225
244
  function roundDown(num) {
@@ -1,4 +1,4 @@
1
- // Copyright 2023 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
@@ -1,4 +1,4 @@
1
- // Copyright 2024 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
package/lib/env.d.js CHANGED
@@ -1,4 +1,4 @@
1
- // Copyright 2024 The Perses Authors
1
+ // Copyright The Perses Authors
2
2
  // Licensed under the Apache License, Version 2.0 (the "License");
3
3
  // you may not use this file except in compliance with the License.
4
4
  // You may obtain a copy of the License at
package/lib/env.d.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/env.d.ts"],"sourcesContent":["// Copyright 2024 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\n/// <reference types=\"@rsbuild/core/types\" />\n"],"names":[],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAEjC,6CAA6C"}
1
+ {"version":3,"sources":["../../src/env.d.ts"],"sourcesContent":["// Copyright 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\n/// <reference types=\"@rsbuild/core/types\" />\n"],"names":[],"mappings":"AAAA,+BAA+B;AAC/B,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAEjC,6CAA6C"}
@@ -1 +1 @@
1
- {"version":3,"file":"getPluginModule.d.ts","sourceRoot":"","sources":["../../src/getPluginModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAoB,MAAM,2BAA2B,CAAC;AAGnF;;GAEG;AACH,wBAAgB,eAAe,IAAI,oBAAoB,CAUtD"}
1
+ {"version":3,"file":"getPluginModule.d.ts","sourceRoot":"","sources":["../../src/getPluginModule.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,oBAAoB,EAAoB,MAAM,2BAA2B,CAAC;AAGnF;;GAEG;AACH,wBAAgB,eAAe,IAAI,oBAAoB,CAUtD"}