Spaces:
Runtime error
Runtime error
Commit
·
1170a8f
1
Parent(s):
a93666a
Add a split view, one for maps, one for wiki
Browse files- frontend/src/components/Map.js +158 -26
frontend/src/components/Map.js
CHANGED
@@ -1,11 +1,11 @@
|
|
1 |
-
import React, { useState, useEffect,
|
2 |
// useCallback
|
3 |
} from 'react';
|
4 |
import { MapContainer, TileLayer,
|
5 |
useMapEvents,
|
6 |
Marker,
|
7 |
Popup ,
|
8 |
-
|
9 |
} from 'react-leaflet';
|
10 |
import L from 'leaflet';
|
11 |
import 'leaflet/dist/leaflet.css';
|
@@ -31,9 +31,58 @@ const ClickHandler = ({ onMapClick }) => {
|
|
31 |
const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:8004';
|
32 |
console.log(BACKEND_URL);
|
33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
const Map = ( { onMapClick, searchQuery } ) => {
|
35 |
const [markerPosition, setMarkerPosition] = useState([0,0]);
|
36 |
const [wikiContent, setWikiContent] = useState(null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
const fetchWiki = async (pageName) => {
|
38 |
try{
|
39 |
const res = await fetch(`${BACKEND_URL}/wiki/${pageName}`);
|
@@ -54,31 +103,114 @@ const Map = ( { onMapClick, searchQuery } ) => {
|
|
54 |
}
|
55 |
}, [searchQuery]);
|
56 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
57 |
return (
|
58 |
-
<
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
82 |
);
|
83 |
};
|
84 |
|
|
|
1 |
+
import React, { useState, useEffect, useRef
|
2 |
// useCallback
|
3 |
} from 'react';
|
4 |
import { MapContainer, TileLayer,
|
5 |
useMapEvents,
|
6 |
Marker,
|
7 |
Popup ,
|
8 |
+
useMap
|
9 |
} from 'react-leaflet';
|
10 |
import L from 'leaflet';
|
11 |
import 'leaflet/dist/leaflet.css';
|
|
|
31 |
const BACKEND_URL = process.env.BACKEND_URL || 'http://localhost:8004';
|
32 |
console.log(BACKEND_URL);
|
33 |
|
34 |
+
const ResizeHandler = ({ trigger }) => {
|
35 |
+
const map = useMap();
|
36 |
+
useEffect(() => {
|
37 |
+
map.invalidateSize();
|
38 |
+
}, [trigger, map]);
|
39 |
+
return null;
|
40 |
+
};
|
41 |
const Map = ( { onMapClick, searchQuery } ) => {
|
42 |
const [markerPosition, setMarkerPosition] = useState([0,0]);
|
43 |
const [wikiContent, setWikiContent] = useState(null);
|
44 |
+
const [panelSize, setPanelSize] = useState('half');
|
45 |
+
const [wikiWidth, setWikiWidth] = useState(50);
|
46 |
+
const isDragging = useRef(false);
|
47 |
+
const startX = useRef(0);
|
48 |
+
const startWidth = useRef(0);
|
49 |
+
const containerRef = useRef(null);
|
50 |
+
|
51 |
+
const handleMouseDown = (e) => {
|
52 |
+
isDragging.current = true;
|
53 |
+
startX.current = e.clientX;
|
54 |
+
startWidth.current = wikiWidth;
|
55 |
+
document.body.style.cursor = 'col-resize';
|
56 |
+
document.body.style.userSelect = 'none';
|
57 |
+
};
|
58 |
+
|
59 |
+
const handleMouseMove = (e) => {
|
60 |
+
if (!isDragging.current || !containerRef.current) return;
|
61 |
+
|
62 |
+
const containerWidth = containerRef.current.offsetWidth;
|
63 |
+
const deltaX = e.clientX - startX.current;
|
64 |
+
const newWidth = Math.max(20, Math.min(80, startWidth.current + (deltaX / containerWidth * 100)));
|
65 |
+
|
66 |
+
setWikiWidth(newWidth);
|
67 |
+
};
|
68 |
+
|
69 |
+
const handleMouseUp = () => {
|
70 |
+
isDragging.current = false;
|
71 |
+
document.body.style.cursor = '';
|
72 |
+
document.body.style.userSelect = '';
|
73 |
+
}
|
74 |
+
|
75 |
+
useEffect(() => {
|
76 |
+
document.addEventListener('mousemove', handleMouseMove);
|
77 |
+
document.addEventListener('mouseup', handleMouseUp);
|
78 |
+
|
79 |
+
return () => {
|
80 |
+
document.removeEventListener('mousemove', handleMouseMove);
|
81 |
+
document.removeEventListener('mouseup', handleMouseUp);
|
82 |
+
};
|
83 |
+
}, []);
|
84 |
+
|
85 |
+
|
86 |
const fetchWiki = async (pageName) => {
|
87 |
try{
|
88 |
const res = await fetch(`${BACKEND_URL}/wiki/${pageName}`);
|
|
|
103 |
}
|
104 |
}, [searchQuery]);
|
105 |
|
106 |
+
const togglePanel = () => {
|
107 |
+
setPanelSize(prev => {
|
108 |
+
if (prev === 'half') return 'half';
|
109 |
+
if (prev === 'full') return 'half';
|
110 |
+
return 'half';
|
111 |
+
});
|
112 |
+
};
|
113 |
+
|
114 |
return (
|
115 |
+
<div ref={containerRef} style={{ display: 'flex', height: '100vh', width: '100%', overflow: 'hidden' }}>
|
116 |
+
{panelSize !== 'closed' && (
|
117 |
+
<>
|
118 |
+
<div style={{
|
119 |
+
width: `${wikiWidth}%`,
|
120 |
+
height: '100%',
|
121 |
+
overflow: 'auto',
|
122 |
+
padding: '20px',
|
123 |
+
backgroundColor: 'white',
|
124 |
+
boxShadow: '2px 0 5px rgba(0,0,0,0.1)',
|
125 |
+
zIndex: 1000,
|
126 |
+
flexShrink: 0
|
127 |
+
}}>
|
128 |
+
<div style={{ marginBottom: '20px' }}>
|
129 |
+
<h2>{wikiContent?.title || 'Search for a location'}</h2>
|
130 |
+
</div>
|
131 |
+
{wikiContent ? (
|
132 |
+
<div>
|
133 |
+
<p>{wikiContent.content}</p>
|
134 |
+
</div>
|
135 |
+
) : (
|
136 |
+
<p>Search for a location to see Wikipedia content</p>
|
137 |
+
)}
|
138 |
+
</div>
|
139 |
+
<div
|
140 |
+
onMouseDown={handleMouseDown}
|
141 |
+
style={{
|
142 |
+
width: '8px',
|
143 |
+
height: '100%',
|
144 |
+
backgroundColor: '#f0f0f0',
|
145 |
+
cursor: 'col-resize',
|
146 |
+
position: 'relative',
|
147 |
+
zIndex: 1001,
|
148 |
+
display: 'flex',
|
149 |
+
alignItems: 'center',
|
150 |
+
justifyContent: 'center',
|
151 |
+
flexShrink: 0
|
152 |
+
}}
|
153 |
+
>
|
154 |
+
<div style={{
|
155 |
+
width: '2px',
|
156 |
+
height: '40px',
|
157 |
+
backgroundColor: '#ccc',
|
158 |
+
borderRadius: '1px'
|
159 |
+
}} />
|
160 |
+
</div>
|
161 |
+
</>
|
162 |
+
)}
|
163 |
+
<div style={{
|
164 |
+
flex: 1,
|
165 |
+
height: '100%',
|
166 |
+
position: 'relative',
|
167 |
+
minWidth: 0,
|
168 |
+
overflow: 'hidden'
|
169 |
+
}}>
|
170 |
+
<MapContainer
|
171 |
+
center={markerPosition}
|
172 |
+
zoom={2}
|
173 |
+
style={{ height: '100%', width: '100%', position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 }}
|
174 |
+
>
|
175 |
+
<ResizeHandler trigger={wikiWidth} />
|
176 |
+
<TileLayer
|
177 |
+
attribution='© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
178 |
+
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
179 |
+
/>
|
180 |
+
<ClickHandler onMapClick={onMapClick}/>
|
181 |
+
<Marker position={markerPosition}>
|
182 |
+
<Popup minWidth={250}>
|
183 |
+
{wikiContent ? (
|
184 |
+
<>
|
185 |
+
<strong>{wikiContent.title}</strong><br />
|
186 |
+
<p style={{ fontSize: '12px' }}>{wikiContent.content}</p>
|
187 |
+
</>
|
188 |
+
) : (
|
189 |
+
"Search for a location to see information"
|
190 |
+
)}
|
191 |
+
</Popup>
|
192 |
+
</Marker>
|
193 |
+
</MapContainer>
|
194 |
+
{panelSize === 'closed' && (
|
195 |
+
<button
|
196 |
+
onClick={togglePanel}
|
197 |
+
style={{
|
198 |
+
position: 'absolute',
|
199 |
+
top: '10px',
|
200 |
+
left: '10px',
|
201 |
+
zIndex: 1000,
|
202 |
+
padding: '5px 10px',
|
203 |
+
backgroundColor: 'white',
|
204 |
+
border: '1px solid #ccc',
|
205 |
+
borderRadius: '4px',
|
206 |
+
cursor: 'pointer'
|
207 |
+
}}
|
208 |
+
>
|
209 |
+
Show Wikipedia
|
210 |
+
</button>
|
211 |
+
)}
|
212 |
+
</div>
|
213 |
+
</div>
|
214 |
);
|
215 |
};
|
216 |
|