/** * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai * Project: Groupe Ka / Ka Maps * * KaMapToolbar — LA grappe de contrôles du Ka Map System v2. Fini les * pilules indépendantes qui flottent partout : une seule colonne compacte * de boutons iconographiques (zoom, 3D, dessiner, me localiser, filtres…), * regroupés par segments hairline. Chaque bouton expose son libellé en * infobulle/aria — le chrome reste minimal, la carte respire. */ import { useEffect, useState, type ReactElement, type ReactNode, } from "react"; import { useKaMap } from "./KaMapView.js"; /* ------------------------------------------------------------------ icônes */ function Icon({ d, filled }: { d: string; filled?: boolean }): ReactElement { return ( ); } const ICONS = { plus: "M12 5v14M5 12h14", minus: "M5 12h14", locate: "M12 2v3M12 19v3M2 12h3M19 12h3M12 8a4 4 0 100 8 4 4 0 000-8z", draw: "M12 19l7-7 3 3-7 7-3-3zM18 13l-1.5-7.5L2 2l3.5 14.5L13 18l5-5zM2 2l7.586 7.586M11 13a2 2 0 100-4 2 2 0 000 4z", layers: "M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5", filters: "M21 4h-7M10 4H3M21 12h-9M8 12H3M21 20h-5M12 20H3M14 2v4M8 10v4M16 18v4", close: "M18 6L6 18M6 6l12 12", }; /* ------------------------------------------------------------- primitives */ export interface KaToolbarButtonProps { label: string; onClick: () => void; active?: boolean; disabled?: boolean; icon?: keyof typeof ICONS; /** Icône/contenu custom (prime sur `icon`). */ children?: ReactNode; badge?: string | number; } export function KaToolbarButton(props: KaToolbarButtonProps): ReactElement { return ( ); } /** Conteneur : colonne de segments (chaque enfant direct = un groupe). */ export function KaMapToolbar(props: { children: ReactNode; className?: string; }): ReactElement { return (
{props.children}
); } export function KaToolbarGroup(props: { children: ReactNode }): ReactElement { return
{props.children}
; } /* ------------------------------------------------------- boutons intégrés */ /** Zoom +/− (remplace le NavigationControl natif — passer navControl:false). */ export function KaToolbarZoom(): ReactElement | null { const map = useKaMap(); if (!map) return null; return ( <> map.map.zoomIn()} /> map.map.zoomOut()} /> ); } /** Bascule 3D — libellé texte court, état visuel accentué. */ export function KaToolbar3D(): ReactElement | null { const map = useKaMap(); const [tilted, setTilted] = useState(() => map?.isTilted() ?? false); useEffect(() => { if (!map) return; return map.events.on("moveend", () => setTilted(map.isTilted())); }, [map]); if (!map) return null; return ( map.setTilt(!tilted)} > {tilted ? "2D" : "3D"} ); } /** Dessiner une zone — icône crayon ; état actif pendant le tracé et tant * qu'une zone est posée (re-clic : annule ou efface). */ export function KaToolbarDraw(): ReactElement | null { const map = useKaMap(); const [drawing, setDrawing] = useState(false); const [hasZone, setHasZone] = useState(false); useEffect(() => { if (!map) return; setDrawing(map.isDrawing()); setHasZone(map.getDrawnPolygon() !== null); return map.events.on("draw", ({ polygon, drawing: d }) => { setDrawing(d); setHasZone(polygon !== null); }); }, [map]); if (!map) return null; return ( { if (drawing) map.cancelDraw(); else if (hasZone) map.clearDrawnPolygon(); else map.startDraw(); }} /> ); } /** Me localiser — géolocalisation sur geste explicite uniquement. */ export function KaToolbarLocate(props: { /** Recentrage effectué — l'app peut synchroniser sa liste. */ onLocated?: (pos: { lat: number; lng: number }) => void; }): ReactElement | null { const map = useKaMap(); const [state, setState] = useState<"idle" | "busy" | "denied">("idle"); if (!map) return null; const locate = () => { if (!("geolocation" in navigator)) { setState("denied"); return; } setState("busy"); navigator.geolocation.getCurrentPosition( (pos) => { setState("idle"); const at = { lat: pos.coords.latitude, lng: pos.coords.longitude }; map.flyTo(at, { zoom: Math.max(map.map.getZoom(), 13.5), duration: 700 }); props.onLocated?.(at); }, () => setState("denied"), { enableHighAccuracy: true, timeout: 10_000 }, ); }; return ( ); }