@centreon/ui 24.4.1 → 24.4.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@centreon/ui",
3
- "version": "24.4.1",
3
+ "version": "24.4.3",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "eslint": "eslint ./src --ext .js,.jsx,.ts,.tsx --max-warnings 0",
@@ -1,6 +1,6 @@
1
1
  import { Dispatch, SetStateAction, useEffect } from 'react';
2
2
 
3
- import { compose, equals, prop, propEq, reject, sortBy, toLower } from 'ramda';
3
+ import { equals, propEq, reject } from 'ramda';
4
4
 
5
5
  import { Line } from '../../common/timeSeries/models';
6
6
 
@@ -51,9 +51,7 @@ const useFilterLines = ({
51
51
  return;
52
52
  }
53
53
 
54
- const sortedLines = sortBy(compose(toLower, prop('name')), filteredLines);
55
-
56
- setLinesGraph(sortedLines);
54
+ setLinesGraph(filteredLines);
57
55
  }, [lines, displayThreshold]);
58
56
 
59
57
  return { displayedLines, newLines: linesGraph ?? lines };
@@ -1,19 +1,19 @@
1
1
  import { Typography } from '@mui/material';
2
2
 
3
- import { useStyles } from './Legend.styles';
3
+ import { useLegendValueStyles } from './Legend.styles';
4
4
 
5
5
  interface Props {
6
6
  value?: string | null;
7
7
  }
8
8
 
