@scality/core-ui 0.192.0 → 0.194.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 (72) hide show
  1. package/dist/components/charts/barchart/Barchart.d.ts.map +1 -1
  2. package/dist/components/charts/barchart/Barchart.js +29 -19
  3. package/dist/components/charts/common/chartUtils.d.ts +7 -2
  4. package/dist/components/charts/common/chartUtils.d.ts.map +1 -1
  5. package/dist/components/charts/common/chartUtils.js +55 -20
  6. package/dist/components/charts/index.d.ts +1 -1
  7. package/dist/components/charts/index.d.ts.map +1 -1
  8. package/dist/components/charts/linetimeseries/LineTimeSerieChart.d.ts +12 -47
  9. package/dist/components/charts/linetimeseries/LineTimeSerieChart.d.ts.map +1 -1
  10. package/dist/components/charts/linetimeseries/LineTimeSerieChart.js +46 -220
  11. package/dist/components/charts/linetimeseries/LineTimeSerieChart.types.d.ts +77 -0
  12. package/dist/components/charts/linetimeseries/LineTimeSerieChart.types.d.ts.map +1 -0
  13. package/dist/components/charts/linetimeseries/LineTimeSerieChart.types.js +6 -0
  14. package/dist/components/charts/linetimeseries/LineTimeSerieChartTooltip.d.ts +18 -0
  15. package/dist/components/charts/linetimeseries/LineTimeSerieChartTooltip.d.ts.map +1 -0
  16. package/dist/components/charts/linetimeseries/LineTimeSerieChartTooltip.js +65 -0
  17. package/dist/components/charts/linetimeseries/useChartData.d.ts +44 -0
  18. package/dist/components/charts/linetimeseries/useChartData.d.ts.map +1 -0
  19. package/dist/components/charts/linetimeseries/useChartData.js +207 -0
  20. package/dist/components/charts/linetimeseries/useChartHover.d.ts +15 -0
  21. package/dist/components/charts/linetimeseries/useChartHover.d.ts.map +1 -0
  22. package/dist/components/charts/linetimeseries/useChartHover.js +29 -0
  23. package/dist/components/checkbox/Checkbox.component.d.ts.map +1 -1
  24. package/dist/components/checkbox/Checkbox.component.js +15 -7
  25. package/dist/components/constrainedtext/Constrainedtext.component.js +2 -2
  26. package/dist/components/dropdown/Dropdown.component.d.ts.map +1 -1
  27. package/dist/components/dropdown/Dropdown.component.js +3 -0
  28. package/dist/components/error-pages/ErrorPage401.component.js +1 -1
  29. package/dist/components/error-pages/ErrorPage404.component.js +1 -1
  30. package/dist/components/error-pages/ErrorPage500.component.js +1 -1
  31. package/dist/components/icon/CustomsIcons.d.ts +10 -0
  32. package/dist/components/icon/CustomsIcons.d.ts.map +1 -1
  33. package/dist/components/icon/CustomsIcons.js +8 -0
  34. package/dist/components/icon/Icon.component.d.ts +0 -1
  35. package/dist/components/icon/Icon.component.d.ts.map +1 -1
  36. package/dist/components/icon/Icon.component.js +5 -2
  37. package/dist/components/infomessage/InfoMessage.component.js +1 -1
  38. package/dist/components/tablev2/Tablestyle.d.ts.map +1 -1
  39. package/dist/components/tablev2/Tablestyle.js +2 -3
  40. package/dist/components/tooltip/Tooltip.component.js +1 -1
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1 -0
  44. package/dist/utils.d.ts +14 -0
  45. package/dist/utils.d.ts.map +1 -1
  46. package/dist/utils.js +25 -0
  47. package/package.json +1 -1
  48. package/src/lib/components/charts/barchart/Barchart.tsx +123 -106
  49. package/src/lib/components/charts/common/chartUtils.test.ts +27 -12
  50. package/src/lib/components/charts/common/chartUtils.ts +67 -23
  51. package/src/lib/components/charts/index.ts +1 -1
  52. package/src/lib/components/charts/linetimeseries/LineTimeSerieChart.tsx +136 -516
  53. package/src/lib/components/charts/linetimeseries/LineTimeSerieChart.types.ts +93 -0
  54. package/src/lib/components/charts/linetimeseries/LineTimeSerieChartTooltip.tsx +137 -0
  55. package/src/lib/components/charts/linetimeseries/useChartData.ts +322 -0
  56. package/src/lib/components/charts/linetimeseries/useChartHover.ts +35 -0
  57. package/src/lib/components/checkbox/Checkbox.component.tsx +19 -20
  58. package/src/lib/components/constrainedtext/Constrainedtext.component.tsx +2 -2
  59. package/src/lib/components/dropdown/Dropdown.component.tsx +3 -0
  60. package/src/lib/components/error-pages/ErrorPage401.component.tsx +1 -1
  61. package/src/lib/components/error-pages/ErrorPage404.component.tsx +1 -1
  62. package/src/lib/components/error-pages/ErrorPage500.component.tsx +1 -1
  63. package/src/lib/components/icon/CustomsIcons.tsx +36 -0
  64. package/src/lib/components/icon/Icon.component.tsx +9 -2
  65. package/src/lib/components/infomessage/InfoMessage.component.tsx +1 -1
  66. package/src/lib/components/tablev2/Tablestyle.tsx +2 -4
  67. package/src/lib/components/tooltip/Tooltip.component.tsx +1 -1
  68. package/src/lib/index.ts +1 -0
  69. package/src/lib/utils.ts +38 -0
  70. package/stories/GlobalHealthBar/globalhealthbar.stories.tsx +1 -1
  71. package/stories/banner.stories.tsx +1 -1
  72. package/stories/linetimeseriechart.stories.tsx +325 -6
@@ -1 +1 @@
1
- {"version":3,"file":"Barchart.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/charts/barchart/Barchart.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAiB7D,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,SAAS;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;CACvE,EAAE,CAAC;AAEJ,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,YAAY,IAAI,CAAC,YAAY,EAAE;IACrE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC5E,KAAK,KAAK,CAAC,SAAS,CAAC;AAEtB,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,YAAY,IAAI,CACnD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAC1E,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,KACvE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAEhB,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,YAAY,IAAI;IAClD,IAAI,EAAE,YAAY,GAAG,QAAQ,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAIF,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,YAAY,SAAS,aAAa,CAAC,CAAC,CAAC,4CA4JvE,CAAC"}
1
+ {"version":3,"file":"Barchart.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/charts/barchart/Barchart.tsx"],"names":[],"mappings":"AAwBA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAiB7D,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;IACrB,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC5C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG,SAAS;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,CAAC,EAAE,CAAC;CACvE,EAAE,CAAC;AAEJ,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,YAAY,IAAI,CAAC,YAAY,EAAE;IACrE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC;IAC1B,MAAM,EAAE;QAAE,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CAC5E,KAAK,KAAK,CAAC,SAAS,CAAC;AAEtB,MAAM,MAAM,cAAc,CAAC,CAAC,SAAS,YAAY,IAAI,CACnD,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,EAC1E,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG;IAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,KACvE,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;AAEhB,MAAM,MAAM,aAAa,CAAC,CAAC,SAAS,YAAY,IAAI;IAClD,IAAI,EAAE,YAAY,GAAG,QAAQ,CAAC;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,OAAO,CAAC,EAAE,iBAAiB,CAAC,CAAC,CAAC,CAAC;IAC/B,WAAW,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IAChC,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC9B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,SAAS,GAAG,QAAQ,CAAC;IACtC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAIF,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,YAAY,SAAS,aAAa,CAAC,CAAC,CAAC,4CA6KvE,CAAC"}
@@ -1,12 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState, useRef } from 'react';
2
+ import { useState, useRef, useMemo, useCallback } from 'react';
3
3
  import { Bar, BarChart as RechartsBarChart, CartesianGrid, Tooltip, XAxis, YAxis, } from 'recharts';
4
4
  import { useTheme } from 'styled-components';
5
5
  import { Stack } from '../../../spacing';
6
6
  import { chartColors, fontSize } from '../../../style/theme';
7
7
  import { useChartLegend } from '../legend/ChartLegendWrapper';
8
8
  import { BarchartTooltip } from './BarchartTooltip';
9
- import { formatToISONumber, getTicks } from '../common/chartUtils';
9
+ import { formatTickValue, getTicks } from '../common/chartUtils';
10
10
  import { useChartData } from './Barchart.utils';
11
11
  import { ChartHeader, ChartError, ChartLoading, CustomTick, StyledResponsiveContainer, } from '../common/SharedComponents';
12
12
  const CHART_CONSTANTS = {
@@ -29,28 +29,38 @@ export const Barchart = (props) => {
29
29
  const chartRef = useRef(null);
30
30
  const { height = CHART_CONSTANTS.DEFAULT_HEIGHT, bars, type = { type: 'category' }, unitRange, stacked, stackedBarSort = 'default', defaultSort, tooltip, title, secondaryTitle, helpTooltip, rightTitle, isLoading, isError, } = props;
31
31
  // Create colorSet from ChartLegendWrapper
32
- const colorSet = bars === null || bars === void 0 ? void 0 : bars.reduce((acc, bar) => {
32
+ const colorSet = useMemo(() => bars === null || bars === void 0 ? void 0 : bars.reduce((acc, bar) => {
33
33
  const color = getColor(bar.label);
34
34
  if (color) {
35
35
  acc[bar.label] = color;
36
36
  }
37
37
  return acc;
38
- }, {});
38
+ }, {}), [bars, getColor]);
39
39
  const { rechartsBars, unitLabel, roundReferenceValue, rechartsData, topDomain, } = useChartData(bars || [], type, colorSet || {}, stacked, defaultSort, unitRange, stackedBarSort);
40
40
  const titleWithUnit = unitLabel ? `${title} (${unitLabel})` : title;
41
- return (_jsxs(Stack, { direction: "vertical", style: { gap: '0' }, children: [_jsx(ChartHeader, { title: titleWithUnit, secondaryTitle: secondaryTitle, helpTooltip: helpTooltip, rightTitle: rightTitle }), isError || (!bars && !isLoading) ? (_jsx(ChartError, { height: height })) : isLoading ? (_jsx(ChartLoading, { height: height })) : (_jsx(StyledResponsiveContainer, { ref: chartRef, width: "100%", height: height, children: _jsxs(RechartsBarChart, { data: rechartsData, accessibilityLayer: true, barSize: type.type === 'category'
42
- ? type.gap === 0
43
- ? undefined
44
- : CHART_CONSTANTS.BAR_SIZE
45
- : CHART_CONSTANTS.BAR_SIZE, height: height, margin: CHART_CONSTANTS.CHART_MARGIN, barCategoryGap: type.type === 'category' ? type.gap : undefined, children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), rechartsBars.map((bar) => {
46
- const { fill, dataKey, stackId } = bar;
47
- return (_jsx(Bar, { dataKey: dataKey, fill: chartColors[fill] || fill, minPointSize: stacked ? 0 : CHART_CONSTANTS.MIN_POINT_SIZE, stackId: stackId, isAnimationActive: false, onMouseOver: () => setHoveredValue(dataKey), onMouseLeave: () => setHoveredValue(undefined) }, dataKey));
48
- }), _jsx(YAxis, { interval: 0, domain: [0, topDomain], ticks: getTicks(roundReferenceValue, false), tickFormatter: (value) => formatToISONumber(value), axisLine: { stroke: theme.border }, tick: {
49
- fill: theme.textSecondary,
50
- fontSize: fontSize.smaller,
51
- }, orientation: "right" }), _jsx(XAxis, { dataKey: "category", tick: (props) => (_jsx(CustomTick, { ...props, type: type, tickWidthOffset: CHART_CONSTANTS.TICK_WIDTH_OFFSET })), type: "category", interval: 0, allowDataOverflow: true, tickLine: {
52
- stroke: theme.border,
53
- }, axisLine: {
54
- stroke: theme.border,
55
- } }), _jsx(Tooltip, { content: (props) => (_jsx(BarchartTooltip, { type: type, colorSet: colorSet, tooltipProps: props, hoveredValue: hoveredValue, tooltip: tooltip, unitLabel: unitLabel, chartContainerRef: chartRef })), cursor: false })] }) }))] }));
41
+ const tickFormatter = useCallback((value) => formatTickValue(value, roundReferenceValue), [roundReferenceValue]);
42
+ const renderChartContent = () => {
43
+ if (isError || (!bars && !isLoading)) {
44
+ return _jsx(ChartError, { height: height });
45
+ }
46
+ if (isLoading) {
47
+ return _jsx(ChartLoading, { height: height });
48
+ }
49
+ return (_jsx(StyledResponsiveContainer, { ref: chartRef, width: "100%", height: height, children: _jsxs(RechartsBarChart, { data: rechartsData, accessibilityLayer: true, barSize: type.type === 'category'
50
+ ? type.gap === 0
51
+ ? undefined
52
+ : CHART_CONSTANTS.BAR_SIZE
53
+ : CHART_CONSTANTS.BAR_SIZE, height: height, margin: CHART_CONSTANTS.CHART_MARGIN, barCategoryGap: type.type === 'category' ? type.gap : undefined, children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), rechartsBars.map((bar) => {
54
+ const { fill, dataKey, stackId } = bar;
55
+ return (_jsx(Bar, { dataKey: dataKey, fill: chartColors[fill] || fill, minPointSize: stacked ? 0 : CHART_CONSTANTS.MIN_POINT_SIZE, stackId: stackId, isAnimationActive: false, onMouseOver: () => setHoveredValue(dataKey), onMouseLeave: () => setHoveredValue(undefined) }, dataKey));
56
+ }), _jsx(YAxis, { interval: 0, domain: [0, topDomain], ticks: getTicks(roundReferenceValue, false), tickFormatter: tickFormatter, axisLine: { stroke: theme.border }, tick: {
57
+ fill: theme.textSecondary,
58
+ fontSize: fontSize.smaller,
59
+ }, orientation: "right" }), _jsx(XAxis, { dataKey: "category", tick: (props) => (_jsx(CustomTick, { ...props, type: type, tickWidthOffset: CHART_CONSTANTS.TICK_WIDTH_OFFSET })), type: "category", interval: 0, allowDataOverflow: true, tickLine: {
60
+ stroke: theme.border,
61
+ }, axisLine: {
62
+ stroke: theme.border,
63
+ } }), _jsx(Tooltip, { content: (props) => (_jsx(BarchartTooltip, { type: type, colorSet: colorSet, tooltipProps: props, hoveredValue: hoveredValue, tooltip: tooltip, unitLabel: unitLabel, chartContainerRef: chartRef })), cursor: false })] }) }));
64
+ };
65
+ return (_jsxs(Stack, { direction: "vertical", style: { gap: '0' }, children: [_jsx(ChartHeader, { title: titleWithUnit, secondaryTitle: secondaryTitle, helpTooltip: helpTooltip, rightTitle: rightTitle }), renderChartContent()] }));
56
66
  };
