@centreon/ui 24.4.1-MON-34377-implement-tree-chart-on-centreon-ui.8 → 24.4.1-MON-34377-implement-tree-chart-on-centreon-ui.10

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-MON-34377-implement-tree-chart-on-centreon-ui.8",
3
+ "version": "24.4.1-MON-34377-implement-tree-chart-on-centreon-ui.10",
4
4
  "description": "Centreon UI Components",
5
5
  "scripts": {
6
6
  "update:deps": "pnpx npm-check-updates -i --format group",
@@ -33,25 +33,25 @@ const Links = <TData extends BaseProp>({
33
33
  fill="none"
34
34
  key={key}
35
35
  stroke={
36
- treeLink.getStroke?.({
36
+ treeLink?.getStroke?.({
37
37
  source: link.source.data.data,
38
38
  target: link.target.data.data
39
39
  }) || theme.palette.text.primary
40
40
  }
41
41
  strokeDasharray={
42
- treeLink.getStrokeDasharray?.({
42
+ treeLink?.getStrokeDasharray?.({
43
43
  source: link.source.data.data,
44
44
  target: link.target.data.data
45
45
  }) || '0'
46
46
  }
47
47
  strokeOpacity={
48
- treeLink.getStrokeOpacity?.({
48
+ treeLink?.getStrokeOpacity?.({
49
49
  source: link.source.data.data,
50
50
  target: link.target.data.data
51
51
  }) || 1
52
52
  }
53
53
  strokeWidth={
54
- treeLink.getStrokeWidth?.({
54
+ treeLink?.getStrokeWidth?.({
55
55
  source: link.source.data.data,
56
56
  target: link.target.data.data
57
57
  }) || '2'
@@ -0,0 +1,171 @@
1
+ import { equals } from 'ramda';
2
+
3
+ import {
4
+ ComplexData,
5
+ complexData,
6
+ SimpleData,
7
+ simpleData
8
+ } from './stories/datas';
9
+ import { ComplexContent, SimpleContent } from './stories/contents';
10
+
11
+ import { Node, StandaloneTree, TreeProps } from '.';
12
+
13
+ const validateTree = (tree): void => {
14
+ if (!tree.children) {
15
+ cy.contains(tree.data.name).should('be.visible');
16
+
17
+ return;
18
+ }
19
+
20
+ cy.contains(tree.data.name).should('be.visible');
21
+ tree.children.forEach((child) => {
22
+ validateTree(child);
23
+ });
24
+ };
25
+
26
+ interface InitializeProps
27
+ extends Pick<TreeProps<SimpleData | ComplexData>, 'treeLink' | 'children'> {
28
+ data?: Node<SimpleData | ComplexData>;
29
+ isDefaultExpanded?: (data: SimpleData | ComplexData) => boolean;
30
+ }
31
+
32
+ const initializeStandaloneTree = ({
33
+ data = simpleData,
34
+ isDefaultExpanded = undefined,
35
+ treeLink,
36
+ children = SimpleContent
37
+ }: InitializeProps): void => {
38
+ cy.mount({
39
+ Component: (
40
+ <div style={{ height: '99vh' }}>
41
+ <StandaloneTree
42
+ node={{ height: 70, isDefaultExpanded, width: 70 }}
43
+ tree={data}
44
+ treeLink={treeLink}
45
+ >
46
+ {children}
47
+ </StandaloneTree>
48
+ </div>
49
+ )
50
+ });
51
+ };
52
+
53
+ describe('Simple data tree', () => {
54
+ it('displays the whole tree', () => {
55
+ initializeStandaloneTree({});
56
+
57
+ validateTree(simpleData);
58
+
59
+ cy.makeSnapshot();
60
+ });
61
+
62
+ it("collapses a node's childrens when a node is clicked", () => {
63
+ initializeStandaloneTree({});
64
+
65
+ cy.contains(/^E$/).should('be.visible');
66
+ cy.contains(/^E1$/).should('be.visible');
67
+
68
+ cy.contains(/^C$/).click();
69
+
70
+ cy.contains(/^E$/).should('not.exist');
71
+ cy.contains(/^E1$/).should('not.exist');
72
+
73
+ cy.makeSnapshot();
74
+ });
75
+
76
+ it("expands a node's childrens when a node is clicked", () => {
77
+ initializeStandaloneTree({});
78
+
79
+ cy.contains(/^A$/).click();
80
+
81
+ cy.contains(/^A1$/).should('not.exist');
82
+ cy.contains(/^A2$/).should('not.exist');
83
+ cy.contains(/^A3$/).should('not.exist');
84
+ cy.contains(/^C$/).should('not.exist');
85
+
86
+ cy.contains(/^A$/).click();
87
+
88
+ cy.contains(/^A1$/).should('be.visible');
89
+ cy.contains(/^A2$/).should('be.visible');
90
+ cy.contains(/^A3$/).should('be.visible');
91
+ cy.contains(/^C$/).should('be.visible');
92
+
93
+ cy.makeSnapshot();
94
+ });
95
+
96
+ it('cannot collapses a node when a leaf is clicked', () => {
97
+ initializeStandaloneTree({});
98
+
99
+ cy.contains(/^Z$/).click();
100
+
101
+ cy.contains(/^Z$/).should('be.visible');
102
+
103
+ cy.makeSnapshot();
104
+ });
105
+
106
+ it('expands nodes by default when a prop is set', () => {
107
+ initializeStandaloneTree({
108
+ isDefaultExpanded: (data: SimpleData) => equals('critical', data.status)
109
+ });
110
+
111
+ cy.contains(/^T$/).should('be.visible');
112
+ cy.contains(/^A$/).should('be.visible');
113
+ cy.contains(/^A3$/).should('be.visible');
114
+ cy.contains(/^C$/).should('be.visible');
115
+ cy.contains(/^E$/).should('be.visible');
116
+ cy.contains(/^E1$/).should('be.visible');
117
+
118
+ cy.contains(/^B1$/).should('not.exist');
119
+ cy.contains(/^D1$/).should('not.exist');
120
+
121
+ cy.makeSnapshot();
122
+ });
123
+
124
+ it('displays customized links when a prop is set', () => {
125
+ initializeStandaloneTree({
126
+ treeLink: {
127
+ getStroke: ({ target }) => (target.status === 'ok' ? 'grey' : 'black'),
128
+ getStrokeDasharray: ({ target }) =>
129
+ target.status === 'ok' ? '5,5' : '0',
130
+ getStrokeOpacity: ({ target }) => (target.status === 'ok' ? 0.8 : 1),
131
+ getStrokeWidth: ({ target }) => (target.status === 'ok' ? 1 : 2)
132
+ }
133
+ });
134
+
135
+ cy.contains(/^Z$/).should('be.visible');
136
+
137
+ cy.makeSnapshot();
138
+ });
139
+ });
140
+
141
+ describe('Complex data tree', () => {
142
+ it('cannot collapse a node when a node is not clickable', () => {
143
+ initializeStandaloneTree({
144
+ children: ComplexContent,
145
+ data: complexData
146
+ });
147
+
148
+ cy.contains('BA 3').should('be.visible');
149
+
150
+ cy.contains('BA 2').click();
151
+
152
+ cy.contains('BA 3').should('be.visible');
153
+
154
+ cy.makeSnapshot();
155
+ });
156
+
157
+ it('collapses a node when a node is clickable', () => {
158
+ initializeStandaloneTree({
159
+ children: ComplexContent,
160
+ data: complexData
161
+ });
162
+
163
+ cy.contains('BA 3').should('be.visible');
164
+
165
+ cy.contains('2').click();
166
+
167
+ cy.contains('BA 3').should('not.exist');
168
+
169
+ cy.makeSnapshot();
170
+ });
171
+ });
@@ -54,7 +54,7 @@ export const Tree = <TData extends BaseProp>({
54
54
 
55
55
  const expandCollapseNode = useCallback(
56
56
  (targetNode: Node<TData>): void => {
57
- changeTree(
57
+ changeTree?.(
58
58
  toggleTreeNodesExpanded({ currentTree: formattedTree, targetNode })
59
59
  );
60
60
  },
@@ -76,12 +76,15 @@ export const Tree = <TData extends BaseProp>({
76
76
  );
77
77
 
78
78
  const origin = useMemo(
79
- () => ({ x: 0, y: containerHeight / 2 }),
79
+ () => ({
80
+ x: 0,
81
+ y: containerHeight / 2
82
+ }),
80
83
  [containerHeight]
81
84
  );
82
85
 
83
86
  return (
84
- <Group left={node.width / 2}>
87
+ <Group left={node.width}>
85
88
  <VisxTree
86
89
  left={0}
87
90
  nodeSize={[node.width + nodeMargins.y, node.height + nodeMargins.x]}
@@ -29,17 +29,19 @@ export interface ChildrenProps<TData> {
29
29
  }
30
30
 
31
31
  export interface TreeProps<TData> {
32
- changeTree: (newTree: Node<TData>) => void;
32
+ changeTree?: (newTree: Node<TData>) => void;
33
33
  children: (props: ChildrenProps<TData>) => JSX.Element;
34
34
  containerHeight: number;
35
35
  containerWidth: number;
36
+ contentHeight?: number;
37
+ contentWidth?: number;
36
38
  node: {
37
39
  height: number;
38
40
  isDefaultExpanded?: (data: TData) => boolean;
39
41
  width: number;
40
42
  };
41
43
  tree: Node<TData>;
42
- treeLink: {
44
+ treeLink?: {
43
45
  getStroke?: (props: LinkProps<TData>) => string | undefined;
44
46
  getStrokeDasharray?: (
45
47
  props: LinkProps<TData>
@@ -168,7 +168,7 @@ export const complexData: Node<ComplexData> = {
168
168
  }
169
169
  }
170
170
  ],
171
- data: { id: 0, name: 'BA 3', status: 'ok' }
171
+ data: { id: 0, name: 'BA 4', status: 'ok' }
172
172
  }
173
173
  ],
174
174
  data: { count: 2, id: 0, name: 'ok', status: 'ok' }
@@ -294,7 +294,7 @@ export const moreComplexData: Node<ComplexData> = {
294
294
  { data: { id: 37, name: 'Indicator 37', status: 'ok' } },
295
295
  { data: { id: 38, name: 'Indicator 38', status: 'ok' } }
296
296
  ],
297
- data: { count: 2, id: 0, name: 'ok', status: 'ok' }
297
+ data: { count: 9, id: 0, name: 'ok', status: 'ok' }
298
298
  }
299
299
  ],
300
300
  data: {
@@ -12,6 +12,10 @@ interface Props extends Omit<UseMinimapProps, 'minimapScale' | 'scale'> {
12
12
  height: number;
13
13
  width: number;
14
14
  };
15
+ diffBetweenContentAndSvg: {
16
+ left: number;
17
+ top: number;
18
+ };
15
19
  isDraggingFromContainer: boolean;
16
20
  }
17
21
 
@@ -21,7 +25,8 @@ const Minimap = ({
21
25
  height,
22
26
  width,
23
27
  contentClientRect,
24
- isDraggingFromContainer
28
+ isDraggingFromContainer,
29
+ diffBetweenContentAndSvg
25
30
  }: Props): JSX.Element => {
26
31
  const { classes } = useZoomStyles();
27
32
 
@@ -61,28 +66,20 @@ const Minimap = ({
61
66
  2 /
62
67
  zoom.transformMatrix.scaleY;
63
68
 
64
- const getAdditionalPadding = (): number => {
65
- if (additionalScale > 0.05) {
66
- return 0;
67
- }
68
-
69
- const padding =
70
- additionalScale > 0.012
71
- ? (1 / additionalScale) * (1 / zoom.transformMatrix.scaleY)
72
- : 1 / additionalScale / zoom.transformMatrix.scaleY;
73
-
74
- if (additionalScale < 0.009) {
75
- const test = scaleLinear({
76
- clamp: true,
77
- domain: [0.005, 0.002],
78
- range: [1, 5]
79
- });
80
-
81
- return padding - padding / test(additionalScale);
82
- }
83
-
84
- return padding;
85
- };
69
+ const translateX = useMemo(
70
+ () =>
71
+ -diffBetweenContentAndSvg.left /
72
+ zoom.transformMatrix.scaleX /
73
+ minimapScale,
74
+ [diffBetweenContentAndSvg.left]
75
+ );
76
+ const translateY = useMemo(
77
+ () =>
78
+ -diffBetweenContentAndSvg.top /
79
+ zoom.transformMatrix.scaleX /
80
+ minimapScale,
81
+ [diffBetweenContentAndSvg.top]
82
+ );
86
83
 
87
84
  return (
88
85
  <g className={classes.minimap} clipPath="url(#zoom-clip)">
@@ -93,16 +90,13 @@ const Minimap = ({
93
90
  width={finalWidth}
94
91
  />
95
92
  <g
93
+ className={classes.movingZone}
96
94
  style={{
97
- transform: `scale(${scaleToUse - additionalScale}) translate(0px, ${getAdditionalPadding()}px)`
95
+ transform: `scale(${scaleToUse - additionalScale}) translate(${translateX}px, ${translateY}px)`
98
96
  }}
99
97
  >
100
98
  {children}
101
- <g
102
- style={{
103
- transform: `translate(0px, ${contentClientRect.height / 10}px)`
104
- }}
105
- >
99
+ <g>
106
100
  <rect
107
101
  className={classes.minimapZoom}
108
102
  fillOpacity={0.2}
@@ -1,41 +1,58 @@
1
1
  import Zoom from './Zoom';
2
2
 
3
- const content = (
3
+ const Content = (): JSX.Element => (
4
4
  <g style={{ transform: 'translate(300px, 150px)' }}>
5
5
  <circle fill="blue" r={50} stroke="black" />
6
6
  </g>
7
7
  );
8
8
 
9
- const contentWithMultipleShapes = (
10
- <g style={{ transform: 'translate(0px, -300px)' }}>
11
- <g style={{ transform: 'translate(300px, 150px)' }}>
12
- <circle fill="blue" r={50} stroke="black" />
9
+ const ContentWithMultipleShapes = (): JSX.Element => {
10
+ return (
11
+ <g>
12
+ <g style={{ transform: 'translate(300px, 150px)' }}>
13
+ <circle fill="blue" r={50} stroke="black" />
14
+ </g>
15
+ <g style={{ transform: 'translate(600px, 500px)' }}>
16
+ <circle fill="green" r={70} />
17
+ </g>
18
+ <g style={{ transform: 'translate(150px, 600px)' }}>
19
+ <circle fill="red" r={70} />
20
+ </g>
13
21
  </g>
14
- <g style={{ transform: 'translate(600px, 500px)' }}>
15
- <circle fill="green" r={70} />
16
- </g>
17
- <g style={{ transform: 'translate(150px, 600px)' }}>
18
- <circle fill="red" r={70} />
22
+ );
23
+ };
24
+ const ContentWithMultipleShapesWithNegativeTranslations = (): JSX.Element => {
25
+ return (
26
+ <g>
27
+ <g style={{ transform: 'translate(-300px, -150px)' }}>
28
+ <circle fill="blue" r={50} stroke="black" />
29
+ </g>
30
+ <g style={{ transform: 'translate(600px, 500px)' }}>
31
+ <circle fill="green" r={70} />
32
+ </g>
33
+ <g style={{ transform: 'translate(150px, 600px)' }}>
34
+ <circle fill="red" r={70} />
35
+ </g>
19
36
  </g>
20
- </g>
21
- );
37
+ );
38
+ };
22
39
 
23
40
  interface Props {
24
41
  minimapPosition?;
25
42
  showMinimap: boolean;
26
- useMultipleShapes?: boolean;
43
+ tenplate?: () => JSX.Element;
27
44
  }
28
45
 
29
46
  const initialize = ({
30
47
  showMinimap,
31
48
  minimapPosition,
32
- useMultipleShapes
49
+ template = Content
33
50
  }: Props): void => {
34
51
  cy.mount({
35
52
  Component: (
36
53
  <div style={{ height: '400px', width: '100%' }}>
37
54
  <Zoom minimapPosition={minimapPosition} showMinimap={showMinimap}>
38
- {useMultipleShapes ? contentWithMultipleShapes : content}
55
+ {template}
39
56
  </Zoom>
40
57
  </div>
41
58
  )
@@ -195,7 +212,25 @@ describe('Zoom', () => {
195
212
  });
196
213
 
197
214
  it('applies a scale down on the minimap when the content is higher than the original height', () => {
198
- initialize({ showMinimap: true, useMultipleShapes: true });
215
+ initialize({ showMinimap: true, template: ContentWithMultipleShapes });
216
+
217
+ cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
218
+ cy.get('svg').should('have.attr', 'height', '400');
219
+
220
+ cy.findByTestId('minimap-interaction')
221
+ .parent()
222
+ .find('g')
223
+ .should('have.attr', 'style')
224
+ .and('include', 'transform: scale(0.684211) translate(-85px, -105px);');
225
+
226
+ cy.makeSnapshot();
227
+ });
228
+
229
+ it('applies a scale down on the minimap when the content has negative translation values', () => {
230
+ initialize({
231
+ showMinimap: true,
232
+ template: ContentWithMultipleShapesWithNegativeTranslations
233
+ });
199
234
 
200
235
  cy.get('g[clip-path="url(#zoom-clip)"]').should('be.visible');
201
236
  cy.get('svg').should('have.attr', 'height', '400');
@@ -203,11 +238,8 @@ describe('Zoom', () => {
203
238
  cy.findByTestId('minimap-interaction')
204
239
  .parent()
205
240
  .find('g')
206
- .should(
207
- 'have.attr',
208
- 'style',
209
- 'transform: scale(0.684211) translate(0px, 57px);'
210
- );
241
+ .should('have.attr', 'style')
242
+ .and('include', 'transform: scale(0.448276) translate(345px, 195px);');
211
243
 
212
244
  cy.makeSnapshot();
213
245
  });
@@ -24,19 +24,21 @@ const meta: Meta<typeof Zoom> = {
24
24
  export default meta;
25
25
  type Story = StoryObj<typeof Zoom>;
26
26
 
27
- const content = (
28
- <g style={{ transform: 'translate(0px, -200px)' }}>
29
- <g style={{ transform: 'translate(300px, 150px)' }}>
30
- <circle fill="blue" r={50} stroke="black" />
27
+ const Content = (): JSX.Element => {
28
+ return (
29
+ <g style={{ transform: 'translate(-150px, -100px)' }}>
30
+ <g style={{ transform: 'translate(300px, 150px)' }}>
31
+ <circle fill="blue" r={50} stroke="black" />
32
+ </g>
33
+ <g style={{ transform: 'translate(600px, 400px)' }}>
34
+ <circle fill="green" r={70} />
35
+ </g>
36
+ <g style={{ transform: `translate(2400px, 1400px)` }}>
37
+ <circle fill="red" r={70} />
38
+ </g>
31
39
  </g>
32
- <g style={{ transform: 'translate(600px, 400px)' }}>
33
- <circle fill="green" r={70} />
34
- </g>
35
- <g style={{ transform: 'translate(150px, 800px)' }}>
36
- <circle fill="red" r={70} />
37
- </g>
38
- </g>
39
- );
40
+ );
41
+ };
40
42
 
41
43
  const Template = ({ children, ...args }: ZoomProps): JSX.Element => (
42
44
  <div style={{ height: '400px', width: '100%' }}>
@@ -44,16 +46,44 @@ const Template = ({ children, ...args }: ZoomProps): JSX.Element => (
44
46
  </div>
45
47
  );
46
48
 
49
+ const TemplateResponsive = ({
50
+ redCircleXPosition,
51
+ redCircleYPosition,
52
+ ...args
53
+ }: ZoomProps): JSX.Element => (
54
+ <div style={{ height: '90vh', width: '100%' }}>
55
+ <Zoom {...args}>
56
+ {() => (
57
+ <g>
58
+ <g style={{ transform: 'translate(500px, 100px)' }}>
59
+ <circle fill="blue" r={50} stroke="black" />
60
+ </g>
61
+ <g style={{ transform: 'translate(600px, 400px)' }}>
62
+ <circle fill="green" r={70} />
63
+ </g>
64
+ <g
65
+ style={{
66
+ transform: `translate(${redCircleXPosition || 100}px, ${redCircleYPosition || 100}px)`
67
+ }}
68
+ >
69
+ <circle fill="red" r={70} />
70
+ </g>
71
+ </g>
72
+ )}
73
+ </Zoom>
74
+ </div>
75
+ );
76
+
47
77
  export const WithoutMinimap: Story = {
48
78
  args: {
49
- children: content
79
+ children: Content
50
80
  },
51
81
  render: Template
52
82
  };
53
83
 
54
84
  export const WithMinimap: Story = {
55
85
  args: {
56
- children: content,
86
+ children: Content,
57
87
  showMinimap: true
58
88
  },
59
89
  render: Template
@@ -61,9 +91,25 @@ export const WithMinimap: Story = {
61
91
 
62
92
  export const WithMinimapPosition: Story = {
63
93
  args: {
64
- children: content,
94
+ children: Content,
65
95
  minimapPosition: 'bottom-right',
66
96
  showMinimap: true
67
97
  },
68
98
  render: Template
69
99
  };
100
+
101
+ export const Playground: Story = {
102
+ argTypes: {
103
+ redCircleXPosition: {
104
+ control: { max: 2600, min: 400, step: 10, type: 'range' }
105
+ },
106
+ redCircleYPosition: {
107
+ control: { max: 2600, min: 400, step: 10, type: 'range' }
108
+ }
109
+ },
110
+ args: {
111
+ children: Content,
112
+ showMinimap: true
113
+ },
114
+ render: TemplateResponsive
115
+ };
@@ -1,8 +1,6 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
2
 
3
3
  import { RectClipPath } from '@visx/clip-path';
4
- import { equals, type } from 'ramda';
5
- import { Group } from '@visx/group';
6
4
  import { ProvidedZoom } from '@visx/zoom/lib/types';
7
5
 
8
6
  import ZoomInIcon from '@mui/icons-material/Add';
@@ -15,10 +13,10 @@ import { minimapScale, radius } from './constants';
15
13
  import { useZoom } from './useZoom';
16
14
  import { useZoomStyles } from './Zoom.styles';
17
15
  import Minimap from './Minimap';
18
- import { MinimapPosition, ZoomState } from './models';
16
+ import { ChildrenProps, MinimapPosition, ZoomState } from './models';
19
17
 
20
18
  export interface Props {
21
- children: JSX.Element | (({ width, height }) => JSX.Element);
19
+ children: ({ width, height, transformMatrix }: ChildrenProps) => JSX.Element;
22
20
  height: number;
23
21
  minimapPosition: MinimapPosition;
24
22
  showMinimap?: boolean;
@@ -36,16 +34,21 @@ const ZoomContent = ({
36
34
  }: Props): JSX.Element => {
37
35
  const { classes } = useZoomStyles();
38
36
  const contentRef = useRef<SVGGElement | null>(null);
37
+ const minimapSvgRef = useRef<SVGSVGElement | null>(null);
38
+ const minimapContentRef = useRef<SVGSVGElement | null>(null);
39
39
  const [contentClientRect, setContentClientRect] = useState<{
40
40
  height: number;
41
41
  width: number;
42
42
  } | null>(null);
43
43
 
44
44
  const resizeObserver = new ResizeObserver(() => {
45
+ const contentBoundingClientRect = (
46
+ contentRef.current as SVGGElement
47
+ ).getBoundingClientRect();
48
+
45
49
  setContentClientRect({
46
- height: (contentRef.current as SVGGElement).getBoundingClientRect()
47
- .height,
48
- width: (contentRef.current as SVGGElement).getBoundingClientRect().width
50
+ height: contentBoundingClientRect.height,
51
+ width: contentBoundingClientRect.width
49
52
  });
50
53
  });
51
54
 
@@ -62,7 +65,15 @@ const ZoomContent = ({
62
65
 
63
66
  const { move, dragEnd, dragStart, isDragging } = useZoom();
64
67
 
65
- const isChildrenObject = equals(type(children), 'Object');
68
+ const diffBetweenContentAndSvg = minimapSvgRef.current &&
69
+ minimapContentRef.current && {
70
+ left:
71
+ minimapContentRef.current.getBoundingClientRect().left -
72
+ minimapSvgRef.current.getBoundingClientRect().left,
73
+ top:
74
+ minimapContentRef.current.getBoundingClientRect().top -
75
+ minimapSvgRef.current.getBoundingClientRect().top
76
+ };
66
77
 
67
78
  return (
68
79
  <div style={{ position: 'relative' }}>
@@ -90,7 +101,12 @@ const ZoomContent = ({
90
101
  ref={contentRef}
91
102
  transform={zoom.toString()}
92
103
  >
93
- {isChildrenObject ? children : children({ height, width })}
104
+ {children({
105
+ contentClientRect,
106
+ height,
107
+ transformMatrix: zoom.transformMatrix,
108
+ width
109
+ })}
94
110
  </g>
95
111
  </svg>
96
112
  <div className={classes.actionsAndZoom} data-position={minimapPosition}>
@@ -99,18 +115,27 @@ const ZoomContent = ({
99
115
  className={classes.minimapContainer}
100
116
  data-testid="minimap"
101
117
  height={height * minimapScale}
118
+ ref={minimapSvgRef}
102
119
  width={width * minimapScale}
103
120
  >
104
121
  <Minimap
105
122
  contentClientRect={contentClientRect}
123
+ diffBetweenContentAndSvg={
124
+ diffBetweenContentAndSvg || { left: 0, top: 0 }
125
+ }
106
126
  height={height}
107
127
  isDraggingFromContainer={isDragging}
108
128
  width={width}
109
129
  zoom={zoom}
110
130
  >
111
- <Group left={0} top={contentClientRect.height / 10}>
112
- {isChildrenObject ? children : children({ height, width })}
113
- </Group>
131
+ <g ref={minimapContentRef}>
132
+ {children({
133
+ contentClientRect,
134
+ height,
135
+ transformMatrix: zoom.transformMatrix,
136
+ width
137
+ })}
138
+ </g>
114
139
  </Minimap>
115
140
  </svg>
116
141
  )}
@@ -14,3 +14,12 @@ export type MinimapPosition =
14
14
  | 'top-right'
15
15
  | 'bottom-left'
16
16
  | 'bottom-right';
17
+
18
+ export interface ChildrenProps extends ZoomState {
19
+ contentClientRect: {
20
+ height: number;
21
+ width: number;
22
+ } | null;
23
+ height: number;
24
+ width: number;
25
+ }
@@ -0,0 +1,55 @@
1
+ import { CSSProperties } from 'react';
2
+
3
+ interface Props {
4
+ contentClientRect: {
5
+ height: number;
6
+ width: number;
7
+ } | null;
8
+ }
9
+
10
+ export const applyTranformStylesForZoom = ({
11
+ contentClientRect
12
+ }: Props): CSSProperties => {
13
+ const contentRect = {
14
+ height: contentClientRect?.height || 1,
15
+ width: contentClientRect?.width || 1
16
+ };
17
+ const isPortrait = contentRect.height > contentRect.width;
18
+ const sizes = isPortrait ? ['width', 'height'] : ['height', 'width'];
19
+ const sizeScale = contentRect[sizes[0]] / contentRect[sizes[1]];
20
+
21
+ const lengthToUse = isPortrait
22
+ ? contentRect[sizes[1]] - contentRect[sizes[0]]
23
+ : contentRect[sizes[0]];
24
+
25
+ const t = sizeScale > 0.85 && isPortrait ? sizeScale * 4 : sizeScale / 2;
26
+ const xScaleFactor = sizeScale > 0.7 && !isPortrait ? 10 : 6;
27
+
28
+ return {
29
+ transform: `translate(-${isPortrait ? 0 : contentRect.width * (sizeScale / xScaleFactor)}px, -${lengthToUse * (isPortrait ? t + 0.08 : t / 2)}px)`
30
+ };
31
+ };
32
+
33
+ // DO NOT REMOVE: As the component is in work in progress, please this code in case we need
34
+ // const getAdditionalPadding = (): number => {
35
+ // if (additionalScale > 0.05) {
36
+ // return 0;
37
+ // }
38
+
39
+ // const padding =
40
+ // additionalScale > 0.012
41
+ // ? (1 / additionalScale) * (1 / zoom.transformMatrix.scaleY)
42
+ // : 1 / additionalScale / zoom.transformMatrix.scaleY;
43
+
44
+ // if (additionalScale < 0.009) {
45
+ // const tweakScale = scaleLinear({
46
+ // clamp: true,
47
+ // domain: [0.009, 0.002],
48
+ // range: [1, 8]
49
+ // });
50
+
51
+ // return padding - padding / tweakScale(additionalScale);
52
+ // }
53
+
54
+ // return padding;
55
+ // };