aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch134
-rw-r--r--package.json3
-rw-r--r--subprojects/frontend/package.json2
-rw-r--r--subprojects/frontend/src/graph/GraphArea.tsx189
-rw-r--r--yarn.lock14
5 files changed, 247 insertions, 95 deletions
diff --git a/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch b/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch
new file mode 100644
index 00000000..88d32c72
--- /dev/null
+++ b/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch
@@ -0,0 +1,134 @@
1diff --git a/src/zoom.js b/src/zoom.js
2index d56438823b2882856f156b0915ccbac038d6923e..50936066a3597c46ad65f690e9c8417e9d3375f8 100644
3--- a/src/zoom.js
4+++ b/src/zoom.js
5@@ -14,6 +14,10 @@ function defaultFilter(event) {
6 return (!event.ctrlKey || event.type === 'wheel') && !event.button;
7 }
8
9+function defaultCenter(event) {
10+ return pointer(event, this);
11+}
12+
13 function defaultExtent() {
14 var e = this;
15 if (e instanceof SVGElement) {
16@@ -27,6 +31,10 @@ function defaultExtent() {
17 return [[0, 0], [e.clientWidth, e.clientHeight]];
18 }
19
20+function defaultCentroid(extent) {
21+ return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
22+}
23+
24 function defaultTransform() {
25 return this.__zoom || identity;
26 }
27@@ -52,7 +60,9 @@ function defaultConstrain(transform, extent, translateExtent) {
28
29 export default function() {
30 var filter = defaultFilter,
31+ center = defaultCenter,
32 extent = defaultExtent,
33+ centroid = defaultCentroid,
34 constrain = defaultConstrain,
35 wheelDelta = defaultWheelDelta,
36 touchable = defaultTouchable,
37@@ -148,9 +158,6 @@ export default function() {
38 return x === transform.x && y === transform.y ? transform : new Transform(transform.k, x, y);
39 }
40
41- function centroid(extent) {
42- return [(+extent[0][0] + +extent[1][0]) / 2, (+extent[0][1] + +extent[1][1]) / 2];
43- }
44
45 function schedule(transition, transform, point, event) {
46 transition
47@@ -243,6 +250,7 @@ export default function() {
48 if (g.wheel) {
49 if (g.mouse[0][0] !== p[0] || g.mouse[0][1] !== p[1]) {
50 g.mouse[1] = t.invert(g.mouse[0] = p);
51+ g.mouse[2] = center.apply(this, arguments);
52 }
53 clearTimeout(g.wheel);
54 }
55@@ -252,14 +260,14 @@ export default function() {
56
57 // Otherwise, capture the mouse point and location at the start.
58 else {
59- g.mouse = [p, t.invert(p)];
60+ g.mouse = [p, t.invert(p), center.apply(this, arguments)];
61 interrupt(this);
62 g.start();
63 }
64
65 noevent(event);
66 g.wheel = setTimeout(wheelidled, wheelDelay);
67- g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[0], g.mouse[1]), g.extent, translateExtent));
68+ g.zoom("mouse", constrain(translate(scale(t, k), g.mouse[2], t.invert(g.mouse[2])), g.extent, translateExtent));
69
70 function wheelidled() {
71 g.wheel = null;
72@@ -278,7 +286,7 @@ export default function() {
73
74 dragDisable(event.view);
75 nopropagation(event);
76- g.mouse = [p, this.__zoom.invert(p)];
77+ g.prev = p;
78 interrupt(this);
79 g.start();
80
81@@ -288,8 +296,10 @@ export default function() {
82 var dx = event.clientX - x0, dy = event.clientY - y0;
83 g.moved = dx * dx + dy * dy > clickDistance2;
84 }
85+ var p = pointer(event, currentTarget);
86 g.event(event)
87- .zoom("mouse", constrain(translate(g.that.__zoom, g.mouse[0] = pointer(event, currentTarget), g.mouse[1]), g.extent, translateExtent));
88+ .zoom("mouse", constrain(translate(g.that.__zoom, p, g.that.__zoom.invert(g.prev)), g.extent, translateExtent));
89+ g.prev = p;
90 }
91
92 function mouseupped(event) {
93@@ -303,7 +313,7 @@ export default function() {
94 function dblclicked(event, ...args) {
95 if (!filter.apply(this, arguments)) return;
96 var t0 = this.__zoom,
97- p0 = pointer(event.changedTouches ? event.changedTouches[0] : event, this),
98+ p0 = center.call(this, event.changedTouches ? event.changedTouches[0] : event),
99 p1 = t0.invert(p0),
100 k1 = t0.k * (event.shiftKey ? 0.5 : 2),
101 t1 = constrain(translate(scale(t0, k1), p0, p1), extent.apply(this, args), translateExtent);
102@@ -322,7 +332,7 @@ export default function() {
103
104 nopropagation(event);
105 for (i = 0; i < n; ++i) {
106- t = touches[i], p = pointer(t, this);
107+ t = touches[i], p = center.call(this, t);
108 p = [p, this.__zoom.invert(p), t.identifier];
109 if (!g.touch0) g.touch0 = p, started = true, g.taps = 1 + !!touchstarting;
110 else if (!g.touch1 && g.touch0[2] !== p[2]) g.touch1 = p, g.taps = 0;
111@@ -345,7 +355,7 @@ export default function() {
112
113 noevent(event);
114 for (i = 0; i < n; ++i) {
115- t = touches[i], p = pointer(t, this);
116+ t = touches[i], p = center.call(this, t);
117 if (g.touch0 && g.touch0[2] === t.identifier) g.touch0[0] = p;
118 else if (g.touch1 && g.touch1[2] === t.identifier) g.touch1[0] = p;
119 }
120@@ -406,6 +416,14 @@ export default function() {
121 return arguments.length ? (touchable = typeof _ === "function" ? _ : constant(!!_), zoom) : touchable;
122 };
123
124+ zoom.center = function(_) {
125+ return arguments.length ? (center = typeof _ === "function" ? _ : constant([+_[0], +_[1]]), zoom) : center;
126+ };
127+
128+ zoom.centroid = function(_) {
129+ return arguments.length ? (centroid = typeof _ === "function" ? _ : constant([+_[0], +_[1]]), zoom) : centroid;
130+ };
131+
132 zoom.extent = function(_) {
133 return arguments.length ? (extent = typeof _ === "function" ? _ : constant([[+_[0][0], +_[0][1]], [+_[1][0], +_[1][1]]]), zoom) : extent;
134 };
diff --git a/package.json b/package.json
index bdac68a6..d527c291 100644
--- a/package.json
+++ b/package.json
@@ -29,6 +29,7 @@
29 "typescript": "5.1.6" 29 "typescript": "5.1.6"
30 }, 30 },
31 "resolutions": { 31 "resolutions": {
32 "d3-zoom@npm:^3.0.0": "git@github.com:d3/d3-zoom.git#commit=3afbe2ae2dfb3129231c5567db56dafb2d6a56a6" 32 "d3-zoom@npm:^3.0.0": "patch:d3-zoom@npm%3A3.0.0#~/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch",
33 "d3-zoom@npm:3": "patch:d3-zoom@npm%3A3.0.0#~/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch"
33 } 34 }
34} 35}
diff --git a/subprojects/frontend/package.json b/subprojects/frontend/package.json
index 9df8d85f..97f6baf6 100644
--- a/subprojects/frontend/package.json
+++ b/subprojects/frontend/package.json
@@ -51,7 +51,7 @@
51 "d3": "^7.8.5", 51 "d3": "^7.8.5",
52 "d3-graphviz": "patch:d3-graphviz@npm%3A5.1.0#~/.yarn/patches/d3-graphviz-npm-5.1.0-ba6bed3fec.patch", 52 "d3-graphviz": "patch:d3-graphviz@npm%3A5.1.0#~/.yarn/patches/d3-graphviz-npm-5.1.0-ba6bed3fec.patch",
53 "d3-selection": "^3.0.0", 53 "d3-selection": "^3.0.0",
54 "d3-zoom": "^3.0.0", 54 "d3-zoom": "patch:d3-zoom@npm%3A3.0.0#~/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch",
55 "escape-string-regexp": "^5.0.0", 55 "escape-string-regexp": "^5.0.0",
56 "json-stringify-pretty-compact": "^4.0.0", 56 "json-stringify-pretty-compact": "^4.0.0",
57 "lodash-es": "^4.17.21", 57 "lodash-es": "^4.17.21",
diff --git a/subprojects/frontend/src/graph/GraphArea.tsx b/subprojects/frontend/src/graph/GraphArea.tsx
index aec5d1c6..6ca3bc87 100644
--- a/subprojects/frontend/src/graph/GraphArea.tsx
+++ b/subprojects/frontend/src/graph/GraphArea.tsx
@@ -11,6 +11,7 @@ import Box from '@mui/material/Box';
11import IconButton from '@mui/material/IconButton'; 11import IconButton from '@mui/material/IconButton';
12import Stack from '@mui/material/Stack'; 12import Stack from '@mui/material/Stack';
13import { useTheme } from '@mui/material/styles'; 13import { useTheme } from '@mui/material/styles';
14import { CSSProperties } from '@mui/material/styles/createTypography';
14import * as d3 from 'd3'; 15import * as d3 from 'd3';
15import { type Graphviz, graphviz } from 'd3-graphviz'; 16import { type Graphviz, graphviz } from 'd3-graphviz';
16import type { BaseType, Selection } from 'd3-selection'; 17import type { BaseType, Selection } from 'd3-selection';
@@ -109,15 +110,21 @@ export default function GraphArea(): JSX.Element {
109 zoomBehavior as unknown as { 110 zoomBehavior as unknown as {
110 center(callback: (event: MouseEvent) => [number, number]): unknown; 111 center(callback: (event: MouseEvent) => [number, number]): unknown;
111 } 112 }
112 ).center((event: MouseEvent) => { 113 ).center((event: MouseEvent | Touch) => {
113 const { width, height } = element.getBoundingClientRect(); 114 const { width, height } = element.getBoundingClientRect();
114 const [x, y] = d3.pointer(event, element); 115 const [x, y] = d3.pointer(event, element);
115 return [x - width / 2, y - height / 2]; 116 return [x - width / 2, y - height / 2];
116 }); 117 });
118 // Custom `centroid` method added via patch.
119 (
120 zoomBehavior as unknown as {
121 centroid(centroid: [number, number]): unknown;
122 }
123 ).centroid([0, 0]);
117 zoomBehavior.on('zoom', (event: d3.D3ZoomEvent<HTMLDivElement, unknown>) => 124 zoomBehavior.on('zoom', (event: d3.D3ZoomEvent<HTMLDivElement, unknown>) =>
118 setZoom(event.transform), 125 setZoom(event.transform),
119 ); 126 );
120 d3.select(element).call(zoomBehavior).on('dblclick.zoom', null); 127 d3.select(element).call(zoomBehavior);
121 zoomRef.current = zoomBehavior; 128 zoomRef.current = zoomBehavior;
122 }, []); 129 }, []);
123 130
@@ -213,25 +220,17 @@ export default function GraphArea(): JSX.Element {
213 [editorStore], 220 [editorStore],
214 ); 221 );
215 222
216 const { 223 const changeZoom = useCallback((event: React.MouseEvent, factor: number) => {
217 transitions: { 224 if (canvasRef.current === undefined || zoomRef.current === undefined) {
218 duration: { short: zoomDuration }, 225 return;
219 }, 226 }
220 } = theme; 227 const selection = d3.select(canvasRef.current);
221 const changeZoom = useCallback( 228 const zoomTransition = selection.transition().duration(250);
222 (event: React.MouseEvent, factor: number) => { 229 const center: [number, number] = [0, 0];
223 if (canvasRef.current === undefined || zoomRef.current === undefined) { 230 zoomRef.current.scaleBy(zoomTransition, factor, center);
224 return; 231 event.preventDefault();
225 } 232 event.stopPropagation();
226 const selection = d3.select(canvasRef.current); 233 }, []);
227 const zoomTransition = selection.transition().duration(zoomDuration);
228 const center: [number, number] = [0, 0];
229 zoomRef.current.scaleBy(zoomTransition, factor, center);
230 event.preventDefault();
231 event.stopPropagation();
232 },
233 [zoomDuration],
234 );
235 234
236 const fitZoom = useCallback((event: React.MouseEvent) => { 235 const fitZoom = useCallback((event: React.MouseEvent) => {
237 if ( 236 if (
@@ -255,7 +254,8 @@ export default function GraphArea(): JSX.Element {
255 (canvasHeight - 64) / height, 254 (canvasHeight - 64) / height,
256 ); 255 );
257 const selection = d3.select(canvasRef.current); 256 const selection = d3.select(canvasRef.current);
258 zoomRef.current.transform(selection, d3.zoomIdentity.scale(factor)); 257 const zoomTransition = selection.transition().duration(250);
258 zoomRef.current.transform(zoomTransition, d3.zoomIdentity.scale(factor));
259 } 259 }
260 event.preventDefault(); 260 event.preventDefault();
261 event.stopPropagation(); 261 event.stopPropagation();
@@ -268,79 +268,96 @@ export default function GraphArea(): JSX.Element {
268 height: '100%', 268 height: '100%',
269 position: 'relative', 269 position: 'relative',
270 overflow: 'hidden', 270 overflow: 'hidden',
271 '& svg': {
272 userSelect: 'none',
273 '& .node': {
274 '& text': {
275 ...theme.typography.body2,
276 fill: theme.palette.text.primary,
277 },
278 '& [stroke="black"]': {
279 stroke: theme.palette.text.primary,
280 },
281 '& [fill="green"]': {
282 fill:
283 theme.palette.mode === 'dark'
284 ? theme.palette.primary.dark
285 : theme.palette.primary.light,
286 },
287 '& [fill="white"]': {
288 fill: theme.palette.background.default,
289 stroke: theme.palette.background.default,
290 },
291 },
292 '& .edge': {
293 '& text': {
294 ...theme.typography.caption,
295 fill: theme.palette.text.primary,
296 },
297 '& [stroke="black"]': {
298 stroke: theme.palette.text.primary,
299 },
300 '& [fill="black"]': {
301 fill: theme.palette.text.primary,
302 },
303 },
304 '& .edge-UNKNOWN': {
305 '& text': {
306 fill: theme.palette.text.secondary,
307 },
308 '& [stroke="black"]': {
309 stroke: theme.palette.text.secondary,
310 },
311 '& [fill="black"]': {
312 fill: theme.palette.text.secondary,
313 },
314 },
315 '& .edge-ERROR': {
316 '& text': {
317 fill: theme.palette.error.main,
318 },
319 '& [stroke="black"]': {
320 stroke: theme.palette.error.main,
321 },
322 '& [fill="black"]': {
323 fill: theme.palette.error.main,
324 },
325 },
326 },
327 }} 271 }}
328 ref={setCanvas}
329 > 272 >
330 <Box 273 <Box
331 sx={{ 274 sx={{
332 position: 'absolute', 275 position: 'absolute',
333 top: '50%', 276 overflow: 'hidden',
334 left: '50%', 277 top: 0,
335 transform: ` 278 left: 0,
279 right: 0,
280 bottom: 0,
281 }}
282 ref={setCanvas}
283 >
284 <Box
285 sx={{
286 position: 'absolute',
287 top: '50%',
288 left: '50%',
289 transform: `
336 translate(${zoom.x}px, ${zoom.y}px) 290 translate(${zoom.x}px, ${zoom.y}px)
337 scale(${zoom.k}) 291 scale(${zoom.k})
338 translate(-50%, -50%) 292 translate(-50%, -50%)
339 `, 293 `,
340 transformOrigin: '0 0', 294 transformOrigin: '0 0',
341 }} 295 '& svg': {
342 ref={setElement} 296 userSelect: 'none',
343 /> 297 '& .node': {
298 '& text': {
299 ...(theme.typography.body2 as Omit<
300 CSSProperties,
301 '@font-face'
302 >),
303 fill: theme.palette.text.primary,
304 },
305 '& [stroke="black"]': {
306 stroke: theme.palette.text.primary,
307 },
308 '& [fill="green"]': {
309 fill:
310 theme.palette.mode === 'dark'
311 ? theme.palette.primary.dark
312 : theme.palette.primary.light,
313 },
314 '& [fill="white"]': {
315 fill: theme.palette.background.default,
316 stroke: theme.palette.background.default,
317 },
318 },
319 '& .edge': {
320 '& text': {
321 ...(theme.typography.caption as Omit<
322 CSSProperties,
323 '@font-face'
324 >),
325 fill: theme.palette.text.primary,
326 },
327 '& [stroke="black"]': {
328 stroke: theme.palette.text.primary,
329 },
330 '& [fill="black"]': {
331 fill: theme.palette.text.primary,
332 },
333 },
334 '& .edge-UNKNOWN': {
335 '& text': {
336 fill: theme.palette.text.secondary,
337 },
338 '& [stroke="black"]': {
339 stroke: theme.palette.text.secondary,
340 },
341 '& [fill="black"]': {
342 fill: theme.palette.text.secondary,
343 },
344 },
345 '& .edge-ERROR': {
346 '& text': {
347 fill: theme.palette.error.main,
348 },
349 '& [stroke="black"]': {
350 stroke: theme.palette.error.main,
351 },
352 '& [fill="black"]': {
353 fill: theme.palette.error.main,
354 },
355 },
356 },
357 }}
358 ref={setElement}
359 />
360 </Box>
344 <Stack 361 <Stack
345 direction="column" 362 direction="column"
346 p={1} 363 p={1}
diff --git a/yarn.lock b/yarn.lock
index 2101a994..ca10e7c7 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2151,7 +2151,7 @@ __metadata:
2151 d3: "npm:^7.8.5" 2151 d3: "npm:^7.8.5"
2152 d3-graphviz: "patch:d3-graphviz@npm%3A5.1.0#~/.yarn/patches/d3-graphviz-npm-5.1.0-ba6bed3fec.patch" 2152 d3-graphviz: "patch:d3-graphviz@npm%3A5.1.0#~/.yarn/patches/d3-graphviz-npm-5.1.0-ba6bed3fec.patch"
2153 d3-selection: "npm:^3.0.0" 2153 d3-selection: "npm:^3.0.0"
2154 d3-zoom: "npm:^3.0.0" 2154 d3-zoom: "patch:d3-zoom@npm%3A3.0.0#~/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch"
2155 escape-string-regexp: "npm:^5.0.0" 2155 escape-string-regexp: "npm:^5.0.0"
2156 eslint: "npm:^8.47.0" 2156 eslint: "npm:^8.47.0"
2157 eslint-config-airbnb: "npm:^19.0.4" 2157 eslint-config-airbnb: "npm:^19.0.4"
@@ -4144,29 +4144,29 @@ __metadata:
4144 languageName: node 4144 languageName: node
4145 linkType: hard 4145 linkType: hard
4146 4146
4147"d3-zoom@git@github.com:d3/d3-zoom.git#commit=3afbe2ae2dfb3129231c5567db56dafb2d6a56a6": 4147"d3-zoom@npm:3.0.0":
4148 version: 3.0.0 4148 version: 3.0.0
4149 resolution: "d3-zoom@git@github.com:d3/d3-zoom.git#commit=3afbe2ae2dfb3129231c5567db56dafb2d6a56a6" 4149 resolution: "d3-zoom@npm:3.0.0"
4150 dependencies: 4150 dependencies:
4151 d3-dispatch: "npm:1 - 3" 4151 d3-dispatch: "npm:1 - 3"
4152 d3-drag: "npm:2 - 3" 4152 d3-drag: "npm:2 - 3"
4153 d3-interpolate: "npm:1 - 3" 4153 d3-interpolate: "npm:1 - 3"
4154 d3-selection: "npm:2 - 3" 4154 d3-selection: "npm:2 - 3"
4155 d3-transition: "npm:2 - 3" 4155 d3-transition: "npm:2 - 3"
4156 checksum: 0cca9382fd9cf7383ecd89ae2f4a828b5088e7801eb89da0656c1d3512cda03bfade727db936721b8004a3142dbbf955a408c99cf370539e3a985da9161c6d54 4156 checksum: ee2036479049e70d8c783d594c444fe00e398246048e3f11a59755cd0e21de62ece3126181b0d7a31bf37bcf32fd726f83ae7dea4495ff86ec7736ce5ad36fd3
4157 languageName: node 4157 languageName: node
4158 linkType: hard 4158 linkType: hard
4159 4159
4160"d3-zoom@npm:3": 4160"d3-zoom@patch:d3-zoom@npm%3A3.0.0#~/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch":
4161 version: 3.0.0 4161 version: 3.0.0
4162 resolution: "d3-zoom@npm:3.0.0" 4162 resolution: "d3-zoom@patch:d3-zoom@npm%3A3.0.0#~/.yarn/patches/d3-zoom-npm-3.0.0-18f706a421.patch::version=3.0.0&hash=135f1c"
4163 dependencies: 4163 dependencies:
4164 d3-dispatch: "npm:1 - 3" 4164 d3-dispatch: "npm:1 - 3"
4165 d3-drag: "npm:2 - 3" 4165 d3-drag: "npm:2 - 3"
4166 d3-interpolate: "npm:1 - 3" 4166 d3-interpolate: "npm:1 - 3"
4167 d3-selection: "npm:2 - 3" 4167 d3-selection: "npm:2 - 3"
4168 d3-transition: "npm:2 - 3" 4168 d3-transition: "npm:2 - 3"
4169 checksum: ee2036479049e70d8c783d594c444fe00e398246048e3f11a59755cd0e21de62ece3126181b0d7a31bf37bcf32fd726f83ae7dea4495ff86ec7736ce5ad36fd3 4169 checksum: fc56f5be9f492b7d7bba91b166f4b93e1761599a23496b0f2890e295476971a8db040b0c2069b670897984ed28c4f0d151caf1d51e305fe635e80cdb94f2c2af
4170 languageName: node 4170 languageName: node
4171 linkType: hard 4171 linkType: hard
4172 4172