Improve map UX: compact modal, visible crosshair, smart marker overlap

- 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 <noreply@anthropic.com>
This commit is contained in:
Your Name 2025-12-22 22:44:50 -05:00
parent 2c4c9a7065
commit c3e9426e64
2 changed files with 66 additions and 3 deletions

View File

@ -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
? `<svg class="sw-offset-line" style="position:absolute;bottom:0;left:50%;transform:translateX(-50%);overflow:visible;pointer-events:none;z-index:-1;">
<line x1="0" y1="0" x2="${-offset.x}" y2="${-offset.y}" stroke="rgba(56,189,248,0.4)" stroke-width="2" stroke-dasharray="4,4"/>
<circle cx="${-offset.x}" cy="${-offset.y}" r="4" fill="#38bdf8" opacity="0.6"/>
</svg>`
: '';
root.innerHTML = `
<div class="sw-template-mini-wrap"></div>
<div class="post-pin-pointer-small"></div>
${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);

View File

@ -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);