/**
* 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 (
);
}