@centreon/ui 26.9.1 → 26.10.1

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.
@@ -1,21 +1,18 @@
1
- import { getTicks } from '@visx/scale';
2
- import { ScaleLinear } from 'd3-scale';
3
1
  import { isEmpty } from 'ramda';
4
2
  import { useMemo } from 'react';
5
3
 
6
4
  import type { ChartAxis } from '../../Chart/models';
7
- import useAxisY from '../Axes/useAxisY';
8
- import { Line } from '../timeSeries/models';
5
+ import type { Data } from '../Axes/models';
6
+ import type { Thresholds } from '../models';
7
+ import { getFormattedAxisValues } from '../utils';
9
8
 
10
9
  interface UseComputeYAxisMaxCharactersProps {
10
+ firstUnit: string;
11
+ secondUnit: string;
12
+ thresholdUnit?: string;
13
+ thresholds?: Thresholds;
14
+ graphData: Data;
11
15
  axis?: ChartAxis;
12
- base: number;
13
- displayedLines: Array<Line>;
14
- graphWidth: number;
15
- graphHeight: number;
16
- isHorizontal: boolean;
17
- leftScale: ScaleLinear<number, number, never>;
18
- rightScale: ScaleLinear<number, number, never>;
19
16
  }
20
17
 
21
18
  interface UseComputteYAxisMaxCharactersState {
@@ -24,52 +21,71 @@ interface UseComputteYAxisMaxCharactersState {
24
21
  }
25
22
 
26
23
  export const useComputeYAxisMaxCharacters = ({
24
+ thresholds,
25
+ firstUnit,
26
+ secondUnit,
27
+ graphData,
27
28
  axis,
28
- base,
29
- displayedLines,
30
- graphWidth,
31
- graphHeight,
32
- isHorizontal,
33
- leftScale,
34
- rightScale
29
+ thresholdUnit
35
30
  }: UseComputeYAxisMaxCharactersProps): UseComputteYAxisMaxCharactersState => {
36
- const { axisLeft, axisRight } = useAxisY({
37
- data: {
38
- baseAxis: base,
39
- lines: displayedLines,
40
- ...axis
41
- },
42
- graphHeight,
43
- graphWidth,
44
- isHorizontal
45
- });
31
+ const maxLeftValue = useMemo(
32
+ () =>
33
+ getFormattedAxisValues({
34
+ axisUnit: axis?.axisYLeft?.unit ?? firstUnit,
35
+ base: graphData?.baseAxis,
36
+ lines: graphData?.lines ?? [],
37
+ threshold: thresholds?.critical ?? [],
38
+ thresholdUnit,
39
+ timeSeries: graphData?.timeSeries ?? []
40
+ }),
41
+ [
42
+ thresholds?.critical,
43
+ axis?.axisYLeft?.unit,
44
+ firstUnit,
45
+ graphData?.timeSeries,
46
+ thresholdUnit,
47
+ graphData?.lines,
48
+ graphData?.baseAxis
49
+ ]
50
+ );
46
51
 
47
- // Always add a character space in case the algorithm does not compute the displayed value in axis.
48
- const maxLeftAxisCharacters = useMemo(() => {
49
- if (!leftScale) {
50
- return 0;
51
- }
52
-
53
- const ticks = getTicks(leftScale, axisLeft.numTicks);
54
- const formattedTicks = ticks.map(axisLeft.tickFormat);
55
-
56
- return isEmpty(formattedTicks)
57
- ? 2
58
- : Math.max(...formattedTicks.map((value) => value.length), 2) + 1;
59
- }, [leftScale, axisLeft]);
52
+ const maxRightValue = useMemo(
53
+ () =>
54
+ getFormattedAxisValues({
55
+ axisUnit: axis?.axisYRight?.unit ?? secondUnit,
56
+ base: graphData.baseAxis,
57
+ lines: graphData.lines ?? [],
58
+ threshold: thresholds?.critical ?? [],
59
+ thresholdUnit,
60
+ timeSeries: graphData.timeSeries ?? []
61
+ }),
62
+ [
63
+ thresholds?.critical,
64
+ axis?.axisYRight?.unit,
65
+ secondUnit,
66
+ graphData.timeSeries,
67
+ thresholdUnit,
68
+ graphData.lines,
69
+ graphData.baseAxis
70
+ ]
71
+ );
60
72
 
61
- const maxRightAxisCharacters = useMemo(() => {
62
- if (!rightScale) {
63
- return 0;
64
- }
65
-
66
- const ticks = getTicks(rightScale, axisRight.numTicks);
67
- const formattedTicks = ticks.map(axisRight.tickFormat);
73
+ // Always add a character space in case the algorithm does not compute the displayed value in axis.
74
+ const maxLeftAxisCharacters = useMemo(
75
+ () =>
76
+ isEmpty(maxLeftValue)
77
+ ? 2
78
+ : Math.max(...maxLeftValue.map((value) => value.length), 2) + 1,
79
+ [maxLeftValue]
80
+ );
68
81
 
69
- return isEmpty(formattedTicks)
70
- ? 2
71
- : Math.max(...formattedTicks.map((value) => value.length), 2) + 1;
72
- }, [rightScale, axisRight]);
82
+ const maxRightAxisCharacters = useMemo(
83
+ () =>
84
+ isEmpty(maxRightValue)
85
+ ? 5
86
+ : Math.max(...maxRightValue.map((value) => value.length), 5) + 1,
87
+ [maxRightValue]
88
+ );
73
89
 
74
90
  return {
75
91
  maxLeftAxisCharacters,
@@ -22,6 +22,7 @@ import {
22
22
  } from 'ramda';
23
23
 
24
24
  import type { BarStyle } from '../BarChart/models';
25
+ import { margin } from '../Chart/common';
25
26
  import type { LineStyle } from '../Chart/models';
26
27
  import type { Threshold, Thresholds } from './models';
27
28
  import { formatMetricValueWithUnit } from './timeSeries';
@@ -262,8 +263,16 @@ export const getFormattedAxisValues = ({
262
263
  .filter((v) => v) as Array<string>;
263
264
  };
264
265
 
265
- export const computeGElementMarginLeft = (maxLeftCharacters: number): number =>
266
- maxLeftCharacters * 5;
266
+ interface ComputeGElementMarginLeftProps {
267
+ maxCharacters: number;
268
+ hasSecondUnit?: boolean;
269
+ }
270
+
271
+ export const computeGElementMarginLeft = ({
272
+ maxCharacters,
273
+ hasSecondUnit
274
+ }: ComputeGElementMarginLeftProps): number =>
275
+ maxCharacters * 5 + (hasSecondUnit ? margin.top * 0.8 : margin.top * 0.6);
267
276
 
268
277
  export const computPixelsToShiftMouse = (
269
278
  xScale: import('d3-scale').ScaleTime<number, number>
@@ -45,6 +45,7 @@ declare module '@mui/material/styles' {
45
45
  main: string;
46
46
  };
47
47
  statusBackground: StatusBackground;
48
+ tile: TypeTile;
48
49
  }
49
50
  interface StatusBackground {
50
51
  error: string;
@@ -64,6 +65,13 @@ declare module '@mui/material/styles' {
64
65
  main: string;
65
66
  };
66
67
  statusBackground: StatusBackground;
68
+ tile: TypeTile;
69
+ }
70
+
71
+ interface TypeTile {
72
+ background: string;
73
+ border: string;
74
+ borderHover: string;
67
75
  }
68
76
 
69
77
  interface TypeBackground {
@@ -294,6 +302,11 @@ export const lightPalette: PaletteOptions = {
294
302
  primary: '#000000',
295
303
  secondary: '#666666'
296
304
  },
305
+ tile: {
306
+ background: '#FFFFFF',
307
+ border: '#E3E3E3',
308
+ borderHover: '#CCCCCC'
309
+ },
297
310
  warning: {
298
311
  contrastText: '#000',
299
312
  dark: '#FC7E00',
@@ -436,6 +449,11 @@ export const darkPalette: PaletteOptions = {
436
449
  primary: '#FFFFFF',
437
450
  secondary: '#CCCCCC'
438
451
  },
452
+ tile: {
453
+ background: '#2E2E2E',
454
+ border: '#4A4A4A',
455
+ borderHover: '#6D6D6D'
456
+ },
439
457
  warning: {
440
458
  contrastText: '#fff',
441
459
  main: '#C55400'
@@ -112,6 +112,11 @@
112
112
  --color-status-background-warning: #fd9b27;
113
113
  --color-success-main: #88b922;
114
114
 
115
+ /* Tile container colors */
116
+ --color-tile-background: #ffffff;
117
+ --color-tile-border: #e3e3e3;
118
+ --color-tile-border-hover: #cccccc;
119
+
115
120
  /* Text colors */
116
121
  --color-text-disabled: #999999;
117
122
  --color-text-primary: #000000;
@@ -195,6 +200,9 @@
195
200
  --color-stauts-background-unknown: #666666;
196
201
  --color-status-background-warning: #c55400;
197
202
  --color-success-main: #5f8118;
203
+ --color-tile-background: #2e2e2e;
204
+ --color-tile-border: #4a4a4a;
205
+ --color-tile-border-hover: #6d6d6d;
198
206
  --color-text-disabled: #666666;
199
207
  --color-text-primary: #ffffff;
200
208
  --color-text-secondary: #cccccc;
@@ -1,6 +1,5 @@
1
1
  import type { SeverityCode } from '@centreon/ui';
2
2
 
3
- import { Fragment } from 'react';
4
3
  import { Link } from 'react-router';
5
4
  import { makeStyles } from 'tss-react/mui';
6
5
 
@@ -8,29 +7,23 @@ import StatusCounter from './StatusCounter';
8
7
 
9
8
  const useStyles = makeStyles()((theme) => ({
10
9
  container: {
11
- display: 'inline-block',
10
+ alignItems: 'center',
11
+ display: 'flex',
12
+ gap: theme.spacing(1),
12
13
  listStyle: 'none',
13
14
  margin: 0,
14
- padding: 0,
15
- [theme.breakpoints.down(1025)]: {
16
- flexFlow: 'row wrap'
17
- }
15
+ padding: 0
18
16
  },
19
17
  item: {
20
- display: 'inline-block',
18
+ alignItems: 'center',
19
+ display: 'flex',
21
20
  margin: 0,
22
- padding: 0,
23
- paddingRight: theme.spacing(0.25)
21
+ padding: 0
24
22
  },
25
23
  link: {
24
+ alignItems: 'center',
25
+ display: 'flex',
26
26
  textDecoration: 'none'
27
- },
28
- splitter: {
29
- display: 'none',
30
- [theme.breakpoints.down(1025)]: {
31
- display: 'block',
32
- marginBottom: theme.spacing(0.25)
33
- }
34
27
  }
35
28
  }));
36
29
 
@@ -38,6 +31,7 @@ export interface CounterProps {
38
31
  counters: Array<{
39
32
  ariaLabel: string;
40
33
  count: string | number;
34
+ detail?: string | number;
41
35
  onClick: (e: React.MouseEvent) => void;
42
36
  severityCode: SeverityCode;
43
37
  to: string;
@@ -50,20 +44,21 @@ export default ({ counters }: CounterProps): JSX.Element => {
50
44
  return (
51
45
  <ul className={classes.container}>
52
46
  {counters.map(
53
- ({ to, ariaLabel, onClick, count, severityCode }, index) => (
54
- <Fragment key={to.toString().replace(/\W/g, '')}>
55
- {index === 2 && <li className={classes.splitter} />}
56
- <li className={classes.item}>
57
- <Link
58
- aria-label={ariaLabel}
59
- className={classes.link}
60
- onClick={onClick}
61
- to={to}
62
- >
63
- <StatusCounter count={count} severityCode={severityCode} />
64
- </Link>
65
- </li>
66
- </Fragment>
47
+ ({ to, ariaLabel, onClick, count, detail, severityCode }) => (
48
+ <li className={classes.item} key={to.toString().replace(/\W/g, '')}>
49
+ <Link
50
+ aria-label={ariaLabel}
51
+ className={classes.link}
52
+ onClick={onClick}
53
+ to={to}
54
+ >
55
+ <StatusCounter
56
+ count={count}
57
+ detail={detail}
58
+ severityCode={severityCode}
59
+ />
60
+ </Link>
61
+ </li>
67
62
  )
68
63
  )}
69
64
  </ul>
@@ -1,63 +1,72 @@
1
- import { Badge, Tooltip } from '@mui/material';
1
+ import { Tooltip } from '@mui/material';
2
2
 
3
3
  import { getStatusColors, type SeverityCode } from '@centreon/ui';
4
4
 
5
5
  import numeral from 'numeral';
6
+ import { isNil } from 'ramda';
6
7
  import { makeStyles } from 'tss-react/mui';
7
8
 
8
9
  export interface StyleProps {
9
10
  severityCode?: SeverityCode | null;
10
11
  }
11
12
 
12
- const useStyles = makeStyles<StyleProps>()((theme, { severityCode }) => ({
13
- badge: {
14
- background: severityCode
15
- ? getStatusColors({ severityCode, theme })?.backgroundColor
16
- : 'transparent',
17
- borderRadius: theme.spacing(1.25),
18
- color: theme.palette.common.black,
19
- cursor: 'pointer',
20
- fontSize: theme.typography.body2.fontSize,
21
- fontWeight: theme.typography.fontWeightBold,
22
- height: theme.spacing(2.125),
23
- lineHeight: theme.spacing(2.125),
24
- minWidth: theme.spacing(2.125),
25
- position: 'relative',
26
- right: 0,
27
- top: 0,
28
- transform: 'none'
29
- }
30
- }));
13
+ const useStyles = makeStyles<StyleProps>()((theme, { severityCode }) => {
14
+ const statusColors = severityCode
15
+ ? getStatusColors({ severityCode, theme })
16
+ : null;
17
+
18
+ return {
19
+ container: {
20
+ alignItems: 'center',
21
+ color: theme.palette.mode === 'dark' ? '#EAEEF7' : '#1B2233',
22
+ cursor: 'pointer',
23
+ display: 'inline-flex',
24
+ gap: '3px'
25
+ },
26
+ count: {
27
+ fontFamily: 'ui-monospace, "Roboto Mono", Menlo, monospace',
28
+ fontSize: '12px',
29
+ fontWeight: theme.typography.fontWeightMedium,
30
+ lineHeight: 1
31
+ },
32
+ dot: {
33
+ backgroundColor: statusColors?.backgroundColor ?? theme.palette.divider,
34
+ borderRadius: '50%',
35
+ flexShrink: 0,
36
+ height: '9px',
37
+ width: '9px'
38
+ }
39
+ };
40
+ });
31
41
 
32
42
  export interface Props {
33
43
  className?: string;
34
44
  count: string | number;
45
+ detail?: string | number;
35
46
  severityCode?: SeverityCode | null;
36
47
  }
37
48
 
38
49
  const StatusCounter = ({
39
50
  severityCode = null,
40
51
  count,
52
+ detail,
41
53
  className
42
54
  }: Props): JSX.Element => {
43
55
  const { classes, cx } = useStyles({ severityCode });
44
- const shouldDisableTooltip = Number(count) < 1000;
56
+ const hasDetail = !isNil(detail);
57
+ const shouldDisableTooltip = !hasDetail && Number(count) < 1000;
45
58
  const formattedCount = numeral(count).format('0.[0]a');
46
59
 
47
60
  return (
48
61
  <Tooltip
49
62
  disableHoverListener={shouldDisableTooltip}
50
63
  followCursor
51
- title={count}
64
+ title={hasDetail ? detail : count}
52
65
  >
53
- <Badge
54
- badgeContent={formattedCount}
55
- classes={{
56
- badge: cx(classes.badge, className)
57
- }}
58
- max={Number.POSITIVE_INFINITY}
59
- overlap="circular"
60
- />
66
+ <span className={cx(classes.container, className)}>
67
+ <span className={classes.dot} />
68
+ <span className={classes.count}>{formattedCount}</span>
69
+ </span>
61
70
  </Tooltip>
62
71
  );
63
72
  };