aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-08-23 11:48:37 +0200
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-08-23 11:48:37 +0200
commit4cce842950d1fa1cc9d35da7541f3ee14d24fdff (patch)
treebb64cc6b2bd0bbab011d2e44dd41aa6c141ecac8
parentfeat: dot visualization (diff)
downloadrefinery-4cce842950d1fa1cc9d35da7541f3ee14d24fdff.tar.gz
refinery-4cce842950d1fa1cc9d35da7541f3ee14d24fdff.tar.zst
refinery-4cce842950d1fa1cc9d35da7541f3ee14d24fdff.zip
refactor: d3 zoom centering
Uses patch from https://github.com/d3/d3-zoom/pull/212
-rw-r--r--package.json3
-rw-r--r--subprojects/frontend/src/graph/GraphArea.tsx83
-rw-r--r--yarn.lock33
3 files changed, 48 insertions, 71 deletions
diff --git a/package.json b/package.json
index 5515a3cd..bdac68a6 100644
--- a/package.json
+++ b/package.json
@@ -27,5 +27,8 @@
27 "devDependencies": { 27 "devDependencies": {
28 "eslint": "^8.47.0", 28 "eslint": "^8.47.0",
29 "typescript": "5.1.6" 29 "typescript": "5.1.6"
30 },
31 "resolutions": {
32 "d3-zoom@npm:^3.0.0": "git@github.com:d3/d3-zoom.git#commit=3afbe2ae2dfb3129231c5567db56dafb2d6a56a6"
30 } 33 }
31} 34}
diff --git a/subprojects/frontend/src/graph/GraphArea.tsx b/subprojects/frontend/src/graph/GraphArea.tsx
index b55245d8..1da7eaef 100644
--- a/subprojects/frontend/src/graph/GraphArea.tsx
+++ b/subprojects/frontend/src/graph/GraphArea.tsx
@@ -8,9 +8,9 @@ import Box from '@mui/material/Box';
8import * as d3 from 'd3'; 8import * as d3 from 'd3';
9import { type Graphviz, graphviz } from 'd3-graphviz'; 9import { type Graphviz, graphviz } from 'd3-graphviz';
10import type { BaseType, Selection } from 'd3-selection'; 10import type { BaseType, Selection } from 'd3-selection';
11import { zoom as d3Zoom } from 'd3-zoom';
11import { reaction, type IReactionDisposer } from 'mobx'; 12import { reaction, type IReactionDisposer } from 'mobx';
12import { useCallback, useRef, useState } from 'react'; 13import { useCallback, useRef, useState } from 'react';
13import { useResizeDetector } from 'react-resize-detector';
14 14
15import { useRootStore } from '../RootStoreProvider'; 15import { useRootStore } from '../RootStoreProvider';
16import type { SemanticsSuccessResult } from '../xtext/xtextServiceResults'; 16import type { SemanticsSuccessResult } from '../xtext/xtextServiceResults';
@@ -88,59 +88,30 @@ export default function GraphArea(): JSX.Element {
88 d3.ZoomBehavior<HTMLDivElement, unknown> | undefined 88 d3.ZoomBehavior<HTMLDivElement, unknown> | undefined
89 >(); 89 >();
90 const [zoom, setZoom] = useState<Transform>({ x: 0, y: 0, k: 1 }); 90 const [zoom, setZoom] = useState<Transform>({ x: 0, y: 0, k: 1 });
91 const widthRef = useRef<number | undefined>();
92 const heightRef = useRef<number | undefined>();
93 91
94 const onResize = useCallback( 92 const setCanvas = useCallback((element: HTMLDivElement | null) => {
95 (width: number | undefined, height: number | undefined) => { 93 canvasRef.current = element ?? undefined;
96 if (canvasRef.current === undefined || zoomRef.current === undefined) { 94 if (element === null) {
97 return; 95 return;
98 } 96 }
99 let moveX = 0; 97 const zoomBehavior = d3Zoom<HTMLDivElement, unknown>();
100 let moveY = 0; 98 // `@types/d3-zoom` does not contain the `center` function, because it is
101 if (widthRef.current !== undefined && width !== undefined) { 99 // only available as a pull request for `d3-zoom`.
102 moveX = (width - widthRef.current) / 2; 100 (
103 } 101 zoomBehavior as unknown as {
104 if (heightRef.current !== undefined && height !== undefined) { 102 center(callback: (event: MouseEvent) => [number, number]): unknown;
105 moveY = (height - heightRef.current) / 2;
106 }
107 widthRef.current = width;
108 heightRef.current = height;
109 if (moveX === 0 && moveY === 0) {
110 return;
111 }
112 const currentTransform = d3.zoomTransform(canvasRef.current);
113 zoomRef.current.translateBy(
114 d3.select(canvasRef.current),
115 moveX / currentTransform.k - moveX,
116 moveY / currentTransform.k - moveY,
117 );
118 },
119 [],
120 );
121
122 const { ref: setCanvasResize } = useResizeDetector({
123 onResize,
124 });
125
126 const setCanvas = useCallback(
127 (element: HTMLDivElement | null) => {
128 canvasRef.current = element ?? undefined;
129 setCanvasResize(element);
130 if (element === null) {
131 return;
132 } 103 }
133 const zoomBehavior = d3.zoom<HTMLDivElement, unknown>(); 104 ).center((event: MouseEvent) => {
134 zoomBehavior.on( 105 const { width, height } = element.getBoundingClientRect();
135 'zoom', 106 const [x, y] = d3.pointer(event, element);
136 (event: d3.D3ZoomEvent<HTMLDivElement, unknown>) => 107 return [x - width / 2, y - height / 2];
137 setZoom(event.transform), 108 });
138 ); 109 zoomBehavior.on('zoom', (event: d3.D3ZoomEvent<HTMLDivElement, unknown>) =>
139 d3.select(element).call(zoomBehavior); 110 setZoom(event.transform),
140 zoomRef.current = zoomBehavior; 111 );
141 }, 112 d3.select(element).call(zoomBehavior);
142 [setCanvasResize], 113 zoomRef.current = zoomBehavior;
143 ); 114 }, []);
144 115
145 const setElement = useCallback( 116 const setElement = useCallback(
146 (element: HTMLDivElement | null) => { 117 (element: HTMLDivElement | null) => {
@@ -210,8 +181,8 @@ export default function GraphArea(): JSX.Element {
210 rect.setAttribute('width', String(xmax - xmin)); 181 rect.setAttribute('width', String(xmax - xmin));
211 rect.setAttribute('height', String(ymax - ymin)); 182 rect.setAttribute('height', String(ymax - ymin));
212 rect.setAttribute('height', String(ymax - ymin)); 183 rect.setAttribute('height', String(ymax - ymin));
213 rect.setAttribute('rx', '12'); 184 rect.setAttribute('rx', '8');
214 rect.setAttribute('ry', '12'); 185 rect.setAttribute('ry', '8');
215 node.replaceChild(rect, path); 186 node.replaceChild(rect, path);
216 }); 187 });
217 }); 188 });
@@ -302,8 +273,8 @@ export default function GraphArea(): JSX.Element {
302 <Box 273 <Box
303 sx={{ 274 sx={{
304 position: 'absolute', 275 position: 'absolute',
305 top: `${50 * zoom.k}%`, 276 top: '50%',
306 left: `${50 * zoom.k}%`, 277 left: '50%',
307 transform: ` 278 transform: `
308 translate(${zoom.x}px, ${zoom.y}px) 279 translate(${zoom.x}px, ${zoom.y}px)
309 scale(${zoom.k}) 280 scale(${zoom.k})
diff --git a/yarn.lock b/yarn.lock
index b1d7b9d6..2101a994 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2663,13 +2663,13 @@ __metadata:
2663 languageName: node 2663 languageName: node
2664 linkType: hard 2664 linkType: hard
2665 2665
2666"@types/d3-zoom@npm:*": 2666"@types/d3-zoom@npm:*, @types/d3-zoom@npm:^3.0.4":
2667 version: 3.0.3 2667 version: 3.0.4
2668 resolution: "@types/d3-zoom@npm:3.0.3" 2668 resolution: "@types/d3-zoom@npm:3.0.4"
2669 dependencies: 2669 dependencies:
2670 "@types/d3-interpolate": "npm:*" 2670 "@types/d3-interpolate": "npm:*"
2671 "@types/d3-selection": "npm:*" 2671 "@types/d3-selection": "npm:*"
2672 checksum: 7b48870cadf18ba1104613605fc6157dc230f71ecac7f2f0d7e689b44a62ab29377edf98e935a6eee8b87c32456effe3cf6aab321cc20bb31695d21f62fdb311 2672 checksum: 52d1b5f2a1490c25c69b03dcd71601f908ea1e3dedf79e379be4ff23e3a6be85d0694d04c1b23686343a74117e86683a61433ed926637aea545268fd55aa634f
2673 languageName: node 2673 languageName: node
2674 linkType: hard 2674 linkType: hard
2675 2675
@@ -2683,16 +2683,6 @@ __metadata:
2683 languageName: node 2683 languageName: node
2684 linkType: hard 2684 linkType: hard
2685 2685
2686"@types/d3-zoom@npm:^3.0.4":
2687 version: 3.0.4
2688 resolution: "@types/d3-zoom@npm:3.0.4"
2689 dependencies:
2690 "@types/d3-interpolate": "npm:*"
2691 "@types/d3-selection": "npm:*"
2692 checksum: 52d1b5f2a1490c25c69b03dcd71601f908ea1e3dedf79e379be4ff23e3a6be85d0694d04c1b23686343a74117e86683a61433ed926637aea545268fd55aa634f
2693 languageName: node
2694 linkType: hard
2695
2696"@types/d3@npm:^7.4.0": 2686"@types/d3@npm:^7.4.0":
2697 version: 7.4.0 2687 version: 7.4.0
2698 resolution: "@types/d3@npm:7.4.0" 2688 resolution: "@types/d3@npm:7.4.0"
@@ -4154,7 +4144,20 @@ __metadata:
4154 languageName: node 4144 languageName: node
4155 linkType: hard 4145 linkType: hard
4156 4146
4157"d3-zoom@npm:3, d3-zoom@npm:^3.0.0": 4147"d3-zoom@git@github.com:d3/d3-zoom.git#commit=3afbe2ae2dfb3129231c5567db56dafb2d6a56a6":
4148 version: 3.0.0
4149 resolution: "d3-zoom@git@github.com:d3/d3-zoom.git#commit=3afbe2ae2dfb3129231c5567db56dafb2d6a56a6"
4150 dependencies:
4151 d3-dispatch: "npm:1 - 3"
4152 d3-drag: "npm:2 - 3"
4153 d3-interpolate: "npm:1 - 3"
4154 d3-selection: "npm:2 - 3"
4155 d3-transition: "npm:2 - 3"
4156 checksum: 0cca9382fd9cf7383ecd89ae2f4a828b5088e7801eb89da0656c1d3512cda03bfade727db936721b8004a3142dbbf955a408c99cf370539e3a985da9161c6d54
4157 languageName: node
4158 linkType: hard
4159
4160"d3-zoom@npm:3":
4158 version: 3.0.0 4161 version: 3.0.0
4159 resolution: "d3-zoom@npm:3.0.0" 4162 resolution: "d3-zoom@npm:3.0.0"
4160 dependencies: 4163 dependencies: