From 8249aa7e16e043e99cf17b6ef2e10366e7e816e5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 13 Dec 2025 01:45:24 -0500 Subject: [PATCH] update frontend --- src/components/Map/markerManager.js | 64 +++------ src/styles/mapMarkers.css | 204 ++++++++++++---------------- 2 files changed, 107 insertions(+), 161 deletions(-) diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js index 8cb2ef8..6480a74 100644 --- a/src/components/Map/markerManager.js +++ b/src/components/Map/markerManager.js @@ -1,50 +1,30 @@ import maplibregl from "maplibre-gl"; -/** - * Supprime tous les marqueurs - */ export function clearAllMarkers(markersRef, expandedElRef) { if (markersRef.current) { for (const m of markersRef.current) { - try { - m.marker.remove(); - } catch { - // ignore - } + try { m.marker.remove(); } catch {} } } markersRef.current = []; - if (expandedElRef) { - expandedElRef.current = null; - } + if (expandedElRef) expandedElRef.current = null; } -/** - * Marker stable: root 0x0 + UI en absolute au-dessus d'un tip fixe. - * => Le pin ne "jump" plus quand tu expand/collapse. - */ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) { const map = mapRef.current; if (!map) return; - // --- coordonnées --- const lat = - typeof post.lat === "number" - ? post.lat - : typeof post.Lat === "number" - ? post.Lat - : typeof post.latitude === "number" - ? post.latitude - : null; + typeof post.lat === "number" ? post.lat : + typeof post.Lat === "number" ? post.Lat : + typeof post.latitude === "number" ? post.latitude : + null; const lon = - typeof post.lon === "number" - ? post.lon - : typeof post.Lon === "number" - ? post.Lon - : typeof post.longitude === "number" - ? post.longitude - : null; + typeof post.lon === "number" ? post.lon : + typeof post.Lon === "number" ? post.Lon : + typeof post.longitude === "number" ? post.longitude : + null; if (lat == null || lon == null) { console.warn("post sans coordonnée:", post); @@ -58,20 +38,16 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) { title; const author = (post.author || post.Author || "").trim() || "unknown"; - const created = - (post.created_at || post.CreatedAt || "").toString().slice(0, 19); + const created = (post.created_at || post.CreatedAt || "").toString().slice(0, 19); const category = (post.category || post.Category || "").toUpperCase() || ""; const subCat = (post.sub_category || post.SubCategory || "").toString(); - // Root ancré stable const root = document.createElement("div"); root.className = "post-pin"; - // Layer UI (au-dessus du tip) const layer = document.createElement("div"); layer.className = "post-pin-layer"; - // Compact UI const compact = document.createElement("div"); compact.className = "post-pin-compact"; compact.innerHTML = ` @@ -79,7 +55,6 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) {
${escapeHtml(title)}
`; - // Expanded UI const expanded = document.createElement("div"); expanded.className = "post-pin-expanded post-hidden"; expanded.innerHTML = ` @@ -115,7 +90,9 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) {
Live chat / comments (placeholder)
`; - // Tip fixe (le point de la map) + // IMPORTANT: cliquer sur les boutons ne doit pas re-toggle le marker + expanded.addEventListener("click", (e) => e.stopPropagation()); + const tip = document.createElement("div"); tip.className = "post-pin-tip"; @@ -124,10 +101,7 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) { root.appendChild(layer); root.appendChild(tip); - const marker = new maplibregl.Marker({ - element: root, - anchor: "bottom", - }) + const marker = new maplibregl.Marker({ element: root, anchor: "bottom" }) .setLngLat([lon, lat]) .addTo(map); @@ -145,10 +119,12 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) { } } + // expose pour fermer depuis un autre marker + root.__setExpanded = setExpanded; + root.addEventListener("click", (ev) => { ev.stopPropagation(); - // Fermer l'ancien expanded if (expandedElRef.current && expandedElRef.current !== root) { const prev = expandedElRef.current; if (prev && prev.__setExpanded) prev.__setExpanded(false); @@ -157,12 +133,8 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) { const isExpanded = !expanded.classList.contains("post-hidden"); setExpanded(!isExpanded); }); - - // expose pour pouvoir fermer depuis un autre marker - root.__setExpanded = setExpanded; } -/* petite fonction pour éviter d’injecter du HTML sale */ function escapeHtml(str) { return String(str) .replace(/&/g, "&") diff --git a/src/styles/mapMarkers.css b/src/styles/mapMarkers.css index e53a68c..0400b50 100644 --- a/src/styles/mapMarkers.css +++ b/src/styles/mapMarkers.css @@ -1,166 +1,140 @@ -/* MapLibre gère la position/transform, on ne touche pas */ -.post-pin { +/* SocioWire — Marker stable (no jump) + Root 0x0 anchored, UI absolutely positioned above a fixed tip */ + +.post-pin{ + position: relative; + width: 0; + height: 0; + pointer-events: auto; + transform: translate3d(0,0,0); +} + +.post-pin-layer{ + position: absolute; + left: 0; + top: 0; + transform: translate(-50%, calc(-100% - 14px)); /* 14px ~ tip height */ display: flex; flex-direction: column; align-items: center; - pointer-events: auto; } -/* ===== ÉTAT COMPACT : petite bulle + petite pointe ===== */ +.post-pin-tip{ + position: absolute; + left: 0; + top: 0; + transform: translate(-50%, -100%); + width: 0; + height: 0; + border-left: 10px solid transparent; + border-right: 10px solid transparent; + border-top: 11px solid rgba(8, 20, 40, 0.97); + filter: drop-shadow(0 2px 4px rgba(0,0,0,0.8)); +} -.post-pin--compact .post-pin-bubble { +.post-hidden{ display:none !important; } + +/* ===== COMPACT ===== */ +.post-pin-compact{ display: inline-flex; align-items: center; padding: 4px 8px; border-radius: 999px; background: rgba(6, 40, 80, 0.95); border: 1px solid rgba(80, 180, 255, 0.9); - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); + box-shadow: 0 2px 4px rgba(0,0,0,0.5); + max-width: 200px; } -.post-pin-dot { +.post-pin-dot{ width: 10px; height: 10px; border-radius: 999px; background: #ff8a00; margin-right: 6px; + flex-shrink: 0; } -.post-pin-text { +.post-pin-text{ color: #e8f5ff; font-size: 11px; font-weight: 500; - max-width: 120px; + max-width: 180px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -/* petite pointe */ -.post-pin-pointer-small { - width: 0; - height: 0; - margin-top: 2px; - border-left: 7px solid transparent; - border-right: 7px solid transparent; - border-top: 9px solid rgba(6, 40, 80, 0.95); - filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6)); -} - -/* ===== ÉTAT ÉTENDU : gros post (carte) ===== */ - -.post-pin--expanded .post-card { +/* ===== EXPANDED ===== */ +.post-pin-expanded{ min-width: 260px; max-width: 320px; background: rgba(8, 20, 40, 0.97); border-radius: 14px; padding: 10px 12px; - box-shadow: 0 6px 18px rgba(0, 0, 0, 0.8); + box-shadow: 0 6px 18px rgba(0,0,0,0.8); border: 1px solid rgba(90, 190, 255, 0.9); color: #e8f5ff; box-sizing: border-box; } -/* header : catégorie + heure/date */ -.post-card-header { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 11px; - margin-bottom: 6px; +/* header */ +.post-card-header{ + display:flex; + justify-content:space-between; + align-items:center; + font-size:11px; + margin-bottom:6px; } +.post-card-cat{ font-weight:600; color:#8fc5ff; } +.post-card-time{ color:#9eb7ff; } -.post-card-cat { - font-weight: 600; - color: #8fc5ff; +/* main */ +.post-card-main{ + display:flex; + gap:8px; + margin-bottom:6px; } - -.post-card-time { - color: #9eb7ff; -} - -/* corps : media + texte */ -.post-card-main { - display: flex; - flex-direction: row; - gap: 8px; - margin-bottom: 6px; -} - -/* fake bloc image/vidéo */ -.post-card-media { - width: 72px; - height: 72px; - border-radius: 10px; +.post-card-media{ + width:72px; + height:72px; + border-radius:10px; background: radial-gradient(circle at 30% 30%, #4a9dff, #07152b); - display: flex; - align-items: center; - justify-content: center; + display:flex; + align-items:center; + justify-content:center; + flex-shrink:0; } - -.post-card-media-label { - font-size: 9px; - text-align: center; - color: #e8f5ff; +.post-card-media-label{ + font-size:9px; + text-align:center; + color:#e8f5ff; } +.post-card-info{ flex:1; min-width:0; } +.post-card-title{ font-size:13px; font-weight:600; margin-bottom:2px; } +.post-card-meta{ font-size:10px; color:#9eb7ff; margin-bottom:4px; } +.post-card-body{ font-size:11px; color:#c7e3ff; max-height:70px; overflow:hidden; } -/* texte principal */ -.post-card-info { - flex: 1; +/* footer */ +.post-card-footer{ + display:flex; + gap:6px; + margin-bottom:6px; + flex-wrap:wrap; } - -.post-card-title { - font-size: 13px; - font-weight: 600; - margin-bottom: 2px; -} - -.post-card-meta { - font-size: 10px; - color: #9eb7ff; - margin-bottom: 4px; -} - -.post-card-body { - font-size: 11px; - color: #c7e3ff; - max-height: 70px; - overflow: hidden; -} - -/* footer boutons */ -.post-card-footer { - display: flex; - gap: 6px; - margin-bottom: 6px; -} - -.post-card-btn { - border: none; - border-radius: 999px; - padding: 3px 8px; - font-size: 10px; +.post-card-btn{ + border:none; + border-radius:999px; + padding:3px 8px; + font-size:10px; background: rgba(5, 35, 70, 0.95); - color: #e8f5ff; - cursor: pointer; + color:#e8f5ff; + cursor:pointer; } - -/* zone commentaires */ -.post-card-comments { - font-size: 10px; - color: #9eb7ff; - padding: 4px 6px; - border-radius: 8px; +.post-card-comments{ + font-size:10px; + color:#9eb7ff; + padding:4px 6px; + border-radius:8px; background: rgba(3, 14, 32, 0.95); } - -/* Pointe sous le gros post */ -.post-card-pointer { - width: 0; - height: 0; - margin-top: 3px; - border-left: 10px solid transparent; - border-right: 10px solid transparent; - border-top: 11px solid rgba(8, 20, 40, 0.97); - filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.8)); -}