Auto deploy: 2025-12-09 06:24:43

This commit is contained in:
Your Name 2025-12-09 06:24:47 +00:00
parent 5325e81b65
commit b1e9e4379b
2 changed files with 214 additions and 55 deletions

View File

@ -1,13 +1,13 @@
import maplibregl from "maplibre-gl"; import maplibregl from "maplibre-gl";
/** /**
* Retire tous les marqueurs existants * Supprime tous les marqueurs
*/ */
export function clearAllMarkers(markersRef, expandedElRef) { export function clearAllMarkers(markersRef, expandedElRef) {
if (markersRef.current) { if (markersRef.current) {
for (const item of markersRef.current) { for (const m of markersRef.current) {
try { try {
item.marker.remove(); m.marker.remove();
} catch { } catch {
// ignore // ignore
} }
@ -20,14 +20,15 @@ export function clearAllMarkers(markersRef, expandedElRef) {
} }
/** /**
* Crée un marqueur "pin" avec bulle + pointe. * Crée un marqueur pour un post :
* Position verrouillée sur [lon, lat] via MapLibre (anchor: 'bottom'). * - état compact : petite bulle + pointe
* - état étendu : gros post (carte) comme sur ton dessin
*/ */
export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) { export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) {
const map = mapRef.current; const map = mapRef.current;
if (!map) return; if (!map) return;
// --- Coordonnées --- // --- coordonnées ---
const lat = const lat =
typeof post.lat === "number" typeof post.lat === "number"
? post.lat ? post.lat
@ -52,63 +53,121 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) {
} }
const title = (post.title || post.Title || "").trim() || "Untitled"; const title = (post.title || post.Title || "").trim() || "Untitled";
const snippet = const body =
(post.snippet || post.Snippet || "").trim() || (post.snippet || post.Snippet || "").trim() ||
(post.body || post.Body || "").trim() || (post.body || post.Body || "").trim() ||
title; title;
// --- DOM du pin --- const author = (post.author || post.Author || "").trim() || "unknown";
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"); const root = document.createElement("div");
root.className = "post-pin"; root.className = "post-pin post-pin--compact";
const bubble = document.createElement("div"); function renderCompact() {
bubble.className = "post-pin-bubble"; root.className = "post-pin post-pin--compact";
root.innerHTML = `
<div class="post-pin-bubble">
<div class="post-pin-dot"></div>
<div class="post-pin-text">${escapeHtml(title)}</div>
</div>
<div class="post-pin-pointer-small"></div>
`;
}
const dot = document.createElement("div"); function renderExpanded() {
dot.className = "post-pin-dot"; root.className = "post-pin post-pin--expanded";
root.innerHTML = `
<div class="post-card">
<div class="post-card-header">
<div class="post-card-cat">
${escapeHtml(category || "NEWS")}
${subCat ? "· " + escapeHtml(subCat) : ""}
</div>
<div class="post-card-time">
${escapeHtml(created || "")}
</div>
</div>
const text = document.createElement("div"); <div class="post-card-main">
text.className = "post-pin-text"; <div class="post-card-media">
text.textContent = title; <div class="post-card-media-label">
Image / Vidéo
</div>
</div>
bubble.appendChild(dot); <div class="post-card-info">
bubble.appendChild(text); <div class="post-card-title">
${escapeHtml(title)}
</div>
<div class="post-card-meta">
by ${escapeHtml(author)} · lat ${lat.toFixed(3)} · lon ${lon.toFixed(3)}
</div>
<div class="post-card-body">
${escapeHtml(body)}
</div>
</div>
</div>
const pointer = document.createElement("div"); <div class="post-card-footer">
pointer.className = "post-pin-pointer"; <button class="post-card-btn">Live</button>
<button class="post-card-btn">Like</button>
<button class="post-card-btn">Share</button>
<button class="post-card-btn">Fix</button>
</div>
root.appendChild(bubble); <div class="post-card-comments">
root.appendChild(pointer); Live chat / comments (placeholder)
</div>
</div>
<div class="post-card-pointer"></div>
`;
}
// état initial : compact
renderCompact();
// --- Marker MapLibre : position = [lon, lat], ancré en bas ---
const marker = new maplibregl.Marker({ const marker = new maplibregl.Marker({
element: root, element: root,
anchor: "bottom", // la pointe touche la coordonnée anchor: "bottom", // la pointe touche exactement la coordonnée
}) })
.setLngLat([lon, lat]) .setLngLat([lon, lat])
.addTo(map); .addTo(map);
const entry = { id: post.id, marker, el: root, post }; markersRef.current.push({ id: post.id, marker, el: root, post });
markersRef.current.push(entry);
// --- Gestion du mode "agrandi" ---
root.addEventListener("click", (ev) => { root.addEventListener("click", (ev) => {
ev.stopPropagation(); ev.stopPropagation();
// Fermer l'ancien agrandi // Fermer l'ancien gros post
if (expandedElRef.current && expandedElRef.current !== root) { if (expandedElRef.current && expandedElRef.current !== root) {
expandedElRef.current.classList.remove("post-pin-expanded"); expandedElRef.current.__renderCompact &&
expandedElRef.current.__renderCompact();
expandedElRef.current = null;
} }
// Toggle sur ce pin // Toggle pour ce post
const isExpanded = root.classList.toggle("post-pin-expanded"); const isExpanded = root.classList.contains("post-pin--expanded");
expandedElRef.current = isExpanded ? root : null;
// Mettre à jour le texte (plus long si agrandi)
if (isExpanded) { if (isExpanded) {
text.textContent = snippet; renderCompact();
expandedElRef.current = null;
} else { } else {
text.textContent = title; renderExpanded();
expandedElRef.current = root;
// on garde les fonctions pour pouvoir re-compactifier
root.__renderCompact = renderCompact;
} }
}); });
} }
/* petite fonction pour éviter dinjecter du HTML sale */
function escapeHtml(str) {
return String(str)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}

View File

@ -1,4 +1,4 @@
/* Le conteneur du pin : MapLibre gère la position/transform */ /* MapLibre gère la position/transform, on ne touche pas */
.post-pin { .post-pin {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -6,9 +6,9 @@
pointer-events: auto; pointer-events: auto;
} }
/* ===== PETIT POST (compact) ===== */ /* ===== ÉTAT COMPACT : petite bulle + petite pointe ===== */
.post-pin-bubble { .post-pin--compact .post-pin-bubble {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
padding: 4px 8px; padding: 4px 8px;
@ -36,31 +36,131 @@
text-overflow: ellipsis; text-overflow: ellipsis;
} }
/* PETITE POINTE sous la bulle pointe exactement sur la coordonnée */ /* petite pointe */
.post-pin-pointer { .post-pin-pointer-small {
width: 0; width: 0;
height: 0; height: 0;
margin-top: 2px; margin-top: 2px;
border-left: 7px solid transparent; border-left: 7px solid transparent;
border-right: 7px solid transparent; border-right: 7px solid transparent;
border-top: 9px solid rgba(6, 40, 80, 0.95); /* couleur de la bulle */ border-top: 9px solid rgba(6, 40, 80, 0.95);
filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6)); filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.6));
} }
/* ===== GROS POST (quand on clique) ===== */ /* ===== ÉTAT ÉTENDU : gros post (carte) ===== */
.post-pin.post-pin-expanded .post-pin-bubble { .post-pin--expanded .post-card {
padding: 8px 10px; min-width: 260px;
border-radius: 12px; max-width: 320px;
max-width: 260px; background: rgba(8, 20, 40, 0.97);
background: rgba(8, 20, 40, 0.98); border-radius: 14px;
padding: 10px 12px;
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;
} }
/* Titre + texte multi-ligne comme ta maquette */ /* header : catégorie + heure/date */
.post-pin.post-pin-expanded .post-pin-text { .post-card-header {
white-space: normal; display: flex;
max-width: 260px; justify-content: space-between;
line-height: 1.35; align-items: center;
font-size: 11px;
margin-bottom: 6px;
} }
/* On garde la même pointe, donc le gros post pointe toujours sur la position */ .post-card-cat {
font-weight: 600;
color: #8fc5ff;
}
.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;
}
.post-card-media-label {
font-size: 9px;
text-align: center;
color: #e8f5ff;
}
/* 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;
}
/* 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));
}