From c3e9426e64691ff0dace0dbb506ab82062283c96 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 22 Dec 2025 22:44:50 -0500 Subject: [PATCH] Improve map UX: compact modal, visible crosshair, smart marker overlap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Reduce create post modal height (max 45vh) and add scroll - Lower modal position (bottom: 8%) to keep map visible - Move crosshair higher (top: 30%) for better visibility - Implement smart marker offsetting for nearby posts - Add visual connection line from offset markers to true position - Create cascade effect for clustered markers (45° circular pattern) - Maintain pointer accuracy when zooming 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 --- src/components/Map/markerManager.js | 63 ++++++++++++++++++++++++++++- src/styles/map.css | 6 ++- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js index dbbf505..66b46c6 100644 --- a/src/components/Map/markerManager.js +++ b/src/components/Map/markerManager.js @@ -294,6 +294,50 @@ function openCenteredOverlay({ map, post, theme }) { }; } +function calculateSmartOffset(post, markersRef, map) { + if (!map || !markersRef.current) return { x: 0, y: 0 }; + + const lat = typeof post.lat === "number" ? post.lat : typeof post.latitude === "number" ? post.latitude : null; + const lon = typeof post.lon === "number" ? post.lon : typeof post.lng === "number" ? post.lng : null; + if (lat == null || lon == null) return { x: 0, y: 0 }; + + const currentPoint = map.project([lon, lat]); + const PROXIMITY_THRESHOLD = 80; // pixels + const MAX_OFFSET = 60; // max offset in pixels + + const nearbyMarkers = []; + + for (const marker of markersRef.current) { + if (!marker.post || marker.post.id === post.id) continue; + + const mLat = typeof marker.post.lat === "number" ? marker.post.lat : marker.post.latitude; + const mLon = typeof marker.post.lon === "number" ? marker.post.lon : marker.post.lng; + if (mLat == null || mLon == null) continue; + + const mPoint = map.project([mLon, mLat]); + const dist = Math.sqrt( + Math.pow(currentPoint.x - mPoint.x, 2) + + Math.pow(currentPoint.y - mPoint.y, 2) + ); + + if (dist < PROXIMITY_THRESHOLD) { + nearbyMarkers.push({ marker, distance: dist, point: mPoint }); + } + } + + if (nearbyMarkers.length === 0) return { x: 0, y: 0 }; + + // Create a cascade effect: offset markers in a circular pattern + const index = nearbyMarkers.length; + const angle = (index * 45) * (Math.PI / 180); // 45 degrees apart + const offsetMagnitude = Math.min(20 + (index * 12), MAX_OFFSET); + + return { + x: Math.cos(angle) * offsetMagnitude, + y: Math.sin(angle) * offsetMagnitude + }; +} + export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, theme = "blue") { const map = mapRef.current; if (!map) return; @@ -316,6 +360,9 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the const lngLat = [lon, lat]; + // Calculate smart offset for nearby markers + const offset = calculateSmartOffset(post, markersRef, map); + const root = document.createElement("div"); root.classList.add("sw-appear"); requestAnimationFrame(() => root.classList.add("sw-appear-in")); @@ -334,9 +381,19 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the root.className = "post-pin post-pin--compact sw-appear sw-appear-in"; root.style.zIndex = "1"; + // Add connecting line if offset is significant + const hasOffset = Math.abs(offset.x) > 5 || Math.abs(offset.y) > 5; + const lineHTML = hasOffset + ? ` + + + ` + : ''; + root.innerHTML = `
+ ${lineHTML} `; const wrap = root.querySelector(".sw-template-mini-wrap"); @@ -352,7 +409,11 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, the renderCompact(); root.__renderCompact = renderCompact; - const marker = new maplibregl.Marker({ element: root, anchor: "bottom" }) + const marker = new maplibregl.Marker({ + element: root, + anchor: "bottom", + offset: [offset.x, offset.y] + }) .setLngLat(lngLat) .addTo(map); diff --git a/src/styles/map.css b/src/styles/map.css index 941f3c9..10c95cf 100644 --- a/src/styles/map.css +++ b/src/styles/map.css @@ -109,7 +109,7 @@ /* Crosshair pour création */ .map-crosshair{ left:50%; - top:40%; + top:30%; transform:translate(-50%,-50%); } .crosshair-aim{ @@ -141,10 +141,12 @@ /* Panneau de création */ .create-post-panel{ left:50%; - bottom:20%; + bottom:8%; transform:translateX(-50%); width:92%; max-width:520px; + max-height:45vh; + overflow-y:auto; background: rgba(15, 23, 42, 0.96); border-radius:16px; border:1px solid rgba(148, 163, 184, 0.9);