Spaces:
Running
Running
dragging is working
Browse files
frontend/src/components/map/MapViewer.tsx
CHANGED
|
@@ -200,126 +200,6 @@ function MapController({ center, zoom, locked }: { center: [number, number]; zoo
|
|
| 200 |
return null;
|
| 201 |
}
|
| 202 |
|
| 203 |
-
/**
|
| 204 |
-
* ProcessDragger: Custom DOM-based drag for process markers with real-time visual feedback.
|
| 205 |
-
*/
|
| 206 |
-
function ProcessDragger({
|
| 207 |
-
locked,
|
| 208 |
-
groupCoordinates,
|
| 209 |
-
processes,
|
| 210 |
-
onGroupCoordinatesChange,
|
| 211 |
-
onProcessesChange,
|
| 212 |
-
}: {
|
| 213 |
-
locked: boolean;
|
| 214 |
-
groupCoordinates: Record<string, any>;
|
| 215 |
-
processes: any[];
|
| 216 |
-
onGroupCoordinatesChange?: (coords: Record<string, any>) => void;
|
| 217 |
-
onProcessesChange?: (processes: any[]) => void;
|
| 218 |
-
}) {
|
| 219 |
-
const map = useMap();
|
| 220 |
-
|
| 221 |
-
const isDragging = useRef(false);
|
| 222 |
-
const dragType = useRef<'group' | 'sub' | null>(null);
|
| 223 |
-
const dragId = useRef<string>('');
|
| 224 |
-
const dragEl = useRef<HTMLElement | null>(null);
|
| 225 |
-
const startMouseX = useRef(0);
|
| 226 |
-
const startMouseY = useRef(0);
|
| 227 |
-
const groupCoordsRef = useRef(groupCoordinates);
|
| 228 |
-
const processesRef = useRef(processes);
|
| 229 |
-
groupCoordsRef.current = groupCoordinates;
|
| 230 |
-
processesRef.current = processes;
|
| 231 |
-
|
| 232 |
-
useEffect(() => {
|
| 233 |
-
if (locked) return;
|
| 234 |
-
const container = map.getContainer();
|
| 235 |
-
|
| 236 |
-
// Enable pointer-events on our custom pane so markers receive events
|
| 237 |
-
const pane = map.getPane('activeMarkerPane');
|
| 238 |
-
if (pane) pane.style.pointerEvents = 'auto';
|
| 239 |
-
|
| 240 |
-
const onMouseDown = (e: MouseEvent) => {
|
| 241 |
-
const target = e.target as HTMLElement;
|
| 242 |
-
|
| 243 |
-
// Look for draggable marker inside .leaflet-marker-icon wrapper
|
| 244 |
-
const markerInner = target.closest('.draggable-marker') as HTMLElement | null;
|
| 245 |
-
if (!markerInner) return;
|
| 246 |
-
|
| 247 |
-
const type = markerInner.getAttribute('data-marker-type') as 'group' | 'sub' | null;
|
| 248 |
-
const id = markerInner.getAttribute('data-marker-id') || '';
|
| 249 |
-
if (!type || !id) return;
|
| 250 |
-
|
| 251 |
-
// The Leaflet wrapper is the parent of our inner div
|
| 252 |
-
const leafletWrapper = markerInner.parentElement;
|
| 253 |
-
if (!leafletWrapper) return;
|
| 254 |
-
|
| 255 |
-
e.preventDefault();
|
| 256 |
-
e.stopPropagation();
|
| 257 |
-
|
| 258 |
-
isDragging.current = true;
|
| 259 |
-
dragType.current = type;
|
| 260 |
-
dragId.current = id;
|
| 261 |
-
dragEl.current = leafletWrapper;
|
| 262 |
-
startMouseX.current = e.clientX;
|
| 263 |
-
startMouseY.current = e.clientY;
|
| 264 |
-
leafletWrapper.style.transition = 'none';
|
| 265 |
-
leafletWrapper.style.zIndex = '9999';
|
| 266 |
-
map.dragging.disable();
|
| 267 |
-
document.body.style.cursor = 'grabbing';
|
| 268 |
-
};
|
| 269 |
-
|
| 270 |
-
const onMouseMove = (e: MouseEvent) => {
|
| 271 |
-
if (!isDragging.current || !dragEl.current) return;
|
| 272 |
-
const dx = e.clientX - startMouseX.current;
|
| 273 |
-
const dy = e.clientY - startMouseY.current;
|
| 274 |
-
dragEl.current.style.transform = `translate(${dx}px, ${dy}px)`;
|
| 275 |
-
};
|
| 276 |
-
|
| 277 |
-
const onMouseUp = (e: MouseEvent) => {
|
| 278 |
-
if (!isDragging.current) return;
|
| 279 |
-
isDragging.current = false;
|
| 280 |
-
document.body.style.cursor = '';
|
| 281 |
-
map.dragging.enable();
|
| 282 |
-
|
| 283 |
-
// Reset visual transform
|
| 284 |
-
if (dragEl.current) {
|
| 285 |
-
dragEl.current.style.transform = '';
|
| 286 |
-
dragEl.current.style.transition = '';
|
| 287 |
-
dragEl.current.style.zIndex = '';
|
| 288 |
-
dragEl.current = null;
|
| 289 |
-
}
|
| 290 |
-
|
| 291 |
-
const latlng = map.mouseEventToLatLng(e);
|
| 292 |
-
const lat = latlng.lat.toString();
|
| 293 |
-
const lon = latlng.lng.toString();
|
| 294 |
-
|
| 295 |
-
if (dragType.current === 'group' && onGroupCoordinatesChange) {
|
| 296 |
-
const gIdx = dragId.current;
|
| 297 |
-
const newCoords = { ...groupCoordsRef.current };
|
| 298 |
-
newCoords[gIdx] = { ...(newCoords[gIdx] || {}), lat, lon };
|
| 299 |
-
onGroupCoordinatesChange(newCoords);
|
| 300 |
-
} else if (dragType.current === 'sub' && onProcessesChange) {
|
| 301 |
-
const subIdx = parseInt(dragId.current);
|
| 302 |
-
const newProcesses = [...processesRef.current];
|
| 303 |
-
newProcesses[subIdx] = { ...newProcesses[subIdx], lat, lon };
|
| 304 |
-
onProcessesChange(newProcesses);
|
| 305 |
-
}
|
| 306 |
-
};
|
| 307 |
-
|
| 308 |
-
container.addEventListener('mousedown', onMouseDown, true);
|
| 309 |
-
window.addEventListener('mousemove', onMouseMove);
|
| 310 |
-
window.addEventListener('mouseup', onMouseUp);
|
| 311 |
-
|
| 312 |
-
return () => {
|
| 313 |
-
container.removeEventListener('mousedown', onMouseDown, true);
|
| 314 |
-
window.removeEventListener('mousemove', onMouseMove);
|
| 315 |
-
window.removeEventListener('mouseup', onMouseUp);
|
| 316 |
-
if (pane) pane.style.pointerEvents = '';
|
| 317 |
-
};
|
| 318 |
-
}, [locked, map, onGroupCoordinatesChange, onProcessesChange]);
|
| 319 |
-
|
| 320 |
-
return null;
|
| 321 |
-
}
|
| 322 |
-
|
| 323 |
function MapMoveHandler({
|
| 324 |
onMoveEnd,
|
| 325 |
locked,
|
|
@@ -1028,12 +908,13 @@ export default function MapViewer({
|
|
| 1028 |
<Marker
|
| 1029 |
key={`group-${gIdx}`}
|
| 1030 |
position={[lat, lon]}
|
| 1031 |
-
draggable={
|
| 1032 |
pane="activeMarkerPane"
|
| 1033 |
eventHandlers={{
|
| 1034 |
click: () => {
|
| 1035 |
if (onProcessesSelect) onProcessesSelect(subIdxs, !isAnySelected);
|
| 1036 |
-
}
|
|
|
|
| 1037 |
}}
|
| 1038 |
icon={createDivIcon('', groupNames[gIdx] || `Process ${gIdx + 1}`, '', gScale, isAnySelected, 'group', String(gIdx))}
|
| 1039 |
/>
|
|
@@ -1052,12 +933,13 @@ export default function MapViewer({
|
|
| 1052 |
<Marker
|
| 1053 |
key={`sub-${si}`}
|
| 1054 |
position={[lat, lon]}
|
| 1055 |
-
draggable={
|
| 1056 |
pane="activeMarkerPane"
|
| 1057 |
eventHandlers={{
|
| 1058 |
click: () => {
|
| 1059 |
if (onProcessesSelect) onProcessesSelect([si], !isSubSelected);
|
| 1060 |
-
}
|
|
|
|
| 1061 |
}}
|
| 1062 |
icon={createDivIcon('', p.name, '', p.box_scale ? parseFloat(String(p.box_scale)) : 1.2, isSubSelected, 'sub', String(si))}
|
| 1063 |
/>
|
|
@@ -1091,8 +973,11 @@ export default function MapViewer({
|
|
| 1091 |
<Marker
|
| 1092 |
key={`child-${si}-${ci}`}
|
| 1093 |
position={[lat, lon]}
|
| 1094 |
-
draggable={
|
| 1095 |
pane="expandedContentPane"
|
|
|
|
|
|
|
|
|
|
| 1096 |
icon={createDivIcon('', child.name, '', child.box_scale ? parseFloat(String(child.box_scale)) : 0.9, isChildSelected, 'child', `${si}-${ci}`)}
|
| 1097 |
/>
|
| 1098 |
);
|
|
@@ -1162,13 +1047,7 @@ export default function MapViewer({
|
|
| 1162 |
}}
|
| 1163 |
/>
|
| 1164 |
|
| 1165 |
-
|
| 1166 |
-
locked={locked}
|
| 1167 |
-
groupCoordinates={groupCoordinates}
|
| 1168 |
-
processes={processes}
|
| 1169 |
-
onGroupCoordinatesChange={onGroupCoordinatesChange}
|
| 1170 |
-
onProcessesChange={onProcessesChange}
|
| 1171 |
-
/>
|
| 1172 |
|
| 1173 |
{onMoveEnd && <MapMoveHandler onMoveEnd={onMoveEnd} locked={locked} />}
|
| 1174 |
<MapMountFitter center={center} zoom={zoom} />
|
|
|
|
| 200 |
return null;
|
| 201 |
}
|
| 202 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 203 |
function MapMoveHandler({
|
| 204 |
onMoveEnd,
|
| 205 |
locked,
|
|
|
|
| 908 |
<Marker
|
| 909 |
key={`group-${gIdx}`}
|
| 910 |
position={[lat, lon]}
|
| 911 |
+
draggable={true}
|
| 912 |
pane="activeMarkerPane"
|
| 913 |
eventHandlers={{
|
| 914 |
click: () => {
|
| 915 |
if (onProcessesSelect) onProcessesSelect(subIdxs, !isAnySelected);
|
| 916 |
+
},
|
| 917 |
+
dragend: (e) => handleDragEnd(e, 'group', String(gIdx))
|
| 918 |
}}
|
| 919 |
icon={createDivIcon('', groupNames[gIdx] || `Process ${gIdx + 1}`, '', gScale, isAnySelected, 'group', String(gIdx))}
|
| 920 |
/>
|
|
|
|
| 933 |
<Marker
|
| 934 |
key={`sub-${si}`}
|
| 935 |
position={[lat, lon]}
|
| 936 |
+
draggable={true}
|
| 937 |
pane="activeMarkerPane"
|
| 938 |
eventHandlers={{
|
| 939 |
click: () => {
|
| 940 |
if (onProcessesSelect) onProcessesSelect([si], !isSubSelected);
|
| 941 |
+
},
|
| 942 |
+
dragend: (e) => handleDragEnd(e, 'sub', String(si))
|
| 943 |
}}
|
| 944 |
icon={createDivIcon('', p.name, '', p.box_scale ? parseFloat(String(p.box_scale)) : 1.2, isSubSelected, 'sub', String(si))}
|
| 945 |
/>
|
|
|
|
| 973 |
<Marker
|
| 974 |
key={`child-${si}-${ci}`}
|
| 975 |
position={[lat, lon]}
|
| 976 |
+
draggable={true}
|
| 977 |
pane="expandedContentPane"
|
| 978 |
+
eventHandlers={{
|
| 979 |
+
dragend: (e) => handleDragEnd(e, 'child', `${si}-${ci}`)
|
| 980 |
+
}}
|
| 981 |
icon={createDivIcon('', child.name, '', child.box_scale ? parseFloat(String(child.box_scale)) : 0.9, isChildSelected, 'child', `${si}-${ci}`)}
|
| 982 |
/>
|
| 983 |
);
|
|
|
|
| 1047 |
}}
|
| 1048 |
/>
|
| 1049 |
|
| 1050 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1051 |
|
| 1052 |
{onMoveEnd && <MapMoveHandler onMoveEnd={onMoveEnd} locked={locked} />}
|
| 1053 |
<MapMountFitter center={center} zoom={zoom} />
|