@centreon/ui 26.3.10 → 26.3.12

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": "26.3.10",
3
+ "version": "26.3.12",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "update:deps": "pnpx npm-check-updates -i --format group",
@@ -152,7 +152,7 @@
152
152
  "notistack": "^3.0.2",
153
153
  "numeral": "^2.0.6",
154
154
  "ramda": "0.30.1",
155
- "react-grid-layout": "^1.5.0",
155
+ "react-grid-layout": "^2.2.2",
156
156
  "react-resizable": "^3.0.5",
157
157
  "react-router": "7",
158
158
  "react-transition-group": "^4.4.5",
@@ -1,12 +1,13 @@
1
1
  import { Typography } from '@mui/material';
2
2
 
3
- import type { ComponentMeta } from '@storybook/react';
4
- import type { Layout } from 'react-grid-layout';
3
+ import type { Meta, StoryObj } from '@storybook/react';
4
+ import { ReactElement } from 'react';
5
+ import type { LayoutItem } from 'react-grid-layout';
5
6
 
6
7
  import FluidTypography from '../Typography/FluidTypography';
7
8
  import { DashboardLayout } from '.';
8
9
 
9
- interface CustomLayout extends Layout {
10
+ interface CustomLayout extends LayoutItem {
10
11
  content: string;
11
12
  shouldUseFluidTypography: boolean;
12
13
  }
@@ -59,21 +60,21 @@ const generateLayout = (maxElements: number): Array<CustomLayout> => {
59
60
  };
60
61
 
61
62
  interface DashboardTemplateProps {
62
- header?: JSX.Element;
63
+ header?: ReactElement;
63
64
  layout?: Array<CustomLayout>;
64
65
  }
65
66
 
66
- const Header = (): JSX.Element => (
67
+ const Header = (): ReactElement => (
67
68
  <Typography variant="body2">The title</Typography>
68
69
  );
69
70
 
70
71
  const DashboardTemplate = ({
71
72
  header,
72
73
  layout = dashboardLayout
73
- }: DashboardTemplateProps): JSX.Element => (
74
+ }: DashboardTemplateProps): ReactElement => (
74
75
  <DashboardLayout.Layout<CustomLayout> layout={layout}>
75
76
  {layout.map(({ i, content, shouldUseFluidTypography }) => (
76
- <DashboardLayout.Item header={header} key={i}>
77
+ <DashboardLayout.Item header={header} id={i} key={i}>
77
78
  {shouldUseFluidTypography ? (
78
79
  <FluidTypography text={content} />
79
80
  ) : (
@@ -84,20 +85,26 @@ const DashboardTemplate = ({
84
85
  </DashboardLayout.Layout>
85
86
  );
86
87
 
87
- export default {
88
- argTypes: {},
88
+ const meta: Meta<typeof DashboardTemplate> = {
89
89
  component: DashboardTemplate,
90
90
  title: 'Dashboard'
91
- } as ComponentMeta<typeof DashboardTemplate>;
91
+ };
92
92
 
93
- export const normal = DashboardTemplate.bind({});
93
+ export default meta;
94
+ type Story = StoryObj<typeof DashboardTemplate>;
94
95
 
95
- export const withManyPanels = DashboardTemplate.bind({});
96
- withManyPanels.args = {
97
- layout: generateLayout(100)
96
+ export const normal: Story = {
97
+ args: {}
98
98
  };
99
99
 
100
- export const withItemHeader = DashboardTemplate.bind({});
101
- withItemHeader.args = {
102
- header: <Header />
100
+ export const withManyPanels: Story = {
101
+ args: {
102
+ layout: generateLayout(100)
103
+ }
104
+ };
105
+
106
+ export const withItemHeader: Story = {
107
+ args: {
108
+ header: <Header />
109
+ }
103
110
  };
@@ -4,10 +4,9 @@ import { useAtomValue } from 'jotai';
4
4
  import { equals, isNil, omit, type } from 'ramda';
5
5
  import {
6
6
  type CSSProperties,
7
- type ForwardedRef,
8
- forwardRef,
9
7
  type MouseEvent,
10
8
  type ReactElement,
9
+ RefObject,
11
10
  useEffect,
12
11
  useMemo
13
12
  } from 'react';
@@ -31,148 +30,145 @@ interface DashboardItemProps {
31
30
  onMouseUp?: (e: MouseEvent<HTMLDivElement>) => void;
32
31
  onTouchEnd?: (e) => void;
33
32
  style?: CSSProperties;
33
+ ref?: RefObject<HTMLDivElement>;
34
34
  }
35
35
 
36
- const Item = forwardRef<HTMLDivElement, DashboardItemProps>(
37
- (
38
- {
39
- children,
40
- style,
41
- className,
42
- header,
43
- onMouseDown,
44
- onMouseUp,
45
- onTouchEnd,
46
- id,
47
- disablePadding = false,
48
- canMove = false,
49
- additionalMemoProps = []
50
- }: DashboardItemProps,
51
- ref: ForwardedRef<HTMLDivElement>
52
- ): ReactElement => {
53
- const { isInViewport, setElement } = useViewportIntersection({
54
- rootMargin: '140px 0px 140px 0px'
55
- });
56
- const hasHeader = !isNil(header);
57
-
58
- const { classes, cx } = useDashboardItemStyles({ hasHeader });
59
- const theme = useTheme();
60
-
61
- const isResizingItem = useAtomValue(isResizingItemAtom);
62
-
63
- const isResizing = useMemo(
64
- () => equals(id, isResizingItem),
65
- [isResizingItem, id]
66
- );
67
-
68
- const sanitizedReactGridLayoutClassName = useMemo(
69
- () => (isResizing ? className : className?.replace(' resizing ', '')),
70
- [className, isResizing]
71
- );
72
-
73
- const listeners = {
74
- onMouseDown,
75
- onMouseUp,
76
- onTouchEnd
77
- };
78
-
79
- const cardContainerListeners = !hasHeader ? listeners : {};
80
-
81
- useEffect(() => {
82
- if (isNil(ref?.current)) {
83
- return;
84
- }
85
-
86
- setElement(ref.current);
87
- }, [ref, setElement]);
88
-
89
- const newTransform =
90
- style?.transform &&
91
- `translate3d(${style.transform.match(/translate\(([a-z0-9 ,-]+)\)/)[1]}, 0px)`;
92
-
93
- return useMemoComponent({
94
- Component: (
95
- <div
96
- {...cardContainerListeners}
97
- className={sanitizedReactGridLayoutClassName}
98
- ref={ref}
99
- style={{
100
- ...omit(['transform'], style || {}),
101
- transform: newTransform
102
- }}
103
- >
104
- <ExpandableContainer>
105
- {({ isExpanded, label, key, ...rest }) => {
106
- const canControl = isExpanded ? false : canMove;
107
-
108
- const childrenHeader = equals(type(header), 'Function')
109
- ? (header as (params: Parameters) => ReactElement)({
110
- isExpanded,
111
- key,
112
- label,
113
- ref,
114
- ...rest
115
- })
116
- : header;
117
-
118
- return (
119
- <div className={classes.widgetSubContainer} key={key}>
120
- <Card
121
- className={classes.widgetContainer}
122
- data-padding={!disablePadding}
123
- >
124
- {childrenHeader && (
125
- <div
126
- className={classes.widgetHeader}
127
- data-can-move={canControl}
128
- >
129
- {canControl && (
130
- <div
131
- {...listeners}
132
- className={classes.widgetHeaderDraggable}
133
- data-testid={`${id}_move_panel`}
134
- />
135
- )}
136
- {childrenHeader}
137
- </div>
138
- )}
36
+ const Item = ({
37
+ children,
38
+ style,
39
+ className,
40
+ header,
41
+ onMouseDown,
42
+ onMouseUp,
43
+ onTouchEnd,
44
+ id,
45
+ disablePadding = false,
46
+ canMove = false,
47
+ additionalMemoProps = [],
48
+ ref
49
+ }: DashboardItemProps): ReactElement => {
50
+ const { isInViewport, setElement } = useViewportIntersection({
51
+ rootMargin: '140px 0px 140px 0px'
52
+ });
53
+ const hasHeader = !isNil(header);
54
+
55
+ const { classes, cx } = useDashboardItemStyles({ hasHeader });
56
+ const theme = useTheme();
57
+
58
+ const isResizingItem = useAtomValue(isResizingItemAtom);
59
+
60
+ const isResizing = useMemo(
61
+ () => equals(id, isResizingItem),
62
+ [isResizingItem, id]
63
+ );
64
+
65
+ const sanitizedReactGridLayoutClassName = useMemo(
66
+ () => (isResizing ? className : className?.replace(' resizing ', '')),
67
+ [className, isResizing]
68
+ );
69
+
70
+ const listeners = {
71
+ onMouseDown,
72
+ onMouseUp,
73
+ onTouchEnd
74
+ };
75
+
76
+ const cardContainerListeners = !hasHeader ? listeners : {};
77
+
78
+ useEffect(() => {
79
+ if (isNil(ref?.current)) {
80
+ return;
81
+ }
82
+
83
+ setElement(ref.current);
84
+ }, [ref, setElement]);
85
+
86
+ const newTransform =
87
+ style?.transform &&
88
+ `translate3d(${style.transform.match(/translate\(([a-z0-9 ,-]+)\)/)[1]}, 0px)`;
89
+
90
+ return useMemoComponent({
91
+ Component: (
92
+ <div
93
+ {...cardContainerListeners}
94
+ className={sanitizedReactGridLayoutClassName}
95
+ ref={ref}
96
+ style={{
97
+ ...omit(['transform'], style || {}),
98
+ transform: newTransform
99
+ }}
100
+ >
101
+ <ExpandableContainer>
102
+ {({ isExpanded, label, key, ...rest }) => {
103
+ const canControl = isExpanded ? false : canMove;
104
+
105
+ const childrenHeader = equals(type(header), 'Function')
106
+ ? (header as (params: Parameters) => ReactElement)({
107
+ isExpanded,
108
+ key,
109
+ label,
110
+ ref,
111
+ ...rest
112
+ })
113
+ : header;
114
+
115
+ return (
116
+ <div className={classes.widgetSubContainer} key={key}>
117
+ <Card
118
+ className={classes.widgetContainer}
119
+ data-padding={!disablePadding}
120
+ >
121
+ {childrenHeader && (
139
122
  <div
140
- className={cx(
141
- classes.widgetContent,
142
- !disablePadding && classes.widgetPadding
143
- )}
123
+ className={classes.widgetHeader}
124
+ data-can-move={canControl}
144
125
  >
145
- {!isInViewport ? (
146
- <LoadingSkeleton
147
- animation={false}
148
- data-widget-skeleton={id}
149
- height="100%"
150
- width="100%"
126
+ {canControl && (
127
+ <div
128
+ {...listeners}
129
+ className={classes.widgetHeaderDraggable}
130
+ data-testid={`${id}_move_panel`}
151
131
  />
152
- ) : (
153
- children
154
132
  )}
133
+ {childrenHeader}
155
134
  </div>
156
- </Card>
157
- </div>
158
- );
159
- }}
160
- </ExpandableContainer>
161
- </div>
162
- ),
163
- memoProps: isInViewport
164
- ? [
165
- style,
166
- className,
167
- header,
168
- theme.palette.mode,
169
- canMove,
170
- isInViewport,
171
- ...additionalMemoProps
172
- ]
173
- : [isInViewport, theme.palette.mode, style]
174
- });
175
- }
176
- );
135
+ )}
136
+ <div
137
+ className={cx(
138
+ classes.widgetContent,
139
+ !disablePadding && classes.widgetPadding
140
+ )}
141
+ >
142
+ {!isInViewport ? (
143
+ <LoadingSkeleton
144
+ animation={false}
145
+ data-widget-skeleton={id}
146
+ height="100%"
147
+ width="100%"
148
+ />
149
+ ) : (
150
+ children
151
+ )}
152
+ </div>
153
+ </Card>
154
+ </div>
155
+ );
156
+ }}
157
+ </ExpandableContainer>
158
+ </div>
159
+ ),
160
+ memoProps: isInViewport
161
+ ? [
162
+ style,
163
+ className,
164
+ header,
165
+ theme.palette.mode,
166
+ canMove,
167
+ isInViewport,
168
+ ...additionalMemoProps
169
+ ]
170
+ : [isInViewport, theme.palette.mode, style]
171
+ });
172
+ };
177
173
 
178
174
  export default Item;
@@ -1,27 +1,30 @@
1
1
  import { Box } from '@mui/material';
2
2
 
3
3
  import { useSetAtom } from 'jotai';
4
- import { useCallback, useEffect, useRef, useState } from 'react';
5
- import GridLayout, { type Layout, WidthProvider } from 'react-grid-layout';
6
-
7
- import { ParentSize, useMemoComponent } from '..';
4
+ import { ReactElement, useCallback, useEffect, useState } from 'react';
5
+ import {
6
+ type Layout,
7
+ LayoutItem,
8
+ ReactGridLayout,
9
+ useContainerWidth
10
+ } from 'react-grid-layout';
11
+
12
+ import { useMemoComponent } from '..';
8
13
  import { isResizingItemAtom } from './atoms';
9
14
  import { useDashboardLayoutStyles } from './Dashboard.styles';
10
15
  import { getColumnsFromScreenSize, getLayout, rowHeight } from './utils';
11
16
  import 'react-grid-layout/css/styles.css';
12
17
 
13
- const ReactGridLayout = WidthProvider(GridLayout);
14
-
15
18
  interface DashboardLayoutProps<T> {
16
19
  additionalMemoProps?: Array<unknown>;
17
- changeLayout?: (newLayout: Array<Layout>) => void;
18
- children: Array<JSX.Element>;
20
+ changeLayout?: (newLayout: Layout) => void;
21
+ children: Array<ReactElement>;
19
22
  displayGrid?: boolean;
20
23
  isStatic?: boolean;
21
24
  layout: Array<T>;
22
25
  }
23
26
 
24
- const Handle = (axis, ref) => {
27
+ const Handle = (axis: string, ref: React.Ref<HTMLSpanElement>) => {
25
28
  return (
26
29
  <span
27
30
  className={`react-resizable-handle react-resizable-handle-${axis}`}
@@ -32,14 +35,14 @@ const Handle = (axis, ref) => {
32
35
  );
33
36
  };
34
37
 
35
- const DashboardLayout = <T extends Layout>({
38
+ const DashboardLayout = <T extends LayoutItem>({
36
39
  children,
37
40
  changeLayout,
38
41
  layout,
39
42
  isStatic = false,
40
43
  additionalMemoProps = []
41
- }: DashboardLayoutProps<T>): JSX.Element => {
42
- const dashboardContainerRef = useRef<HTMLDivElement | null>(null);
44
+ }: DashboardLayoutProps<T>): ReactElement => {
45
+ const { width, containerRef } = useContainerWidth();
43
46
 
44
47
  const { classes } = useDashboardLayoutStyles(isStatic);
45
48
 
@@ -52,8 +55,8 @@ const DashboardLayout = <T extends Layout>({
52
55
  };
53
56
 
54
57
  const startResize = useCallback(
55
- (_, _e, newItem: T) => {
56
- setIsResizingItem(newItem.i);
58
+ (_: Layout, _e: LayoutItem | null, newItem: LayoutItem | null) => {
59
+ setIsResizingItem((newItem as LayoutItem & { id: string })?.id ?? null);
57
60
  },
58
61
  [setIsResizingItem]
59
62
  );
@@ -70,35 +73,30 @@ const DashboardLayout = <T extends Layout>({
70
73
  };
71
74
  }, [resize]);
72
75
 
76
+ const currentLayout = getLayout(layout);
77
+
73
78
  return useMemoComponent({
74
79
  Component: (
75
- <Box
76
- ref={dashboardContainerRef}
77
- sx={{ overflowX: 'hidden', overflowY: 'auto' }}
78
- >
79
- <ParentSize>
80
- {({ width }): JSX.Element => (
81
- <Box className={classes.container}>
82
- <ReactGridLayout
83
- cols={columns}
84
- layout={getLayout(layout)}
85
- margin={[12, 12]}
86
- onLayoutChange={changeLayout}
87
- onResizeStart={startResize}
88
- onResizeStop={stopResize}
89
- resizeHandle={Handle}
90
- resizeHandles={['s', 'e', 'se', 'sw', 'w']}
91
- rowHeight={rowHeight}
92
- width={width}
93
- >
94
- {children}
95
- </ReactGridLayout>
96
- </Box>
97
- )}
98
- </ParentSize>
80
+ <Box ref={containerRef} sx={{ overflowX: 'hidden', overflowY: 'auto' }}>
81
+ <Box className={classes.container}>
82
+ <ReactGridLayout
83
+ gridConfig={{ cols: columns, margin: [12, 12], rowHeight }}
84
+ layout={currentLayout}
85
+ onLayoutChange={changeLayout}
86
+ onResizeStart={startResize}
87
+ onResizeStop={stopResize}
88
+ resizeConfig={{
89
+ handleComponent: Handle,
90
+ handles: ['s', 'e', 'se', 'sw', 'w']
91
+ }}
92
+ width={width}
93
+ >
94
+ {children}
95
+ </ReactGridLayout>
96
+ </Box>
99
97
  </Box>
100
98
  ),
101
- memoProps: [columns, layout, isStatic, ...additionalMemoProps]
99
+ memoProps: [columns, currentLayout, isStatic, ...additionalMemoProps]
102
100
  });
103
101
  };
104
102
 
@@ -1,5 +1,5 @@
1
1
  import { lt } from 'ramda';
2
- import type { Layout } from 'react-grid-layout';
2
+ import type { LayoutItem } from 'react-grid-layout';
3
3
 
4
4
  const minColumns = 1;
5
5
  const breakpoint = 768;
@@ -13,7 +13,7 @@ export const getIsSmallScreenSize = (): boolean =>
13
13
  export const getColumnsFromScreenSize = (): number =>
14
14
  getIsSmallScreenSize() ? minColumns : maxColumns;
15
15
 
16
- export const getLayout = <T extends Layout>(layout: Array<T>): Array<T> => {
16
+ export const getLayout = <T extends LayoutItem>(layout: Array<T>): Array<T> => {
17
17
  const isSmallScreenSize = getIsSmallScreenSize();
18
18
  if (!isSmallScreenSize) {
19
19
  return layout;