@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,79 +1,48 @@
1
1
  import ExpandLessIcon from '@mui/icons-material/ExpandLess';
2
2
  import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
3
3
  import type { SvgIconProps } from '@mui/material';
4
- import { Badge, ClickAwayListener } from '@mui/material';
4
+ import { ClickAwayListener, Tooltip } from '@mui/material';
5
5
 
6
- import { ComponentType, useEffect, useState } from 'react';
6
+ import type { ComponentType, MouseEvent } from 'react';
7
+ import { useEffect, useState } from 'react';
8
+ import { Link } from 'react-router';
7
9
  import { makeStyles } from 'tss-react/mui';
8
10
 
9
11
  import useCloseOnLegacyPage from './useCloseOnLegacyPage';
10
12
 
11
13
  const useStyles = makeStyles()((theme) => ({
12
- button: {
13
- '& > svg': {
14
- height: '0.9em',
15
- margin: `-${theme.spacing(0.5)}`,
16
- [theme.breakpoints.down(1025)]: {
17
- margin: `-${theme.spacing(0.5)}`
18
- }
19
- },
20
- appearance: 'none',
21
- background: 'none',
22
- border: 0,
23
- color: theme.palette.common.white,
24
- cursor: 'pointer',
25
- display: 'flex',
26
-
27
- [theme.breakpoints.up(1025)]: {
28
- alignItems: 'center',
29
- flexFlow: 'row no-wrap',
30
- marginTop: theme.spacing(0.5)
31
- },
32
-
33
- [theme.breakpoints.down(1025)]: {
34
- alignItems: 'center',
35
- flexFlow: 'column wrap',
36
- order: 1
37
- },
38
-
39
- padding: '0'
40
- },
41
14
  container: {
15
+ alignItems: 'center',
16
+ display: 'flex',
42
17
  position: 'relative'
43
18
  },
44
- header: {
45
- [theme.breakpoints.down(1025)]: {
46
- display: 'flex',
47
- flexFlow: 'row no-wrap'
48
- }
49
- },
50
- iconWrapper: {
51
- [theme.breakpoints.up(1025)]: {
52
- position: 'absolute',
53
- top: '0'
54
- }
19
+ icon: {
20
+ '& > svg': {
21
+ display: 'block',
22
+ height: '16px',
23
+ width: '16px'
24
+ },
25
+ alignItems: 'center',
26
+ color: 'inherit',
27
+ display: 'inline-flex',
28
+ textDecoration: 'none'
55
29
  },
56
30
  indicators: {
31
+ alignItems: 'center',
32
+ display: 'inline-flex',
57
33
  lineHeight: 1,
58
34
  [theme.breakpoints.down(600)]: {
59
35
  display: 'none'
60
- },
61
- [theme.breakpoints.down(1025)]: {
62
- flex: 'initial',
63
- marginLeft: theme.spacing(0.5),
64
- order: 2
65
- },
66
- [theme.breakpoints.up(1025)]: {
67
- height: theme.spacing(2.5),
68
- marginLeft: theme.spacing(3.75)
69
36
  }
70
37
  },
71
38
  subMenu: {
72
39
  backgroundColor: theme.palette.background.default,
40
+ borderRadius: theme.spacing(1),
73
41
  boxShadow: theme.shadows[3],
74
42
  boxSizing: 'border-box',
75
43
  left: 0,
76
44
  minWidth: theme.spacing(20),
45
+ overflow: 'hidden',
77
46
  position: 'absolute',
78
47
  textAlign: 'left',
79
48
  top: `calc(100% + ${theme.spacing(1.25)})`,
@@ -83,38 +52,53 @@ const useStyles = makeStyles()((theme) => ({
83
52
  subMenuOpen: {
84
53
  visibility: 'visible'
85
54
  },
86
- textWrapper: {
55
+ tile: {
56
+ '& > svg': {
57
+ height: '16px',
58
+ width: '16px'
59
+ },
60
+ '&:hover': {
61
+ borderColor: theme.palette.tile.borderHover
62
+ },
87
63
  alignItems: 'center',
64
+ appearance: 'none',
65
+ background: theme.palette.tile.background,
66
+ border: `1px solid ${theme.palette.tile.border}`,
67
+ borderRadius: theme.spacing(1),
68
+ color: theme.palette.text.primary,
88
69
  display: 'inline-flex',
89
- flex: '100%',
90
- fontSize: theme.typography.body1.fontSize,
91
- fontWeight: theme.typography.fontWeightBold,
92
- lineHeight: '1',
93
- whiteSpace: 'nowrap',
94
- [theme.breakpoints.down(1025)]: {
95
- display: 'none'
96
- }
70
+ flexFlow: 'row nowrap',
71
+ gap: theme.spacing(1),
72
+ padding: '5px 9px'
73
+ },
74
+ tileButton: {
75
+ cursor: 'pointer'
97
76
  }
98
77
  }));
99
78
 
100
79
  interface TopCounterLayoutProps {
101
80
  Icon: ComponentType<SvgIconProps>;
81
+ iconLink?: string;
82
+ iconOnClick?: (e: MouseEvent<HTMLAnchorElement>) => void;
102
83
  renderIndicators: () => JSX.Element;
103
- renderSubMenu: (params: { closeSubMenu: () => void }) => JSX.Element;
104
- showPendingBadge?: boolean;
84
+ renderSubMenu?: (params: { closeSubMenu: () => void }) => JSX.Element;
105
85
  title: string;
86
+ tooltipDescription?: string;
106
87
  }
107
88
 
108
89
  const TopCounterLayout = ({
109
90
  Icon,
110
91
  title,
92
+ iconLink,
93
+ iconOnClick,
111
94
  renderIndicators,
112
95
  renderSubMenu,
113
- showPendingBadge
96
+ tooltipDescription
114
97
  }: TopCounterLayoutProps): JSX.Element => {
115
98
  const { classes, cx } = useStyles();
116
99
  const [toggled, setToggled] = useState(false);
117
100
  const subMenuId = title.replace(/[^A-Za-z]/, '-');
101
+ const isExpandable = Boolean(renderSubMenu);
118
102
  useCloseOnLegacyPage({ setToggled });
119
103
 
120
104
  useEffect(() => {
@@ -137,6 +121,38 @@ const TopCounterLayout = ({
137
121
  };
138
122
  }, [toggled]);
139
123
 
124
+ const iconWithTooltip = (
125
+ <Tooltip
126
+ disableInteractive
127
+ enterDelay={500}
128
+ enterNextDelay={500}
129
+ placement="bottom"
130
+ title={tooltipDescription ?? title}
131
+ >
132
+ <Icon />
133
+ </Tooltip>
134
+ );
135
+
136
+ if (!isExpandable) {
137
+ return (
138
+ <div className={classes.tile}>
139
+ {iconLink ? (
140
+ <Link
141
+ aria-label={title}
142
+ className={classes.icon}
143
+ onClick={iconOnClick}
144
+ to={iconLink}
145
+ >
146
+ {iconWithTooltip}
147
+ </Link>
148
+ ) : (
149
+ <span className={classes.icon}>{iconWithTooltip}</span>
150
+ )}
151
+ <span className={classes.indicators}>{renderIndicators()}</span>
152
+ </div>
153
+ );
154
+ }
155
+
140
156
  return (
141
157
  <ClickAwayListener
142
158
  onClickAway={(): void => {
@@ -147,40 +163,27 @@ const TopCounterLayout = ({
147
163
  }}
148
164
  >
149
165
  <div className={classes.container}>
150
- <div className={classes.header}>
151
- <div className={classes.indicators}>{renderIndicators()}</div>
152
- <button
153
- aria-controls={`${subMenuId}-menu`}
154
- aria-expanded={toggled}
155
- aria-haspopup="true"
156
- aria-label={title}
157
- className={classes.button}
158
- id={`${subMenuId}-button`}
159
- onClick={(): void => setToggled(!toggled)}
160
- type="button"
161
- >
162
- <span className={classes.iconWrapper}>
163
- <Badge
164
- anchorOrigin={{ horizontal: 'right', vertical: 'top' }}
165
- color="pending"
166
- invisible={!showPendingBadge}
167
- overlap="circular"
168
- variant="dot"
169
- >
170
- <Icon />
171
- </Badge>
172
- </span>
173
- <span className={classes.textWrapper}>{title}</span>
174
- {toggled ? <ExpandLessIcon /> : <ExpandMoreIcon />}
175
- </button>
176
- </div>
166
+ <button
167
+ aria-controls={`${subMenuId}-menu`}
168
+ aria-expanded={toggled}
169
+ aria-haspopup="true"
170
+ aria-label={title}
171
+ className={cx(classes.tile, classes.tileButton)}
172
+ id={`${subMenuId}-button`}
173
+ onClick={(): void => setToggled(!toggled)}
174
+ type="button"
175
+ >
176
+ <span className={classes.icon}>{iconWithTooltip}</span>
177
+ <span className={classes.indicators}>{renderIndicators()}</span>
178
+ {toggled ? <ExpandLessIcon /> : <ExpandMoreIcon />}
179
+ </button>
177
180
  <div
178
181
  aria-labelledby={`${subMenuId}-button`}
179
182
  className={cx(classes.subMenu, { [classes.subMenuOpen]: toggled })}
180
183
  id={`${subMenuId}-menu`}
181
184
  role="menu"
182
185
  >
183
- {renderSubMenu({ closeSubMenu: () => setToggled(false) })}
186
+ {renderSubMenu?.({ closeSubMenu: () => setToggled(false) })}
184
187
  </div>
185
188
  </div>
186
189
  </ClickAwayListener>
@@ -1,6 +1,4 @@
1
1
  export * from './formaters';
2
2
  export type { CounterProps } from './ResourceCounters';
3
3
  export { default as TopCounterResourceCounters } from './ResourceCounters';
4
- export type { SubMenuProps } from './ResourceSubMenu';
5
- export { default as TopCounterResourceSubMenu } from './ResourceSubMenu';
6
4
  export { default as TopCounterLayout } from './TopCounterLayout';
@@ -1,110 +0,0 @@
1
- import { List, ListItem } from '@mui/material';
2
-
3
- import { getStatusColors, type SeverityCode } from '@centreon/ui';
4
- import { ThemeMode } from '@centreon/ui-context';
5
-
6
- import { equals } from 'ramda';
7
- import { Link } from 'react-router';
8
- import { makeStyles } from 'tss-react/mui';
9
-
10
- const useStyles = makeStyles()((theme) => ({
11
- count: {
12
- marginLeft: 'auto'
13
- },
14
- link: {
15
- alignItems: 'center',
16
- color: 'inherit',
17
- display: 'flex',
18
- flex: '100%',
19
- padding: `0 ${theme.spacing(1)}`,
20
- textDecoration: 'none'
21
- },
22
- status: {
23
- alignItems: 'center',
24
- display: 'flex'
25
- },
26
- statusCounter: {
27
- borderRadius: '50%',
28
- height: theme.spacing(1),
29
- marginRight: theme.spacing(1),
30
- width: theme.spacing(1)
31
- },
32
- submenu: {
33
- fontSize: theme.typography.body2.fontSize,
34
- padding: 0
35
- },
36
- submenuItem: {
37
- '&:hover': {
38
- background: equals(theme.palette.mode, ThemeMode.dark)
39
- ? theme.palette.primary.dark
40
- : theme.palette.primary.light,
41
- color: equals(theme.palette.mode, ThemeMode.dark)
42
- ? theme.palette.common.white
43
- : theme.palette.primary.main
44
- },
45
- '&:not(:last-child)': {
46
- borderBottom: `1px solid ${theme.palette.divider}`
47
- }
48
- }
49
- }));
50
-
51
- export interface SubMenuProps {
52
- items: Array<{
53
- countTestId?: string;
54
- onClick: (e: React.MouseEvent) => void;
55
- severityCode: SeverityCode;
56
- submenuCount: string | number;
57
- submenuTitle: string;
58
- to: string;
59
- }>;
60
- onClose?: () => void;
61
- }
62
-
63
- const SubMenu = ({ items, onClose }: SubMenuProps): JSX.Element => {
64
- const { classes, theme } = useStyles();
65
-
66
- return (
67
- <List className={classes.submenu}>
68
- {items.map(
69
- ({
70
- onClick,
71
- severityCode,
72
- submenuTitle,
73
- submenuCount,
74
- countTestId,
75
- to
76
- }) => (
77
- <ListItem
78
- className={classes.submenuItem}
79
- disableGutters
80
- key={to}
81
- onClick={onClose}
82
- >
83
- <Link
84
- className={classes.link}
85
- onClick={onClick}
86
- role="menuitem"
87
- to={to}
88
- >
89
- <span className={classes.status}>
90
- <span
91
- className={classes.statusCounter}
92
- style={{
93
- backgroundColor: getStatusColors({ severityCode, theme })
94
- ?.backgroundColor
95
- }}
96
- />
97
- <span>{submenuTitle}</span>
98
- </span>
99
- <span className={classes.count} data-testid={countTestId}>
100
- {submenuCount}
101
- </span>
102
- </Link>
103
- </ListItem>
104
- )
105
- )}
106
- </List>
107
- );
108
- };
109
-
110
- export default SubMenu;