import maplibregl from "maplibre-gl"; import React from "react"; import { createRoot } from "react-dom/client"; import TemplateMarkerCard from "./TemplateMarkerCard"; import { getDefaultTemplateCached } from "../../api/templates"; export function clearAllMarkers(markersRef, expandedElRef) { if (markersRef.current) { for (const m of markersRef.current) { try { if (m?.el && m.el.__reactRoot) { try { m.el.__reactRoot.unmount(); } catch {} m.el.__reactRoot = null; } m.marker.remove(); } catch {} } } markersRef.current = []; if (expandedElRef) expandedElRef.current = null; } function rectsOverlap(a, b) { return !(a.right < b.left || a.left > b.right || a.bottom < b.top || a.top > b.bottom); } export function applyOcclusionForExpanded(markersRef, expandedEl) { if (!expandedEl) return; const expandedCard = expandedEl.querySelector(".post-card"); if (!expandedCard) return; const PAD_X = 26; const PAD_TOP = 20; const PAD_BOTTOM = 140; const raw = expandedCard.getBoundingClientRect(); const expandedRect = { left: raw.left - PAD_X, right: raw.right + PAD_X, top: raw.top - PAD_TOP, bottom: raw.bottom + PAD_BOTTOM, }; const list = markersRef.current || []; for (const item of list) { const el = item?.el; if (!el || el === expandedEl) continue; const r = el.getBoundingClientRect(); const isBelow = r.top >= raw.top; if (!isBelow) { el.style.visibility = "visible"; el.style.pointerEvents = "auto"; continue; } const hide = rectsOverlap(expandedRect, r); el.style.visibility = hide ? "hidden" : "visible"; el.style.pointerEvents = hide ? "none" : "auto"; } } export function clearOcclusion(markersRef) { const list = markersRef.current || []; for (const item of list) { const el = item?.el; if (!el) continue; el.style.visibility = "visible"; el.style.pointerEvents = "auto"; } } function escapeHtml(str) { return String(str) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function currentTheme() { try { const t = document?.body?.getAttribute("data-theme") || "dark"; if (t === "dark" || t === "blue" || t === "light") return t; } catch {} return "dark"; } function ensureReactRoot(el) { if (el.__reactRoot) return el.__reactRoot; el.__reactRoot = createRoot(el); return el.__reactRoot; } export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) { const map = mapRef.current; if (!map) return; 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) { console.warn("post sans coordonnée:", post); return; } const title = (post.title || "").trim() || "Untitled"; const root = document.createElement("div"); root.className = "post-pin post-pin--compact"; root.style.zIndex = "1"; function renderCompact() { root.className = "post-pin post-pin--compact"; root.style.zIndex = "1"; root.innerHTML = `
${escapeHtml(title)}
`; if (root.__reactRoot) { try { root.__reactRoot.unmount(); } catch {} root.__reactRoot = null; } clearOcclusion(markersRef); } async function renderExpanded() { root.className = "post-pin post-pin--expanded"; root.style.zIndex = "999999"; root.innerHTML = `
Loading template…
`; const cardEl = root.querySelector(".post-card"); if (!cardEl) return; const theme = currentTheme(); try { const tpl = await getDefaultTemplateCached("news"); const rr = ensureReactRoot(cardEl); rr.render( React.createElement(TemplateMarkerCard, { post, template: tpl, theme, mode: "full", }) ); } catch (e) { cardEl.innerHTML = `
${escapeHtml(title)}
(template load failed)
`; } requestAnimationFrame(() => { applyOcclusionForExpanded(markersRef, root); }); } renderCompact(); root.__renderCompact = renderCompact; const marker = new maplibregl.Marker({ element: root, anchor: "bottom" }) .setLngLat([lon, lat]) .addTo(map); markersRef.current.push({ id: post.id, marker, el: root, post }); root.addEventListener("click", (ev) => { ev.stopPropagation(); if (expandedElRef.current && expandedElRef.current !== root) { if (expandedElRef.current.__renderCompact) expandedElRef.current.__renderCompact(); expandedElRef.current = null; } const isExpanded = root.classList.contains("post-pin--expanded"); if (isExpanded) { renderCompact(); expandedElRef.current = null; } else { renderExpanded(); expandedElRef.current = root; } }); }