@@ -48,7 +48,7 @@ export declare const normalizeChartDataWithUnits: <T extends Record<string, any>
48
48
  * Missing data points are only added when the gap between consecutive data points is bigger than 2 intervals
49
49
  * Used by LineTimeSerieChart and Sparkline
50
50
  *
51
- * @param orginalValues - The array of the data points are already sorted according to the time series
51
+ * @param originalValues - The array of the data points are already sorted according to the time series
52
52
  * @param startingTimeStamp - The starting timestamp in seconds
53
53
  * @param sampleDuration - The time span value in seconds
54
54
  * @param sampleInterval - The time difference between two data points in seconds
@@ -87,5 +87,10 @@ export declare const formatXAxisDate: (duration: number) => "time" | "day-month-
87
87
  * @returns Formatted string type
88
88
  */
89
89
  export declare const getTooltipDateFormat: (duration: number) => TooltipDateFormat;
90
- export declare const formatToISONumber: (value: number) => string;
90
+ /**
91
+ * Formats a tick value for chart Y-axis display.
92
+ * - Fixed decimals for alignment when topValue < 1 (e.g., 0.1 → 0.10)
93
+ * - Compact notation for large values (>= 10k)
94
+ */
95
+ export declare const formatTickValue: (value: number, topValue: number) => string;
91
96
  //# sourceMappingURL=chartUtils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chartUtils.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/charts/common/chartUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAMrC,eAAO,MAAM,eAAe;;CAAwB,CAAC;AAMrD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,UAAW,MAAM,KAAG,MA+BtD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,aAAc,MAAM,iBAAiB,OAAO,aAmBhE,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,EACH,QAAQ,EAAE,MAAM,GACf;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAyCA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QACjE,CAAC,EAAE,YACC,MAAM,aACL,SAAS,GAAG,SAAS,cACpB,MAAM,KACjB;IACD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,CAAC,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CAgCnB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,EAClD,iBAAiB,CAAC,EAAE,MAAM,EAC1B,cAAc,CAAC,EAAE,MAAM,EACvB,cAAc,CAAC,EAAE,MAAM,GACtB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,CAoEpC;AACD;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,aAChB,MAAM,KACf,MAAM,GAAG,uBAAuB,GAAG,sBAQrC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,iBAUxD,CAAC;AAEF,eAAO,MAAM,iBAAiB,UAAW,MAAM,KAAG,MAKjD,CAAC"}
1
+ {"version":3,"file":"chartUtils.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/charts/common/chartUtils.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAOrC,eAAO,MAAM,eAAe;;CAAwB,CAAC;AA+BrD;;;GAGG;AACH,eAAO,MAAM,sBAAsB,UAAW,MAAM,KAAG,MA+BtD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,QAAQ,aACT,MAAM,iBACD,OAAO,KACrB,MAAM,EAmBR,CAAC;AAEF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE;IACT,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;CACf,EAAE,EACH,QAAQ,EAAE,MAAM,GACf;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/B,CAyCA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,2BAA2B,GAAI,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,QACjE,CAAC,EAAE,YACC,MAAM,aACL,SAAS,GAAG,SAAS,cACpB,MAAM,KACjB;IACD,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,CAAC,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CAgCnB,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,EAClD,iBAAiB,CAAC,EAAE,MAAM,EAC1B,cAAc,CAAC,EAAE,MAAM,EACvB,cAAc,CAAC,EAAE,MAAM,GACtB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,CA4EpC;AACD;;;;;;;;;;;;;;;;;;GAkBG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,aAChB,MAAM,KACf,MAAM,GAAG,uBAAuB,GAAG,sBAQrC,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,iBAUxD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,eAAe,UAAW,MAAM,YAAY,MAAM,KAAG,MAOjE,CAAC"}
@@ -1,4 +1,5 @@
1
1
  import { NAN_STRING } from '../../constants';
2
+ import { formatISONumber } from '../../../utils';
2
3
  /* -------------------------------------------------------------------------- */
3
4
  /* constants */
4
5
  /* -------------------------------------------------------------------------- */
@@ -6,6 +7,28 @@ export const maxWidthTooltip = { maxWidth: '20rem' };
6
7
  /* -------------------------------------------------------------------------- */
7
8
  /* utils functions */
