Auto deploy: 2025-12-09 06:00:27
This commit is contained in:
parent
4b7d8c3965
commit
ae2be1a71b
|
|
@ -1,108 +1,170 @@
|
||||||
import maplibregl from "maplibre-gl";
|
import maplibregl from "maplibre-gl";
|
||||||
import { getCoords } from "./mapGeo";
|
|
||||||
import { extractPostText } from "./mapFilter";
|
|
||||||
|
|
||||||
// Supprime tous les markers actuels
|
/**
|
||||||
|
* Supprime tous les marqueurs de la carte
|
||||||
|
*/
|
||||||
export function clearAllMarkers(markersRef, expandedElRef) {
|
export function clearAllMarkers(markersRef, expandedElRef) {
|
||||||
markersRef.current.forEach((m) => m.marker.remove());
|
if (markersRef.current) {
|
||||||
|
for (const item of markersRef.current) {
|
||||||
|
try {
|
||||||
|
item.marker.remove();
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
markersRef.current = [];
|
markersRef.current = [];
|
||||||
if (expandedElRef) {
|
if (expandedElRef) {
|
||||||
expandedElRef.current = null;
|
expandedElRef.current = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Crée un marker pour un post
|
/**
|
||||||
|
* Crée un DOM marker pour un post et l'ancre sur sa coordonnée.
|
||||||
|
* - mapRef : ref vers la map MapLibre
|
||||||
|
* - markersRef : ref vers la liste [{ id, marker, el, post }]
|
||||||
|
* - expandedElRef : ref vers l'élément DOM du post actuellement "grand"
|
||||||
|
*/
|
||||||
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;
|
||||||
|
|
||||||
const coords = getCoords(post);
|
// Récupère les coords (lat/lon / Lat/Lon / latitude/longitude)
|
||||||
if (!coords) return;
|
const lat =
|
||||||
|
typeof post.lat === "number"
|
||||||
|
? post.lat
|
||||||
|
: typeof post.Lat === "number"
|
||||||
|
? post.Lat
|
||||||
|
: typeof post.latitude === "number"
|
||||||
|
? post.latitude
|
||||||
|
: null;
|
||||||
|
|
||||||
// évite les doublons (même id déjà affiché)
|
const lon =
|
||||||
if (
|
typeof post.lon === "number"
|
||||||
typeof post.id === "number" &&
|
? post.lon
|
||||||
markersRef.current.some((m) => m.id === post.id)
|
: typeof post.Lon === "number"
|
||||||
) {
|
? post.Lon
|
||||||
|
: typeof post.longitude === "number"
|
||||||
|
? post.longitude
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (lat == null || lon == null) {
|
||||||
|
console.warn("Post sans coordonnée valide:", post);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const [lngP, latP] = coords;
|
// Petit texte (on garde court)
|
||||||
if (
|
const title = (post.title || post.Title || "").trim() || "Untitled";
|
||||||
typeof lngP !== "number" ||
|
const snippet =
|
||||||
typeof latP !== "number" ||
|
(post.snippet || post.Snippet || "").trim() ||
|
||||||
Number.isNaN(lngP) ||
|
(post.body || post.Body || "").trim() ||
|
||||||
Number.isNaN(latP)
|
title;
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { title, body, short } = extractPostText(post);
|
// ELEMENT DOM DU MARKER
|
||||||
|
const root = document.createElement("div");
|
||||||
|
root.className = "post-marker post-marker-compact";
|
||||||
|
|
||||||
const compactHTML = `
|
const inner = document.createElement("div");
|
||||||
<div class="marker-inner">
|
inner.className = "marker-inner";
|
||||||
<div class="marker-dot"></div>
|
|
||||||
<span class="marker-text">${short}</span>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const expandedHTML = `
|
const dot = document.createElement("div");
|
||||||
<div class="marker-expanded-inner">
|
dot.className = "marker-dot";
|
||||||
<div class="marker-expanded-header">
|
|
||||||
<div class="marker-dot"></div>
|
|
||||||
<span class="marker-expanded-title">${title}</span>
|
|
||||||
</div>
|
|
||||||
<div class="marker-expanded-body">
|
|
||||||
${body}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const el = document.createElement("div");
|
const text = document.createElement("div");
|
||||||
el.className = "post-marker post-marker-compact";
|
text.className = "marker-text";
|
||||||
el.innerHTML = compactHTML;
|
text.textContent = title;
|
||||||
|
|
||||||
|
inner.appendChild(dot);
|
||||||
|
inner.appendChild(text);
|
||||||
|
root.appendChild(inner);
|
||||||
|
|
||||||
|
// Marker MapLibre -> POSITION = [lon, lat] SANS OFFSET
|
||||||
const marker = new maplibregl.Marker({
|
const marker = new maplibregl.Marker({
|
||||||
element: el,
|
element: root,
|
||||||
anchor: "bottom",
|
anchor: "bottom", // le bas du marker pointe sur la coordonnée
|
||||||
})
|
})
|
||||||
.setLngLat(coords)
|
.setLngLat([lon, lat])
|
||||||
.addTo(map);
|
.addTo(map);
|
||||||
|
|
||||||
const meta = {
|
// Sauvegarde pour pouvoir les nettoyer
|
||||||
id: typeof post.id === "number" ? post.id : null,
|
const entry = { id: post.id, marker, el: root, post };
|
||||||
marker,
|
markersRef.current.push(entry);
|
||||||
el,
|
|
||||||
compactHTML,
|
|
||||||
expandedHTML,
|
|
||||||
};
|
|
||||||
markersRef.current.push(meta);
|
|
||||||
|
|
||||||
const toggle = () => {
|
// Quand on clique -> on ouvre le gros post par-dessus, mais à la même coordonnée
|
||||||
if (expandedElRef.current && expandedElRef.current !== el) {
|
root.addEventListener("click", (ev) => {
|
||||||
const prev = markersRef.current.find(
|
ev.stopPropagation();
|
||||||
(m) => m.el === expandedElRef.current
|
|
||||||
);
|
// Fermer le précédent gros post
|
||||||
if (prev) {
|
if (expandedElRef.current && expandedElRef.current !== root) {
|
||||||
prev.el.className = "post-marker post-marker-compact";
|
collapseMarkerElement(expandedElRef.current);
|
||||||
prev.el.innerHTML = prev.compactHTML;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expandedElRef.current === el) {
|
// Toggle compact/expanded sur ce marker
|
||||||
el.className = "post-marker post-marker-compact";
|
if (root.classList.contains("post-marker-expanded")) {
|
||||||
el.innerHTML = compactHTML;
|
collapseMarkerElement(root);
|
||||||
expandedElRef.current = null;
|
expandedElRef.current = null;
|
||||||
} else {
|
} else {
|
||||||
el.className = "post-marker post-marker-expanded";
|
expandMarkerElement(root, { title, snippet });
|
||||||
el.innerHTML = expandedHTML;
|
expandedElRef.current = root;
|
||||||
expandedElRef.current = el;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
el.addEventListener("click", toggle);
|
|
||||||
el.addEventListener("touchend", (e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
toggle();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Passe un marker en mode "grand post"
|
||||||
|
*/
|
||||||
|
function expandMarkerElement(root, { title, snippet }) {
|
||||||
|
root.classList.remove("post-marker-compact");
|
||||||
|
root.classList.add("post-marker-expanded");
|
||||||
|
|
||||||
|
// Nettoie l'intérieur et reconstruit
|
||||||
|
root.innerHTML = "";
|
||||||
|
|
||||||
|
const container = document.createElement("div");
|
||||||
|
container.className = "marker-expanded-inner";
|
||||||
|
|
||||||
|
const header = document.createElement("div");
|
||||||
|
header.className = "marker-expanded-header";
|
||||||
|
|
||||||
|
const dot = document.createElement("div");
|
||||||
|
dot.className = "marker-dot";
|
||||||
|
|
||||||
|
const titleEl = document.createElement("div");
|
||||||
|
titleEl.className = "marker-expanded-title";
|
||||||
|
titleEl.textContent = title;
|
||||||
|
|
||||||
|
header.appendChild(dot);
|
||||||
|
header.appendChild(titleEl);
|
||||||
|
|
||||||
|
const bodyEl = document.createElement("div");
|
||||||
|
bodyEl.className = "marker-expanded-body";
|
||||||
|
bodyEl.textContent = snippet;
|
||||||
|
|
||||||
|
container.appendChild(header);
|
||||||
|
container.appendChild(bodyEl);
|
||||||
|
|
||||||
|
root.appendChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repasse un marker en mode petit post
|
||||||
|
*/
|
||||||
|
function collapseMarkerElement(root) {
|
||||||
|
root.classList.remove("post-marker-expanded");
|
||||||
|
root.classList.add("post-marker-compact");
|
||||||
|
root.innerHTML = "";
|
||||||
|
|
||||||
|
const inner = document.createElement("div");
|
||||||
|
inner.className = "marker-inner";
|
||||||
|
|
||||||
|
const dot = document.createElement("div");
|
||||||
|
dot.className = "marker-dot";
|
||||||
|
|
||||||
|
const text = document.createElement("div");
|
||||||
|
text.className = "marker-text";
|
||||||
|
text.textContent = "…";
|
||||||
|
|
||||||
|
inner.appendChild(dot);
|
||||||
|
inner.appendChild(text);
|
||||||
|
root.appendChild(inner);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,12 @@
|
||||||
|
/* IMPORTANT: on ne touche pas au transform du marker :
|
||||||
|
MapLibre l'utilise pour placer le point. */
|
||||||
|
|
||||||
.post-marker {
|
.post-marker {
|
||||||
position: relative;
|
|
||||||
transform: none !important; /* pas de translate chelou */
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
pointer-events: auto;
|
pointer-events: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Petit post */
|
/* Petit post compact */
|
||||||
.post-marker-compact {
|
.post-marker-compact .marker-inner {
|
||||||
padding: 2px 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.post-marker-expanded {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marker-inner {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
|
|
@ -27,25 +17,8 @@
|
||||||
max-width: 220px;
|
max-width: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.marker-dot {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
border-radius: 999px;
|
|
||||||
background: #3ec6ff;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marker-text {
|
|
||||||
color: #e8f5ff;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 500;
|
|
||||||
white-space: nowrap;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Gros post */
|
/* Gros post */
|
||||||
.marker-expanded-inner {
|
.post-marker-expanded .marker-expanded-inner {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
|
|
@ -56,22 +29,4 @@
|
||||||
max-width: 260px;
|
max-width: 260px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.marker-expanded-header {
|
/* Etc… (le reste de ton style peut rester pareil) */
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marker-expanded-title {
|
|
||||||
color: #f5fbff;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.marker-expanded-body {
|
|
||||||
color: #c7e3ff;
|
|
||||||
font-size: 11px;
|
|
||||||
line-height: 1.35;
|
|
||||||
max-height: 120px;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue