aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--subprojects/frontend/src/graph/DotGraphVisualizer.tsx53
-rw-r--r--subprojects/frontend/src/graph/ZoomCanvas.tsx14
2 files changed, 43 insertions, 24 deletions
diff --git a/subprojects/frontend/src/graph/DotGraphVisualizer.tsx b/subprojects/frontend/src/graph/DotGraphVisualizer.tsx
index 02f31085..eec72a7d 100644
--- a/subprojects/frontend/src/graph/DotGraphVisualizer.tsx
+++ b/subprojects/frontend/src/graph/DotGraphVisualizer.tsx
@@ -9,7 +9,7 @@ import { type Graphviz, graphviz } from 'd3-graphviz';
9import type { BaseType, Selection } from 'd3-selection'; 9import type { BaseType, Selection } from 'd3-selection';
10import { reaction, type IReactionDisposer } from 'mobx'; 10import { reaction, type IReactionDisposer } from 'mobx';
11import { observer } from 'mobx-react-lite'; 11import { observer } from 'mobx-react-lite';
12import { useCallback, useRef } from 'react'; 12import { useCallback, useRef, useState } from 'react';
13 13
14import getLogger from '../utils/getLogger'; 14import getLogger from '../utils/getLogger';
15 15
@@ -44,6 +44,7 @@ function DotGraphVisualizer({
44 const graphvizRef = useRef< 44 const graphvizRef = useRef<
45 Graphviz<BaseType, unknown, null, undefined> | undefined 45 Graphviz<BaseType, unknown, null, undefined> | undefined
46 >(); 46 >();
47 const [animate, setAnimate] = useState(true);
47 48
48 const setElement = useCallback( 49 const setElement = useCallback(
49 (element: HTMLDivElement | null) => { 50 (element: HTMLDivElement | null) => {
@@ -72,15 +73,20 @@ function DotGraphVisualizer({
72 renderer.tweenPrecision('5%'); 73 renderer.tweenPrecision('5%');
73 renderer.tweenShapes(false); 74 renderer.tweenShapes(false);
74 renderer.convertEqualSidedPolygons(false); 75 renderer.convertEqualSidedPolygons(false);
75 const transition = () => 76 if (animate) {
76 d3.transition().duration(transitionTimeOrDefault).ease(d3.easeCubic); 77 const transition = () =>
77 /* eslint-disable-next-line @typescript-eslint/no-unsafe-argument, 78 d3
78 @typescript-eslint/no-explicit-any -- 79 .transition()
79 Workaround for error in `@types/d3-graphviz`. 80 .duration(transitionTimeOrDefault)
80 */ 81 .ease(d3.easeCubic);
81 renderer.transition(transition as any); 82 /* eslint-disable-next-line @typescript-eslint/no-unsafe-argument,
82 let animate = true; 83 @typescript-eslint/no-explicit-any --
83 let previousSize = 0; 84 Workaround for error in `@types/d3-graphviz`.
85 */
86 renderer.transition(transition as any);
87 } else {
88 renderer.tweenPaths(false);
89 }
84 let newViewBox = { width: 0, height: 0 }; 90 let newViewBox = { width: 0, height: 0 };
85 renderer.onerror(LOG.error.bind(LOG)); 91 renderer.onerror(LOG.error.bind(LOG));
86 renderer.on( 92 renderer.on(
@@ -108,7 +114,11 @@ function DotGraphVisualizer({
108 d3.select(element).selectAll('title').remove(); 114 d3.select(element).selectAll('title').remove();
109 }); 115 });
110 if (fitZoom !== undefined) { 116 if (fitZoom !== undefined) {
111 renderer.on('transitionStart', () => fitZoom(newViewBox)); 117 if (animate) {
118 renderer.on('transitionStart', () => fitZoom(newViewBox));
119 } else {
120 renderer.on('end', () => fitZoom(false));
121 }
112 } 122 }
113 disposerRef.current = reaction( 123 disposerRef.current = reaction(
114 () => dotSource(graph), 124 () => dotSource(graph),
@@ -119,20 +129,25 @@ function DotGraphVisualizer({
119 const [source, size] = result; 129 const [source, size] = result;
120 // Disable tweening for large graphs to improve performance. 130 // Disable tweening for large graphs to improve performance.
121 // See https://github.com/magjac/d3-graphviz/issues/232#issuecomment-1157555213 131 // See https://github.com/magjac/d3-graphviz/issues/232#issuecomment-1157555213
122 const newAnimate = size + previousSize < animateThresholdOrDefault; 132 const newAnimate = size < animateThresholdOrDefault;
123 if (animate !== newAnimate) { 133 if (animate === newAnimate) {
124 renderer.tweenPaths(newAnimate); 134 renderer.renderDot(source);
125 animate = newAnimate; 135 } else {
136 setAnimate(newAnimate);
126 } 137 }
127 previousSize = size;
128 renderer.renderDot(source);
129 }, 138 },
130 { fireImmediately: true }, 139 { fireImmediately: true },
131 ); 140 );
132 graphvizRef.current = renderer; 141 graphvizRef.current = renderer;
133 } 142 }
134 }, 143 },
135 [graph, fitZoom, transitionTimeOrDefault, animateThresholdOrDefault], 144 [
145 graph,
146 fitZoom,
147 transitionTimeOrDefault,
148 animateThresholdOrDefault,
149 animate,
150 ],
136 ); 151 );
137 152
138 return <GraphTheme ref={setElement} />; 153 return <GraphTheme ref={setElement} />;
@@ -141,7 +156,7 @@ function DotGraphVisualizer({
141DotGraphVisualizer.defaultProps = { 156DotGraphVisualizer.defaultProps = {
142 fitZoom: undefined, 157 fitZoom: undefined,
143 transitionTime: 250, 158 transitionTime: 250,
144 animateThreshold: 50, 159 animateThreshold: 100,
145}; 160};
146 161
147export default observer(DotGraphVisualizer); 162export default observer(DotGraphVisualizer);
diff --git a/subprojects/frontend/src/graph/ZoomCanvas.tsx b/subprojects/frontend/src/graph/ZoomCanvas.tsx
index 2bb7f139..0254bc59 100644
--- a/subprojects/frontend/src/graph/ZoomCanvas.tsx
+++ b/subprojects/frontend/src/graph/ZoomCanvas.tsx
@@ -34,10 +34,11 @@ export type ChangeZoomCallback = (factor: number) => void;
34 34
35export type SetFitZoomCallback = (fitZoom: boolean) => void; 35export type SetFitZoomCallback = (fitZoom: boolean) => void;
36 36
37export type FitZoomCallback = (newSize?: { 37export type FitZoomCallback = ((newSize?: {
38 width: number; 38 width: number;
39 height: number; 39 height: number;
40}) => void; 40}) => void) &
41 ((newSize: boolean) => void);
41 42
42export default function ZoomCanvas({ 43export default function ZoomCanvas({
43 children, 44 children,
@@ -79,7 +80,7 @@ export default function ZoomCanvas({
79 } 80 }
80 let width = 0; 81 let width = 0;
81 let height = 0; 82 let height = 0;
82 if (newSize === undefined) { 83 if (newSize === undefined || typeof newSize === 'boolean') {
83 const elementRect = elementRef.current.getBoundingClientRect(); 84 const elementRect = elementRef.current.getBoundingClientRect();
84 const currentFactor = d3.zoomTransform(canvasRef.current).k; 85 const currentFactor = d3.zoomTransform(canvasRef.current).k;
85 width = elementRect.width / currentFactor; 86 width = elementRect.width / currentFactor;
@@ -96,8 +97,11 @@ export default function ZoomCanvas({
96 (canvasRect.width - 2 * fitPaddingOrDefault) / width, 97 (canvasRect.width - 2 * fitPaddingOrDefault) / width,
97 (canvasRect.height - 2 * fitPaddingOrDefault) / height, 98 (canvasRect.height - 2 * fitPaddingOrDefault) / height,
98 ); 99 );
99 const zoomTransition = makeTransition(canvasRef.current); 100 const target =
100 zoomRef.current.transform(zoomTransition, d3.zoomIdentity.scale(factor)); 101 newSize === false
102 ? d3.select(canvasRef.current)
103 : makeTransition(canvasRef.current);
104 zoomRef.current.transform(target, d3.zoomIdentity.scale(factor));
101 }, 105 },
102 [fitPaddingOrDefault, makeTransition], 106 [fitPaddingOrDefault, makeTransition],
103 ); 107 );