8
9
  /* -------------------------------------------------------------------------- */
10
+ /**
11
+ * Get the appropriate rounding increment based on value magnitude.
12
+ * - For values < 5 * magnitude: use half magnitude (finer granularity)
13
+ * - For values >= 5 * magnitude: use full magnitude
14
+ *
15
+ * Examples:
16
+ * - 150 → increment 50 (150 < 500, so use 100/2)
17
+ * - 550 → increment 100 (550 >= 500, so use 100)
18
+ * - 1500 → increment 500 (1500 < 5000, so use 1000/2)
19
+ * - 5500 → increment 1000 (5500 >= 5000, so use 1000)
20
+ */
21
+ const getIncrement = (value) => {
22
+ if (value < 10)
23
+ return 1;
24
+ const magnitude = Math.pow(10, Math.floor(Math.log10(value)));
25
+ // If value is in lower half of magnitude range, use half magnitude
26
+ if (value < 5 * magnitude) {
27
+ return magnitude / 2;
28
+ }
29
+ // Otherwise use full magnitude
30
+ return magnitude;
31
+ };
9
32
  /**
10
33
  * Round a value to a nice number for chart display
11
34
  * Used by Barchart and LineTimeSerieChart for Y-axis scaling
@@ -16,23 +39,24 @@ export const getRoundReferenceValue = (value) => {
16
39
  // Buffer the value by 10% to avoid being too close to the edge of the chart
17
40
  const bufferedValue = value * 1.1;
18
41
  if (value >= 10) {
19
- const remainder = value % 10;
20
- const incremented = value + (10 - remainder);
21
- // If the remainder is less than 5, round down to the nearest 10
22
- if (remainder < 5) {
23
- return value - remainder;
42
+ const increment = getIncrement(value);
43
+ const remainder = value % increment;
44
+ const roundedDown = value - remainder;
45
+ const roundedUp = roundedDown + increment;
46
+ // If remainder is less than half the increment, round down
47
+ if (remainder < increment / 2) {
48
+ return roundedDown;
24
49
  }
25
- // If incrementing would exceed the buffered max, also round down
26
- if (incremented > bufferedValue) {
27
- return value - remainder;
50
+ // If rounding up would exceed the buffered max, round down
51
+ if (roundedUp > bufferedValue) {
52
+ return roundedDown;
28
53
  }
29
- // Otherwise, round up to the next 10
30
- return incremented;
54
+ // Otherwise, round up
55
+ return roundedUp;
31
56
  }
57
+ // For values < 10, use the magnitude-based approach
32
58
  const magnitude = Math.pow(10, Math.floor(Math.log10(value)));
33
59
  const remainder = bufferedValue % magnitude;
34
- // Round to nice numbers based on normalized value
35
- // appearance for small values
36
60
  return remainder === 0 ? bufferedValue : bufferedValue - remainder;
37
61
  };
38
62
  /**
@@ -131,7 +155,7 @@ export const normalizeChartDataWithUnits = (data, maxValue, unitRange, excludeKe
131
155
  * Missing data points are only added when the gap between consecutive data points is bigger than 2 intervals
132
156
  * Used by LineTimeSerieChart and Sparkline
133
157
  *
134
- * @param orginalValues - The array of the data points are already sorted according to the time series
158
+ * @param originalValues - The array of the data points are already sorted according to the time series
135
159
  * @param startingTimeStamp - The starting timestamp in seconds
136
160
  * @param sampleDuration - The time span value in seconds
137
161
  * @param sampleInterval - The time difference between two data points in seconds
@@ -146,9 +170,13 @@ export function addMissingDataPoint(originalValues, startingTimeStamp, sampleDur
146
170
  sampleInterval <= 0) {
147
171
  return [];
148
172
  }
149
- // If there are no original values, return empty array
173
+ // If there are no original values, generate placeholder timestamps for the entire duration
150
174
  if (originalValues.length === 0) {
151
- return [];
175
+ const newValues = [];
176
+ for (let i = startingTimeStamp; i < startingTimeStamp + sampleDuration; i += sampleInterval) {
177
+ newValues.push([i, NAN_STRING]);
178
+ }
179
+ return newValues;
152
180
  }
153
181
  const newValues = [];
154
182
  // add missing data points for the starting time
@@ -235,9 +263,16 @@ export const getTooltipDateFormat = (duration) => {
235
263
  return 'day-month-abbreviated-year-hour-minute';
236
264
  }
237
265
  };
238
- export const formatToISONumber = (value) => {
239
- const formattedValue = new Intl.NumberFormat('fr-FR')
240
- .format(value)
241
- .replace(',', '.');
242
- return formattedValue;
266
+ /**
267
+ * Formats a tick value for chart Y-axis display.
268
+ * - Fixed decimals for alignment when topValue < 1 (e.g., 0.1 → 0.10)
269
+ * - Compact notation for large values (>= 10k)
270
+ */
271
+ export const formatTickValue = (value, topValue) => {
272
+ const decimals = topValue < 1 ? Math.ceil(-Math.log10(topValue)) + 1 : 2;
273
+ return formatISONumber(value, {
274
+ decimals,
275
+ fixedDecimals: topValue < 1,
276
+ compact: topValue >= 10000,
277
+ });
243
278
  };
@@ -1,7 +1,7 @@
1
1
  export { Barchart } from './barchart/Barchart';
2
2
  export type { BarchartProps, BarchartBars, BarchartTooltipFn, BarchartSortFn, Point, } from './barchart/Barchart';
3
3
  export { LineTimeSerieChart } from './linetimeseries/LineTimeSerieChart';
4
- export type { LineChartProps, Serie, } from './linetimeseries/LineTimeSerieChart';
4
+ export type { LineChartProps, Serie, } from './linetimeseries/LineTimeSerieChart.types';
5
5
  export { GlobalHealthBar } from './globalhealthbar/GlobalHealthBar';
6
6
  export type { GlobalHealthProps } from './globalhealthbar/GlobalHealthBar';