9
9
  const InteractiveValue = ({ value }: Props): JSX.Element | null => {
10
- const { classes } = useStyles({});
10
+ const { classes } = useLegendValueStyles({});
11
11
  if (!value) {
12
12
  return null;
13
13
  }
14
14
 
15
15
  return (
16
- <Typography className={classes.legendValue} variant="h6">
16
+ <Typography className={classes.text} variant="h6">
17
17
  {value}
18
18
  </Typography>
19
19
  );
@@ -6,66 +6,112 @@ interface MakeStylesProps {
6
6
  limitLegendRows?: boolean;
7
7
  }
8
8
 
9
+ export const legendWidth = 21;
10
+ const legendItemHeight = 5.25;
11
+ const legendItemHeightCompact = 1.75;
12
+
9
13
  export const useStyles = makeStyles<MakeStylesProps>()(
10
14
  (theme, { limitLegendRows }) => ({
11
15
  highlight: {
12
16
  color: theme.typography.body1.color
13
17
  },
14
18
  item: {
15
- display: 'grid',
16
- gridTemplateColumns: 'min-content minmax(50px, 1fr)',
17
- marginBottom: theme.spacing(1)
19
+ minWidth: theme.spacing(legendWidth)
18
20
  },
19
21
  items: {
22
+ '&[data-mode="compact"]': {
23
+ gridAutoRows: theme.spacing(legendItemHeightCompact),
24
+ height: limitLegendRows
25
+ ? theme.spacing(legendItemHeightCompact * 2 + 1.5)
26
+ : 'unset'
27
+ },
28
+ columnGap: theme.spacing(3),
20
29
  display: 'grid',
21
- gridTemplateColumns: 'repeat(auto-fit, minmax(150px, max-content))',
22
- marginLeft: theme.spacing(0.5),
23
- maxHeight: limitLegendRows ? theme.spacing(14) : 'unset',
30
+ gridAutoRows: theme.spacing(legendItemHeight),
31
+ gridTemplateColumns: `repeat(auto-fit, ${theme.spacing(legendWidth)})`,
32
+ maxHeight: limitLegendRows
33
+ ? theme.spacing(legendItemHeight * 2 + 1)
34
+ : 'unset',
24
35
  overflowY: 'auto',
36
+ rowGap: theme.spacing(1),
25
37
  width: '100%'
26
38
  },
27
39
  legend: {
28
40
  marginLeft: margin.left,
29
41
  marginRight: margin.right,
30
- maxHeight: theme.spacing(24),
31
- overflowX: 'hidden',
32
- overflowY: 'auto'
42
+ overflow: 'hidden'
33
43
  },
34
- legendData: {
35
- display: 'flex',
36
- flexDirection: 'column'
44
+ minMaxAvgContainer: {
45
+ columnGap: theme.spacing(0.5),
46
+ display: 'grid',
47
+ gridTemplateColumns: 'repeat(2, min-content)',
48
+ whiteSpace: 'nowrap'
37
49
  },
38
- legendName: {
50
+ normal: {
51
+ color: theme.palette.text.primary
52
+ },
53
+ toggable: {
54
+ cursor: 'pointer'
55
+ }
56
+ })
57
+ );
58
+
59
+ interface StylesProps {
60
+ color?: string;
61
+ }
62
+
63
+ export const useLegendHeaderStyles = makeStyles<StylesProps>()(
64
+ (theme, { color }) => ({
65
+ container: {
39
66
  display: 'flex',
40
67
  flexDirection: 'row',
41
- justifyContent: 'start',
42
- marginRight: theme.spacing(0.5),
43
- overflow: 'hidden',
44
- textOverflow: 'ellipsis'
68
+ gap: theme.spacing(0.5),
69
+ width: '100%'
45
70
  },
46
- legendUnit: {
47
- justifyContent: 'start',
48
- marginLeft: 'auto',
49
- marginRight: theme.spacing(0.5),
50
- overflow: 'hidden',
51
- textOverflow: 'ellipsis'
71
+ disabled: {
72
+ color: theme.palette.text.disabled
52
73
  },
53
- legendValue: {
54
- fontWeight: theme.typography.body1.fontWeight
74
+ icon: {
75
+ backgroundColor: color,
76
+ borderRadius: theme.shape.borderRadius,
77
+ height: theme.spacing(1.5),
78
+ minWidth: theme.spacing(1.5),
79
+ width: theme.spacing(1.5)
80
+ },
81
+ legendName: {
82
+ '&[data-mode="compact"]': {
83
+ maxWidth: theme.spacing(legendWidth * 0.5)
84
+ },
85
+ maxWidth: theme.spacing(legendWidth * 0.75)
86
+ },
87
+ markerAndLegendName: {
88
+ alignItems: 'center',
89
+ display: 'flex',
90
+ flexDirection: 'row',
91
+ gap: theme.spacing(0.5)
55
92
  },
56
93
  minMaxAvgContainer: {
57
94
  columnGap: theme.spacing(0.5),
58
95
  display: 'grid',
59
- gridAutoRows: theme.spacing(2),
60
96
  gridTemplateColumns: 'repeat(2, min-content)',
61
97
  whiteSpace: 'nowrap'
62
98
  },
63
- minMaxAvgValue: { fontWeight: 600 },
64
- normal: {
65
- color: theme.palette.text.primary
66
- },
67
- toggable: {
68
- cursor: 'pointer'
99
+ text: {
100
+ fontWeight: theme.typography.fontWeightMedium,
101
+ lineHeight: 1
69
102
  }
70
103
  })
71
104
  );
105
+
106
+ export const useLegendContentStyles = makeStyles()((theme) => ({
107
+ minMaxAvgValue: { fontWeight: theme.typography.fontWeightMedium },
108
+ text: {
109
+ lineHeight: 0.9
110
+ }
111
+ }));
112
+
113
+ export const useLegendValueStyles = makeStyles()({
114
+ text: {
115
+ lineHeight: 1.4
116
+ }
117
+ });
@@ -2,7 +2,7 @@ import { useTranslation } from 'react-i18next';
2
2
 
3
3
  import { Typography } from '@mui/material';
4
4
 
5
- import { useStyles } from './Legend.styles';
5
+ import { useLegendContentStyles } from './Legend.styles';
6
6
 
7
7
  interface Props {
8
8
  data: string;
@@ -10,15 +10,21 @@ interface Props {
10
10
  }
11
11
 
12
12
  const LegendContent = ({ data, label }: Props): JSX.Element => {
13
- const { classes } = useStyles({});
13
+ const { classes, cx } = useLegendContentStyles();
14
14
 
15
15
  const { t } = useTranslation();
16
16
 
17
17
  return (
18
- <div data-testid={label}>
19
- <Typography variant="caption">{t(label)}: </Typography>
20
- <Typography className={classes.minMaxAvgValue} variant="caption">
21
- {data}
18
+ <div className={classes.text} data-testid={label}>
19
+ <Typography className={classes.text} component="span" variant="caption">
20
+ {t(label)}:{' '}
21
+ <Typography
22
+ className={cx(classes.minMaxAvgValue, classes.text)}
23
+ component="span"
24
+ variant="caption"
25
+ >
26
+ {data}
27
+ </Typography>
22
28
  </Typography>
23
29
  </div>
24
30
  );
@@ -1,43 +1,91 @@
1
1
  import { includes, isEmpty, split } from 'ramda';
2
2
 
3
- import { Tooltip, Typography } from '@mui/material';
3
+ import { Typography } from '@mui/material';
4
4
 
5
+ import { EllipsisTypography, formatMetricValue } from '../../..';
5
6
  import { Line } from '../../common/timeSeries/models';
7
+ import { Tooltip } from '../../../components';
6
8
 
7
- import { useStyles } from './Legend.styles';
9
+ import { useLegendHeaderStyles } from './Legend.styles';
10
+ import { LegendDisplayMode } from './models';
11
+ import LegendContent from './LegendContent';
8
12
 
9
13
  interface Props {
14
+ color: string;
15
+ disabled?: boolean;
10
16
  line: Line;
17
+ minMaxAvg?;
18
+ value?: string | null;
11
19
  }
12
20
 
13
- const LegendHeader = ({ line }: Props): JSX.Element => {
14
- const { classes } = useStyles({});
21
+ const LegendHeader = ({
22
+ line,
23
+ color,
24
+ disabled,
25
+ value,
26
+ minMaxAvg
27
+ }: Props): JSX.Element => {
28
+ const { classes, cx } = useLegendHeaderStyles({ color });
29
+
15
30
  const { unit, name, legend } = line;
16
31
 
17
32
  const legendName = legend || name;
18
- const unitName = ` (${unit})`;
33
+ const hasUnit = !isEmpty(unit);
34
+ const unitName = `(${unit})`;
19
35
  const metricName = includes('#', legendName)
20
36
  ? split('#')(legendName)[1]
21
37
  : legendName;
22
38
 
39
+ const getEndText = (): string => {
40
+ if (value) {
41
+ return `${value}${hasUnit ? ` ${unit}` : ''}`;
42
+ }
43
+
44
+ return hasUnit ? ` ${unitName}` : '';
45
+ };
46
+
23
47
  return (
24
- <div className={classes.legendName}>
25
- <Tooltip placement="top" title={legendName + unitName}>
26
- <Typography
27
- className={classes.legendName}
28
- component="p"
29
- variant="caption"
30
- >
31
- {metricName}
32
- </Typography>
33
- </Tooltip>
34
- <Typography
35
- className={classes.legendUnit}
36
- component="p"
37
- variant="caption"
48
+ <div className={classes.container}>
49
+ <Tooltip
50
+ followCursor={false}
51
+ label={
52
+ minMaxAvg ? (
53
+ <div>
54
+ <Typography>{`${legendName} ${unitName}`}</Typography>
55
+ <div className={classes.minMaxAvgContainer}>
56
+ {minMaxAvg.map(({ label, value: subValue }) => (
57
+ <LegendContent
58
+ data={formatMetricValue({
59
+ unit: line.unit,
60
+ value: subValue
61
+ })}
62
+ key={label}
63
+ label={label}
64
+ />
65
+ ))}
66
+ </div>
67
+ </div>
68
+ ) : (
69
+ `${legendName} ${unitName}`
70
+ )
71
+ }
72
+ placement="top"
38
73
  >
39
- {!isEmpty(line?.unit) && `(${line.unit})`}
40
- </Typography>
74
+ <div className={classes.markerAndLegendName}>
75
+ <div className={cx(classes.icon, { [classes.disabled]: disabled })} />
76
+ <EllipsisTypography
77
+ className={cx(classes.text, classes.legendName)}
78
+ data-mode={
79
+ value ? LegendDisplayMode.Compact : LegendDisplayMode.Normal
80
+ }
81
+ >
82
+ {metricName}
83
+ </EllipsisTypography>
84
+ </div>
85
+ </Tooltip>
86
+ {hasUnit && (
87
+ <Typography className={classes.text}>{getEndText()}</Typography>
88
+ )}
41
89
  </div>
42
90
  );
43
91
  };
@@ -16,8 +16,7 @@ import { timeValueAtom } from '../InteractiveComponents/interactionWithGraphAtom
16
16
  import InteractiveValue from './InteractiveValue';
17
17
  import { useStyles } from './Legend.styles';
18
18
  import LegendHeader from './LegendHeader';
19
- import LegendMarker from './Marker';
20
- import { GetMetricValueProps } from './models';
19
+ import { GetMetricValueProps, LegendDisplayMode } from './models';
21
20
  import useInteractiveValues from './useInteractiveValues';
22
21
  import useLegend from './useLegend';
23
22
  import LegendContent from './LegendContent';
@@ -29,6 +28,7 @@ interface Props {
29
28
  lines: Array<Line>;
30
29
  renderExtraComponent?: ReactNode;
31
30
  setLinesGraph: Dispatch<SetStateAction<Array<Line> | null>>;
31
+ shouldDisplayLegendInCompactMode: boolean;
32
32
  timeSeries: Array<TimeValue>;
33
33
  toggable?: boolean;
34
34
  xScale;
@@ -43,7 +43,8 @@ const MainLegend = ({
43
43
  renderExtraComponent,
44
44
  displayAnchor = true,
45
45
  setLinesGraph,
46
- xScale
46
+ xScale,
47
+ shouldDisplayLegendInCompactMode
47
48
  }: Props): JSX.Element => {
48
49
  const { classes, cx } = useStyles({ limitLegendRows });
49
50
  const theme = useTheme();
@@ -85,9 +86,13 @@ const MainLegend = ({
85
86
  selectMetricLine(metric_id);
86
87
  };
87
88
 
89
+ const mode = shouldDisplayLegendInCompactMode
90
+ ? LegendDisplayMode.Compact
91
+ : LegendDisplayMode.Normal;
92
+
88
93
  return (
89
94
  <div className={classes.legend}>
90
- <div className={classes.items}>
95
+ <div className={classes.items} data-mode={mode}>
91
96
  {displayedLines.map((line) => {
92
97
  const { color, display, highlight, metric_id } = line;
93
98
 
@@ -126,22 +131,37 @@ const MainLegend = ({
126
131
  onMouseEnter={(): void => highlightLine(metric_id)}
127
132
  onMouseLeave={(): void => clearHighlight()}
128
133
  >
129
- <LegendMarker color={markerColor} disabled={!display} />
130
- <div className={classes.legendData}>
131
- <LegendHeader line={line} />
132
- {displayAnchor && <InteractiveValue value={interactiveValue} />}
133
- {!interactiveValue && (
134
- <div className={classes.minMaxAvgContainer}>
135
- {minMaxAvg.map(({ label, value }) => (
136
- <LegendContent
137
- data={getMetricValue({ unit: line.unit, value })}
138
- key={label}
139
- label={label}
140
- />
141
- ))}
142
- </div>
143
- )}
144
- </div>
134
+ <LegendHeader
135
+ color={markerColor}
136
+ disabled={!display}
137
+ line={line}
138
+ minMaxAvg={
139
+ shouldDisplayLegendInCompactMode ? minMaxAvg : undefined
140
+ }
141
+ value={
142
+ shouldDisplayLegendInCompactMode
143
+ ? interactiveValue
144
+ : undefined
145
+ }
146
+ />
147
+ {!shouldDisplayLegendInCompactMode && (
148
+ <div>
149
+ {displayAnchor && (
150
+ <InteractiveValue value={interactiveValue} />
151
+ )}
152
+ {!interactiveValue && (
153
+ <div className={classes.minMaxAvgContainer}>
154
+ {minMaxAvg.map(({ label, value }) => (
155
+ <LegendContent
156
+ data={getMetricValue({ unit: line.unit, value })}
157
+ key={label}
158
+ label={label}
159
+ />
160
+ ))}
161
+ </div>
162
+ )}
163
+ </div>
164
+ )}
145
165
  </Box>
146
166
  );
147
167
  })}
@@ -152,8 +172,15 @@ const MainLegend = ({
152
172
  };
153
173
 
154
174
  const Legend = (props: Props): JSX.Element => {
155
- const { toggable, limitLegendRows, timeSeries, lines, base, displayAnchor } =
156
- props;
175
+ const {
176
+ toggable,
177
+ limitLegendRows,
178
+ timeSeries,
179
+ lines,
180
+ base,
181
+ displayAnchor,
182
+ shouldDisplayLegendInCompactMode
183
+ } = props;
157
184
  const timeValue = useAtomValue(timeValueAtom);
158
185
 
159
186
  return useMemoComponent({
@@ -165,7 +192,8 @@ const Legend = (props: Props): JSX.Element => {
165
192
  base,
166
193
  toggable,
167
194
  limitLegendRows,
168
- displayAnchor
195
+ displayAnchor,
196
+ shouldDisplayLegendInCompactMode
169
197
  ]
170
198
  });
171
199
  };
@@ -9,3 +9,8 @@ export interface GetMetricValueProps {
9
9
  unit: string;
10
10
  value: number | null;
11
11
  }
12
+
13
+ export enum LegendDisplayMode {
14
+ Compact = 'compact',
15
+ Normal = 'normal'
16
+ }
@@ -32,20 +32,19 @@ const useLegend = ({ lines, setLinesGraph }: Props): LegendActions => {
32
32
  find(propEq(metric_id, 'metric_id'), lines) as Line;
33
33
 
34
34
  const toggleMetricLine = (metric_id): void => {
35
- const line = getLineByMetric(metric_id);
35
+ const data = lines.map((line) => ({
36
+ ...line,
37
+ display: equals(line.metric_id, metric_id) ? !line.display : line.display
38
+ }));
36
39
 
37
- setLinesGraph([
38
- ...reject(propEq(metric_id, 'metric_id'), lines),
39
- { ...line, display: !line.display }
40
- ]);
40
+ setLinesGraph(data);
41
41
  };
42
42
 
43
43
  const highlightLine = (metric_id): void => {
44
- const fadedLines = map((line) => ({ ...line, highlight: false }), lines);
45
- const data = [
46
- ...reject(propEq(metric_id, 'metric_id'), fadedLines),
47
- { ...getLineByMetric(metric_id), highlight: true }
48
- ];
44
+ const data = lines.map((line) => ({
45
+ ...line,
46
+ highlight: equals(line.metric_id, metric_id)
47
+ }));
49
48
 
50
49
  setLinesGraph(data);
51
50
  };
@@ -1,7 +1,7 @@
1
1
  import { MutableRefObject, useMemo, useRef, useState } from 'react';
2
2
 
3
3
  import { Group, Tooltip } from '@visx/visx';
4
- import { flatten, isNil, pluck } from 'ramda';
4
+ import { flatten, gt, isNil, lte, pluck, reduce } from 'ramda';
5
5
 
6
6
  import { ClickAwayListener, Fade, Skeleton, useTheme } from '@mui/material';
7
7
 
@@ -33,6 +33,7 @@ import {
33
33
  import { useIntersection } from './useLineChartIntersection';
34
34
  import { CurveType } from './BasicComponents/Lines/models';
35
35
  import Thresholds from './BasicComponents/Thresholds';
36
+ import { legendWidth } from './Legend/Legend.styles';
36
37
 
37
38
  const extraMargin = 10;
38
39
 
@@ -161,6 +162,15 @@ const LineChart = ({
161
162
  const displayLegend = legend?.display ?? true;
162
163
  const displayTooltip = !isNil(tooltip?.renderComponent);
163
164
 
165
+ const legendItemsWidth = reduce(
166
+ (acc) => acc + legendWidth * 8 + 24,
167
+ 0,
168
+ displayedLines
169
+ );
170
+
171
+ const shouldDisplayLegendInCompactMode =
172
+ lte(graphWidth, 808) && gt(legendItemsWidth, graphWidth);
173
+
164
174
  if (!isInViewport) {
165
175
  return (
166
176
  <Skeleton
@@ -294,6 +304,7 @@ const LineChart = ({
294
304
  lines={newLines}
295
305
  renderExtraComponent={legend?.renderExtraComponent}
296
306
  setLinesGraph={setLinesGraph}
307
+ shouldDisplayLegendInCompactMode={shouldDisplayLegendInCompactMode}
297
308
  timeSeries={timeSeries}
298
309
  xScale={xScale}
299
310
  />
@@ -1,43 +0,0 @@
1
- import { equals } from 'ramda';
2
- import { makeStyles } from 'tss-react/mui';
3
-
4
- export enum LegendMarkerVariant {
5
- 'dot',
6
- 'bar'
7
- }
8
-
9
- interface StylesProps {
10
- color?: string;
11
- variant: LegendMarkerVariant;
12
- }
13
-
14
- const useStyles = makeStyles<StylesProps>()((theme, { color, variant }) => ({
15
- disabled: {
16
- color: theme.palette.text.disabled
17
- },
18
- icon: {
19
- backgroundColor: color,
20
- borderRadius: equals(LegendMarkerVariant.dot, variant) ? '50%' : 0,
21
- height: equals(LegendMarkerVariant.dot, variant) ? 9 : '100%',
22
- marginRight: theme.spacing(0.5),
23
- width: 9
24
- }
25
- }));
26
-
27
- interface Props {
28
- color: string;
29
- disabled?: boolean;
30
- variant?: LegendMarkerVariant;
31
- }
32
-
33
- const LegendMarker = ({
34
- disabled,
35
- color,
36
- variant = LegendMarkerVariant.bar
37
- }: Props): JSX.Element => {
38
- const { classes, cx } = useStyles({ color, variant });
39
-
40
- return <div className={cx(classes.icon, { [classes.disabled]: disabled })} />;
41
- };
42
-
43
- export default LegendMarker;