From bc4cfc9d147c9f184523777e5f01b8c760fe394b Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 13 Dec 2025 01:59:12 -0500 Subject: [PATCH] update frontend --- src/components/Map/markerManager.js | 198 +++++++++++++++----------- src/styles/mapMarkers.css | 213 ++++++++++++++++------------ 2 files changed, 240 insertions(+), 171 deletions(-) diff --git a/src/components/Map/markerManager.js b/src/components/Map/markerManager.js index 6480a74..5379b5f 100644 --- a/src/components/Map/markerManager.js +++ b/src/components/Map/markerManager.js @@ -1,137 +1,169 @@ 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 {} + try { + m.marker.remove(); + } catch {} } } markersRef.current = []; if (expandedElRef) expandedElRef.current = null; } +function shortText(s, max = 22) { + const t = String(s || "").trim(); + if (!t) return "Post"; + return t.length > max ? t.slice(0, max - 3) + "..." : t; +} + +/** + * Crée un marqueur pour un post : + * - compact : bulle + pointe + * - expanded : carte + pointe + */ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) { const map = mapRef.current; if (!map) return; 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); - return; - } + if (lat == null || lon == null) return; const title = (post.title || post.Title || "").trim() || "Untitled"; + const titleShort = shortText(title, 22); + const body = (post.snippet || post.Snippet || "").trim() || (post.body || post.Body || "").trim() || 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(); const root = document.createElement("div"); - root.className = "post-pin"; + root.className = "post-pin post-pin--compact"; + // base z-index (compact) + root.style.zIndex = "1"; - const layer = document.createElement("div"); - layer.className = "post-pin-layer"; - - const compact = document.createElement("div"); - compact.className = "post-pin-compact"; - compact.innerHTML = ` -
-
${escapeHtml(title)}
- `; - - const expanded = document.createElement("div"); - expanded.className = "post-pin-expanded post-hidden"; - expanded.innerHTML = ` -
-
- ${escapeHtml(category || "NEWS")} - ${subCat ? "· " + escapeHtml(subCat) : ""} + function renderCompact() { + root.className = "post-pin post-pin--compact"; + root.style.zIndex = "1"; + root.innerHTML = ` +
+
+
${escapeHtml(titleShort)}
-
${escapeHtml(created || "")}
-
+
+ `; + } -
-
-
Image / Vidéo
-
- -
-
${escapeHtml(title)}
-
- by ${escapeHtml(author)} · lat ${lat.toFixed(3)} · lon ${lon.toFixed(3)} + function renderExpanded() { + root.className = "post-pin post-pin--expanded"; + // ALWAYS on top + root.style.zIndex = "99999"; + root.innerHTML = ` +
+
+
+ ${escapeHtml(category || "NEWS")} + ${subCat ? "· " + escapeHtml(subCat) : ""} +
+
+ ${escapeHtml(created || "")} +
+
+ +
+
+
Image / Vidéo
+
+ +
+
${escapeHtml(title)}
+
+ by ${escapeHtml(author)} · lat ${lat.toFixed(3)} · lon ${lon.toFixed(3)} +
+
${escapeHtml(body)}
+
+
+ +
+ + + + +
+ +
+ Live chat / comments (placeholder)
-
${escapeHtml(body)}
-
+
+ `; + } -
- - - - -
+ function setExpanded(next) { + if (next) renderExpanded(); + else renderCompact(); + } -
Live chat / comments (placeholder)
- `; + // expose pour useMapCore (close on map click) + root.__setExpanded = setExpanded; - // IMPORTANT: cliquer sur les boutons ne doit pas re-toggle le marker - expanded.addEventListener("click", (e) => e.stopPropagation()); + // état initial + renderCompact(); - const tip = document.createElement("div"); - tip.className = "post-pin-tip"; - - layer.appendChild(compact); - layer.appendChild(expanded); - 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); markersRef.current.push({ id: post.id, marker, el: root, post }); - function setExpanded(isOn) { - if (isOn) { - compact.classList.add("post-hidden"); - expanded.classList.remove("post-hidden"); - expandedElRef.current = root; - } else { - expanded.classList.add("post-hidden"); - compact.classList.remove("post-hidden"); - if (expandedElRef.current === root) expandedElRef.current = null; - } - } - - // expose pour fermer depuis un autre marker - root.__setExpanded = setExpanded; - root.addEventListener("click", (ev) => { ev.stopPropagation(); + // ferme l'autre expanded if (expandedElRef.current && expandedElRef.current !== root) { - const prev = expandedElRef.current; - if (prev && prev.__setExpanded) prev.__setExpanded(false); + expandedElRef.current.__setExpanded && + expandedElRef.current.__setExpanded(false); + expandedElRef.current = null; } - const isExpanded = !expanded.classList.contains("post-hidden"); - setExpanded(!isExpanded); + const isExpanded = root.classList.contains("post-pin--expanded"); + if (isExpanded) { + setExpanded(false); + expandedElRef.current = null; + } else { + setExpanded(true); + expandedElRef.current = root; + } }); } diff --git a/src/styles/mapMarkers.css b/src/styles/mapMarkers.css index 0400b50..f04f4cc 100644 --- a/src/styles/mapMarkers.css +++ b/src/styles/mapMarkers.css @@ -1,140 +1,177 @@ -/* 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 */ +/* MapLibre gère la position/transform, on ne touche pas */ +.post-pin { display: flex; flex-direction: column; align-items: center; + pointer-events: auto; + + /* IMPORTANT: z-index fonctionne seulement si l'élément est "positioned" */ + position: relative; + + /* par défaut tout est en bas */ + z-index: 1; } -.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)); +/* quand expand => au-dessus de TOUT */ +.post-pin--expanded { + z-index: 99999; } -.post-hidden{ display:none !important; } +/* ===== ÉTAT COMPACT : petite bulle + petite pointe ===== */ -/* ===== COMPACT ===== */ -.post-pin-compact{ +.post-pin--compact .post-pin-bubble { 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); - max-width: 200px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.5); } -.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: 180px; + max-width: 120px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -/* ===== EXPANDED ===== */ -.post-pin-expanded{ +/* 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 { 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 */ -.post-card-header{ - display:flex; - justify-content:space-between; - align-items:center; - font-size:11px; - margin-bottom:6px; +/* header : catégorie + heure/date */ +.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; } -/* main */ -.post-card-main{ - display:flex; - gap:8px; - margin-bottom:6px; +.post-card-cat { + font-weight: 600; + color: #8fc5ff; } -.post-card-media{ - width:72px; - height:72px; - border-radius:10px; + +.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; background: radial-gradient(circle at 30% 30%, #4a9dff, #07152b); - display:flex; - align-items:center; - justify-content:center; - flex-shrink:0; + display: flex; + align-items: center; + justify-content: center; } -.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; } -/* footer */ -.post-card-footer{ - display:flex; - gap:6px; - margin-bottom:6px; - flex-wrap:wrap; +.post-card-media-label { + font-size: 9px; + text-align: center; + color: #e8f5ff; } -.post-card-btn{ - border:none; - border-radius:999px; - padding:3px 8px; - font-size:10px; + +/* texte principal */ +.post-card-info { + flex: 1; +} + +.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; background: rgba(5, 35, 70, 0.95); - color:#e8f5ff; - cursor:pointer; + color: #e8f5ff; + cursor: pointer; } -.post-card-comments{ - font-size:10px; - color:#9eb7ff; - padding:4px 6px; - border-radius:8px; + +/* zone commentaires */ +.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)); +}