7
7
  export type { Alert } from './globalhealthbar/GlobalHealthBar.hooks';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/components/charts/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,YAAY,EACV,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,KAAK,GACN,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,YAAY,EACV,cAAc,EACd,KAAK,GACN,MAAM,qCAAqC,CAAC;AAE7C,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,YAAY,EAAE,KAAK,EAAE,MAAM,yCAAyC,CAAC;AAErE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,cAAc,GACf,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,sBAAsB,EACtB,QAAQ,EACR,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/components/charts/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/C,YAAY,EACV,aAAa,EACb,YAAY,EACZ,iBAAiB,EACjB,cAAc,EACd,KAAK,GACN,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,YAAY,EACV,cAAc,EACd,KAAK,GACN,MAAM,2CAA2C,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpE,YAAY,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,YAAY,EAAE,KAAK,EAAE,MAAM,yCAAyC,CAAC;AAErE,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAGlD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,cAAc,GACf,MAAM,6BAA6B,CAAC;AAGrC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EACL,qBAAqB,EACrB,gBAAgB,EAChB,kBAAkB,EAClB,0BAA0B,EAC1B,kBAAkB,GACnB,MAAM,uBAAuB,CAAC;AAG/B,OAAO,EACL,sBAAsB,EACtB,QAAQ,EACR,YAAY,EACZ,mBAAmB,EACnB,eAAe,EACf,oBAAoB,EACpB,2BAA2B,GAC5B,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC"}
@@ -1,55 +1,20 @@
1
- import React from 'react';
2
- import { TooltipContentProps } from 'recharts';
3
- export type Serie = {
4
- resource: string;
5
- data: [number, number | string | null][];
6
- getTooltipLabel: (metricPrefix?: string, resource?: string) => string;
7
- metricPrefix?: string;
8
- isLineDashed?: boolean;
9
- };
10
- type NonSymmetricalChartSerie = {
11
- yAxisType?: 'default' | 'percentage';
12
- series: Serie[] | undefined;
13
- };
14
- type SymmetricalChartSerie = {
15
- yAxisType: 'symmetrical';
16
- series: {
17
- above: Serie[] | undefined;
18
- below: Serie[] | undefined;
19
- } | undefined;
20
- };
21
- export type LineChartProps = (NonSymmetricalChartSerie | SymmetricalChartSerie) & {
22
- title: string;
23
- height: number;
24
- startingTimeStamp: number;
25
- interval: number;
26
- duration: number;
27
- unitRange?: {
28
- threshold: number;
29
- label: string;
30
- }[];
31
- syncId?: string;
32
- isLoading?: boolean;
33
- /**
34
- * The format of the x axis, default is 'date-time' which is like 01 Sep 16:00
35
- * If you want to display the date only, you can set it to 'date' which is like 2025-09-01
36
- * This will affect the format of the tooltip as well
37
- */
38
- timeFormat?: 'date-time' | 'date';
39
- yAxisTitle?: string;
40
- helpText?: string;
41
- renderTooltip?: (tooltipProps: TooltipContentProps<number, string>, unitLabel?: string, duration?: number) => React.ReactNode;
42
- };
1
+ import { LineChartProps } from './LineTimeSerieChart.types';
43
2
  /**
44
- * Props for LineTimeSerieChart component
45
- * @param series - The data series to display
3
+ * LineTimeSerieChart - A time series line chart component
4
+ *
5
+ * @param series - The data series to display (can be symmetrical with above/below)
46
6
  * @param title - The title of the chart
47
7
  * @param height - The height of the chart in pixels
48
8
  * @param startingTimeStamp - Starting timestamp in seconds
49
9
  * @param interval - Interval between data points in seconds
50
10
  * @param duration - Total duration of the chart in seconds
51
- *
11
+ * @param unitRange - Configuration for automatic unit scaling
12
+ * @param syncId - ID to synchronize multiple charts
13
+ * @param isLoading - Whether to show loading state
14
+ * @param yAxisType - Type of Y-axis: 'default', 'percentage', or 'symmetrical'
15
+ * @param yAxisTitle - Label for the Y-axis
16
+ * @param helpText - Help text shown as tooltip
17
+ * @param renderTooltip - Custom tooltip renderer
52
18
  */
53
- export declare function LineTimeSerieChart({ series, title, height, startingTimeStamp, interval, duration, unitRange, isLoading, timeFormat, yAxisType, yAxisTitle, helpText, syncId, renderTooltip, ...rest }: LineChartProps): import("react/jsx-runtime").JSX.Element;
54
- export {};
19
+ export declare function LineTimeSerieChart({ series, title, height, startingTimeStamp, interval, duration, unitRange, isLoading, yAxisType, yAxisTitle, helpText, syncId, renderTooltip, }: LineChartProps): import("react/jsx-runtime").JSX.Element;
55
20
  //# sourceMappingURL=LineTimeSerieChart.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LineTimeSerieChart.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/charts/linetimeseries/LineTimeSerieChart.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAiD,MAAM,OAAO,CAAC;AACtE,OAAO,EAML,mBAAmB,EAGpB,MAAM,UAAU,CAAC;AAiClB,MAAM,MAAM,KAAK,GAAG;IAElB,QAAQ,EAAE,MAAM,CAAC;IAEjB,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAEzC,eAAe,EAAE,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAEtE,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAEF,KAAK,wBAAwB,GAAG;IAC9B,SAAS,CAAC,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,MAAM,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;CAC7B,CAAC;AAGF,KAAK,qBAAqB,GAAG;IAC3B,SAAS,EAAE,aAAa,CAAC;IACzB,MAAM,EACF;QACE,KAAK,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;QAC3B,KAAK,EAAE,KAAK,EAAE,GAAG,SAAS,CAAC;KAC5B,GACD,SAAS,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CACzB,wBAAwB,GACxB,qBAAqB,CACxB,GAAG;IACF,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,iBAAiB,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE;QACV,SAAS,EAAE,MAAM,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC;KACf,EAAE,CAAC;IACJ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;;;OAIG;IACH,UAAU,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,CACd,YAAY,EAAE,mBAAmB,CAAC,MAAM,EAAE,MAAM,CAAC,EACjD,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,KACd,KAAK,CAAC,SAAS,CAAC;CACtB,CAAC;AAiHF;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,KAAK,EACL,MAAM,EACN,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAiB,EACjB,UAAwB,EACxB,SAAqB,EACrB,UAAU,EACV,QAAQ,EACR,MAAM,EACN,aAAa,EACb,GAAG,IAAI,EACR,EAAE,cAAc,2CA2VhB"}
1
+ {"version":3,"file":"LineTimeSerieChart.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/charts/linetimeseries/LineTimeSerieChart.tsx"],"names":[],"mappings":"AAoBA,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAW5D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,KAAK,EACL,MAAM,EACN,iBAAiB,EACjB,QAAQ,EACR,QAAQ,EACR,SAAS,EACT,SAAiB,EACjB,SAAqB,EACrB,UAAU,EACV,QAAQ,EACR,MAAM,EACN,aAAa,GACd,EAAE,cAAc,2CAgJhB"}
@@ -1,5 +1,5 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import React, { useCallback, useMemo, useRef, useState } from 'react';
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useCallback, useRef } from 'react';
3
3
  import { CartesianGrid, Line, LineChart, ReferenceLine, Tooltip, XAxis, YAxis, } from 'recharts';
