aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/frontend
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 /subprojects/frontend
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
Diffstat (limited to 'subprojects/frontend')
-rw-r--r--subprojects/frontend/src/graph/GraphArea.tsx83
1 files changed, 27 insertions, 56 deletions
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})