Skip to content

Commit 076abd4

Browse files
committed
Started splitting out mapper into components to be reused with a view-only map viewer page. Removed buggy map modal and replaced with a full screen page instead that can handle both static images and exports from the new mapper
1 parent cc4b78f commit 076abd4

16 files changed

Lines changed: 1456 additions & 606 deletions

File tree

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react';
2+
import * as go from 'gojs';
3+
import { Box } from '@mui/material';
4+
import {
5+
createNodeTemplate,
6+
createLinkTemplate,
7+
registerArrowheadGeometries,
8+
createModel,
9+
setupScrollZoomHandling,
10+
} from './core';
11+
12+
// Set GoJS license key from environment variable
13+
if (process.env.REACT_APP_GOJS_LICENSE_KEY) {
14+
go.Diagram.licenseKey = process.env.REACT_APP_GOJS_LICENSE_KEY;
15+
}
16+
17+
/**
18+
* MapViewerCanvas - A read-only GoJS diagram for viewing maps
19+
*
20+
* Props:
21+
* - data: { nodeDataArray, linkDataArray } - The map data to display
22+
* - onRoomClick: (roomData) => void - Callback when a room is clicked
23+
* - height: string - Height of the canvas (default: '100%')
24+
*/
25+
const MapViewerCanvas = forwardRef(({ data, onRoomClick, height = '100%' }, ref) => {
26+
const diagramDivRef = useRef(null);
27+
const diagramRef = useRef(null);
28+
29+
// Expose diagram methods to parent via ref
30+
useImperativeHandle(ref, () => ({
31+
zoomIn: () => {
32+
const diagram = diagramRef.current;
33+
if (diagram) {
34+
diagram.scale = Math.min(diagram.scale * 1.2, 5);
35+
}
36+
},
37+
zoomOut: () => {
38+
const diagram = diagramRef.current;
39+
if (diagram) {
40+
diagram.scale = Math.max(diagram.scale / 1.2, 0.1);
41+
}
42+
},
43+
zoomToFit: () => {
44+
const diagram = diagramRef.current;
45+
if (diagram) {
46+
diagram.zoomToFit();
47+
}
48+
},
49+
resetView: () => {
50+
const diagram = diagramRef.current;
51+
if (diagram) {
52+
diagram.scale = 1;
53+
diagram.alignDocument(go.Spot.Center, go.Spot.Center);
54+
}
55+
},
56+
}));
57+
58+
useEffect(() => {
59+
if (!diagramDivRef.current) return;
60+
61+
const $ = go.GraphObject.make;
62+
63+
// Register custom arrowhead geometries for links
64+
registerArrowheadGeometries();
65+
66+
const diagram = $(go.Diagram, diagramDivRef.current, {
67+
// Read-only settings
68+
isReadOnly: true,
69+
allowMove: false,
70+
allowDelete: false,
71+
allowInsert: false,
72+
allowLink: false,
73+
allowRelink: false,
74+
allowReshape: false,
75+
allowResize: false,
76+
allowRotate: false,
77+
allowDrop: false,
78+
79+
// Visual settings
80+
'grid.visible': false, // Hide grid in view mode
81+
initialContentAlignment: go.Spot.Center,
82+
'animationManager.isEnabled': false,
83+
84+
// Enable panning
85+
'panningTool.isEnabled': true,
86+
'toolManager.mouseWheelBehavior': go.ToolManager.WheelNone, // We'll handle zoom ourselves
87+
88+
model: createModel(),
89+
});
90+
91+
// Use shared node template with view-only options and tooltips
92+
diagram.nodeTemplate = createNodeTemplate($, {
93+
isEditable: false,
94+
showTooltip: true,
95+
});
96+
97+
// Use shared link template with view-only options
98+
diagram.linkTemplate = createLinkTemplate($, {
99+
isEditable: false,
100+
});
101+
102+
// Handle room clicks
103+
diagram.addDiagramListener('ObjectSingleClicked', (e) => {
104+
const part = e.subject.part;
105+
if (part instanceof go.Node && onRoomClick) {
106+
onRoomClick(part.data);
107+
}
108+
});
109+
110+
// Set up custom scroll/zoom handling (view-only mode)
111+
const cleanupScrollZoom = setupScrollZoomHandling(diagram, diagramDivRef.current, {
112+
isEditable: false,
113+
});
114+
115+
diagramRef.current = diagram;
116+
117+
// Cleanup
118+
return () => {
119+
cleanupScrollZoom();
120+
if (diagramRef.current) {
121+
diagramRef.current.div = null;
122+
}
123+
};
124+
}, [onRoomClick]);
125+
126+
// Update diagram when data changes
127+
useEffect(() => {
128+
const diagram = diagramRef.current;
129+
if (!diagram || !data) return;
130+
131+
// Load the new data
132+
diagram.model = createModel({
133+
nodeDataArray: data.nodeDataArray || [],
134+
linkDataArray: data.linkDataArray || [],
135+
});
136+
137+
// Fit content in view
138+
diagram.zoomToFit();
139+
}, [data]);
140+
141+
return (
142+
<Box
143+
sx={{
144+
width: '100%',
145+
height: height,
146+
backgroundColor: '#fff',
147+
cursor: 'grab',
148+
'&:active': {
149+
cursor: 'grabbing',
150+
},
151+
}}>
152+
<div
153+
ref={diagramDivRef}
154+
style={{
155+
width: '100%',
156+
height: '100%',
157+
}}
158+
/>
159+
</Box>
160+
);
161+
});
162+
163+
MapViewerCanvas.displayName = 'MapViewerCanvas';
164+
165+
export default MapViewerCanvas;

0 commit comments

Comments
 (0)