4
4
  import styled, { useTheme } from 'styled-components';
5
5
  import { Stack } from '../../../spacing';
@@ -7,241 +7,67 @@ import { fontSize } from '../../../style/theme';
7
7
  import { IconHelp } from '../../iconhelper/IconHelper';
8
8
  import { Loader } from '../../loader/Loader.component';
9
9
  import { ChartTitleText } from '../../text/Text.component';
10
- import { LegendShape } from '../legend/ChartLegend';
11
- import { useChartLegend } from '../legend/ChartLegendWrapper';
12
10
  import { StyledResponsiveContainer } from '../common/SharedComponents';
13
- import { ChartTooltipHeader, ChartTooltipItem, ChartTooltipItemsContainer, ChartTooltipPortal, ChartTooltipSeparator, TooltipHeader, } from '../common/ChartTooltip';
14
- import { addMissingDataPoint, formatToISONumber, getTicks, maxWidthTooltip, normalizeChartDataWithUnits, } from '../common/chartUtils';
11
+ import { formatTickValue, getTicks, maxWidthTooltip } from '../common/chartUtils';
15
12
  import { formatXAxisLabel } from './LineTimeSerieChart.utils';
13
+ import { LineTimeSerieChartTooltip } from './LineTimeSerieChartTooltip';
14
+ import { useChartHover } from './useChartHover';
15
+ import { useChartData } from './useChartData';
16
16
  const LineTemporalChartWrapper = styled.div `
17
17
  display: flex;
18
18
  flex-direction: column;
19
19
  justify-content: flex-start;
20
20
  `;
21
- const LineTimeSerieChartTooltip = ({ unitLabel, duration, isChartActive, tooltipProps, renderTooltip, hoveredValue, isSymmetrical, chartContainerRef, }) => {
22
- const { active, payload, label, coordinate } = tooltipProps;
23
- if (!active || !payload || !payload.length || !label || !isChartActive)
24
- return null;
25
- const tooltipContent = renderTooltip ? (renderTooltip(tooltipProps, unitLabel, duration)) : (_jsxs(_Fragment, { children: [_jsx(ChartTooltipHeader, { children: _jsx(TooltipHeader, { duration: duration, value: label }) }), _jsx(ChartTooltipItemsContainer, { children: (() => {
26
- // We can't use the default itemSorter method because it's a custom tooltip.
27
- // Sort the payload here instead
28
- const sortedPayload = [...payload].sort((a, b) => {
29
- const aValue = a.value;
30
- const bValue = b.value;
31
- if (aValue >= 0 && bValue >= 0) {
32
- return bValue - aValue; // Higher positive values first
33
- }
34
- if (aValue < 0 && bValue < 0) {
35
- return bValue - aValue; // Lower negative values first
36
- }
37
- return bValue - aValue; // Positives before negatives
38
- });
39
- // Find the transition point between positive and negative values
40
- const separatorIndex = sortedPayload.findIndex((entry) => entry.value < 0);
41
- const hasBothPositiveAndNegative = separatorIndex > 0 && separatorIndex < sortedPayload.length;
42
- return sortedPayload.map((entry, index) => {
43
- const legendIcon = (_jsx(LegendShape, { color: entry.color, shape: "line", chartColors: { [entry.color]: entry.color } }));
44
- const isHovered = entry.name === hoveredValue;
45
- const formattedValue = !Number.isFinite(entry.value)
46
- ? '-'
47
- : `${entry.value.toFixed(2)}${unitLabel ? ` ${unitLabel}` : ''}`;
48
- return (_jsxs(React.Fragment, { children: [isSymmetrical &&
49
- hasBothPositiveAndNegative &&
50
- index === separatorIndex && _jsx(ChartTooltipSeparator, {}), _jsx(ChartTooltipItem, { label: entry.name, value: formattedValue, legendIcon: legendIcon, isHovered: isHovered })] }, index));
51
- });
52
- })() })] }));
53
- return (_jsx(ChartTooltipPortal, { coordinate: coordinate, chartContainerRef: chartContainerRef, isVisible: active && isChartActive, children: tooltipContent }));
54
- };
55
- const isSymmetricalSeries = (series) => {
56
- return 'above' in series && 'below' in series;
57
- };
58
21
  /**
59
- * Props for LineTimeSerieChart component
60
- * @param series - The data series to display
22
+ * LineTimeSerieChart - A time series line chart component
23
+ *
24
+ * @param series - The data series to display (can be symmetrical with above/below)
61
25
  * @param title - The title of the chart
62
26
  * @param height - The height of the chart in pixels
63
27
  * @param startingTimeStamp - Starting timestamp in seconds
64
28
  * @param interval - Interval between data points in seconds
65
29
  * @param duration - Total duration of the chart in seconds
66
- *
30
+ * @param unitRange - Configuration for automatic unit scaling
31
+ * @param syncId - ID to synchronize multiple charts
32
+ * @param isLoading - Whether to show loading state
33
+ * @param yAxisType - Type of Y-axis: 'default', 'percentage', or 'symmetrical'
34
+ * @param yAxisTitle - Label for the Y-axis
35
+ * @param helpText - Help text shown as tooltip
36
+ * @param renderTooltip - Custom tooltip renderer
67
37
  */
68
- export function LineTimeSerieChart({ series, title, height, startingTimeStamp, interval, duration, unitRange, isLoading = false, timeFormat = 'date-time', yAxisType = 'default', yAxisTitle, helpText, syncId, renderTooltip, ...rest }) {
38
+ export function LineTimeSerieChart({ series, title, height, startingTimeStamp, interval, duration, unitRange, isLoading = false, yAxisType = 'default', yAxisTitle, helpText, syncId, renderTooltip, }) {
69
39
  const theme = useTheme();
70
- const { getColor, selectedResources } = useChartLegend();
71
40
  const chartRef = useRef(null);
72
- const [isChartActive, setIsChartActive] = useState(false);
73
- const [hoveredValue, setHoveredValue] = useState(undefined);
74
- const chartData = useMemo(() => {
75
- // Guard against empty/undefined series data
76
- if (!series || (Array.isArray(series) && series.length === 0)) {
77
- return [];
78
- }
79
- // Handle symmetrical series with empty above/below arrays
80
- if (isSymmetricalSeries(series)) {
81
- if ((!series.above || series.above.length === 0) &&
82
- (!series.below || series.below.length === 0)) {
83
- return [];
84
- }
85
- }
86
- // 1. Add missing data points
87
- const normalizedSeries = yAxisType === 'symmetrical' && isSymmetricalSeries(series)
88
- ? {
89
- above: series.above
90
- ? series.above.map((line) => ({
91
- ...line,
92
- data: addMissingDataPoint(line.data, startingTimeStamp, duration, interval),
93
- }))
94
- : [],
95
- // Convert positive values to negative values
96
- below: series.below
97
- ? series.below.map((line) => ({
98
- ...line,
99
- data: addMissingDataPoint(line.data, startingTimeStamp, duration, interval).map(([timestamp, value]) => [
100
- timestamp,
101
- value === null ? null : `-${Number(value)}`,
102
- ]),
103
- }))
104
- : [],
105
- }
106
- : series.map((line) => ({
107
- ...line,
108
- data: addMissingDataPoint(line.data, startingTimeStamp, duration, interval),
109
- }));
110
- // 2. Convert directly to Recharts format
111
- // Initialize an object to hold data points by timestamp
112
- const dataPointsByTime = {};
113
- const seriesToProcess = yAxisType === 'symmetrical' && isSymmetricalSeries(normalizedSeries)
114
- ? [...normalizedSeries.above, ...normalizedSeries.below]
115
- : normalizedSeries;
116
- seriesToProcess.forEach((serie) => {
117
- const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
118
- serie.data.forEach((point) => {
119
- const timestamp = typeof point[0] === 'number' ? point[0] * 1000 : Number(point[0]);
120
- const value = point[1];
121
- // Initialize this timestamp if it doesn't exist
122
- if (!dataPointsByTime[timestamp]) {
123
- dataPointsByTime[timestamp] = { timestamp };
124
- }
125
- // Add this metric's value to the data point, and convert the value to a number if it's a string
126
- dataPointsByTime[timestamp][label] =
127
- typeof value === 'string' ? Number(value) : value;
128
- });
129
- });
130
- // Convert object to array for Recharts
131
- return Object.values(dataPointsByTime).sort((a, b) => a.timestamp - b.timestamp);
132
- }, [series, startingTimeStamp, duration, interval, yAxisType]);
133
- // Calculate evenly spaced ticks that avoid the very beginning and end
134
- const xAxisTicks = useMemo(() => {
135
- if (!chartData || chartData.length === 0)
136
- return [];
137
- const timestamps = chartData.map((d) => d.timestamp);
138
- const minTimestamp = Math.min(...timestamps);
139
- const maxTimestamp = Math.max(...timestamps);
140
- const timeRange = maxTimestamp - minTimestamp;
141
- // Add padding to avoid labels at the very edges (10% padding on each side)
142
- const padding = timeRange * 0.1;
143
- const paddedStart = minTimestamp + padding;
144
- const paddedEnd = maxTimestamp - padding;
145
- const paddedRange = paddedEnd - paddedStart;
146
- // Create 5 evenly spaced ticks within the padded range
147
- const numTicks = 5;
148
- const tickInterval = paddedRange / (numTicks - 1);
149
- const evenlySpacedTicks = Array.from({ length: numTicks }, (_, index) => paddedStart + index * tickInterval);
150
- return evenlySpacedTicks;
151
- }, [chartData]);
152
- // 3. Transform the data base on the valuebase
153
- const { topValue, unitLabel, rechartsData, topDomain } = useMemo(() => {
154
- var _a;
155
- const values = chartData.flatMap((dataPoint) => Object.entries(dataPoint)
156
- .filter(([key]) => key !== 'timestamp')
157
- .map(([_, value]) => {
158
- if (value === null || value === undefined)
159
- return null;
160
- const num = typeof value === 'string' ? Number(value) : value;
161
- return !isNaN(num) ? num : null;
162
- })
163
- .filter((value) => value !== null));
164
- // Guard against empty values array
165
- if (values.length === 0) {
166
- return {
167
- topValue: 100, // Default value for empty charts
168
- unitLabel: yAxisType === 'percentage' ? '%' : undefined,
169
- rechartsData: [],
170
- topDomain: 100,
171
- };
172
- }
173
- const top = Math.abs(Math.max(...values));
174
- const bottom = Math.abs(Math.min(...values));
175
- const maxValue = Math.max(top, bottom);
176
- // Use shared normalization function
177
- const result = normalizeChartDataWithUnits(chartData, maxValue, unitRange, 'timestamp');
178
- // For percentage charts, ensure Y-axis goes to at least 100%
179
- const topDomain = yAxisType === 'percentage'
180
- ? Math.max(result.topDomain, 100)
181
- : result.topDomain;
182
- return {
183
- topValue: yAxisType === 'percentage' ? Math.max(result.topValue, 100) : result.topValue,
184
- unitLabel: (_a = result.unitLabel) !== null && _a !== void 0 ? _a : (yAxisType === 'percentage' ? '%' : undefined),
185
- rechartsData: result.rechartsData,
186
- topDomain,
187
- };
188
- }, [chartData, yAxisType, unitRange]);
189
- // Group series by resource and create color mapping
190
- const { colorMapping, groupedSeries } = useMemo(() => {
191
- const mapping = {};
192
- // Guard against empty/undefined series
193
- if (!series) {
194
- return { colorMapping: mapping, groupedSeries: {} };
195
- }
196
- const allSeries = isSymmetricalSeries(series)
197
- ? [...(series.above || []), ...(series.below || [])]
198
- : series;
199
- // Group series by resource
200
- const groups = allSeries
201
- .filter((serie) => selectedResources.includes(serie.resource))
202
- .reduce((acc, serie) => {
203
- const key = serie.resource;
204
- if (!acc[key]) {
205
- acc[key] = [];
206
- }
207
- acc[key].push(serie);
208
- return acc;
209
- }, {});
210
- // Get colors from the ChartLegend context
211
- Object.keys(groups).forEach((resource) => {
212
- const color = getColor(resource);
213
- if (color) {
214
- mapping[resource] = color;
215
- }
216
- else {
217
- console.warn(`Color not defined for resource: ${resource}`);
218
- }
219
- });
220
- return {
221
- colorMapping: mapping,
222
- groupedSeries: groups,
223
- };
224
- }, [series, getColor, selectedResources]);
225
- // Format time for display the tick in the x axis
41
+ // Hover state management for tooltip display
42
+ const { handleMouseEnter, handleMouseLeave, chartId } = useChartHover();
43
+ // Process chart data
44
+ const { rechartsData, topDomain, topValue, unitLabel, xAxisTicks, linesToRender, belowSeriesLabels, } = useChartData({
45
+ series,
46
+ startingTimeStamp,
47
+ duration,
48
+ interval,
49
+ yAxisType,
50
+ unitRange,
51
+ });
52
+ // Format X-axis labels based on duration
226
53
  const formatXAxisLabelCallback = useCallback((timestamp) => formatXAxisLabel(timestamp, duration), [duration]);
227
- return (_jsxs(LineTemporalChartWrapper, { children: [_jsxs(Stack, { gap: "r4", children: [_jsxs(ChartTitleText, { children: [title, " ", unitLabel && `(${unitLabel})`] }), helpText && (_jsx(IconHelp, { tooltipMessage: helpText, overlayStyle: maxWidthTooltip })), isLoading && _jsx(Loader, {})] }), _jsx("div", { children: _jsx(StyledResponsiveContainer, { width: "100%", height: height, children: _jsxs(LineChart, { data: rechartsData, ref: chartRef, margin: { top: 0, right: 0, bottom: 0, left: 0 }, "aria-label": `Time series chart for ${title}`, syncId: syncId, onMouseEnter: () => setIsChartActive(true), onMouseLeave: () => setIsChartActive(false), accessibilityLayer: true, children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), _jsx(XAxis, { dataKey: "timestamp", type: "number", domain: ['dataMin', 'dataMax'], ticks: xAxisTicks, tickFormatter: formatXAxisLabelCallback, tickCount: 5, tick: {
228
- fill: theme.textSecondary,
229
- fontSize: fontSize.smaller,
230
- }, axisLine: { stroke: theme.border } }), _jsx(YAxis, { orientation: "right", label: {
231
- value: yAxisTitle,
232
- angle: 90,
233
- dx: 20,
234
- style: {
235
- fill: theme.textSecondary,
236
- fontSize: fontSize.smaller,
237
- },
238
- }, domain: yAxisType === 'symmetrical'
239
- ? [-topDomain, topDomain]
240
- : [0, topDomain], axisLine: { stroke: theme.border }, tick: {
54
+ // Format Y-axis tick values
55
+ const tickFormatter = useCallback((value) => formatTickValue(value, topValue), [topValue]);
56
+ return (_jsxs(LineTemporalChartWrapper, { children: [_jsxs(Stack, { gap: "r4", children: [_jsxs(ChartTitleText, { children: [title, " ", unitLabel && `(${unitLabel})`] }), helpText && (_jsx(IconHelp, { tooltipMessage: helpText, overlayStyle: maxWidthTooltip })), isLoading && _jsx(Loader, {})] }), _jsx(StyledResponsiveContainer, { width: "100%", height: height, children: _jsxs(LineChart, { ref: chartRef, data: rechartsData, margin: { top: 0, right: 0, bottom: 0, left: 0 }, "aria-label": `Time series chart for ${title}`, syncId: syncId, accessibilityLayer: true, onMouseEnter: handleMouseEnter, onMouseLeave: handleMouseLeave, children: [_jsx(CartesianGrid, { vertical: true, horizontal: true, verticalPoints: [0], horizontalPoints: [0], stroke: theme.border, fill: theme.backgroundLevel4, strokeWidth: 1 }), _jsx(XAxis, { dataKey: "timestamp", type: "number", domain: ['dataMin', 'dataMax'], ticks: xAxisTicks, tickFormatter: formatXAxisLabelCallback, tickCount: 5, tick: {
57
+ fill: theme.textSecondary,
58
+ fontSize: fontSize.smaller,
59
+ }, axisLine: { stroke: theme.border } }), _jsx(YAxis, { orientation: "right", label: {
60
+ value: yAxisTitle,
61
+ angle: 90,
62
+ dx: 20,
63
+ style: {
241
64
  fill: theme.textSecondary,
242
65
  fontSize: fontSize.smaller,
243
- }, tickFormatter: (value) => formatToISONumber(value), ticks: getTicks(topValue, yAxisType === 'symmetrical'), interval: 0 }), _jsx(Tooltip, { content: (props) => (_jsx(LineTimeSerieChartTooltip, { unitLabel: unitLabel, duration: duration, renderTooltip: renderTooltip, isSymmetrical: yAxisType === 'symmetrical', tooltipProps: props, isChartActive: isChartActive, hoveredValue: hoveredValue, chartContainerRef: chartRef })) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), Object.entries(groupedSeries).map(([resource, resourceSeries]) => resourceSeries.map((serie, serieIndex) => {
244
- const label = serie.getTooltipLabel(serie.metricPrefix, serie.resource);
245
- return (_jsx(Line, { type: "monotone", dataKey: label, stroke: colorMapping[resource], dot: false, isAnimationActive: false, strokeDasharray: serie.isLineDashed ? '4 4' : undefined, onMouseEnter: () => setHoveredValue(label), onMouseLeave: () => setHoveredValue(undefined) }, `${title}-${resource}-${serieIndex}`));
246
- }))] }) }) })] }));
66
+ },
67
+ }, domain: yAxisType === 'symmetrical'
68
+ ? [-topDomain, topDomain]
69
+ : [0, topDomain], allowDataOverflow: true, axisLine: { stroke: theme.border }, tick: {
70
+ fill: theme.textSecondary,
71
+ fontSize: fontSize.smaller,
72
+ }, tickFormatter: tickFormatter, ticks: getTicks(topValue, yAxisType === 'symmetrical'), interval: 0 }), _jsx(Tooltip, { content: (props) => (_jsx(LineTimeSerieChartTooltip, { unitLabel: unitLabel, duration: duration, renderTooltip: renderTooltip, isSymmetrical: yAxisType === 'symmetrical', belowSeriesLabels: belowSeriesLabels, tooltipProps: props, chartContainerRef: chartRef, chartId: chartId })) }), yAxisType === 'symmetrical' && (_jsx(ReferenceLine, { y: 0, stroke: theme.border })), linesToRender.map((line) => (_jsx(Line, { type: "monotone", dataKey: line.dataKey, stroke: line.stroke, dot: false, isAnimationActive: false, strokeDasharray: line.strokeDasharray }, `${title}-${line.key}`)))] }) })] }));
247
73
  }