@elabs-ai/components-maps 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -154,7 +154,7 @@ type MapMarkerProps = {
154
154
  * `MapMarkerContent` (the visual), `MapMarkerLabel`, `MapMarkerPopup` (opens
155
155
  * on click) and `MapMarkerTooltip` (shows on hover).
156
156
  */
157
- declare function MapMarker({ longitude, latitude, children, onClick, onMouseEnter, onMouseLeave, onDragStart, onDrag, onDragEnd, draggable, ...markerOptions }: MapMarkerProps): react.JSX.Element;
157
+ declare function MapMarker({ longitude, latitude, children, onClick, onMouseEnter, onMouseLeave, onDragStart, onDrag, onDragEnd, draggable, ...markerOptions }: MapMarkerProps): react.JSX.Element | null;
158
158
  interface MapMarkerContentProps {
159
159
  /** Custom marker content. Defaults to a primary-colored dot. */
160
160
  children?: ReactNode;
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  useRef,
14
14
  useState as useState2
15
15
  } from "react";
16
- import { Spinner, StatePanel } from "@elabs-ai/components-ui";
16
+ import { Spinner, StatePanel, useLocale } from "@elabs-ai/components-ui";
17
17
  import { cn } from "@elabs-ai/components-ui/lib/cn";
18
18
 
19
19
  // src/map-canvas/map-context.ts
@@ -115,6 +115,7 @@ var MapCanvas = forwardRef(function MapCanvas2({
115
115
  loading = false,
116
116
  ...props
117
117
  }, ref) {
118
+ const { t } = useLocale();
118
119
  const containerRef = useRef(null);
119
120
  const [mapInstance, setMapInstance] = useState2(null);
120
121
  const [initFailed, setInitFailed] = useState2(false);
@@ -127,6 +128,8 @@ var MapCanvas = forwardRef(function MapCanvas2({
127
128
  const isControlled = viewport !== void 0 && onViewportChange !== void 0;
128
129
  const onViewportChangeRef = useRef(onViewportChange);
129
130
  onViewportChangeRef.current = onViewportChange;
131
+ const projectionRef = useRef(projection);
132
+ projectionRef.current = projection;
130
133
  const mapStyles = useMemo(() => {
131
134
  if (styles) {
132
135
  return {
@@ -176,8 +179,8 @@ var MapCanvas = forwardRef(function MapCanvas2({
176
179
  clearStyleTimeout();
177
180
  styleTimeoutRef.current = setTimeout(() => {
178
181
  setIsStyleLoaded(true);
179
- if (projection) {
180
- map.setProjection(projection);
182
+ if (projectionRef.current) {
183
+ map.setProjection(projectionRef.current);
181
184
  }
182
185
  }, 100);
183
186
  };
@@ -250,8 +253,8 @@ var MapCanvas = forwardRef(function MapCanvas2({
250
253
  StatePanel,
251
254
  {
252
255
  kind: "error",
253
- title: "Map unavailable",
254
- description: "This browser can\u2019t render WebGL maps.",
256
+ title: t("maps.canvas.unavailableTitle"),
257
+ description: t("maps.canvas.unavailableDescription"),
255
258
  className: "h-full"
256
259
  }
257
260
  ) });
@@ -264,10 +267,11 @@ var MapCanvas = forwardRef(function MapCanvas2({
264
267
 
265
268
  // src/map-marker/map-marker.tsx
266
269
  import MapLibreGL2 from "maplibre-gl";
267
- import { createContext as createContext2, use as use2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2 } from "react";
270
+ import { createContext as createContext2, use as use2, useEffect as useEffect3, useMemo as useMemo2, useRef as useRef2, useState as useState3 } from "react";
268
271
  import { createPortal } from "react-dom";
269
272
  import { X } from "lucide-react";
270
273
  import { cn as cn2 } from "@elabs-ai/components-ui/lib/cn";
274
+ import { useLocale as useLocale2 } from "@elabs-ai/components-ui";
271
275
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
272
276
  var MarkerContext = createContext2(null);
273
277
  function useMarkerContext() {
@@ -291,6 +295,7 @@ function MapMarker({
291
295
  ...markerOptions
292
296
  }) {
293
297
  const { map } = useMap();
298
+ const [marker, setMarker] = useState3(null);
294
299
  const callbacksRef = useRef2({
295
300
  onClick,
296
301
  onMouseEnter,
@@ -307,12 +312,16 @@ function MapMarker({
307
312
  onDrag,
308
313
  onDragEnd
309
314
  };
310
- const marker = useMemo2(() => {
315
+ const { anchor, className, offset, rotation, rotationAlignment, pitchAlignment } = markerOptions;
316
+ const initialRef = useRef2({ longitude, latitude, draggable });
317
+ initialRef.current = { longitude, latitude, draggable };
318
+ useEffect3(() => {
311
319
  const markerInstance = new MapLibreGL2.Marker({
312
- ...markerOptions,
320
+ anchor,
321
+ className,
313
322
  element: document.createElement("div"),
314
- draggable
315
- }).setLngLat([longitude, latitude]);
323
+ draggable: initialRef.current.draggable
324
+ }).setLngLat([initialRef.current.longitude, initialRef.current.latitude]);
316
325
  const handleClick = (e) => callbacksRef.current.onClick?.(e);
317
326
  const handleMouseEnter = (e) => callbacksRef.current.onMouseEnter?.(e);
318
327
  const handleMouseLeave = (e) => callbacksRef.current.onMouseLeave?.(e);
@@ -334,17 +343,17 @@ function MapMarker({
334
343
  markerInstance.on("dragstart", handleDragStart);
335
344
  markerInstance.on("drag", handleDrag);
336
345
  markerInstance.on("dragend", handleDragEnd);
337
- return markerInstance;
338
- }, []);
346
+ setMarker(markerInstance);
347
+ }, [anchor, className]);
339
348
  useEffect3(() => {
340
- if (!map) return;
349
+ if (!map || !marker) return;
341
350
  marker.addTo(map);
342
351
  return () => {
343
352
  marker.remove();
344
353
  };
345
- }, [map]);
346
- const { offset, rotation, rotationAlignment, pitchAlignment } = markerOptions;
354
+ }, [map, marker]);
347
355
  useEffect3(() => {
356
+ if (!marker) return;
348
357
  const current = marker.getLngLat();
349
358
  if (current.lng !== longitude || current.lat !== latitude) {
350
359
  marker.setLngLat([longitude, latitude]);
@@ -368,6 +377,9 @@ function MapMarker({
368
377
  marker.setPitchAlignment(pitchAlignment ?? "auto");
369
378
  }
370
379
  }, [marker, longitude, latitude, draggable, offset, rotation, rotationAlignment, pitchAlignment]);
380
+ if (!marker) {
381
+ return null;
382
+ }
371
383
  return /* @__PURE__ */ jsx2(MarkerContext.Provider, { value: { marker, map }, children });
372
384
  }
373
385
  function MapMarkerContent({ children, className }) {
@@ -381,12 +393,13 @@ function DefaultMarkerIcon() {
381
393
  return /* @__PURE__ */ jsx2("div", { className: "relative size-4 rounded-full border-2 border-background bg-primary shadow-sm" });
382
394
  }
383
395
  function PopupCloseButton({ onClick }) {
396
+ const { t } = useLocale2();
384
397
  return /* @__PURE__ */ jsx2(
385
398
  "button",
386
399
  {
387
400
  type: "button",
388
401
  onClick,
389
- "aria-label": "Close popup",
402
+ "aria-label": t("maps.popup.close"),
390
403
  className: "absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset",
391
404
  children: /* @__PURE__ */ jsx2(X, { className: "size-3.5", "aria-hidden": "true" })
392
405
  }
@@ -518,6 +531,7 @@ import { useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3 } from
518
531
  import { createPortal as createPortal2 } from "react-dom";
519
532
  import { X as X2 } from "lucide-react";
520
533
  import { cn as cn3 } from "@elabs-ai/components-ui/lib/cn";
534
+ import { useLocale as useLocale3 } from "@elabs-ai/components-ui";
521
535
  import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
522
536
  function MapPopup({
523
537
  longitude,
@@ -529,6 +543,7 @@ function MapPopup({
529
543
  ...popupOptions
530
544
  }) {
531
545
  const { map } = useMap();
546
+ const { t } = useLocale3();
532
547
  const onCloseRef = useRef3(onClose);
533
548
  onCloseRef.current = onClose;
534
549
  const container = useMemo3(() => document.createElement("div"), []);
@@ -581,7 +596,7 @@ function MapPopup({
581
596
  {
582
597
  type: "button",
583
598
  onClick: handleClose,
584
- "aria-label": "Close popup",
599
+ "aria-label": t("maps.popup.close"),
585
600
  className: "absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset",
586
601
  children: /* @__PURE__ */ jsx3(X2, { className: "size-3.5", "aria-hidden": "true" })
587
602
  }
@@ -595,7 +610,7 @@ function MapPopup({
595
610
  }
596
611
 
597
612
  // src/map-controls/map-controls.tsx
598
- import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef4, useState as useState3 } from "react";
613
+ import { useCallback as useCallback2, useEffect as useEffect5, useRef as useRef4, useState as useState4 } from "react";
599
614
  import { Locate, Maximize, Minus, Plus } from "lucide-react";
600
615
  import { Spinner as Spinner2 } from "@elabs-ai/components-ui";
601
616
  import { cn as cn4 } from "@elabs-ai/components-ui/lib/cn";
@@ -643,7 +658,7 @@ function MapControls({
643
658
  onLocateError
644
659
  }) {
645
660
  const { map } = useMap();
646
- const [waitingForLocation, setWaitingForLocation] = useState3(false);
661
+ const [waitingForLocation, setWaitingForLocation] = useState4(false);
647
662
  const handleZoomIn = useCallback2(() => {
648
663
  map?.zoomTo(map.getZoom() + 1, { duration: 300 });
649
664
  }, [map]);
@@ -751,10 +766,10 @@ import { useEffect as useEffect6, useId } from "react";
751
766
 
752
767
  // src/lib/use-token-color.ts
753
768
  import { resolveTokenColor } from "@elabs-ai/components-tokens";
754
- import { useLayoutEffect, useState as useState4 } from "react";
769
+ import { useLayoutEffect, useState as useState5 } from "react";
755
770
  function useTokenColor(name, fallback = "#000000") {
756
771
  const { map, themeKey } = useMap();
757
- const [color, setColor] = useState4(fallback);
772
+ const [color, setColor] = useState5(fallback);
758
773
  useLayoutEffect(() => {
759
774
  setColor(resolveTokenColor(name, { el: map?.getContainer(), fallback }));
760
775
  }, [name, fallback, map, themeKey]);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/map-canvas/map-canvas.tsx","../src/map-canvas/map-context.ts","../src/map-canvas/use-resolved-basemap-theme.ts","../src/map-marker/map-marker.tsx","../src/map-popup/map-popup.tsx","../src/map-controls/map-controls.tsx","../src/map-route/map-route.tsx","../src/lib/use-token-color.ts","../src/map-arc/map-arc.tsx","../src/lib/arc-math.ts","../src/lib/merge-hover-paint.ts","../src/map-geojson/map-geojson.tsx","../src/map-cluster-layer/map-cluster-layer.tsx"],"sourcesContent":["\"use client\";\n\nimport MapLibreGL from \"maplibre-gl\";\nimport \"maplibre-gl/dist/maplibre-gl.css\";\nimport \"./maps.css\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { Spinner, StatePanel } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nimport { MapContext, type BasemapTheme } from \"./map-context\";\nimport { useResolvedBasemapTheme } from \"./use-resolved-basemap-theme\";\n\n/**\n * Default basemaps: Carto's free light/dark GL styles. These serve ODbL-licensed\n * OpenStreetMap data, which requires attribution on a public surface — see the\n * `attributionControl` note in the map constructor below.\n */\nconst defaultStyles = {\n dark: \"https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json\",\n light: \"https://basemaps.cartocdn.com/gl/positron-gl-style/style.json\",\n};\n\n// A tile-less, dependency-free style with a transparent background. Use it for\n// data visualizations (choropleths, arcs, dot maps) where you draw your own\n// layers and don't need a street basemap: `<MapCanvas blank>`. The transparent\n// background lets the themed container show through.\nconst blankMapStyle: MapLibreGL.StyleSpecification = {\n version: 8,\n sources: {},\n layers: [\n {\n id: \"background\",\n type: \"background\",\n paint: { \"background-color\": \"transparent\" },\n },\n ],\n};\n\n/** Map viewport state. */\nexport interface MapViewport {\n /** Center coordinates [longitude, latitude]. */\n center: [number, number];\n /** Zoom level. */\n zoom: number;\n /** Bearing (rotation) in degrees. */\n bearing: number;\n /** Pitch (tilt) in degrees. */\n pitch: number;\n}\n\nexport type MapStyleOption = string | MapLibreGL.StyleSpecification;\n\n/** The imperative handle exposed by `<MapCanvas ref>`: the MapLibre map itself. */\nexport type MapCanvasRef = MapLibreGL.Map;\n\nexport type MapCanvasProps = {\n children?: ReactNode;\n /** Additional CSS classes for the map container. */\n className?: string;\n /**\n * Basemap flavor. If not provided, it is derived from the active brand theme\n * (`data-theme` + that theme's own `color-scheme`), then a `dark`/`light` root class,\n * then the OS preference.\n */\n theme?: BasemapTheme;\n /** Custom map styles for light and dark themes. Overrides the default Carto styles. */\n styles?: {\n light?: MapStyleOption;\n dark?: MapStyleOption;\n };\n /**\n * Use a transparent, tile-less basemap instead of the default Carto street\n * basemap — a blank canvas. Used alone it renders nothing; add your own\n * layers on top (`<MapGeoJSON>`, `<MapArc>`, markers, …). Ideal for data\n * visualizations. Ignored when an explicit `styles` prop is provided.\n */\n blank?: boolean;\n /** Map projection type. Use `{ type: \"globe\" }` for a 3D globe view. */\n projection?: MapLibreGL.ProjectionSpecification;\n /**\n * Controlled viewport. When provided together with `onViewportChange`, the\n * map becomes controlled and the viewport is driven by this prop.\n */\n viewport?: Partial<MapViewport>;\n /**\n * Callback fired continuously as the viewport changes (pan, zoom, rotate,\n * pitch). Use standalone to observe changes, or with `viewport` for\n * controlled mode.\n */\n onViewportChange?: (viewport: MapViewport) => void;\n /** Show a loading overlay on the map (e.g. while the app fetches map data). */\n loading?: boolean;\n} & Omit<MapLibreGL.MapOptions, \"container\" | \"style\">;\n\nfunction MapLoadingOverlay() {\n return (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-background/50 backdrop-blur-xs\">\n <Spinner label=\"Loading map\" className=\"size-5\" />\n </div>\n );\n}\n\nfunction getViewport(map: MapLibreGL.Map): MapViewport {\n const center = map.getCenter();\n return {\n center: [center.lng, center.lat],\n zoom: map.getZoom(),\n bearing: map.getBearing(),\n pitch: map.getPitch(),\n };\n}\n\n/**\n * The root map surface — a token/theme-aware MapLibre GL canvas. Compose the\n * other `@elabs-ai/components-maps` components (markers, popups, controls, layers) as\n * children; they reach the map through context (`useMap`).\n *\n * The ref exposes the raw MapLibre `Map` instance for imperative work\n * (`flyTo`, `fitBounds`, …).\n */\nexport const MapCanvas = forwardRef<MapCanvasRef, MapCanvasProps>(function MapCanvas(\n {\n children,\n className,\n theme: themeProp,\n styles,\n blank = false,\n projection,\n viewport,\n onViewportChange,\n loading = false,\n ...props\n },\n ref,\n) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [mapInstance, setMapInstance] = useState<MapLibreGL.Map | null>(null);\n const [initFailed, setInitFailed] = useState(false);\n const [isLoaded, setIsLoaded] = useState(false);\n const [isStyleLoaded, setIsStyleLoaded] = useState(false);\n const currentStyleRef = useRef<MapStyleOption | null>(null);\n const styleTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const internalUpdateRef = useRef(false);\n const { resolvedTheme, themeKey } = useResolvedBasemapTheme(themeProp);\n\n const isControlled = viewport !== undefined && onViewportChange !== undefined;\n\n const onViewportChangeRef = useRef(onViewportChange);\n onViewportChangeRef.current = onViewportChange;\n\n const mapStyles = useMemo(() => {\n // Explicit styles win. Otherwise `blank` opts into the transparent\n // tile-less basemap; with neither, fall back to the Carto defaults.\n if (styles) {\n return {\n dark: styles.dark ?? defaultStyles.dark,\n light: styles.light ?? defaultStyles.light,\n };\n }\n if (blank) {\n return { dark: blankMapStyle, light: blankMapStyle };\n }\n return defaultStyles;\n }, [styles, blank]);\n\n // Expose the map instance to the parent component.\n useImperativeHandle(ref, () => mapInstance as MapLibreGL.Map, [mapInstance]);\n\n const clearStyleTimeout = useCallback(() => {\n if (styleTimeoutRef.current) {\n clearTimeout(styleTimeoutRef.current);\n styleTimeoutRef.current = null;\n }\n }, []);\n\n // Initialize the map.\n useEffect(() => {\n if (!containerRef.current) return;\n\n const initialStyle = resolvedTheme === \"dark\" ? mapStyles.dark : mapStyles.light;\n currentStyleRef.current = initialStyle;\n\n let map: MapLibreGL.Map;\n try {\n map = new MapLibreGL.Map({\n container: containerRef.current,\n style: initialStyle,\n renderWorldCopies: false,\n // Attribution control OFF by default — a maintainer decision for internal\n // use, taken deliberately and recorded in CHANGELOG.md + the map-components\n // rule. NOTE THE CONSTRAINT: the default Carto basemap serves OpenStreetMap\n // data, which is ODbL-licensed and requires the credit, and Carto's terms\n // require it too — so a surface that ships PUBLICLY on these tiles must turn\n // it back on with `attributionControl={{ compact: true }}` (it wins through\n // `...props` below), or move to tiles licensed without the requirement via\n // `styles` / `blank`.\n attributionControl: false,\n ...props,\n ...viewport,\n });\n } catch {\n // MapLibre throws at construction when WebGL is unavailable (headless\n // browsers without GPU, remote desktops). Degrade to a quiet panel\n // instead of an unhandled render error.\n setInitFailed(true);\n return;\n }\n\n const styleDataHandler = () => {\n clearStyleTimeout();\n // Delay so the style is fully processed before layer operations — avoids\n // race conditions on setStyle without force-updating every layer.\n styleTimeoutRef.current = setTimeout(() => {\n setIsStyleLoaded(true);\n if (projection) {\n map.setProjection(projection);\n }\n }, 100);\n };\n // No-op under the default (`attributionControl: false` above). This exists for\n // the case a consumer turns the control back ON for a public surface: MapLibre\n // paints even a COMPACT control expanded on first render (`<details open>` +\n // `maplibregl-compact-show`), so the credits land as a text slab until someone\n // clicks the toggle. Collapse it to the labelled ⓘ button instead.\n const loadHandler = () => {\n setIsLoaded(true);\n map\n .getContainer()\n .querySelectorAll<HTMLDetailsElement>(\"details.maplibregl-ctrl-attrib[open]\")\n .forEach((el) => {\n el.open = false;\n });\n };\n\n // Viewport change handler — skip if triggered by an internal update.\n const handleMove = () => {\n if (internalUpdateRef.current) return;\n onViewportChangeRef.current?.(getViewport(map));\n };\n\n map.on(\"load\", loadHandler);\n map.on(\"styledata\", styleDataHandler);\n map.on(\"move\", handleMove);\n setMapInstance(map);\n\n return () => {\n clearStyleTimeout();\n map.off(\"load\", loadHandler);\n map.off(\"styledata\", styleDataHandler);\n map.off(\"move\", handleMove);\n map.remove();\n setIsLoaded(false);\n setIsStyleLoaded(false);\n setMapInstance(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- mount-only: map options are init-time; style/theme/projection changes are synced by the effects below\n }, []);\n\n // Sync controlled viewport to the map.\n useEffect(() => {\n if (!mapInstance || !isControlled || !viewport) return;\n if (mapInstance.isMoving()) return;\n\n const current = getViewport(mapInstance);\n const next = {\n center: viewport.center ?? current.center,\n zoom: viewport.zoom ?? current.zoom,\n bearing: viewport.bearing ?? current.bearing,\n pitch: viewport.pitch ?? current.pitch,\n };\n\n if (\n next.center[0] === current.center[0] &&\n next.center[1] === current.center[1] &&\n next.zoom === current.zoom &&\n next.bearing === current.bearing &&\n next.pitch === current.pitch\n ) {\n return;\n }\n\n internalUpdateRef.current = true;\n mapInstance.jumpTo(next);\n internalUpdateRef.current = false;\n }, [mapInstance, isControlled, viewport]);\n\n // Swap the basemap style when the theme flips.\n useEffect(() => {\n if (!mapInstance || !resolvedTheme) return;\n\n const newStyle = resolvedTheme === \"dark\" ? mapStyles.dark : mapStyles.light;\n\n if (currentStyleRef.current === newStyle) return;\n\n clearStyleTimeout();\n currentStyleRef.current = newStyle;\n setIsStyleLoaded(false);\n\n mapInstance.setStyle(newStyle, { diff: true });\n }, [mapInstance, resolvedTheme, mapStyles, clearStyleTimeout]);\n\n // Sync projection when the prop changes after mount.\n useEffect(() => {\n if (!mapInstance || !isStyleLoaded || !projection) return;\n mapInstance.setProjection(projection);\n }, [mapInstance, isStyleLoaded, projection]);\n\n const contextValue = useMemo(\n () => ({\n map: mapInstance,\n isLoaded: isLoaded && isStyleLoaded,\n resolvedTheme,\n themeKey,\n }),\n [mapInstance, isLoaded, isStyleLoaded, resolvedTheme, themeKey],\n );\n\n if (initFailed) {\n return (\n <div className={cn(\"relative h-full w-full\", className)}>\n <StatePanel\n kind=\"error\"\n title=\"Map unavailable\"\n description=\"This browser can’t render WebGL maps.\"\n className=\"h-full\"\n />\n </div>\n );\n }\n\n return (\n <MapContext.Provider value={contextValue}>\n <div ref={containerRef} className={cn(\"relative h-full w-full\", className)}>\n {(!isLoaded || loading) && <MapLoadingOverlay />}\n {/* SSR-safe: children render only when the map exists on the client. */}\n {mapInstance && children}\n </div>\n </MapContext.Provider>\n );\n});\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { createContext, use } from \"react\";\n\n/** Light-or-dark flavor of the active basemap (derived from the brand theme). */\nexport type BasemapTheme = \"light\" | \"dark\";\n\nexport interface MapContextValue {\n /** The live MapLibre map instance (`null` until the map has mounted). */\n map: MapLibreGL.Map | null;\n /** True once the map AND its style are fully loaded — gate layer operations on this. */\n isLoaded: boolean;\n /** Which basemap flavor is active. */\n resolvedTheme: BasemapTheme;\n /**\n * Changes whenever the active brand theme changes. Layer components use it\n * as a dependency key to re-resolve semantic token colors for WebGL paint.\n */\n themeKey: string;\n}\n\nexport const MapContext = createContext<MapContextValue | null>(null);\n\n/** Access the map instance + load state from any descendant of `<MapCanvas>`. */\nexport function useMap(): MapContextValue {\n const context = use(MapContext);\n if (!context) {\n throw new Error(\"useMap must be used within a <MapCanvas>\");\n }\n return context;\n}\n","\"use client\";\n\nimport { resolveThemeIsDark } from \"@elabs-ai/components-tokens\";\nimport { useEffect, useState } from \"react\";\n\nimport type { BasemapTheme } from \"./map-context\";\n\n/**\n * Brand theme → basemap flavor. When a `data-theme` is set (the ThemeProvider\n * contract) the theme's own `color-scheme` is authoritative via\n * `resolveThemeIsDark` — which is why a CONSUMER-AUTHORED dark theme gets the\n * dark basemap without registering anything here (ADR 0029; the old\n * `THEME_META[theme].dark` lookup only knew the themes this repo ships). A\n * `dark`/`light` class on `<html>` is honored for non-brand hosts (next-themes\n * et al.); the OS preference is only the last-resort fallback.\n */\nfunction getBrandTheme(): BasemapTheme | null {\n if (typeof document === \"undefined\") return null;\n const root = document.documentElement;\n if (root.getAttribute(\"data-theme\")) return resolveThemeIsDark(root) ? \"dark\" : \"light\";\n if (root.classList.contains(\"dark\")) return \"dark\";\n if (root.classList.contains(\"light\")) return \"light\";\n return null;\n}\n\nfunction getSystemTheme(): BasemapTheme {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\nfunction getThemeAttribute(): string {\n if (typeof document === \"undefined\") return \"\";\n return document.documentElement.getAttribute(\"data-theme\") ?? \"\";\n}\n\n/**\n * Resolve the basemap flavor and a change-key for the active brand theme.\n * The observer stays active even with an explicit `theme` prop: token colors\n * (routes, clusters, …) must still re-resolve when `data-theme` changes.\n */\nexport function useResolvedBasemapTheme(themeProp?: BasemapTheme): {\n resolvedTheme: BasemapTheme;\n themeKey: string;\n} {\n const [detected, setDetected] = useState<BasemapTheme>(() => getBrandTheme() ?? getSystemTheme());\n const [themeAttr, setThemeAttr] = useState(getThemeAttribute);\n\n useEffect(() => {\n const update = () => {\n setThemeAttr(getThemeAttribute());\n const brand = getBrandTheme();\n if (brand) setDetected(brand);\n };\n const observer = new MutationObserver(update);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\", \"class\"],\n });\n\n const mediaQuery =\n typeof window.matchMedia === \"function\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n const handleSystemChange = (e: MediaQueryListEvent) => {\n if (!getBrandTheme()) setDetected(e.matches ? \"dark\" : \"light\");\n };\n mediaQuery?.addEventListener(\"change\", handleSystemChange);\n\n return () => {\n observer.disconnect();\n mediaQuery?.removeEventListener(\"change\", handleSystemChange);\n };\n }, []);\n\n const resolvedTheme = themeProp ?? detected;\n return { resolvedTheme, themeKey: `${themeAttr}:${resolvedTheme}` };\n}\n","\"use client\";\n\nimport MapLibreGL, { type MarkerOptions, type PopupOptions } from \"maplibre-gl\";\nimport { createContext, use, useEffect, useMemo, useRef, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nimport { useMap } from \"../map-canvas/map-context\";\n\ntype MarkerContextValue = {\n marker: MapLibreGL.Marker;\n map: MapLibreGL.Map | null;\n};\n\nconst MarkerContext = createContext<MarkerContextValue | null>(null);\n\nfunction useMarkerContext() {\n const context = use(MarkerContext);\n if (!context) {\n throw new Error(\"MapMarker sub-components must be used within <MapMarker>\");\n }\n return context;\n}\n\nexport type MapMarkerProps = {\n /** Longitude coordinate for the marker position. */\n longitude: number;\n /** Latitude coordinate for the marker position. */\n latitude: number;\n /** Marker sub-components (MapMarkerContent, MapMarkerPopup, MapMarkerTooltip, MapMarkerLabel). */\n children: ReactNode;\n /** Callback when the marker is clicked. */\n onClick?: (e: MouseEvent) => void;\n /** Callback when the mouse enters the marker. */\n onMouseEnter?: (e: MouseEvent) => void;\n /** Callback when the mouse leaves the marker. */\n onMouseLeave?: (e: MouseEvent) => void;\n /** Callback when a drag starts (requires `draggable`). */\n onDragStart?: (lngLat: { lng: number; lat: number }) => void;\n /** Callback during a drag (requires `draggable`). */\n onDrag?: (lngLat: { lng: number; lat: number }) => void;\n /** Callback when a drag ends (requires `draggable`). */\n onDragEnd?: (lngLat: { lng: number; lat: number }) => void;\n} & Omit<MarkerOptions, \"element\">;\n\n/**\n * A marker anchored at a lng/lat. Compose the pieces you need:\n * `MapMarkerContent` (the visual), `MapMarkerLabel`, `MapMarkerPopup` (opens\n * on click) and `MapMarkerTooltip` (shows on hover).\n */\nexport function MapMarker({\n longitude,\n latitude,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n draggable = false,\n ...markerOptions\n}: MapMarkerProps) {\n const { map } = useMap();\n\n const callbacksRef = useRef({\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n });\n callbacksRef.current = {\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n };\n\n const marker = useMemo(() => {\n const markerInstance = new MapLibreGL.Marker({\n ...markerOptions,\n element: document.createElement(\"div\"),\n draggable,\n }).setLngLat([longitude, latitude]);\n\n const handleClick = (e: MouseEvent) => callbacksRef.current.onClick?.(e);\n const handleMouseEnter = (e: MouseEvent) => callbacksRef.current.onMouseEnter?.(e);\n const handleMouseLeave = (e: MouseEvent) => callbacksRef.current.onMouseLeave?.(e);\n\n markerInstance.getElement()?.addEventListener(\"click\", handleClick);\n markerInstance.getElement()?.addEventListener(\"mouseenter\", handleMouseEnter);\n markerInstance.getElement()?.addEventListener(\"mouseleave\", handleMouseLeave);\n\n const handleDragStart = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDragStart?.({ lng: lngLat.lng, lat: lngLat.lat });\n };\n const handleDrag = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDrag?.({ lng: lngLat.lng, lat: lngLat.lat });\n };\n const handleDragEnd = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDragEnd?.({ lng: lngLat.lng, lat: lngLat.lat });\n };\n\n markerInstance.on(\"dragstart\", handleDragStart);\n markerInstance.on(\"drag\", handleDrag);\n markerInstance.on(\"dragend\", handleDragEnd);\n\n return markerInstance;\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; position/options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n marker.addTo(map);\n\n return () => {\n marker.remove();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- `marker` is stable (created once above)\n }, [map]);\n\n const { offset, rotation, rotationAlignment, pitchAlignment } = markerOptions;\n\n useEffect(() => {\n const current = marker.getLngLat();\n if (current.lng !== longitude || current.lat !== latitude) {\n marker.setLngLat([longitude, latitude]);\n }\n\n if (marker.isDraggable() !== draggable) {\n marker.setDraggable(draggable);\n }\n\n const currentOffset = marker.getOffset();\n const newOffset = offset ?? [0, 0];\n const [newOffsetX, newOffsetY] = Array.isArray(newOffset)\n ? newOffset\n : [newOffset.x, newOffset.y];\n if (currentOffset.x !== newOffsetX || currentOffset.y !== newOffsetY) {\n marker.setOffset(newOffset);\n }\n\n if (marker.getRotation() !== (rotation ?? 0)) {\n marker.setRotation(rotation ?? 0);\n }\n if (marker.getRotationAlignment() !== (rotationAlignment ?? \"auto\")) {\n marker.setRotationAlignment(rotationAlignment ?? \"auto\");\n }\n if (marker.getPitchAlignment() !== (pitchAlignment ?? \"auto\")) {\n marker.setPitchAlignment(pitchAlignment ?? \"auto\");\n }\n }, [marker, longitude, latitude, draggable, offset, rotation, rotationAlignment, pitchAlignment]);\n\n return <MarkerContext.Provider value={{ marker, map }}>{children}</MarkerContext.Provider>;\n}\n\nexport interface MapMarkerContentProps {\n /** Custom marker content. Defaults to a primary-colored dot. */\n children?: ReactNode;\n /** Additional CSS classes for the marker container. */\n className?: string;\n}\n\n/** The marker's visual, portaled into the MapLibre marker element. */\nexport function MapMarkerContent({ children, className }: MapMarkerContentProps) {\n const { marker } = useMarkerContext();\n\n return createPortal(\n <div className={cn(\"relative cursor-pointer\", className)}>\n {children || <DefaultMarkerIcon />}\n </div>,\n marker.getElement(),\n );\n}\n\nfunction DefaultMarkerIcon() {\n return (\n <div className=\"relative size-4 rounded-full border-2 border-background bg-primary shadow-sm\" />\n );\n}\n\nfunction PopupCloseButton({ onClick }: { onClick: () => void }) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label=\"Close popup\"\n className=\"absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset\"\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n );\n}\n\nexport type MapMarkerPopupProps = {\n /** Popup content. */\n children: ReactNode;\n /** Additional CSS classes for the popup container. */\n className?: string;\n /** Show a close button in the popup (default: false). */\n closeButton?: boolean;\n} & Omit<PopupOptions, \"className\" | \"closeButton\">;\n\n/** A popup attached to the marker — MapLibre toggles it on marker click. */\nexport function MapMarkerPopup({\n children,\n className,\n closeButton = false,\n ...popupOptions\n}: MapMarkerPopupProps) {\n const { marker, map } = useMarkerContext();\n const container = useMemo(() => document.createElement(\"div\"), []);\n const { offset, maxWidth } = popupOptions;\n\n const popup = useMemo(() => {\n return new MapLibreGL.Popup({\n offset: 16,\n ...popupOptions,\n closeButton: false,\n })\n .setMaxWidth(\"none\")\n .setDOMContent(container);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n popup.setDOMContent(container);\n marker.setPopup(popup);\n\n return () => {\n marker.setPopup(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- popup/marker/container are stable\n }, [map]);\n\n // Sync popup options when they change.\n useEffect(() => {\n popup.setOffset(offset ?? 16);\n if (maxWidth) {\n popup.setMaxWidth(maxWidth);\n }\n }, [popup, offset, maxWidth]);\n\n const handleClose = () => popup.remove();\n\n return createPortal(\n <div\n className={cn(\n \"relative max-w-62 rounded-md bg-popover p-3 text-popover-foreground shadow-ring-md\",\n \"animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance\",\n className,\n )}\n >\n {closeButton && <PopupCloseButton onClick={handleClose} />}\n {children}\n </div>,\n container,\n );\n}\n\nexport type MapMarkerTooltipProps = {\n /** Tooltip content. */\n children: ReactNode;\n /** Additional CSS classes for the tooltip container. */\n className?: string;\n} & Omit<PopupOptions, \"className\" | \"closeButton\" | \"closeOnClick\">;\n\n/** A hover tooltip attached to the marker. */\nexport function MapMarkerTooltip({ children, className, ...popupOptions }: MapMarkerTooltipProps) {\n const { marker, map } = useMarkerContext();\n const container = useMemo(() => document.createElement(\"div\"), []);\n const { offset, maxWidth } = popupOptions;\n\n const tooltip = useMemo(() => {\n return new MapLibreGL.Popup({\n offset: 16,\n ...popupOptions,\n closeOnClick: true,\n closeButton: false,\n }).setMaxWidth(\"none\");\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n tooltip.setDOMContent(container);\n\n const handleMouseEnter = () => {\n tooltip.setLngLat(marker.getLngLat()).addTo(map);\n };\n const handleMouseLeave = () => tooltip.remove();\n\n marker.getElement()?.addEventListener(\"mouseenter\", handleMouseEnter);\n marker.getElement()?.addEventListener(\"mouseleave\", handleMouseLeave);\n\n return () => {\n marker.getElement()?.removeEventListener(\"mouseenter\", handleMouseEnter);\n marker.getElement()?.removeEventListener(\"mouseleave\", handleMouseLeave);\n tooltip.remove();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- tooltip/marker/container are stable\n }, [map]);\n\n // Sync tooltip options when they change.\n useEffect(() => {\n tooltip.setOffset(offset ?? 16);\n if (maxWidth) {\n tooltip.setMaxWidth(maxWidth);\n }\n }, [tooltip, offset, maxWidth]);\n\n return createPortal(\n <div\n className={cn(\n \"pointer-events-none rounded-md bg-foreground px-2 py-1 text-meta text-balance text-background shadow-md\",\n \"animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance\",\n className,\n )}\n >\n {children}\n </div>,\n container,\n );\n}\n\nexport interface MapMarkerLabelProps {\n /** Label text content. */\n children: ReactNode;\n /** Additional CSS classes for the label. */\n className?: string;\n /** Position of the label relative to the marker (default: \"top\"). */\n position?: \"top\" | \"bottom\";\n}\n\n/**\n * A small always-visible text label above or below the marker. Portaled into\n * the marker element so it anchors to the marker whether composed as a sibling\n * of `MapMarkerContent` or nested inside it (a cross-theme sweep caught the\n * sibling composition rendering the label against the map container instead).\n */\nexport function MapMarkerLabel({ children, className, position = \"top\" }: MapMarkerLabelProps) {\n const { marker } = useMarkerContext();\n const positionClasses = {\n top: \"bottom-full mb-1\",\n bottom: \"top-full mt-1\",\n };\n\n return createPortal(\n <div\n className={cn(\n \"absolute left-1/2 -translate-x-1/2 whitespace-nowrap\",\n \"text-meta font-medium text-foreground\",\n positionClasses[position],\n className,\n )}\n >\n {children}\n </div>,\n marker.getElement(),\n );\n}\n","\"use client\";\n\nimport MapLibreGL, { type PopupOptions } from \"maplibre-gl\";\nimport { useEffect, useMemo, useRef, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nimport { useMap } from \"../map-canvas/map-context\";\n\nexport type MapPopupProps = {\n /** Longitude coordinate for the popup position. */\n longitude: number;\n /** Latitude coordinate for the popup position. */\n latitude: number;\n /** Callback when the popup is closed. */\n onClose?: () => void;\n /** Popup content. */\n children: ReactNode;\n /** Additional CSS classes for the popup container. */\n className?: string;\n /** Show a close button in the popup (default: false). */\n closeButton?: boolean;\n} & Omit<PopupOptions, \"className\" | \"closeButton\">;\n\n/**\n * A standalone popup anchored at a lng/lat (not attached to a marker) —\n * typically rendered conditionally from app state (e.g. after a layer click).\n */\nexport function MapPopup({\n longitude,\n latitude,\n onClose,\n children,\n className,\n closeButton = false,\n ...popupOptions\n}: MapPopupProps) {\n const { map } = useMap();\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const container = useMemo(() => document.createElement(\"div\"), []);\n const { offset, maxWidth } = popupOptions;\n\n const popup = useMemo(() => {\n return new MapLibreGL.Popup({\n offset: 16,\n ...popupOptions,\n closeButton: false,\n })\n .setMaxWidth(\"none\")\n .setLngLat([longitude, latitude]);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; position/options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n const onCloseProp = () => onCloseRef.current?.();\n\n popup.on(\"close\", onCloseProp);\n\n popup.setDOMContent(container);\n popup.addTo(map);\n\n return () => {\n popup.off(\"close\", onCloseProp);\n if (popup.isOpen()) {\n popup.remove();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- popup/container are stable\n }, [map]);\n\n // Sync popup position and options when they change.\n useEffect(() => {\n const current = popup.getLngLat();\n if (!current || current.lng !== longitude || current.lat !== latitude) {\n popup.setLngLat([longitude, latitude]);\n }\n popup.setOffset(offset ?? 16);\n if (maxWidth) {\n popup.setMaxWidth(maxWidth);\n }\n }, [popup, longitude, latitude, offset, maxWidth]);\n\n const handleClose = () => {\n popup.remove();\n };\n\n return createPortal(\n <div\n className={cn(\n \"relative max-w-62 rounded-md bg-popover p-3 text-popover-foreground shadow-ring-md\",\n \"animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance\",\n className,\n )}\n >\n {closeButton && (\n <button\n type=\"button\"\n onClick={handleClose}\n aria-label=\"Close popup\"\n className=\"absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset\"\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n )}\n {children}\n </div>,\n container,\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { Locate, Maximize, Minus, Plus } from \"lucide-react\";\nimport { Spinner } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nimport { useMap } from \"../map-canvas/map-context\";\n\nexport interface MapControlsProps {\n /** Position of the controls on the map (default: \"bottom-right\"). */\n position?: \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n /** Show zoom in/out buttons (default: true). */\n showZoom?: boolean;\n /** Show a compass button to reset bearing/pitch (default: false). */\n showCompass?: boolean;\n /** Show a locate button to fly to the user's location (default: false). */\n showLocate?: boolean;\n /** Show a fullscreen toggle button (default: false). */\n showFullscreen?: boolean;\n /** Additional CSS classes for the controls container. */\n className?: string;\n /** Callback with user coordinates when located. */\n onLocate?: (coords: { longitude: number; latitude: number }) => void;\n /** Callback when geolocation fails or is denied. */\n onLocateError?: (error: GeolocationPositionError) => void;\n}\n\n// bottom-right sits above MapLibre's attribution control — keep it visible.\nconst positionClasses = {\n \"top-left\": \"top-2 left-2\",\n \"top-right\": \"top-2 right-2\",\n \"bottom-left\": \"bottom-2 left-2\",\n \"bottom-right\": \"bottom-10 right-2\",\n};\n\nfunction ControlGroup({ children }: { children: ReactNode }) {\n return (\n <div className=\"flex flex-col divide-y overflow-hidden rounded-md bg-surface-elevated shadow-ring-sm\">\n {children}\n </div>\n );\n}\n\nfunction ControlButton({\n onClick,\n label,\n children,\n disabled = false,\n}: {\n onClick: () => void;\n label: string;\n children: ReactNode;\n disabled?: boolean;\n}) {\n return (\n <button\n onClick={onClick}\n aria-label={label}\n type=\"button\"\n className={cn(\n \"flex size-8 items-center justify-center text-foreground transition-colors duration-fast\",\n \"hover:bg-surface-muted\",\n \"focus-ring-inset\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n )}\n disabled={disabled}\n >\n {children}\n </button>\n );\n}\n\n/** Branded zoom / compass / locate / fullscreen controls. Render inside `<MapCanvas>`. */\nexport function MapControls({\n position = \"bottom-right\",\n showZoom = true,\n showCompass = false,\n showLocate = false,\n showFullscreen = false,\n className,\n onLocate,\n onLocateError,\n}: MapControlsProps) {\n const { map } = useMap();\n const [waitingForLocation, setWaitingForLocation] = useState(false);\n\n const handleZoomIn = useCallback(() => {\n map?.zoomTo(map.getZoom() + 1, { duration: 300 });\n }, [map]);\n\n const handleZoomOut = useCallback(() => {\n map?.zoomTo(map.getZoom() - 1, { duration: 300 });\n }, [map]);\n\n const handleResetBearing = useCallback(() => {\n map?.resetNorthPitch({ duration: 300 });\n }, [map]);\n\n const handleLocate = useCallback(() => {\n if (!(\"geolocation\" in navigator)) return;\n setWaitingForLocation(true);\n navigator.geolocation.getCurrentPosition(\n (pos) => {\n const coords = {\n longitude: pos.coords.longitude,\n latitude: pos.coords.latitude,\n };\n map?.flyTo({\n center: [coords.longitude, coords.latitude],\n zoom: 14,\n duration: 1500,\n });\n onLocate?.(coords);\n setWaitingForLocation(false);\n },\n (error) => {\n onLocateError?.(error);\n setWaitingForLocation(false);\n },\n );\n }, [map, onLocate, onLocateError]);\n\n const handleFullscreen = useCallback(() => {\n const container = map?.getContainer();\n if (!container) return;\n if (document.fullscreenElement) {\n void document.exitFullscreen();\n } else {\n void container.requestFullscreen();\n }\n }, [map]);\n\n return (\n <div\n className={cn(\"absolute z-10 flex flex-col gap-1.5\", positionClasses[position], className)}\n >\n {showZoom && (\n <ControlGroup>\n <ControlButton onClick={handleZoomIn} label=\"Zoom in\">\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n </ControlButton>\n <ControlButton onClick={handleZoomOut} label=\"Zoom out\">\n <Minus className=\"size-4\" aria-hidden=\"true\" />\n </ControlButton>\n </ControlGroup>\n )}\n {showCompass && (\n <ControlGroup>\n <CompassButton onClick={handleResetBearing} />\n </ControlGroup>\n )}\n {showLocate && (\n <ControlGroup>\n <ControlButton\n onClick={handleLocate}\n label=\"Find my location\"\n disabled={waitingForLocation}\n >\n {waitingForLocation ? (\n <Spinner label=\"Locating\" className=\"size-4 text-foreground\" />\n ) : (\n <Locate className=\"size-4\" aria-hidden=\"true\" />\n )}\n </ControlButton>\n </ControlGroup>\n )}\n {showFullscreen && (\n <ControlGroup>\n <ControlButton onClick={handleFullscreen} label=\"Toggle fullscreen\">\n <Maximize className=\"size-4\" aria-hidden=\"true\" />\n </ControlButton>\n </ControlGroup>\n )}\n </div>\n );\n}\n\nfunction CompassButton({ onClick }: { onClick: () => void }) {\n const { map } = useMap();\n const compassRef = useRef<SVGSVGElement>(null);\n\n useEffect(() => {\n if (!map || !compassRef.current) return;\n\n const compass = compassRef.current;\n\n const updateRotation = () => {\n const bearing = map.getBearing();\n const pitch = map.getPitch();\n compass.style.transform = `rotateX(${pitch}deg) rotateZ(${-bearing}deg)`;\n };\n\n map.on(\"rotate\", updateRotation);\n map.on(\"pitch\", updateRotation);\n updateRotation();\n\n return () => {\n map.off(\"rotate\", updateRotation);\n map.off(\"pitch\", updateRotation);\n };\n }, [map]);\n\n return (\n <ControlButton onClick={onClick} label=\"Reset bearing to north\">\n <svg\n ref={compassRef}\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n className=\"size-5 transition-transform duration-fast\"\n style={{ transformStyle: \"preserve-3d\" }}\n >\n <path d=\"M12 2L16 12H12V2Z\" className=\"fill-destructive\" />\n <path d=\"M12 2L8 12H12V2Z\" className=\"fill-destructive/50\" />\n <path d=\"M12 22L16 12H12V22Z\" className=\"fill-muted-foreground/60\" />\n <path d=\"M12 22L8 12H12V22Z\" className=\"fill-muted-foreground/30\" />\n </svg>\n </ControlButton>\n );\n}\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { useEffect, useId } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport { useTokenColor } from \"../lib/use-token-color\";\n\nexport interface MapRouteProps {\n /** Optional unique identifier for the route layer. */\n id?: string;\n /** Array of [longitude, latitude] coordinate pairs defining the route. */\n coordinates: [number, number][];\n /** Line color as a CSS color value. Defaults to the theme's `--primary` token. */\n color?: string;\n /** Line width in pixels (default: 3). */\n width?: number;\n /** Line opacity from 0 to 1 (default: 0.8). */\n opacity?: number;\n /** Dash pattern [dash length, gap length] for dashed lines. */\n dashArray?: [number, number];\n /** Callback when the route line is clicked. */\n onClick?: () => void;\n /** Callback when the mouse enters the route line. */\n onMouseEnter?: () => void;\n /** Callback when the mouse leaves the route line. */\n onMouseLeave?: () => void;\n /** Whether the route is interactive — shows a pointer cursor on hover (default: true). */\n interactive?: boolean;\n}\n\n/** A GeoJSON line layer for routes/paths. Renders nothing itself — it draws on the map. */\nexport function MapRoute({\n id: propId,\n coordinates,\n color,\n width = 3,\n opacity = 0.8,\n dashArray,\n onClick,\n onMouseEnter,\n onMouseLeave,\n interactive = true,\n}: MapRouteProps) {\n const { map, isLoaded } = useMap();\n const autoId = useId();\n const id = propId ?? autoId;\n const sourceId = `route-source-${id}`;\n const layerId = `route-layer-${id}`;\n\n const primary = useTokenColor(\"--primary\");\n const lineColor = color ?? primary;\n\n // Add source and layer on mount.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data: {\n type: \"Feature\",\n properties: {},\n geometry: { type: \"LineString\", coordinates: [] },\n },\n });\n\n map.addLayer({\n id: layerId,\n type: \"line\",\n source: sourceId,\n layout: { \"line-join\": \"round\", \"line-cap\": \"round\" },\n paint: {\n \"line-color\": lineColor,\n \"line-width\": width,\n \"line-opacity\": opacity,\n ...(dashArray && { \"line-dasharray\": dashArray }),\n },\n });\n\n return () => {\n try {\n if (map.getLayer(layerId)) map.removeLayer(layerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n } catch {\n // style may be mid-reload\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- source/layer created once per map; data + paint are synced by the effects below\n }, [isLoaded, map]);\n\n // When coordinates change, update the source data.\n useEffect(() => {\n if (!isLoaded || !map || coordinates.length < 2) return;\n\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource;\n if (source) {\n source.setData({\n type: \"Feature\",\n properties: {},\n geometry: { type: \"LineString\", coordinates },\n });\n }\n }, [isLoaded, map, coordinates, sourceId]);\n\n // Sync paint when styling (or the resolved theme color) changes.\n useEffect(() => {\n if (!isLoaded || !map || !map.getLayer(layerId)) return;\n\n map.setPaintProperty(layerId, \"line-color\", lineColor);\n map.setPaintProperty(layerId, \"line-width\", width);\n map.setPaintProperty(layerId, \"line-opacity\", opacity);\n map.setPaintProperty(layerId, \"line-dasharray\", dashArray);\n }, [isLoaded, map, layerId, lineColor, width, opacity, dashArray]);\n\n // Handle click and hover events.\n useEffect(() => {\n if (!isLoaded || !map || !interactive) return;\n\n const handleClick = () => {\n onClick?.();\n };\n const handleMouseEnter = () => {\n map.getCanvas().style.cursor = \"pointer\";\n onMouseEnter?.();\n };\n const handleMouseLeave = () => {\n map.getCanvas().style.cursor = \"\";\n onMouseLeave?.();\n };\n\n map.on(\"click\", layerId, handleClick);\n map.on(\"mouseenter\", layerId, handleMouseEnter);\n map.on(\"mouseleave\", layerId, handleMouseLeave);\n\n return () => {\n map.off(\"click\", layerId, handleClick);\n map.off(\"mouseenter\", layerId, handleMouseEnter);\n map.off(\"mouseleave\", layerId, handleMouseLeave);\n };\n }, [isLoaded, map, layerId, onClick, onMouseEnter, onMouseLeave, interactive]);\n\n return null;\n}\n","\"use client\";\n\nimport { resolveTokenColor } from \"@elabs-ai/components-tokens\";\nimport { useLayoutEffect, useState } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\n\n/**\n * Resolve a semantic token (e.g. `\"--primary\"`) to a concrete color MapLibre's\n * WebGL paint can consume, re-resolving whenever the brand theme changes.\n * This is how default layer paints stay token-driven: WebGL can't read CSS\n * custom properties, so we read them at runtime off the map container.\n */\nexport function useTokenColor(name: string, fallback = \"#000000\"): string {\n const { map, themeKey } = useMap();\n const [color, setColor] = useState(fallback);\n\n useLayoutEffect(() => {\n setColor(resolveTokenColor(name, { el: map?.getContainer(), fallback }));\n }, [name, fallback, map, themeKey]);\n\n return color;\n}\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { useEffect, useId, useMemo, useRef } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport { buildArcCoordinates } from \"../lib/arc-math\";\nimport { mergeHoverPaint } from \"../lib/merge-hover-paint\";\nimport { useTokenColor } from \"../lib/use-token-color\";\n\n/** A single arc to render inside `<MapArc data={...}>`. */\nexport type MapArcDatum = {\n /** Unique identifier for this arc. Required for hover state tracking and event payloads. */\n id: string | number;\n /** Start coordinate as [longitude, latitude]. */\n from: [number, number];\n /** End coordinate as [longitude, latitude]. */\n to: [number, number];\n};\n\n/** Event payload passed to MapArc interaction callbacks. */\nexport type MapArcEvent<T extends MapArcDatum = MapArcDatum> = {\n /** The arc datum that was hovered or clicked. */\n arc: T;\n /** Longitude of the cursor at the time of the event. */\n longitude: number;\n /** Latitude of the cursor at the time of the event. */\n latitude: number;\n /** The underlying MapLibre mouse event for advanced use cases. */\n originalEvent: MapLibreGL.MapMouseEvent;\n};\n\nexport type MapArcLinePaint = NonNullable<MapLibreGL.LineLayerSpecification[\"paint\"]>;\nexport type MapArcLineLayout = NonNullable<MapLibreGL.LineLayerSpecification[\"layout\"]>;\n\nexport type MapArcProps<T extends MapArcDatum = MapArcDatum> = {\n /** Array of arcs to render. Each arc must have a unique `id`. */\n data: T[];\n /** Optional unique identifier prefix for the arc source/layers. Auto-generated if not provided. */\n id?: string;\n /**\n * How far each arc bows away from a straight line. `0` renders straight\n * lines; higher values bend further; negative values bend to the opposite\n * side. Arcs cross the antimeridian via the shorter direction. (default: 0.2)\n */\n curvature?: number;\n /** Number of samples used to render each curve. Higher = smoother. (default: 64) */\n samples?: number;\n /**\n * MapLibre paint properties for the arc layer. Merged on top of theme-aware\n * defaults (`line-color` = the `--primary` token, `line-width: 2`,\n * `line-opacity: 0.85`). Any value can be a MapLibre expression for\n * per-feature styling; every field on each arc datum (besides `from`/`to`)\n * is exposed via `[\"get\", ...]`.\n */\n paint?: MapArcLinePaint;\n /** MapLibre layout properties for the arc layer. Defaults to rounded joins/caps. */\n layout?: MapArcLineLayout;\n /**\n * Paint properties applied to the arc currently under the cursor. Each key\n * is merged into `paint` as a `case` expression keyed on per-feature hover\n * state, so only the hovered arc changes appearance.\n */\n hoverPaint?: MapArcLinePaint;\n /** Callback when an arc is clicked. */\n onClick?: (e: MapArcEvent<T>) => void;\n /**\n * Callback fired when the hovered arc changes. Receives the cursor's\n * lng/lat at the moment of entry, and `null` when the cursor leaves the\n * last hovered arc.\n */\n onHover?: (e: MapArcEvent<T> | null) => void;\n /** Whether arcs respond to mouse events (default: true). */\n interactive?: boolean;\n /** Optional MapLibre layer id to insert the arc layers before (z-order control). */\n beforeId?: string;\n};\n\nconst DEFAULT_ARC_CURVATURE = 0.2;\nconst DEFAULT_ARC_SAMPLES = 64;\nconst ARC_HIT_MIN_WIDTH = 12;\nconst ARC_HIT_PADDING = 6;\n\nconst DEFAULT_ARC_LAYOUT: MapArcLineLayout = {\n \"line-join\": \"round\",\n \"line-cap\": \"round\",\n};\n\n/**\n * Curved great-circle-style arcs between coordinate pairs (flight paths,\n * network links). Pairs well with `<MapCanvas blank projection={{ type: \"globe\" }}>`.\n */\nexport function MapArc<T extends MapArcDatum = MapArcDatum>({\n data,\n id: propId,\n curvature = DEFAULT_ARC_CURVATURE,\n samples = DEFAULT_ARC_SAMPLES,\n paint,\n layout,\n hoverPaint,\n onClick,\n onHover,\n interactive = true,\n beforeId,\n}: MapArcProps<T>) {\n const { map, isLoaded } = useMap();\n const autoId = useId();\n const id = propId ?? autoId;\n const sourceId = `arc-source-${id}`;\n const layerId = `arc-layer-${id}`;\n const hitLayerId = `arc-hit-layer-${id}`;\n\n const primary = useTokenColor(\"--primary\");\n\n const mergedPaint = useMemo(\n () =>\n mergeHoverPaint(\n { \"line-color\": primary, \"line-width\": 2, \"line-opacity\": 0.85, ...paint },\n hoverPaint,\n ),\n [primary, paint, hoverPaint],\n );\n const mergedLayout = useMemo(() => ({ ...DEFAULT_ARC_LAYOUT, ...layout }), [layout]);\n\n const hitWidth = useMemo(() => {\n const w = paint?.[\"line-width\"] ?? 2;\n const base = typeof w === \"number\" ? w : ARC_HIT_MIN_WIDTH;\n return Math.max(base + ARC_HIT_PADDING, ARC_HIT_MIN_WIDTH);\n }, [paint]);\n\n const geoJSON = useMemo<GeoJSON.FeatureCollection<GeoJSON.LineString>>(\n () => ({\n type: \"FeatureCollection\",\n features: data.map((arc) => {\n const { from, to, ...properties } = arc;\n return {\n type: \"Feature\",\n properties,\n geometry: {\n type: \"LineString\",\n coordinates: buildArcCoordinates(from, to, curvature, samples),\n },\n };\n }),\n }),\n [data, curvature, samples],\n );\n\n const latestRef = useRef({ data, onClick, onHover });\n latestRef.current = { data, onClick, onHover };\n\n // Add source and layers on mount. The invisible hit layer widens the\n // pointer target so thin arcs stay hoverable/clickable.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data: geoJSON,\n promoteId: \"id\",\n });\n\n map.addLayer(\n {\n id: hitLayerId,\n type: \"line\",\n source: sourceId,\n layout: DEFAULT_ARC_LAYOUT,\n paint: {\n \"line-color\": \"transparent\",\n \"line-width\": hitWidth,\n \"line-opacity\": 1,\n },\n },\n beforeId,\n );\n\n map.addLayer(\n {\n id: layerId,\n type: \"line\",\n source: sourceId,\n layout: mergedLayout,\n paint: mergedPaint,\n },\n beforeId,\n );\n\n return () => {\n try {\n if (map.getLayer(layerId)) map.removeLayer(layerId);\n if (map.getLayer(hitLayerId)) map.removeLayer(hitLayerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n } catch {\n // style may be mid-reload\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- source/layers created once per map; data + paint are synced by the effects below\n }, [isLoaded, map]);\n\n // Sync features when data / curvature / samples change.\n useEffect(() => {\n if (!isLoaded || !map) return;\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource | undefined;\n source?.setData(geoJSON);\n }, [isLoaded, map, geoJSON, sourceId]);\n\n // Sync paint/layout when they change.\n useEffect(() => {\n if (!isLoaded || !map || !map.getLayer(layerId)) return;\n for (const [key, value] of Object.entries(mergedPaint)) {\n map.setPaintProperty(layerId, key as keyof MapArcLinePaint, value as never);\n }\n for (const [key, value] of Object.entries(mergedLayout)) {\n map.setLayoutProperty(layerId, key as keyof MapArcLineLayout, value as never);\n }\n if (map.getLayer(hitLayerId)) {\n map.setPaintProperty(hitLayerId, \"line-width\", hitWidth);\n }\n }, [isLoaded, map, layerId, hitLayerId, mergedPaint, mergedLayout, hitWidth]);\n\n // Interaction handlers (bound to the hit layer).\n useEffect(() => {\n if (!isLoaded || !map || !interactive) return;\n\n let hoveredId: string | number | null = null;\n\n const setHover = (next: string | number | null) => {\n if (next === hoveredId) return;\n const sourceExists = !!map.getSource(sourceId);\n if (hoveredId != null && sourceExists) {\n map.setFeatureState({ source: sourceId, id: hoveredId }, { hover: false });\n }\n hoveredId = next;\n if (next != null && sourceExists) {\n map.setFeatureState({ source: sourceId, id: next }, { hover: true });\n }\n };\n\n const findArc = (featureId: string | number | undefined) =>\n featureId == null\n ? undefined\n : latestRef.current.data.find((arc) => String(arc.id) === String(featureId));\n\n const handleMouseMove = (e: MapLibreGL.MapLayerMouseEvent) => {\n const featureId = e.features?.[0]?.id as string | number | undefined;\n if (featureId == null || featureId === hoveredId) return;\n\n setHover(featureId);\n map.getCanvas().style.cursor = \"pointer\";\n\n const arc = findArc(featureId);\n if (arc) {\n latestRef.current.onHover?.({\n arc: arc as T,\n longitude: e.lngLat.lng,\n latitude: e.lngLat.lat,\n originalEvent: e,\n });\n }\n };\n\n const handleMouseLeave = () => {\n setHover(null);\n map.getCanvas().style.cursor = \"\";\n latestRef.current.onHover?.(null);\n };\n\n const handleClick = (e: MapLibreGL.MapLayerMouseEvent) => {\n const arc = findArc(e.features?.[0]?.id as string | number | undefined);\n if (!arc) return;\n latestRef.current.onClick?.({\n arc: arc as T,\n longitude: e.lngLat.lng,\n latitude: e.lngLat.lat,\n originalEvent: e,\n });\n };\n\n map.on(\"mousemove\", hitLayerId, handleMouseMove);\n map.on(\"mouseleave\", hitLayerId, handleMouseLeave);\n map.on(\"click\", hitLayerId, handleClick);\n\n return () => {\n map.off(\"mousemove\", hitLayerId, handleMouseMove);\n map.off(\"mouseleave\", hitLayerId, handleMouseLeave);\n map.off(\"click\", hitLayerId, handleClick);\n setHover(null);\n map.getCanvas().style.cursor = \"\";\n };\n }, [isLoaded, map, hitLayerId, sourceId, interactive]);\n\n return null;\n}\n","/** Pure geometry for `<MapArc>` — kept engine-free so it can be unit-tested. */\n\n/**\n * Sample a quadratic Bézier between `from` and `to` in lng/lat space.\n * `curvature` is how far the arc bows away from a straight line (0 = straight;\n * negative bends to the opposite side). The destination longitude is unwrapped\n * relative to the origin so arcs cross the antimeridian via the shorter\n * great-circle direction — resulting longitudes may fall outside [-180, 180],\n * which MapLibre renders correctly on the globe projection.\n */\nexport function buildArcCoordinates(\n from: [number, number],\n to: [number, number],\n curvature: number,\n samples: number,\n): [number, number][] {\n const [x0, y0] = from;\n const [xTo, y2] = to;\n const rawDx = xTo - x0;\n const x2 = rawDx > 180 ? xTo - 360 : rawDx < -180 ? xTo + 360 : xTo;\n const dx = x2 - x0;\n const dy = y2 - y0;\n const distance = Math.hypot(dx, dy);\n\n if (distance === 0 || curvature === 0) return [from, [x2, y2]];\n\n const mx = (x0 + x2) / 2;\n const my = (y0 + y2) / 2;\n const nx = -dy / distance;\n const ny = dx / distance;\n const offset = distance * curvature;\n const cx = mx + nx * offset;\n const cy = my + ny * offset;\n\n const points: [number, number][] = [];\n const segments = Math.max(2, Math.floor(samples));\n for (let i = 0; i <= segments; i += 1) {\n const t = i / segments;\n const inv = 1 - t;\n const x = inv * inv * x0 + 2 * inv * t * cx + t * t * x2;\n const y = inv * inv * y0 + 2 * inv * t * cy + t * t * y2;\n points.push([x, y]);\n }\n return points;\n}\n","/**\n * Merge hover paint into base paint as MapLibre `case` expressions keyed on\n * the per-feature `hover` feature-state, so only the hovered feature changes\n * appearance.\n */\nexport function mergeHoverPaint<T extends Record<string, unknown>>(\n paint: T,\n hoverPaint: T | undefined,\n): T {\n if (!hoverPaint) return paint;\n const merged: Record<string, unknown> = { ...paint };\n for (const [key, hoverValue] of Object.entries(hoverPaint)) {\n if (hoverValue === undefined) continue;\n const baseValue = merged[key];\n merged[key] =\n baseValue === undefined\n ? hoverValue\n : [\"case\", [\"boolean\", [\"feature-state\", \"hover\"], false], hoverValue, baseValue];\n }\n return merged as T;\n}\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { useEffect, useId, useMemo, useRef } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport { mergeHoverPaint } from \"../lib/merge-hover-paint\";\nimport { useTokenColor } from \"../lib/use-token-color\";\n\nexport type MapGeoJSONData<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> =\n | GeoJSON.FeatureCollection<GeoJSON.Geometry, P>\n | GeoJSON.Feature<GeoJSON.Geometry, P>\n | GeoJSON.Geometry\n | string;\n\nexport type MapFillPaint = NonNullable<MapLibreGL.FillLayerSpecification[\"paint\"]>;\nexport type MapLinePaint = NonNullable<MapLibreGL.LineLayerSpecification[\"paint\"]>;\n\n/** A rendered feature with strongly-typed `properties`. */\nexport type MapGeoJSONFeature<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> =\n Omit<MapLibreGL.MapGeoJSONFeature, \"properties\"> & { properties: P };\n\n/** Event payload passed to MapGeoJSON interaction callbacks. */\nexport type MapGeoJSONEvent<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = {\n /** The feature under the cursor, with its typed GeoJSON properties. */\n feature: MapGeoJSONFeature<P>;\n /** Longitude of the cursor at the time of the event. */\n longitude: number;\n /** Latitude of the cursor at the time of the event. */\n latitude: number;\n /** The underlying MapLibre mouse event for advanced use cases. */\n originalEvent: MapLibreGL.MapLayerMouseEvent;\n};\n\nexport type MapGeoJSONProps<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = {\n /** GeoJSON data (FeatureCollection, Feature, Geometry) or a URL to fetch it from. */\n data: MapGeoJSONData<P>;\n /** Optional unique identifier prefix for the source/layers. Auto-generated if not provided. */\n id?: string;\n /**\n * Feature property to promote to the feature `id`. Required for hover\n * feature-state (`fillHoverPaint`) and stable `onHover`/`onClick` payloads.\n */\n promoteId?: string;\n /**\n * Paint for the polygon fill layer. Merged on top of a theme-aware neutral\n * default (the `--border` token — a mid neutral that reads on the page\n * surface in every theme). Pass `false` to omit the fill layer entirely\n * (e.g. outlines only).\n */\n fillPaint?: MapFillPaint | false;\n /**\n * Paint for the outline layer. Merged on top of a hairline default\n * (`line-color` = the `--background` token, `line-width` = 0.5) for thin\n * separators. Override `line-color` if your container differs, or pass\n * `false` to omit the layer.\n */\n linePaint?: MapLinePaint | false;\n /**\n * Paint merged onto the fill layer for the feature under the cursor, applied\n * as a `case` expression keyed on hover feature-state. Requires `promoteId`.\n */\n fillHoverPaint?: MapFillPaint;\n /** Callback when a feature is clicked. */\n onClick?: (e: MapGeoJSONEvent<P>) => void;\n /** Callback fired when the hovered feature changes; `null` when the cursor leaves. */\n onHover?: (e: MapGeoJSONEvent<P> | null) => void;\n /** Whether features respond to mouse events (default: false). */\n interactive?: boolean;\n /** Optional MapLibre layer id to insert the layers before (z-order control). */\n beforeId?: string;\n};\n\n/**\n * Renders arbitrary GeoJSON as fill + outline layers on the map. Composes like\n * `MapRoute` / `MapArc` — drop it inside `<MapCanvas>` (typically with `blank`)\n * for choropleths and region/data maps. For full control over expressions and\n * multiple layers, manage layers directly via `useMap()` instead.\n */\nexport function MapGeoJSON<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties>({\n data,\n id: propId,\n promoteId,\n fillPaint,\n linePaint,\n fillHoverPaint,\n onClick,\n onHover,\n interactive = false,\n beforeId,\n}: MapGeoJSONProps<P>) {\n const { map, isLoaded } = useMap();\n const autoId = useId();\n const id = propId ?? autoId;\n const sourceId = `geojson-source-${id}`;\n const fillLayerId = `geojson-fill-${id}`;\n const lineLayerId = `geojson-line-${id}`;\n\n // Theme-driven neutral defaults: landmass = the mid-neutral `--border` rung,\n // separators = the page surface. Both re-resolve on theme change.\n const defaultFill = useTokenColor(\"--border\");\n const defaultLine = useTokenColor(\"--background\");\n\n const showFill = fillPaint !== false;\n const showLine = linePaint !== false;\n\n const mergedFillPaint = useMemo(\n () => mergeHoverPaint({ \"fill-color\": defaultFill, ...(fillPaint || {}) }, fillHoverPaint),\n [defaultFill, fillPaint, fillHoverPaint],\n );\n const mergedLinePaint = useMemo(\n () => ({\n \"line-color\": defaultLine,\n \"line-width\": 0.5,\n ...(linePaint || {}),\n }),\n [defaultLine, linePaint],\n );\n const latestRef = useRef({ onClick, onHover });\n latestRef.current = { onClick, onHover };\n\n // Add source on mount.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data,\n ...(promoteId ? { promoteId } : {}),\n });\n\n return () => {\n try {\n if (map.getLayer(lineLayerId)) map.removeLayer(lineLayerId);\n if (map.getLayer(fillLayerId)) map.removeLayer(fillLayerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n } catch {\n // style may be mid-reload\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- source created once per map; data/layers are synced by the effects below\n }, [isLoaded, map]);\n\n // Sync data when it changes.\n useEffect(() => {\n if (!isLoaded || !map) return;\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource | undefined;\n source?.setData(data as never);\n }, [isLoaded, map, data, sourceId]);\n\n // Sync layers and paint when visibility or styling changes.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n const source = map.getSource(sourceId);\n if (!source) return;\n\n if (showFill && !map.getLayer(fillLayerId)) {\n map.addLayer(\n {\n id: fillLayerId,\n type: \"fill\",\n source: sourceId,\n paint: mergedFillPaint,\n },\n beforeId,\n );\n } else if (!showFill && map.getLayer(fillLayerId)) {\n map.removeLayer(fillLayerId);\n }\n\n if (showLine && !map.getLayer(lineLayerId)) {\n map.addLayer(\n {\n id: lineLayerId,\n type: \"line\",\n source: sourceId,\n paint: mergedLinePaint,\n },\n beforeId,\n );\n } else if (!showLine && map.getLayer(lineLayerId)) {\n map.removeLayer(lineLayerId);\n }\n\n if (showFill && map.getLayer(fillLayerId)) {\n for (const [key, value] of Object.entries(mergedFillPaint)) {\n map.setPaintProperty(fillLayerId, key as keyof MapFillPaint, value as never);\n }\n }\n if (showLine && map.getLayer(lineLayerId)) {\n for (const [key, value] of Object.entries(mergedLinePaint)) {\n map.setPaintProperty(lineLayerId, key as keyof MapLinePaint, value as never);\n }\n }\n }, [\n isLoaded,\n map,\n sourceId,\n fillLayerId,\n lineLayerId,\n showFill,\n showLine,\n mergedFillPaint,\n mergedLinePaint,\n beforeId,\n ]);\n\n // Interaction handlers (bound to the fill layer).\n useEffect(() => {\n if (!isLoaded || !map || !interactive || !showFill) return;\n\n let hoveredId: string | number | null = null;\n\n const setHover = (next: string | number | null) => {\n if (next === hoveredId) return;\n const sourceExists = !!map.getSource(sourceId);\n if (hoveredId != null && sourceExists) {\n map.setFeatureState({ source: sourceId, id: hoveredId }, { hover: false });\n }\n hoveredId = next;\n if (next != null && sourceExists) {\n map.setFeatureState({ source: sourceId, id: next }, { hover: true });\n }\n };\n\n const handleMouseMove = (e: MapLibreGL.MapLayerMouseEvent) => {\n const feature = e.features?.[0];\n if (!feature) return;\n map.getCanvas().style.cursor = \"pointer\";\n\n const featureId = feature.id;\n if (featureId === hoveredId) return;\n setHover(featureId ?? null);\n latestRef.current.onHover?.({\n feature: feature as unknown as MapGeoJSONFeature<P>,\n longitude: e.lngLat.lng,\n latitude: e.lngLat.lat,\n originalEvent: e,\n });\n };\n\n const handleMouseLeave = () => {\n setHover(null);\n map.getCanvas().style.cursor = \"\";\n latestRef.current.onHover?.(null);\n };\n\n const handleClick = (e: MapLibreGL.MapLayerMouseEvent) => {\n const feature = e.features?.[0];\n if (!feature) return;\n latestRef.current.onClick?.({\n feature: feature as unknown as MapGeoJSONFeature<P>,\n longitude: e.lngLat.lng,\n latitude: e.lngLat.lat,\n originalEvent: e,\n });\n };\n\n map.on(\"mousemove\", fillLayerId, handleMouseMove);\n map.on(\"mouseleave\", fillLayerId, handleMouseLeave);\n map.on(\"click\", fillLayerId, handleClick);\n\n return () => {\n map.off(\"mousemove\", fillLayerId, handleMouseMove);\n map.off(\"mouseleave\", fillLayerId, handleMouseLeave);\n map.off(\"click\", fillLayerId, handleClick);\n setHover(null);\n map.getCanvas().style.cursor = \"\";\n };\n }, [isLoaded, map, fillLayerId, sourceId, interactive, showFill]);\n\n return null;\n}\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { useEffect, useId, useMemo } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport { useTokenColor } from \"../lib/use-token-color\";\n\nexport type MapClusterLayerProps<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> =\n {\n /** GeoJSON FeatureCollection data or a URL to fetch GeoJSON from. */\n data: string | GeoJSON.FeatureCollection<GeoJSON.Point, P>;\n /** Maximum zoom level to cluster points on (default: 14). */\n clusterMaxZoom?: number;\n /** Radius of each cluster when clustering points, in pixels (default: 50). */\n clusterRadius?: number;\n /**\n * Colors for cluster circles: [small, medium, large] based on point count.\n * Defaults to the theme's `--success`/`--warning`/`--destructive` tokens.\n */\n clusterColors?: [string, string, string];\n /** Point-count thresholds for the color/size steps: [medium, large] (default: [100, 750]). */\n clusterThresholds?: [number, number];\n /** Color for unclustered individual points. Defaults to the theme's `--primary` token. */\n pointColor?: string;\n /** Callback when an unclustered point is clicked. */\n onPointClick?: (\n feature: GeoJSON.Feature<GeoJSON.Point, P>,\n coordinates: [number, number],\n ) => void;\n /** Callback when a cluster is clicked. If not provided, zooms into the cluster. */\n onClusterClick?: (clusterId: number, coordinates: [number, number], pointCount: number) => void;\n };\n\nconst DEFAULT_CLUSTER_THRESHOLDS: [number, number] = [100, 750];\n\n/**\n * Clustered point rendering for large point datasets. Cluster circles step\n * through the status tokens (success → warning → destructive) as the point\n * count grows; strokes and count labels use the page surface for contrast.\n */\nexport function MapClusterLayer<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties>({\n data,\n clusterMaxZoom = 14,\n clusterRadius = 50,\n clusterColors,\n clusterThresholds = DEFAULT_CLUSTER_THRESHOLDS,\n pointColor,\n onPointClick,\n onClusterClick,\n}: MapClusterLayerProps<P>) {\n const { map, isLoaded } = useMap();\n const id = useId();\n const sourceId = `cluster-source-${id}`;\n const clusterLayerId = `clusters-${id}`;\n const clusterCountLayerId = `cluster-count-${id}`;\n const unclusteredLayerId = `unclustered-point-${id}`;\n\n const success = useTokenColor(\"--success\");\n const warning = useTokenColor(\"--warning\");\n const destructive = useTokenColor(\"--destructive\");\n const primary = useTokenColor(\"--primary\");\n const surface = useTokenColor(\"--background\");\n\n const resolvedClusterColors = useMemo<[string, string, string]>(\n () => clusterColors ?? [success, warning, destructive],\n [clusterColors, success, warning, destructive],\n );\n const resolvedPointColor = pointColor ?? primary;\n\n // Add source and layers on mount.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data,\n cluster: true,\n clusterMaxZoom,\n clusterRadius,\n });\n\n map.addLayer({\n id: clusterLayerId,\n type: \"circle\",\n source: sourceId,\n filter: [\"has\", \"point_count\"],\n paint: {\n \"circle-color\": [\n \"step\",\n [\"get\", \"point_count\"],\n resolvedClusterColors[0],\n clusterThresholds[0],\n resolvedClusterColors[1],\n clusterThresholds[1],\n resolvedClusterColors[2],\n ],\n \"circle-radius\": [\n \"step\",\n [\"get\", \"point_count\"],\n 20,\n clusterThresholds[0],\n 30,\n clusterThresholds[1],\n 40,\n ],\n \"circle-stroke-width\": 1,\n \"circle-stroke-color\": surface,\n \"circle-opacity\": 0.85,\n },\n });\n\n map.addLayer({\n id: clusterCountLayerId,\n type: \"symbol\",\n source: sourceId,\n filter: [\"has\", \"point_count\"],\n layout: {\n \"text-field\": \"{point_count_abbreviated}\",\n \"text-font\": [\"Open Sans\"],\n \"text-size\": 12,\n },\n paint: {\n \"text-color\": surface,\n },\n });\n\n map.addLayer({\n id: unclusteredLayerId,\n type: \"circle\",\n source: sourceId,\n filter: [\"!\", [\"has\", \"point_count\"]],\n paint: {\n \"circle-color\": resolvedPointColor,\n \"circle-radius\": 5,\n \"circle-stroke-width\": 2,\n \"circle-stroke-color\": surface,\n },\n });\n\n return () => {\n try {\n if (map.getLayer(clusterCountLayerId)) map.removeLayer(clusterCountLayerId);\n if (map.getLayer(unclusteredLayerId)) map.removeLayer(unclusteredLayerId);\n if (map.getLayer(clusterLayerId)) map.removeLayer(clusterLayerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n } catch {\n // style may be mid-reload\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- source/layers created once per map; data + paint are synced by the effects below\n }, [isLoaded, map, sourceId]);\n\n // Update source data when the data prop changes (only for non-URL data).\n useEffect(() => {\n if (!isLoaded || !map || typeof data === \"string\") return;\n\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource;\n if (source) {\n source.setData(data);\n }\n }, [isLoaded, map, data, sourceId]);\n\n // Sync layer styles when props (or the resolved theme colors) change.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n if (map.getLayer(clusterLayerId)) {\n map.setPaintProperty(clusterLayerId, \"circle-color\", [\n \"step\",\n [\"get\", \"point_count\"],\n resolvedClusterColors[0],\n clusterThresholds[0],\n resolvedClusterColors[1],\n clusterThresholds[1],\n resolvedClusterColors[2],\n ]);\n map.setPaintProperty(clusterLayerId, \"circle-radius\", [\n \"step\",\n [\"get\", \"point_count\"],\n 20,\n clusterThresholds[0],\n 30,\n clusterThresholds[1],\n 40,\n ]);\n map.setPaintProperty(clusterLayerId, \"circle-stroke-color\", surface);\n }\n\n if (map.getLayer(clusterCountLayerId)) {\n map.setPaintProperty(clusterCountLayerId, \"text-color\", surface);\n }\n\n if (map.getLayer(unclusteredLayerId)) {\n map.setPaintProperty(unclusteredLayerId, \"circle-color\", resolvedPointColor);\n map.setPaintProperty(unclusteredLayerId, \"circle-stroke-color\", surface);\n }\n }, [\n isLoaded,\n map,\n clusterLayerId,\n clusterCountLayerId,\n unclusteredLayerId,\n resolvedClusterColors,\n clusterThresholds,\n resolvedPointColor,\n surface,\n ]);\n\n // Handle click events.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n // Cluster click handler — zoom into the cluster by default.\n const handleClusterClick = async (e: MapLibreGL.MapMouseEvent) => {\n const features = map.queryRenderedFeatures(e.point, {\n layers: [clusterLayerId],\n });\n const feature = features[0];\n if (!feature) return;\n const clusterId = feature.properties?.cluster_id as number;\n const pointCount = feature.properties?.point_count as number;\n const coordinates = (feature.geometry as GeoJSON.Point).coordinates as [number, number];\n\n if (onClusterClick) {\n onClusterClick(clusterId, coordinates, pointCount);\n } else {\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource;\n const zoom = await source.getClusterExpansionZoom(clusterId);\n map.easeTo({\n center: coordinates,\n zoom,\n });\n }\n };\n\n // Unclustered point click handler.\n const handlePointClick = (\n e: MapLibreGL.MapMouseEvent & {\n features?: MapLibreGL.MapGeoJSONFeature[];\n },\n ) => {\n const feature = e.features?.[0];\n if (!onPointClick || !feature) return;\n const coordinates = (feature.geometry as GeoJSON.Point).coordinates.slice() as [\n number,\n number,\n ];\n\n // Handle world copies.\n while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {\n coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;\n }\n\n onPointClick(feature as unknown as GeoJSON.Feature<GeoJSON.Point, P>, coordinates);\n };\n\n // Cursor style handlers.\n const handleMouseEnterCluster = () => {\n map.getCanvas().style.cursor = \"pointer\";\n };\n const handleMouseLeaveCluster = () => {\n map.getCanvas().style.cursor = \"\";\n };\n const handleMouseEnterPoint = () => {\n if (onPointClick) {\n map.getCanvas().style.cursor = \"pointer\";\n }\n };\n const handleMouseLeavePoint = () => {\n map.getCanvas().style.cursor = \"\";\n };\n\n map.on(\"click\", clusterLayerId, handleClusterClick);\n map.on(\"click\", unclusteredLayerId, handlePointClick);\n map.on(\"mouseenter\", clusterLayerId, handleMouseEnterCluster);\n map.on(\"mouseleave\", clusterLayerId, handleMouseLeaveCluster);\n map.on(\"mouseenter\", unclusteredLayerId, handleMouseEnterPoint);\n map.on(\"mouseleave\", unclusteredLayerId, handleMouseLeavePoint);\n\n return () => {\n map.off(\"click\", clusterLayerId, handleClusterClick);\n map.off(\"click\", unclusteredLayerId, handlePointClick);\n map.off(\"mouseenter\", clusterLayerId, handleMouseEnterCluster);\n map.off(\"mouseleave\", clusterLayerId, handleMouseLeaveCluster);\n map.off(\"mouseenter\", unclusteredLayerId, handleMouseEnterPoint);\n map.off(\"mouseleave\", unclusteredLayerId, handleMouseLeavePoint);\n };\n }, [isLoaded, map, clusterLayerId, unclusteredLayerId, sourceId, onClusterClick, onPointClick]);\n\n return null;\n}\n"],"mappings":"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","names":["useEffect","useState","MapCanvas","useState","useEffect","MapLibreGL","createContext","use","useEffect","useMemo","useRef","cn","jsx","jsxs","createContext","use","useRef","useMemo","MapLibreGL","useEffect","cn","positionClasses","MapLibreGL","useEffect","useMemo","useRef","createPortal","X","cn","jsx","jsxs","useRef","useMemo","MapLibreGL","useEffect","createPortal","cn","X","useCallback","useEffect","useRef","useState","Spinner","cn","jsx","jsxs","cn","useState","useCallback","Spinner","useRef","useEffect","useEffect","useState","useState","useEffect","useEffect","useId","useMemo","useRef","useId","useMemo","useRef","useEffect","useEffect","useId","useMemo","useRef","useId","useMemo","useRef","useEffect","useEffect","useId","useMemo","useId","useMemo","useEffect"]}
1
+ {"version":3,"sources":["../src/map-canvas/map-canvas.tsx","../src/map-canvas/map-context.ts","../src/map-canvas/use-resolved-basemap-theme.ts","../src/map-marker/map-marker.tsx","../src/map-popup/map-popup.tsx","../src/map-controls/map-controls.tsx","../src/map-route/map-route.tsx","../src/lib/use-token-color.ts","../src/map-arc/map-arc.tsx","../src/lib/arc-math.ts","../src/lib/merge-hover-paint.ts","../src/map-geojson/map-geojson.tsx","../src/map-cluster-layer/map-cluster-layer.tsx"],"sourcesContent":["\"use client\";\n\nimport MapLibreGL from \"maplibre-gl\";\nimport \"maplibre-gl/dist/maplibre-gl.css\";\nimport \"./maps.css\";\nimport {\n forwardRef,\n useCallback,\n useEffect,\n useImperativeHandle,\n useMemo,\n useRef,\n useState,\n type ReactNode,\n} from \"react\";\nimport { Spinner, StatePanel, useLocale } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nimport { MapContext, type BasemapTheme } from \"./map-context\";\nimport { useResolvedBasemapTheme } from \"./use-resolved-basemap-theme\";\n\n/**\n * Default basemaps: Carto's free light/dark GL styles. These serve ODbL-licensed\n * OpenStreetMap data, which requires attribution on a public surface — see the\n * `attributionControl` note in the map constructor below.\n */\nconst defaultStyles = {\n dark: \"https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json\",\n light: \"https://basemaps.cartocdn.com/gl/positron-gl-style/style.json\",\n};\n\n// A tile-less, dependency-free style with a transparent background. Use it for\n// data visualizations (choropleths, arcs, dot maps) where you draw your own\n// layers and don't need a street basemap: `<MapCanvas blank>`. The transparent\n// background lets the themed container show through.\nconst blankMapStyle: MapLibreGL.StyleSpecification = {\n version: 8,\n sources: {},\n layers: [\n {\n id: \"background\",\n type: \"background\",\n paint: { \"background-color\": \"transparent\" },\n },\n ],\n};\n\n/** Map viewport state. */\nexport interface MapViewport {\n /** Center coordinates [longitude, latitude]. */\n center: [number, number];\n /** Zoom level. */\n zoom: number;\n /** Bearing (rotation) in degrees. */\n bearing: number;\n /** Pitch (tilt) in degrees. */\n pitch: number;\n}\n\nexport type MapStyleOption = string | MapLibreGL.StyleSpecification;\n\n/** The imperative handle exposed by `<MapCanvas ref>`: the MapLibre map itself. */\nexport type MapCanvasRef = MapLibreGL.Map;\n\nexport type MapCanvasProps = {\n children?: ReactNode;\n /** Additional CSS classes for the map container. */\n className?: string;\n /**\n * Basemap flavor. If not provided, it is derived from the active brand theme\n * (`data-theme` + that theme's own `color-scheme`), then a `dark`/`light` root class,\n * then the OS preference.\n */\n theme?: BasemapTheme;\n /** Custom map styles for light and dark themes. Overrides the default Carto styles. */\n styles?: {\n light?: MapStyleOption;\n dark?: MapStyleOption;\n };\n /**\n * Use a transparent, tile-less basemap instead of the default Carto street\n * basemap — a blank canvas. Used alone it renders nothing; add your own\n * layers on top (`<MapGeoJSON>`, `<MapArc>`, markers, …). Ideal for data\n * visualizations. Ignored when an explicit `styles` prop is provided.\n */\n blank?: boolean;\n /** Map projection type. Use `{ type: \"globe\" }` for a 3D globe view. */\n projection?: MapLibreGL.ProjectionSpecification;\n /**\n * Controlled viewport. When provided together with `onViewportChange`, the\n * map becomes controlled and the viewport is driven by this prop.\n */\n viewport?: Partial<MapViewport>;\n /**\n * Callback fired continuously as the viewport changes (pan, zoom, rotate,\n * pitch). Use standalone to observe changes, or with `viewport` for\n * controlled mode.\n */\n onViewportChange?: (viewport: MapViewport) => void;\n /** Show a loading overlay on the map (e.g. while the app fetches map data). */\n loading?: boolean;\n} & Omit<MapLibreGL.MapOptions, \"container\" | \"style\">;\n\nfunction MapLoadingOverlay() {\n return (\n <div className=\"absolute inset-0 z-10 flex items-center justify-center bg-background/50 backdrop-blur-xs\">\n <Spinner label=\"Loading map\" className=\"size-5\" />\n </div>\n );\n}\n\nfunction getViewport(map: MapLibreGL.Map): MapViewport {\n const center = map.getCenter();\n return {\n center: [center.lng, center.lat],\n zoom: map.getZoom(),\n bearing: map.getBearing(),\n pitch: map.getPitch(),\n };\n}\n\n/**\n * The root map surface — a token/theme-aware MapLibre GL canvas. Compose the\n * other `@elabs-ai/components-maps` components (markers, popups, controls, layers) as\n * children; they reach the map through context (`useMap`).\n *\n * The ref exposes the raw MapLibre `Map` instance for imperative work\n * (`flyTo`, `fitBounds`, …).\n */\nexport const MapCanvas = forwardRef<MapCanvasRef, MapCanvasProps>(function MapCanvas(\n {\n children,\n className,\n theme: themeProp,\n styles,\n blank = false,\n projection,\n viewport,\n onViewportChange,\n loading = false,\n ...props\n },\n ref,\n) {\n const { t } = useLocale();\n const containerRef = useRef<HTMLDivElement>(null);\n const [mapInstance, setMapInstance] = useState<MapLibreGL.Map | null>(null);\n const [initFailed, setInitFailed] = useState(false);\n const [isLoaded, setIsLoaded] = useState(false);\n const [isStyleLoaded, setIsStyleLoaded] = useState(false);\n const currentStyleRef = useRef<MapStyleOption | null>(null);\n const styleTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n const internalUpdateRef = useRef(false);\n const { resolvedTheme, themeKey } = useResolvedBasemapTheme(themeProp);\n\n const isControlled = viewport !== undefined && onViewportChange !== undefined;\n\n const onViewportChangeRef = useRef(onViewportChange);\n onViewportChangeRef.current = onViewportChange;\n\n // Read from the mount-only `styledata` handler below so a `projection` prop\n // change after mount isn't reapplied with the value captured at mount time.\n const projectionRef = useRef(projection);\n projectionRef.current = projection;\n\n const mapStyles = useMemo(() => {\n // Explicit styles win. Otherwise `blank` opts into the transparent\n // tile-less basemap; with neither, fall back to the Carto defaults.\n if (styles) {\n return {\n dark: styles.dark ?? defaultStyles.dark,\n light: styles.light ?? defaultStyles.light,\n };\n }\n if (blank) {\n return { dark: blankMapStyle, light: blankMapStyle };\n }\n return defaultStyles;\n }, [styles, blank]);\n\n // Expose the map instance to the parent component.\n useImperativeHandle(ref, () => mapInstance as MapLibreGL.Map, [mapInstance]);\n\n const clearStyleTimeout = useCallback(() => {\n if (styleTimeoutRef.current) {\n clearTimeout(styleTimeoutRef.current);\n styleTimeoutRef.current = null;\n }\n }, []);\n\n // Initialize the map.\n useEffect(() => {\n if (!containerRef.current) return;\n\n const initialStyle = resolvedTheme === \"dark\" ? mapStyles.dark : mapStyles.light;\n currentStyleRef.current = initialStyle;\n\n let map: MapLibreGL.Map;\n try {\n map = new MapLibreGL.Map({\n container: containerRef.current,\n style: initialStyle,\n renderWorldCopies: false,\n // Attribution control OFF by default — a maintainer decision for internal\n // use, taken deliberately and recorded in CHANGELOG.md + the map-components\n // rule. NOTE THE CONSTRAINT: the default Carto basemap serves OpenStreetMap\n // data, which is ODbL-licensed and requires the credit, and Carto's terms\n // require it too — so a surface that ships PUBLICLY on these tiles must turn\n // it back on with `attributionControl={{ compact: true }}` (it wins through\n // `...props` below), or move to tiles licensed without the requirement via\n // `styles` / `blank`.\n attributionControl: false,\n ...props,\n ...viewport,\n });\n } catch {\n // MapLibre throws at construction when WebGL is unavailable (headless\n // browsers without GPU, remote desktops). Degrade to a quiet panel\n // instead of an unhandled render error.\n setInitFailed(true);\n return;\n }\n\n const styleDataHandler = () => {\n clearStyleTimeout();\n // Delay so the style is fully processed before layer operations — avoids\n // race conditions on setStyle without force-updating every layer.\n styleTimeoutRef.current = setTimeout(() => {\n setIsStyleLoaded(true);\n if (projectionRef.current) {\n map.setProjection(projectionRef.current);\n }\n }, 100);\n };\n // No-op under the default (`attributionControl: false` above). This exists for\n // the case a consumer turns the control back ON for a public surface: MapLibre\n // paints even a COMPACT control expanded on first render (`<details open>` +\n // `maplibregl-compact-show`), so the credits land as a text slab until someone\n // clicks the toggle. Collapse it to the labelled ⓘ button instead.\n const loadHandler = () => {\n setIsLoaded(true);\n map\n .getContainer()\n .querySelectorAll<HTMLDetailsElement>(\"details.maplibregl-ctrl-attrib[open]\")\n .forEach((el) => {\n el.open = false;\n });\n };\n\n // Viewport change handler — skip if triggered by an internal update.\n const handleMove = () => {\n if (internalUpdateRef.current) return;\n onViewportChangeRef.current?.(getViewport(map));\n };\n\n map.on(\"load\", loadHandler);\n map.on(\"styledata\", styleDataHandler);\n map.on(\"move\", handleMove);\n setMapInstance(map);\n\n return () => {\n clearStyleTimeout();\n map.off(\"load\", loadHandler);\n map.off(\"styledata\", styleDataHandler);\n map.off(\"move\", handleMove);\n map.remove();\n setIsLoaded(false);\n setIsStyleLoaded(false);\n setMapInstance(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- mount-only: map options are init-time; style/theme/projection changes are synced by the effects below\n }, []);\n\n // Sync controlled viewport to the map.\n useEffect(() => {\n if (!mapInstance || !isControlled || !viewport) return;\n if (mapInstance.isMoving()) return;\n\n const current = getViewport(mapInstance);\n const next = {\n center: viewport.center ?? current.center,\n zoom: viewport.zoom ?? current.zoom,\n bearing: viewport.bearing ?? current.bearing,\n pitch: viewport.pitch ?? current.pitch,\n };\n\n if (\n next.center[0] === current.center[0] &&\n next.center[1] === current.center[1] &&\n next.zoom === current.zoom &&\n next.bearing === current.bearing &&\n next.pitch === current.pitch\n ) {\n return;\n }\n\n internalUpdateRef.current = true;\n mapInstance.jumpTo(next);\n internalUpdateRef.current = false;\n }, [mapInstance, isControlled, viewport]);\n\n // Swap the basemap style when the theme flips.\n useEffect(() => {\n if (!mapInstance || !resolvedTheme) return;\n\n const newStyle = resolvedTheme === \"dark\" ? mapStyles.dark : mapStyles.light;\n\n if (currentStyleRef.current === newStyle) return;\n\n clearStyleTimeout();\n currentStyleRef.current = newStyle;\n setIsStyleLoaded(false);\n\n mapInstance.setStyle(newStyle, { diff: true });\n }, [mapInstance, resolvedTheme, mapStyles, clearStyleTimeout]);\n\n // Sync projection when the prop changes after mount.\n useEffect(() => {\n if (!mapInstance || !isStyleLoaded || !projection) return;\n mapInstance.setProjection(projection);\n }, [mapInstance, isStyleLoaded, projection]);\n\n const contextValue = useMemo(\n () => ({\n map: mapInstance,\n isLoaded: isLoaded && isStyleLoaded,\n resolvedTheme,\n themeKey,\n }),\n [mapInstance, isLoaded, isStyleLoaded, resolvedTheme, themeKey],\n );\n\n if (initFailed) {\n return (\n <div className={cn(\"relative h-full w-full\", className)}>\n <StatePanel\n kind=\"error\"\n title={t(\"maps.canvas.unavailableTitle\")}\n description={t(\"maps.canvas.unavailableDescription\")}\n className=\"h-full\"\n />\n </div>\n );\n }\n\n return (\n <MapContext.Provider value={contextValue}>\n <div ref={containerRef} className={cn(\"relative h-full w-full\", className)}>\n {(!isLoaded || loading) && <MapLoadingOverlay />}\n {/* SSR-safe: children render only when the map exists on the client. */}\n {mapInstance && children}\n </div>\n </MapContext.Provider>\n );\n});\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { createContext, use } from \"react\";\n\n/** Light-or-dark flavor of the active basemap (derived from the brand theme). */\nexport type BasemapTheme = \"light\" | \"dark\";\n\nexport interface MapContextValue {\n /** The live MapLibre map instance (`null` until the map has mounted). */\n map: MapLibreGL.Map | null;\n /** True once the map AND its style are fully loaded — gate layer operations on this. */\n isLoaded: boolean;\n /** Which basemap flavor is active. */\n resolvedTheme: BasemapTheme;\n /**\n * Changes whenever the active brand theme changes. Layer components use it\n * as a dependency key to re-resolve semantic token colors for WebGL paint.\n */\n themeKey: string;\n}\n\nexport const MapContext = createContext<MapContextValue | null>(null);\n\n/** Access the map instance + load state from any descendant of `<MapCanvas>`. */\nexport function useMap(): MapContextValue {\n const context = use(MapContext);\n if (!context) {\n throw new Error(\"useMap must be used within a <MapCanvas>\");\n }\n return context;\n}\n","\"use client\";\n\nimport { resolveThemeIsDark } from \"@elabs-ai/components-tokens\";\nimport { useEffect, useState } from \"react\";\n\nimport type { BasemapTheme } from \"./map-context\";\n\n/**\n * Brand theme → basemap flavor. When a `data-theme` is set (the ThemeProvider\n * contract) the theme's own `color-scheme` is authoritative via\n * `resolveThemeIsDark` — which is why a CONSUMER-AUTHORED dark theme gets the\n * dark basemap without registering anything here (ADR 0029; the old\n * `THEME_META[theme].dark` lookup only knew the themes this repo ships). A\n * `dark`/`light` class on `<html>` is honored for non-brand hosts (next-themes\n * et al.); the OS preference is only the last-resort fallback.\n */\nfunction getBrandTheme(): BasemapTheme | null {\n if (typeof document === \"undefined\") return null;\n const root = document.documentElement;\n if (root.getAttribute(\"data-theme\")) return resolveThemeIsDark(root) ? \"dark\" : \"light\";\n if (root.classList.contains(\"dark\")) return \"dark\";\n if (root.classList.contains(\"light\")) return \"light\";\n return null;\n}\n\nfunction getSystemTheme(): BasemapTheme {\n if (typeof window === \"undefined\" || typeof window.matchMedia !== \"function\") return \"light\";\n return window.matchMedia(\"(prefers-color-scheme: dark)\").matches ? \"dark\" : \"light\";\n}\n\nfunction getThemeAttribute(): string {\n if (typeof document === \"undefined\") return \"\";\n return document.documentElement.getAttribute(\"data-theme\") ?? \"\";\n}\n\n/**\n * Resolve the basemap flavor and a change-key for the active brand theme.\n * The observer stays active even with an explicit `theme` prop: token colors\n * (routes, clusters, …) must still re-resolve when `data-theme` changes.\n */\nexport function useResolvedBasemapTheme(themeProp?: BasemapTheme): {\n resolvedTheme: BasemapTheme;\n themeKey: string;\n} {\n const [detected, setDetected] = useState<BasemapTheme>(() => getBrandTheme() ?? getSystemTheme());\n const [themeAttr, setThemeAttr] = useState(getThemeAttribute);\n\n useEffect(() => {\n const update = () => {\n setThemeAttr(getThemeAttribute());\n const brand = getBrandTheme();\n if (brand) setDetected(brand);\n };\n const observer = new MutationObserver(update);\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: [\"data-theme\", \"class\"],\n });\n\n const mediaQuery =\n typeof window.matchMedia === \"function\"\n ? window.matchMedia(\"(prefers-color-scheme: dark)\")\n : null;\n const handleSystemChange = (e: MediaQueryListEvent) => {\n if (!getBrandTheme()) setDetected(e.matches ? \"dark\" : \"light\");\n };\n mediaQuery?.addEventListener(\"change\", handleSystemChange);\n\n return () => {\n observer.disconnect();\n mediaQuery?.removeEventListener(\"change\", handleSystemChange);\n };\n }, []);\n\n const resolvedTheme = themeProp ?? detected;\n return { resolvedTheme, themeKey: `${themeAttr}:${resolvedTheme}` };\n}\n","\"use client\";\n\nimport MapLibreGL, { type MarkerOptions, type PopupOptions } from \"maplibre-gl\";\nimport { createContext, use, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { useLocale } from \"@elabs-ai/components-ui\";\n\nimport { useMap } from \"../map-canvas/map-context\";\n\ntype MarkerContextValue = {\n marker: MapLibreGL.Marker;\n map: MapLibreGL.Map | null;\n};\n\nconst MarkerContext = createContext<MarkerContextValue | null>(null);\n\nfunction useMarkerContext() {\n const context = use(MarkerContext);\n if (!context) {\n throw new Error(\"MapMarker sub-components must be used within <MapMarker>\");\n }\n return context;\n}\n\nexport type MapMarkerProps = {\n /** Longitude coordinate for the marker position. */\n longitude: number;\n /** Latitude coordinate for the marker position. */\n latitude: number;\n /** Marker sub-components (MapMarkerContent, MapMarkerPopup, MapMarkerTooltip, MapMarkerLabel). */\n children: ReactNode;\n /** Callback when the marker is clicked. */\n onClick?: (e: MouseEvent) => void;\n /** Callback when the mouse enters the marker. */\n onMouseEnter?: (e: MouseEvent) => void;\n /** Callback when the mouse leaves the marker. */\n onMouseLeave?: (e: MouseEvent) => void;\n /** Callback when a drag starts (requires `draggable`). */\n onDragStart?: (lngLat: { lng: number; lat: number }) => void;\n /** Callback during a drag (requires `draggable`). */\n onDrag?: (lngLat: { lng: number; lat: number }) => void;\n /** Callback when a drag ends (requires `draggable`). */\n onDragEnd?: (lngLat: { lng: number; lat: number }) => void;\n} & Omit<MarkerOptions, \"element\">;\n\n/**\n * A marker anchored at a lng/lat. Compose the pieces you need:\n * `MapMarkerContent` (the visual), `MapMarkerLabel`, `MapMarkerPopup` (opens\n * on click) and `MapMarkerTooltip` (shows on hover).\n */\nexport function MapMarker({\n longitude,\n latitude,\n children,\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n draggable = false,\n ...markerOptions\n}: MapMarkerProps) {\n const { map } = useMap();\n const [marker, setMarker] = useState<MapLibreGL.Marker | null>(null);\n\n const callbacksRef = useRef({\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n });\n callbacksRef.current = {\n onClick,\n onMouseEnter,\n onMouseLeave,\n onDragStart,\n onDrag,\n onDragEnd,\n };\n\n const { anchor, className, offset, rotation, rotationAlignment, pitchAlignment } = markerOptions;\n\n // `longitude`/`latitude`/`draggable` at construction time only — the sync\n // effect below corrects them on the very next commit, and are read from a\n // ref here so they don't force a rebuild on every position change (see the\n // `anchor`/`className` note below for what SHOULD force one).\n const initialRef = useRef({ longitude, latitude, draggable });\n initialRef.current = { longitude, latitude, draggable };\n\n // Builds (and tears down) the actual MapLibre `Marker` instance. This is a\n // real `useEffect`, never `useMemo`: `useMemo`'s initializer runs during\n // RENDER — including on the server, where `document` doesn't exist — and\n // React offers no guarantee it runs only once (it may discard and recompute\n // a memoized value), which would silently leak marker instances/listeners.\n //\n // `anchor` and `className` have no MapLibre setter (`Marker` bakes both into\n // its constructor), so they are the only options that must REBUILD the\n // instance; every other option (position, draggable, offset, rotation,\n // alignment) has a live setter and is kept in sync by the effect further\n // below without ever recreating the marker.\n useEffect(() => {\n const markerInstance = new MapLibreGL.Marker({\n anchor,\n className,\n element: document.createElement(\"div\"),\n draggable: initialRef.current.draggable,\n }).setLngLat([initialRef.current.longitude, initialRef.current.latitude]);\n\n const handleClick = (e: MouseEvent) => callbacksRef.current.onClick?.(e);\n const handleMouseEnter = (e: MouseEvent) => callbacksRef.current.onMouseEnter?.(e);\n const handleMouseLeave = (e: MouseEvent) => callbacksRef.current.onMouseLeave?.(e);\n\n markerInstance.getElement()?.addEventListener(\"click\", handleClick);\n markerInstance.getElement()?.addEventListener(\"mouseenter\", handleMouseEnter);\n markerInstance.getElement()?.addEventListener(\"mouseleave\", handleMouseLeave);\n\n const handleDragStart = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDragStart?.({ lng: lngLat.lng, lat: lngLat.lat });\n };\n const handleDrag = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDrag?.({ lng: lngLat.lng, lat: lngLat.lat });\n };\n const handleDragEnd = () => {\n const lngLat = markerInstance.getLngLat();\n callbacksRef.current.onDragEnd?.({ lng: lngLat.lng, lat: lngLat.lat });\n };\n\n markerInstance.on(\"dragstart\", handleDragStart);\n markerInstance.on(\"drag\", handleDrag);\n markerInstance.on(\"dragend\", handleDragEnd);\n\n setMarker(markerInstance);\n }, [anchor, className]);\n\n // Attaches/detaches the CURRENT marker instance to the map — its own effect\n // so both a `map` change and an `anchor`/`className`-driven rebuild above\n // are handled the same way, without a double `remove()`.\n useEffect(() => {\n if (!map || !marker) return;\n\n marker.addTo(map);\n\n return () => {\n marker.remove();\n };\n }, [map, marker]);\n\n useEffect(() => {\n if (!marker) return;\n\n const current = marker.getLngLat();\n if (current.lng !== longitude || current.lat !== latitude) {\n marker.setLngLat([longitude, latitude]);\n }\n\n if (marker.isDraggable() !== draggable) {\n marker.setDraggable(draggable);\n }\n\n const currentOffset = marker.getOffset();\n const newOffset = offset ?? [0, 0];\n const [newOffsetX, newOffsetY] = Array.isArray(newOffset)\n ? newOffset\n : [newOffset.x, newOffset.y];\n if (currentOffset.x !== newOffsetX || currentOffset.y !== newOffsetY) {\n marker.setOffset(newOffset);\n }\n\n if (marker.getRotation() !== (rotation ?? 0)) {\n marker.setRotation(rotation ?? 0);\n }\n if (marker.getRotationAlignment() !== (rotationAlignment ?? \"auto\")) {\n marker.setRotationAlignment(rotationAlignment ?? \"auto\");\n }\n if (marker.getPitchAlignment() !== (pitchAlignment ?? \"auto\")) {\n marker.setPitchAlignment(pitchAlignment ?? \"auto\");\n }\n }, [marker, longitude, latitude, draggable, offset, rotation, rotationAlignment, pitchAlignment]);\n\n // No marker yet (first client tick after mount, or the moment an\n // `anchor`/`className` rebuild is in flight) — render nothing rather than\n // a sub-component (`MapMarkerContent` et al.) reaching into a null marker,\n // and nothing at all on the server.\n if (!marker) {\n return null;\n }\n\n return <MarkerContext.Provider value={{ marker, map }}>{children}</MarkerContext.Provider>;\n}\n\nexport interface MapMarkerContentProps {\n /** Custom marker content. Defaults to a primary-colored dot. */\n children?: ReactNode;\n /** Additional CSS classes for the marker container. */\n className?: string;\n}\n\n/** The marker's visual, portaled into the MapLibre marker element. */\nexport function MapMarkerContent({ children, className }: MapMarkerContentProps) {\n const { marker } = useMarkerContext();\n\n return createPortal(\n <div className={cn(\"relative cursor-pointer\", className)}>\n {children || <DefaultMarkerIcon />}\n </div>,\n marker.getElement(),\n );\n}\n\nfunction DefaultMarkerIcon() {\n return (\n <div className=\"relative size-4 rounded-full border-2 border-background bg-primary shadow-sm\" />\n );\n}\n\nfunction PopupCloseButton({ onClick }: { onClick: () => void }) {\n const { t } = useLocale();\n return (\n <button\n type=\"button\"\n onClick={onClick}\n aria-label={t(\"maps.popup.close\")}\n className=\"absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset\"\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n );\n}\n\nexport type MapMarkerPopupProps = {\n /** Popup content. */\n children: ReactNode;\n /** Additional CSS classes for the popup container. */\n className?: string;\n /** Show a close button in the popup (default: false). */\n closeButton?: boolean;\n} & Omit<PopupOptions, \"className\" | \"closeButton\">;\n\n/** A popup attached to the marker — MapLibre toggles it on marker click. */\nexport function MapMarkerPopup({\n children,\n className,\n closeButton = false,\n ...popupOptions\n}: MapMarkerPopupProps) {\n const { marker, map } = useMarkerContext();\n const container = useMemo(() => document.createElement(\"div\"), []);\n const { offset, maxWidth } = popupOptions;\n\n const popup = useMemo(() => {\n return new MapLibreGL.Popup({\n offset: 16,\n ...popupOptions,\n closeButton: false,\n })\n .setMaxWidth(\"none\")\n .setDOMContent(container);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n popup.setDOMContent(container);\n marker.setPopup(popup);\n\n return () => {\n marker.setPopup(null);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- popup/marker/container are stable\n }, [map]);\n\n // Sync popup options when they change.\n useEffect(() => {\n popup.setOffset(offset ?? 16);\n if (maxWidth) {\n popup.setMaxWidth(maxWidth);\n }\n }, [popup, offset, maxWidth]);\n\n const handleClose = () => popup.remove();\n\n return createPortal(\n <div\n className={cn(\n \"relative max-w-62 rounded-md bg-popover p-3 text-popover-foreground shadow-ring-md\",\n \"animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance\",\n className,\n )}\n >\n {closeButton && <PopupCloseButton onClick={handleClose} />}\n {children}\n </div>,\n container,\n );\n}\n\nexport type MapMarkerTooltipProps = {\n /** Tooltip content. */\n children: ReactNode;\n /** Additional CSS classes for the tooltip container. */\n className?: string;\n} & Omit<PopupOptions, \"className\" | \"closeButton\" | \"closeOnClick\">;\n\n/** A hover tooltip attached to the marker. */\nexport function MapMarkerTooltip({ children, className, ...popupOptions }: MapMarkerTooltipProps) {\n const { marker, map } = useMarkerContext();\n const container = useMemo(() => document.createElement(\"div\"), []);\n const { offset, maxWidth } = popupOptions;\n\n const tooltip = useMemo(() => {\n return new MapLibreGL.Popup({\n offset: 16,\n ...popupOptions,\n closeOnClick: true,\n closeButton: false,\n }).setMaxWidth(\"none\");\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n tooltip.setDOMContent(container);\n\n const handleMouseEnter = () => {\n tooltip.setLngLat(marker.getLngLat()).addTo(map);\n };\n const handleMouseLeave = () => tooltip.remove();\n\n marker.getElement()?.addEventListener(\"mouseenter\", handleMouseEnter);\n marker.getElement()?.addEventListener(\"mouseleave\", handleMouseLeave);\n\n return () => {\n marker.getElement()?.removeEventListener(\"mouseenter\", handleMouseEnter);\n marker.getElement()?.removeEventListener(\"mouseleave\", handleMouseLeave);\n tooltip.remove();\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- tooltip/marker/container are stable\n }, [map]);\n\n // Sync tooltip options when they change.\n useEffect(() => {\n tooltip.setOffset(offset ?? 16);\n if (maxWidth) {\n tooltip.setMaxWidth(maxWidth);\n }\n }, [tooltip, offset, maxWidth]);\n\n return createPortal(\n <div\n className={cn(\n \"pointer-events-none rounded-md bg-foreground px-2 py-1 text-meta text-balance text-background shadow-md\",\n \"animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance\",\n className,\n )}\n >\n {children}\n </div>,\n container,\n );\n}\n\nexport interface MapMarkerLabelProps {\n /** Label text content. */\n children: ReactNode;\n /** Additional CSS classes for the label. */\n className?: string;\n /** Position of the label relative to the marker (default: \"top\"). */\n position?: \"top\" | \"bottom\";\n}\n\n/**\n * A small always-visible text label above or below the marker. Portaled into\n * the marker element so it anchors to the marker whether composed as a sibling\n * of `MapMarkerContent` or nested inside it (a cross-theme sweep caught the\n * sibling composition rendering the label against the map container instead).\n */\nexport function MapMarkerLabel({ children, className, position = \"top\" }: MapMarkerLabelProps) {\n const { marker } = useMarkerContext();\n const positionClasses = {\n top: \"bottom-full mb-1\",\n bottom: \"top-full mt-1\",\n };\n\n return createPortal(\n <div\n className={cn(\n \"absolute left-1/2 -translate-x-1/2 whitespace-nowrap\",\n \"text-meta font-medium text-foreground\",\n positionClasses[position],\n className,\n )}\n >\n {children}\n </div>,\n marker.getElement(),\n );\n}\n","\"use client\";\n\nimport MapLibreGL, { type PopupOptions } from \"maplibre-gl\";\nimport { useEffect, useMemo, useRef, type ReactNode } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { X } from \"lucide-react\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\nimport { useLocale } from \"@elabs-ai/components-ui\";\n\nimport { useMap } from \"../map-canvas/map-context\";\n\nexport type MapPopupProps = {\n /** Longitude coordinate for the popup position. */\n longitude: number;\n /** Latitude coordinate for the popup position. */\n latitude: number;\n /** Callback when the popup is closed. */\n onClose?: () => void;\n /** Popup content. */\n children: ReactNode;\n /** Additional CSS classes for the popup container. */\n className?: string;\n /** Show a close button in the popup (default: false). */\n closeButton?: boolean;\n} & Omit<PopupOptions, \"className\" | \"closeButton\">;\n\n/**\n * A standalone popup anchored at a lng/lat (not attached to a marker) —\n * typically rendered conditionally from app state (e.g. after a layer click).\n */\nexport function MapPopup({\n longitude,\n latitude,\n onClose,\n children,\n className,\n closeButton = false,\n ...popupOptions\n}: MapPopupProps) {\n const { map } = useMap();\n const { t } = useLocale();\n const onCloseRef = useRef(onClose);\n onCloseRef.current = onClose;\n const container = useMemo(() => document.createElement(\"div\"), []);\n const { offset, maxWidth } = popupOptions;\n\n const popup = useMemo(() => {\n return new MapLibreGL.Popup({\n offset: 16,\n ...popupOptions,\n closeButton: false,\n })\n .setMaxWidth(\"none\")\n .setLngLat([longitude, latitude]);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; position/options are synced by the effect below\n }, []);\n\n useEffect(() => {\n if (!map) return;\n\n const onCloseProp = () => onCloseRef.current?.();\n\n popup.on(\"close\", onCloseProp);\n\n popup.setDOMContent(container);\n popup.addTo(map);\n\n return () => {\n popup.off(\"close\", onCloseProp);\n if (popup.isOpen()) {\n popup.remove();\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- popup/container are stable\n }, [map]);\n\n // Sync popup position and options when they change.\n useEffect(() => {\n const current = popup.getLngLat();\n if (!current || current.lng !== longitude || current.lat !== latitude) {\n popup.setLngLat([longitude, latitude]);\n }\n popup.setOffset(offset ?? 16);\n if (maxWidth) {\n popup.setMaxWidth(maxWidth);\n }\n }, [popup, longitude, latitude, offset, maxWidth]);\n\n const handleClose = () => {\n popup.remove();\n };\n\n return createPortal(\n <div\n className={cn(\n \"relative max-w-62 rounded-md bg-popover p-3 text-popover-foreground shadow-ring-md\",\n \"animate-in fade-in-0 zoom-in-95 duration-fast ease-entrance\",\n className,\n )}\n >\n {closeButton && (\n <button\n type=\"button\"\n onClick={handleClose}\n aria-label={t(\"maps.popup.close\")}\n className=\"absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset\"\n >\n <X className=\"size-3.5\" aria-hidden=\"true\" />\n </button>\n )}\n {children}\n </div>,\n container,\n );\n}\n","\"use client\";\n\nimport { useCallback, useEffect, useRef, useState, type ReactNode } from \"react\";\nimport { Locate, Maximize, Minus, Plus } from \"lucide-react\";\nimport { Spinner } from \"@elabs-ai/components-ui\";\nimport { cn } from \"@elabs-ai/components-ui/lib/cn\";\n\nimport { useMap } from \"../map-canvas/map-context\";\n\nexport interface MapControlsProps {\n /** Position of the controls on the map (default: \"bottom-right\"). */\n position?: \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n /** Show zoom in/out buttons (default: true). */\n showZoom?: boolean;\n /** Show a compass button to reset bearing/pitch (default: false). */\n showCompass?: boolean;\n /** Show a locate button to fly to the user's location (default: false). */\n showLocate?: boolean;\n /** Show a fullscreen toggle button (default: false). */\n showFullscreen?: boolean;\n /** Additional CSS classes for the controls container. */\n className?: string;\n /** Callback with user coordinates when located. */\n onLocate?: (coords: { longitude: number; latitude: number }) => void;\n /** Callback when geolocation fails or is denied. */\n onLocateError?: (error: GeolocationPositionError) => void;\n}\n\n// bottom-right sits above MapLibre's attribution control — keep it visible.\nconst positionClasses = {\n \"top-left\": \"top-2 left-2\",\n \"top-right\": \"top-2 right-2\",\n \"bottom-left\": \"bottom-2 left-2\",\n \"bottom-right\": \"bottom-10 right-2\",\n};\n\nfunction ControlGroup({ children }: { children: ReactNode }) {\n return (\n <div className=\"flex flex-col divide-y overflow-hidden rounded-md bg-surface-elevated shadow-ring-sm\">\n {children}\n </div>\n );\n}\n\nfunction ControlButton({\n onClick,\n label,\n children,\n disabled = false,\n}: {\n onClick: () => void;\n label: string;\n children: ReactNode;\n disabled?: boolean;\n}) {\n return (\n <button\n onClick={onClick}\n aria-label={label}\n type=\"button\"\n className={cn(\n \"flex size-8 items-center justify-center text-foreground transition-colors duration-fast\",\n \"hover:bg-surface-muted\",\n \"focus-ring-inset\",\n \"disabled:pointer-events-none disabled:opacity-50\",\n )}\n disabled={disabled}\n >\n {children}\n </button>\n );\n}\n\n/** Branded zoom / compass / locate / fullscreen controls. Render inside `<MapCanvas>`. */\nexport function MapControls({\n position = \"bottom-right\",\n showZoom = true,\n showCompass = false,\n showLocate = false,\n showFullscreen = false,\n className,\n onLocate,\n onLocateError,\n}: MapControlsProps) {\n const { map } = useMap();\n const [waitingForLocation, setWaitingForLocation] = useState(false);\n\n const handleZoomIn = useCallback(() => {\n map?.zoomTo(map.getZoom() + 1, { duration: 300 });\n }, [map]);\n\n const handleZoomOut = useCallback(() => {\n map?.zoomTo(map.getZoom() - 1, { duration: 300 });\n }, [map]);\n\n const handleResetBearing = useCallback(() => {\n map?.resetNorthPitch({ duration: 300 });\n }, [map]);\n\n const handleLocate = useCallback(() => {\n if (!(\"geolocation\" in navigator)) return;\n setWaitingForLocation(true);\n navigator.geolocation.getCurrentPosition(\n (pos) => {\n const coords = {\n longitude: pos.coords.longitude,\n latitude: pos.coords.latitude,\n };\n map?.flyTo({\n center: [coords.longitude, coords.latitude],\n zoom: 14,\n duration: 1500,\n });\n onLocate?.(coords);\n setWaitingForLocation(false);\n },\n (error) => {\n onLocateError?.(error);\n setWaitingForLocation(false);\n },\n );\n }, [map, onLocate, onLocateError]);\n\n const handleFullscreen = useCallback(() => {\n const container = map?.getContainer();\n if (!container) return;\n if (document.fullscreenElement) {\n void document.exitFullscreen();\n } else {\n void container.requestFullscreen();\n }\n }, [map]);\n\n return (\n <div\n className={cn(\"absolute z-10 flex flex-col gap-1.5\", positionClasses[position], className)}\n >\n {showZoom && (\n <ControlGroup>\n <ControlButton onClick={handleZoomIn} label=\"Zoom in\">\n <Plus className=\"size-4\" aria-hidden=\"true\" />\n </ControlButton>\n <ControlButton onClick={handleZoomOut} label=\"Zoom out\">\n <Minus className=\"size-4\" aria-hidden=\"true\" />\n </ControlButton>\n </ControlGroup>\n )}\n {showCompass && (\n <ControlGroup>\n <CompassButton onClick={handleResetBearing} />\n </ControlGroup>\n )}\n {showLocate && (\n <ControlGroup>\n <ControlButton\n onClick={handleLocate}\n label=\"Find my location\"\n disabled={waitingForLocation}\n >\n {waitingForLocation ? (\n <Spinner label=\"Locating\" className=\"size-4 text-foreground\" />\n ) : (\n <Locate className=\"size-4\" aria-hidden=\"true\" />\n )}\n </ControlButton>\n </ControlGroup>\n )}\n {showFullscreen && (\n <ControlGroup>\n <ControlButton onClick={handleFullscreen} label=\"Toggle fullscreen\">\n <Maximize className=\"size-4\" aria-hidden=\"true\" />\n </ControlButton>\n </ControlGroup>\n )}\n </div>\n );\n}\n\nfunction CompassButton({ onClick }: { onClick: () => void }) {\n const { map } = useMap();\n const compassRef = useRef<SVGSVGElement>(null);\n\n useEffect(() => {\n if (!map || !compassRef.current) return;\n\n const compass = compassRef.current;\n\n const updateRotation = () => {\n const bearing = map.getBearing();\n const pitch = map.getPitch();\n compass.style.transform = `rotateX(${pitch}deg) rotateZ(${-bearing}deg)`;\n };\n\n map.on(\"rotate\", updateRotation);\n map.on(\"pitch\", updateRotation);\n updateRotation();\n\n return () => {\n map.off(\"rotate\", updateRotation);\n map.off(\"pitch\", updateRotation);\n };\n }, [map]);\n\n return (\n <ControlButton onClick={onClick} label=\"Reset bearing to north\">\n <svg\n ref={compassRef}\n viewBox=\"0 0 24 24\"\n aria-hidden=\"true\"\n className=\"size-5 transition-transform duration-fast\"\n style={{ transformStyle: \"preserve-3d\" }}\n >\n <path d=\"M12 2L16 12H12V2Z\" className=\"fill-destructive\" />\n <path d=\"M12 2L8 12H12V2Z\" className=\"fill-destructive/50\" />\n <path d=\"M12 22L16 12H12V22Z\" className=\"fill-muted-foreground/60\" />\n <path d=\"M12 22L8 12H12V22Z\" className=\"fill-muted-foreground/30\" />\n </svg>\n </ControlButton>\n );\n}\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { useEffect, useId } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport { useTokenColor } from \"../lib/use-token-color\";\n\nexport interface MapRouteProps {\n /** Optional unique identifier for the route layer. */\n id?: string;\n /** Array of [longitude, latitude] coordinate pairs defining the route. */\n coordinates: [number, number][];\n /** Line color as a CSS color value. Defaults to the theme's `--primary` token. */\n color?: string;\n /** Line width in pixels (default: 3). */\n width?: number;\n /** Line opacity from 0 to 1 (default: 0.8). */\n opacity?: number;\n /** Dash pattern [dash length, gap length] for dashed lines. */\n dashArray?: [number, number];\n /** Callback when the route line is clicked. */\n onClick?: () => void;\n /** Callback when the mouse enters the route line. */\n onMouseEnter?: () => void;\n /** Callback when the mouse leaves the route line. */\n onMouseLeave?: () => void;\n /** Whether the route is interactive — shows a pointer cursor on hover (default: true). */\n interactive?: boolean;\n}\n\n/** A GeoJSON line layer for routes/paths. Renders nothing itself — it draws on the map. */\nexport function MapRoute({\n id: propId,\n coordinates,\n color,\n width = 3,\n opacity = 0.8,\n dashArray,\n onClick,\n onMouseEnter,\n onMouseLeave,\n interactive = true,\n}: MapRouteProps) {\n const { map, isLoaded } = useMap();\n const autoId = useId();\n const id = propId ?? autoId;\n const sourceId = `route-source-${id}`;\n const layerId = `route-layer-${id}`;\n\n const primary = useTokenColor(\"--primary\");\n const lineColor = color ?? primary;\n\n // Add source and layer on mount.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data: {\n type: \"Feature\",\n properties: {},\n geometry: { type: \"LineString\", coordinates: [] },\n },\n });\n\n map.addLayer({\n id: layerId,\n type: \"line\",\n source: sourceId,\n layout: { \"line-join\": \"round\", \"line-cap\": \"round\" },\n paint: {\n \"line-color\": lineColor,\n \"line-width\": width,\n \"line-opacity\": opacity,\n ...(dashArray && { \"line-dasharray\": dashArray }),\n },\n });\n\n return () => {\n try {\n if (map.getLayer(layerId)) map.removeLayer(layerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n } catch {\n // style may be mid-reload\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- source/layer created once per map; data + paint are synced by the effects below\n }, [isLoaded, map]);\n\n // When coordinates change, update the source data.\n useEffect(() => {\n if (!isLoaded || !map || coordinates.length < 2) return;\n\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource;\n if (source) {\n source.setData({\n type: \"Feature\",\n properties: {},\n geometry: { type: \"LineString\", coordinates },\n });\n }\n }, [isLoaded, map, coordinates, sourceId]);\n\n // Sync paint when styling (or the resolved theme color) changes.\n useEffect(() => {\n if (!isLoaded || !map || !map.getLayer(layerId)) return;\n\n map.setPaintProperty(layerId, \"line-color\", lineColor);\n map.setPaintProperty(layerId, \"line-width\", width);\n map.setPaintProperty(layerId, \"line-opacity\", opacity);\n map.setPaintProperty(layerId, \"line-dasharray\", dashArray);\n }, [isLoaded, map, layerId, lineColor, width, opacity, dashArray]);\n\n // Handle click and hover events.\n useEffect(() => {\n if (!isLoaded || !map || !interactive) return;\n\n const handleClick = () => {\n onClick?.();\n };\n const handleMouseEnter = () => {\n map.getCanvas().style.cursor = \"pointer\";\n onMouseEnter?.();\n };\n const handleMouseLeave = () => {\n map.getCanvas().style.cursor = \"\";\n onMouseLeave?.();\n };\n\n map.on(\"click\", layerId, handleClick);\n map.on(\"mouseenter\", layerId, handleMouseEnter);\n map.on(\"mouseleave\", layerId, handleMouseLeave);\n\n return () => {\n map.off(\"click\", layerId, handleClick);\n map.off(\"mouseenter\", layerId, handleMouseEnter);\n map.off(\"mouseleave\", layerId, handleMouseLeave);\n };\n }, [isLoaded, map, layerId, onClick, onMouseEnter, onMouseLeave, interactive]);\n\n return null;\n}\n","\"use client\";\n\nimport { resolveTokenColor } from \"@elabs-ai/components-tokens\";\nimport { useLayoutEffect, useState } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\n\n/**\n * Resolve a semantic token (e.g. `\"--primary\"`) to a concrete color MapLibre's\n * WebGL paint can consume, re-resolving whenever the brand theme changes.\n * This is how default layer paints stay token-driven: WebGL can't read CSS\n * custom properties, so we read them at runtime off the map container.\n */\nexport function useTokenColor(name: string, fallback = \"#000000\"): string {\n const { map, themeKey } = useMap();\n const [color, setColor] = useState(fallback);\n\n useLayoutEffect(() => {\n setColor(resolveTokenColor(name, { el: map?.getContainer(), fallback }));\n }, [name, fallback, map, themeKey]);\n\n return color;\n}\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { useEffect, useId, useMemo, useRef } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport { buildArcCoordinates } from \"../lib/arc-math\";\nimport { mergeHoverPaint } from \"../lib/merge-hover-paint\";\nimport { useTokenColor } from \"../lib/use-token-color\";\n\n/** A single arc to render inside `<MapArc data={...}>`. */\nexport type MapArcDatum = {\n /** Unique identifier for this arc. Required for hover state tracking and event payloads. */\n id: string | number;\n /** Start coordinate as [longitude, latitude]. */\n from: [number, number];\n /** End coordinate as [longitude, latitude]. */\n to: [number, number];\n};\n\n/** Event payload passed to MapArc interaction callbacks. */\nexport type MapArcEvent<T extends MapArcDatum = MapArcDatum> = {\n /** The arc datum that was hovered or clicked. */\n arc: T;\n /** Longitude of the cursor at the time of the event. */\n longitude: number;\n /** Latitude of the cursor at the time of the event. */\n latitude: number;\n /** The underlying MapLibre mouse event for advanced use cases. */\n originalEvent: MapLibreGL.MapMouseEvent;\n};\n\nexport type MapArcLinePaint = NonNullable<MapLibreGL.LineLayerSpecification[\"paint\"]>;\nexport type MapArcLineLayout = NonNullable<MapLibreGL.LineLayerSpecification[\"layout\"]>;\n\nexport type MapArcProps<T extends MapArcDatum = MapArcDatum> = {\n /** Array of arcs to render. Each arc must have a unique `id`. */\n data: T[];\n /** Optional unique identifier prefix for the arc source/layers. Auto-generated if not provided. */\n id?: string;\n /**\n * How far each arc bows away from a straight line. `0` renders straight\n * lines; higher values bend further; negative values bend to the opposite\n * side. Arcs cross the antimeridian via the shorter direction. (default: 0.2)\n */\n curvature?: number;\n /** Number of samples used to render each curve. Higher = smoother. (default: 64) */\n samples?: number;\n /**\n * MapLibre paint properties for the arc layer. Merged on top of theme-aware\n * defaults (`line-color` = the `--primary` token, `line-width: 2`,\n * `line-opacity: 0.85`). Any value can be a MapLibre expression for\n * per-feature styling; every field on each arc datum (besides `from`/`to`)\n * is exposed via `[\"get\", ...]`.\n */\n paint?: MapArcLinePaint;\n /** MapLibre layout properties for the arc layer. Defaults to rounded joins/caps. */\n layout?: MapArcLineLayout;\n /**\n * Paint properties applied to the arc currently under the cursor. Each key\n * is merged into `paint` as a `case` expression keyed on per-feature hover\n * state, so only the hovered arc changes appearance.\n */\n hoverPaint?: MapArcLinePaint;\n /** Callback when an arc is clicked. */\n onClick?: (e: MapArcEvent<T>) => void;\n /**\n * Callback fired when the hovered arc changes. Receives the cursor's\n * lng/lat at the moment of entry, and `null` when the cursor leaves the\n * last hovered arc.\n */\n onHover?: (e: MapArcEvent<T> | null) => void;\n /** Whether arcs respond to mouse events (default: true). */\n interactive?: boolean;\n /** Optional MapLibre layer id to insert the arc layers before (z-order control). */\n beforeId?: string;\n};\n\nconst DEFAULT_ARC_CURVATURE = 0.2;\nconst DEFAULT_ARC_SAMPLES = 64;\nconst ARC_HIT_MIN_WIDTH = 12;\nconst ARC_HIT_PADDING = 6;\n\nconst DEFAULT_ARC_LAYOUT: MapArcLineLayout = {\n \"line-join\": \"round\",\n \"line-cap\": \"round\",\n};\n\n/**\n * Curved great-circle-style arcs between coordinate pairs (flight paths,\n * network links). Pairs well with `<MapCanvas blank projection={{ type: \"globe\" }}>`.\n */\nexport function MapArc<T extends MapArcDatum = MapArcDatum>({\n data,\n id: propId,\n curvature = DEFAULT_ARC_CURVATURE,\n samples = DEFAULT_ARC_SAMPLES,\n paint,\n layout,\n hoverPaint,\n onClick,\n onHover,\n interactive = true,\n beforeId,\n}: MapArcProps<T>) {\n const { map, isLoaded } = useMap();\n const autoId = useId();\n const id = propId ?? autoId;\n const sourceId = `arc-source-${id}`;\n const layerId = `arc-layer-${id}`;\n const hitLayerId = `arc-hit-layer-${id}`;\n\n const primary = useTokenColor(\"--primary\");\n\n const mergedPaint = useMemo(\n () =>\n mergeHoverPaint(\n { \"line-color\": primary, \"line-width\": 2, \"line-opacity\": 0.85, ...paint },\n hoverPaint,\n ),\n [primary, paint, hoverPaint],\n );\n const mergedLayout = useMemo(() => ({ ...DEFAULT_ARC_LAYOUT, ...layout }), [layout]);\n\n const hitWidth = useMemo(() => {\n const w = paint?.[\"line-width\"] ?? 2;\n const base = typeof w === \"number\" ? w : ARC_HIT_MIN_WIDTH;\n return Math.max(base + ARC_HIT_PADDING, ARC_HIT_MIN_WIDTH);\n }, [paint]);\n\n const geoJSON = useMemo<GeoJSON.FeatureCollection<GeoJSON.LineString>>(\n () => ({\n type: \"FeatureCollection\",\n features: data.map((arc) => {\n const { from, to, ...properties } = arc;\n return {\n type: \"Feature\",\n properties,\n geometry: {\n type: \"LineString\",\n coordinates: buildArcCoordinates(from, to, curvature, samples),\n },\n };\n }),\n }),\n [data, curvature, samples],\n );\n\n const latestRef = useRef({ data, onClick, onHover });\n latestRef.current = { data, onClick, onHover };\n\n // Add source and layers on mount. The invisible hit layer widens the\n // pointer target so thin arcs stay hoverable/clickable.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data: geoJSON,\n promoteId: \"id\",\n });\n\n map.addLayer(\n {\n id: hitLayerId,\n type: \"line\",\n source: sourceId,\n layout: DEFAULT_ARC_LAYOUT,\n paint: {\n \"line-color\": \"transparent\",\n \"line-width\": hitWidth,\n \"line-opacity\": 1,\n },\n },\n beforeId,\n );\n\n map.addLayer(\n {\n id: layerId,\n type: \"line\",\n source: sourceId,\n layout: mergedLayout,\n paint: mergedPaint,\n },\n beforeId,\n );\n\n return () => {\n try {\n if (map.getLayer(layerId)) map.removeLayer(layerId);\n if (map.getLayer(hitLayerId)) map.removeLayer(hitLayerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n } catch {\n // style may be mid-reload\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- source/layers created once per map; data + paint are synced by the effects below\n }, [isLoaded, map]);\n\n // Sync features when data / curvature / samples change.\n useEffect(() => {\n if (!isLoaded || !map) return;\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource | undefined;\n source?.setData(geoJSON);\n }, [isLoaded, map, geoJSON, sourceId]);\n\n // Sync paint/layout when they change.\n useEffect(() => {\n if (!isLoaded || !map || !map.getLayer(layerId)) return;\n for (const [key, value] of Object.entries(mergedPaint)) {\n map.setPaintProperty(layerId, key as keyof MapArcLinePaint, value as never);\n }\n for (const [key, value] of Object.entries(mergedLayout)) {\n map.setLayoutProperty(layerId, key as keyof MapArcLineLayout, value as never);\n }\n if (map.getLayer(hitLayerId)) {\n map.setPaintProperty(hitLayerId, \"line-width\", hitWidth);\n }\n }, [isLoaded, map, layerId, hitLayerId, mergedPaint, mergedLayout, hitWidth]);\n\n // Interaction handlers (bound to the hit layer).\n useEffect(() => {\n if (!isLoaded || !map || !interactive) return;\n\n let hoveredId: string | number | null = null;\n\n const setHover = (next: string | number | null) => {\n if (next === hoveredId) return;\n const sourceExists = !!map.getSource(sourceId);\n if (hoveredId != null && sourceExists) {\n map.setFeatureState({ source: sourceId, id: hoveredId }, { hover: false });\n }\n hoveredId = next;\n if (next != null && sourceExists) {\n map.setFeatureState({ source: sourceId, id: next }, { hover: true });\n }\n };\n\n const findArc = (featureId: string | number | undefined) =>\n featureId == null\n ? undefined\n : latestRef.current.data.find((arc) => String(arc.id) === String(featureId));\n\n const handleMouseMove = (e: MapLibreGL.MapLayerMouseEvent) => {\n const featureId = e.features?.[0]?.id as string | number | undefined;\n if (featureId == null || featureId === hoveredId) return;\n\n setHover(featureId);\n map.getCanvas().style.cursor = \"pointer\";\n\n const arc = findArc(featureId);\n if (arc) {\n latestRef.current.onHover?.({\n arc: arc as T,\n longitude: e.lngLat.lng,\n latitude: e.lngLat.lat,\n originalEvent: e,\n });\n }\n };\n\n const handleMouseLeave = () => {\n setHover(null);\n map.getCanvas().style.cursor = \"\";\n latestRef.current.onHover?.(null);\n };\n\n const handleClick = (e: MapLibreGL.MapLayerMouseEvent) => {\n const arc = findArc(e.features?.[0]?.id as string | number | undefined);\n if (!arc) return;\n latestRef.current.onClick?.({\n arc: arc as T,\n longitude: e.lngLat.lng,\n latitude: e.lngLat.lat,\n originalEvent: e,\n });\n };\n\n map.on(\"mousemove\", hitLayerId, handleMouseMove);\n map.on(\"mouseleave\", hitLayerId, handleMouseLeave);\n map.on(\"click\", hitLayerId, handleClick);\n\n return () => {\n map.off(\"mousemove\", hitLayerId, handleMouseMove);\n map.off(\"mouseleave\", hitLayerId, handleMouseLeave);\n map.off(\"click\", hitLayerId, handleClick);\n setHover(null);\n map.getCanvas().style.cursor = \"\";\n };\n }, [isLoaded, map, hitLayerId, sourceId, interactive]);\n\n return null;\n}\n","/** Pure geometry for `<MapArc>` — kept engine-free so it can be unit-tested. */\n\n/**\n * Sample a quadratic Bézier between `from` and `to` in lng/lat space.\n * `curvature` is how far the arc bows away from a straight line (0 = straight;\n * negative bends to the opposite side). The destination longitude is unwrapped\n * relative to the origin so arcs cross the antimeridian via the shorter\n * great-circle direction — resulting longitudes may fall outside [-180, 180],\n * which MapLibre renders correctly on the globe projection.\n */\nexport function buildArcCoordinates(\n from: [number, number],\n to: [number, number],\n curvature: number,\n samples: number,\n): [number, number][] {\n const [x0, y0] = from;\n const [xTo, y2] = to;\n const rawDx = xTo - x0;\n const x2 = rawDx > 180 ? xTo - 360 : rawDx < -180 ? xTo + 360 : xTo;\n const dx = x2 - x0;\n const dy = y2 - y0;\n const distance = Math.hypot(dx, dy);\n\n if (distance === 0 || curvature === 0) return [from, [x2, y2]];\n\n const mx = (x0 + x2) / 2;\n const my = (y0 + y2) / 2;\n const nx = -dy / distance;\n const ny = dx / distance;\n const offset = distance * curvature;\n const cx = mx + nx * offset;\n const cy = my + ny * offset;\n\n const points: [number, number][] = [];\n const segments = Math.max(2, Math.floor(samples));\n for (let i = 0; i <= segments; i += 1) {\n const t = i / segments;\n const inv = 1 - t;\n const x = inv * inv * x0 + 2 * inv * t * cx + t * t * x2;\n const y = inv * inv * y0 + 2 * inv * t * cy + t * t * y2;\n points.push([x, y]);\n }\n return points;\n}\n","/**\n * Merge hover paint into base paint as MapLibre `case` expressions keyed on\n * the per-feature `hover` feature-state, so only the hovered feature changes\n * appearance.\n */\nexport function mergeHoverPaint<T extends Record<string, unknown>>(\n paint: T,\n hoverPaint: T | undefined,\n): T {\n if (!hoverPaint) return paint;\n const merged: Record<string, unknown> = { ...paint };\n for (const [key, hoverValue] of Object.entries(hoverPaint)) {\n if (hoverValue === undefined) continue;\n const baseValue = merged[key];\n merged[key] =\n baseValue === undefined\n ? hoverValue\n : [\"case\", [\"boolean\", [\"feature-state\", \"hover\"], false], hoverValue, baseValue];\n }\n return merged as T;\n}\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { useEffect, useId, useMemo, useRef } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport { mergeHoverPaint } from \"../lib/merge-hover-paint\";\nimport { useTokenColor } from \"../lib/use-token-color\";\n\nexport type MapGeoJSONData<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> =\n | GeoJSON.FeatureCollection<GeoJSON.Geometry, P>\n | GeoJSON.Feature<GeoJSON.Geometry, P>\n | GeoJSON.Geometry\n | string;\n\nexport type MapFillPaint = NonNullable<MapLibreGL.FillLayerSpecification[\"paint\"]>;\nexport type MapLinePaint = NonNullable<MapLibreGL.LineLayerSpecification[\"paint\"]>;\n\n/** A rendered feature with strongly-typed `properties`. */\nexport type MapGeoJSONFeature<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> =\n Omit<MapLibreGL.MapGeoJSONFeature, \"properties\"> & { properties: P };\n\n/** Event payload passed to MapGeoJSON interaction callbacks. */\nexport type MapGeoJSONEvent<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = {\n /** The feature under the cursor, with its typed GeoJSON properties. */\n feature: MapGeoJSONFeature<P>;\n /** Longitude of the cursor at the time of the event. */\n longitude: number;\n /** Latitude of the cursor at the time of the event. */\n latitude: number;\n /** The underlying MapLibre mouse event for advanced use cases. */\n originalEvent: MapLibreGL.MapLayerMouseEvent;\n};\n\nexport type MapGeoJSONProps<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = {\n /** GeoJSON data (FeatureCollection, Feature, Geometry) or a URL to fetch it from. */\n data: MapGeoJSONData<P>;\n /** Optional unique identifier prefix for the source/layers. Auto-generated if not provided. */\n id?: string;\n /**\n * Feature property to promote to the feature `id`. Required for hover\n * feature-state (`fillHoverPaint`) and stable `onHover`/`onClick` payloads.\n */\n promoteId?: string;\n /**\n * Paint for the polygon fill layer. Merged on top of a theme-aware neutral\n * default (the `--border` token — a mid neutral that reads on the page\n * surface in every theme). Pass `false` to omit the fill layer entirely\n * (e.g. outlines only).\n */\n fillPaint?: MapFillPaint | false;\n /**\n * Paint for the outline layer. Merged on top of a hairline default\n * (`line-color` = the `--background` token, `line-width` = 0.5) for thin\n * separators. Override `line-color` if your container differs, or pass\n * `false` to omit the layer.\n */\n linePaint?: MapLinePaint | false;\n /**\n * Paint merged onto the fill layer for the feature under the cursor, applied\n * as a `case` expression keyed on hover feature-state. Requires `promoteId`.\n */\n fillHoverPaint?: MapFillPaint;\n /** Callback when a feature is clicked. */\n onClick?: (e: MapGeoJSONEvent<P>) => void;\n /** Callback fired when the hovered feature changes; `null` when the cursor leaves. */\n onHover?: (e: MapGeoJSONEvent<P> | null) => void;\n /** Whether features respond to mouse events (default: false). */\n interactive?: boolean;\n /** Optional MapLibre layer id to insert the layers before (z-order control). */\n beforeId?: string;\n};\n\n/**\n * Renders arbitrary GeoJSON as fill + outline layers on the map. Composes like\n * `MapRoute` / `MapArc` — drop it inside `<MapCanvas>` (typically with `blank`)\n * for choropleths and region/data maps. For full control over expressions and\n * multiple layers, manage layers directly via `useMap()` instead.\n */\nexport function MapGeoJSON<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties>({\n data,\n id: propId,\n promoteId,\n fillPaint,\n linePaint,\n fillHoverPaint,\n onClick,\n onHover,\n interactive = false,\n beforeId,\n}: MapGeoJSONProps<P>) {\n const { map, isLoaded } = useMap();\n const autoId = useId();\n const id = propId ?? autoId;\n const sourceId = `geojson-source-${id}`;\n const fillLayerId = `geojson-fill-${id}`;\n const lineLayerId = `geojson-line-${id}`;\n\n // Theme-driven neutral defaults: landmass = the mid-neutral `--border` rung,\n // separators = the page surface. Both re-resolve on theme change.\n const defaultFill = useTokenColor(\"--border\");\n const defaultLine = useTokenColor(\"--background\");\n\n const showFill = fillPaint !== false;\n const showLine = linePaint !== false;\n\n const mergedFillPaint = useMemo(\n () => mergeHoverPaint({ \"fill-color\": defaultFill, ...(fillPaint || {}) }, fillHoverPaint),\n [defaultFill, fillPaint, fillHoverPaint],\n );\n const mergedLinePaint = useMemo(\n () => ({\n \"line-color\": defaultLine,\n \"line-width\": 0.5,\n ...(linePaint || {}),\n }),\n [defaultLine, linePaint],\n );\n const latestRef = useRef({ onClick, onHover });\n latestRef.current = { onClick, onHover };\n\n // Add source on mount.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data,\n ...(promoteId ? { promoteId } : {}),\n });\n\n return () => {\n try {\n if (map.getLayer(lineLayerId)) map.removeLayer(lineLayerId);\n if (map.getLayer(fillLayerId)) map.removeLayer(fillLayerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n } catch {\n // style may be mid-reload\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- source created once per map; data/layers are synced by the effects below\n }, [isLoaded, map]);\n\n // Sync data when it changes.\n useEffect(() => {\n if (!isLoaded || !map) return;\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource | undefined;\n source?.setData(data as never);\n }, [isLoaded, map, data, sourceId]);\n\n // Sync layers and paint when visibility or styling changes.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n const source = map.getSource(sourceId);\n if (!source) return;\n\n if (showFill && !map.getLayer(fillLayerId)) {\n map.addLayer(\n {\n id: fillLayerId,\n type: \"fill\",\n source: sourceId,\n paint: mergedFillPaint,\n },\n beforeId,\n );\n } else if (!showFill && map.getLayer(fillLayerId)) {\n map.removeLayer(fillLayerId);\n }\n\n if (showLine && !map.getLayer(lineLayerId)) {\n map.addLayer(\n {\n id: lineLayerId,\n type: \"line\",\n source: sourceId,\n paint: mergedLinePaint,\n },\n beforeId,\n );\n } else if (!showLine && map.getLayer(lineLayerId)) {\n map.removeLayer(lineLayerId);\n }\n\n if (showFill && map.getLayer(fillLayerId)) {\n for (const [key, value] of Object.entries(mergedFillPaint)) {\n map.setPaintProperty(fillLayerId, key as keyof MapFillPaint, value as never);\n }\n }\n if (showLine && map.getLayer(lineLayerId)) {\n for (const [key, value] of Object.entries(mergedLinePaint)) {\n map.setPaintProperty(lineLayerId, key as keyof MapLinePaint, value as never);\n }\n }\n }, [\n isLoaded,\n map,\n sourceId,\n fillLayerId,\n lineLayerId,\n showFill,\n showLine,\n mergedFillPaint,\n mergedLinePaint,\n beforeId,\n ]);\n\n // Interaction handlers (bound to the fill layer).\n useEffect(() => {\n if (!isLoaded || !map || !interactive || !showFill) return;\n\n let hoveredId: string | number | null = null;\n\n const setHover = (next: string | number | null) => {\n if (next === hoveredId) return;\n const sourceExists = !!map.getSource(sourceId);\n if (hoveredId != null && sourceExists) {\n map.setFeatureState({ source: sourceId, id: hoveredId }, { hover: false });\n }\n hoveredId = next;\n if (next != null && sourceExists) {\n map.setFeatureState({ source: sourceId, id: next }, { hover: true });\n }\n };\n\n const handleMouseMove = (e: MapLibreGL.MapLayerMouseEvent) => {\n const feature = e.features?.[0];\n if (!feature) return;\n map.getCanvas().style.cursor = \"pointer\";\n\n const featureId = feature.id;\n if (featureId === hoveredId) return;\n setHover(featureId ?? null);\n latestRef.current.onHover?.({\n feature: feature as unknown as MapGeoJSONFeature<P>,\n longitude: e.lngLat.lng,\n latitude: e.lngLat.lat,\n originalEvent: e,\n });\n };\n\n const handleMouseLeave = () => {\n setHover(null);\n map.getCanvas().style.cursor = \"\";\n latestRef.current.onHover?.(null);\n };\n\n const handleClick = (e: MapLibreGL.MapLayerMouseEvent) => {\n const feature = e.features?.[0];\n if (!feature) return;\n latestRef.current.onClick?.({\n feature: feature as unknown as MapGeoJSONFeature<P>,\n longitude: e.lngLat.lng,\n latitude: e.lngLat.lat,\n originalEvent: e,\n });\n };\n\n map.on(\"mousemove\", fillLayerId, handleMouseMove);\n map.on(\"mouseleave\", fillLayerId, handleMouseLeave);\n map.on(\"click\", fillLayerId, handleClick);\n\n return () => {\n map.off(\"mousemove\", fillLayerId, handleMouseMove);\n map.off(\"mouseleave\", fillLayerId, handleMouseLeave);\n map.off(\"click\", fillLayerId, handleClick);\n setHover(null);\n map.getCanvas().style.cursor = \"\";\n };\n }, [isLoaded, map, fillLayerId, sourceId, interactive, showFill]);\n\n return null;\n}\n","\"use client\";\n\nimport type MapLibreGL from \"maplibre-gl\";\nimport { useEffect, useId, useMemo } from \"react\";\n\nimport { useMap } from \"../map-canvas/map-context\";\nimport { useTokenColor } from \"../lib/use-token-color\";\n\nexport type MapClusterLayerProps<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> =\n {\n /** GeoJSON FeatureCollection data or a URL to fetch GeoJSON from. */\n data: string | GeoJSON.FeatureCollection<GeoJSON.Point, P>;\n /** Maximum zoom level to cluster points on (default: 14). */\n clusterMaxZoom?: number;\n /** Radius of each cluster when clustering points, in pixels (default: 50). */\n clusterRadius?: number;\n /**\n * Colors for cluster circles: [small, medium, large] based on point count.\n * Defaults to the theme's `--success`/`--warning`/`--destructive` tokens.\n */\n clusterColors?: [string, string, string];\n /** Point-count thresholds for the color/size steps: [medium, large] (default: [100, 750]). */\n clusterThresholds?: [number, number];\n /** Color for unclustered individual points. Defaults to the theme's `--primary` token. */\n pointColor?: string;\n /** Callback when an unclustered point is clicked. */\n onPointClick?: (\n feature: GeoJSON.Feature<GeoJSON.Point, P>,\n coordinates: [number, number],\n ) => void;\n /** Callback when a cluster is clicked. If not provided, zooms into the cluster. */\n onClusterClick?: (clusterId: number, coordinates: [number, number], pointCount: number) => void;\n };\n\nconst DEFAULT_CLUSTER_THRESHOLDS: [number, number] = [100, 750];\n\n/**\n * Clustered point rendering for large point datasets. Cluster circles step\n * through the status tokens (success → warning → destructive) as the point\n * count grows; strokes and count labels use the page surface for contrast.\n */\nexport function MapClusterLayer<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties>({\n data,\n clusterMaxZoom = 14,\n clusterRadius = 50,\n clusterColors,\n clusterThresholds = DEFAULT_CLUSTER_THRESHOLDS,\n pointColor,\n onPointClick,\n onClusterClick,\n}: MapClusterLayerProps<P>) {\n const { map, isLoaded } = useMap();\n const id = useId();\n const sourceId = `cluster-source-${id}`;\n const clusterLayerId = `clusters-${id}`;\n const clusterCountLayerId = `cluster-count-${id}`;\n const unclusteredLayerId = `unclustered-point-${id}`;\n\n const success = useTokenColor(\"--success\");\n const warning = useTokenColor(\"--warning\");\n const destructive = useTokenColor(\"--destructive\");\n const primary = useTokenColor(\"--primary\");\n const surface = useTokenColor(\"--background\");\n\n const resolvedClusterColors = useMemo<[string, string, string]>(\n () => clusterColors ?? [success, warning, destructive],\n [clusterColors, success, warning, destructive],\n );\n const resolvedPointColor = pointColor ?? primary;\n\n // Add source and layers on mount.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n map.addSource(sourceId, {\n type: \"geojson\",\n data,\n cluster: true,\n clusterMaxZoom,\n clusterRadius,\n });\n\n map.addLayer({\n id: clusterLayerId,\n type: \"circle\",\n source: sourceId,\n filter: [\"has\", \"point_count\"],\n paint: {\n \"circle-color\": [\n \"step\",\n [\"get\", \"point_count\"],\n resolvedClusterColors[0],\n clusterThresholds[0],\n resolvedClusterColors[1],\n clusterThresholds[1],\n resolvedClusterColors[2],\n ],\n \"circle-radius\": [\n \"step\",\n [\"get\", \"point_count\"],\n 20,\n clusterThresholds[0],\n 30,\n clusterThresholds[1],\n 40,\n ],\n \"circle-stroke-width\": 1,\n \"circle-stroke-color\": surface,\n \"circle-opacity\": 0.85,\n },\n });\n\n map.addLayer({\n id: clusterCountLayerId,\n type: \"symbol\",\n source: sourceId,\n filter: [\"has\", \"point_count\"],\n layout: {\n \"text-field\": \"{point_count_abbreviated}\",\n \"text-font\": [\"Open Sans\"],\n \"text-size\": 12,\n },\n paint: {\n \"text-color\": surface,\n },\n });\n\n map.addLayer({\n id: unclusteredLayerId,\n type: \"circle\",\n source: sourceId,\n filter: [\"!\", [\"has\", \"point_count\"]],\n paint: {\n \"circle-color\": resolvedPointColor,\n \"circle-radius\": 5,\n \"circle-stroke-width\": 2,\n \"circle-stroke-color\": surface,\n },\n });\n\n return () => {\n try {\n if (map.getLayer(clusterCountLayerId)) map.removeLayer(clusterCountLayerId);\n if (map.getLayer(unclusteredLayerId)) map.removeLayer(unclusteredLayerId);\n if (map.getLayer(clusterLayerId)) map.removeLayer(clusterLayerId);\n if (map.getSource(sourceId)) map.removeSource(sourceId);\n } catch {\n // style may be mid-reload\n }\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- source/layers created once per map; data + paint are synced by the effects below\n }, [isLoaded, map, sourceId]);\n\n // Update source data when the data prop changes (only for non-URL data).\n useEffect(() => {\n if (!isLoaded || !map || typeof data === \"string\") return;\n\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource;\n if (source) {\n source.setData(data);\n }\n }, [isLoaded, map, data, sourceId]);\n\n // Sync layer styles when props (or the resolved theme colors) change.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n if (map.getLayer(clusterLayerId)) {\n map.setPaintProperty(clusterLayerId, \"circle-color\", [\n \"step\",\n [\"get\", \"point_count\"],\n resolvedClusterColors[0],\n clusterThresholds[0],\n resolvedClusterColors[1],\n clusterThresholds[1],\n resolvedClusterColors[2],\n ]);\n map.setPaintProperty(clusterLayerId, \"circle-radius\", [\n \"step\",\n [\"get\", \"point_count\"],\n 20,\n clusterThresholds[0],\n 30,\n clusterThresholds[1],\n 40,\n ]);\n map.setPaintProperty(clusterLayerId, \"circle-stroke-color\", surface);\n }\n\n if (map.getLayer(clusterCountLayerId)) {\n map.setPaintProperty(clusterCountLayerId, \"text-color\", surface);\n }\n\n if (map.getLayer(unclusteredLayerId)) {\n map.setPaintProperty(unclusteredLayerId, \"circle-color\", resolvedPointColor);\n map.setPaintProperty(unclusteredLayerId, \"circle-stroke-color\", surface);\n }\n }, [\n isLoaded,\n map,\n clusterLayerId,\n clusterCountLayerId,\n unclusteredLayerId,\n resolvedClusterColors,\n clusterThresholds,\n resolvedPointColor,\n surface,\n ]);\n\n // Handle click events.\n useEffect(() => {\n if (!isLoaded || !map) return;\n\n // Cluster click handler — zoom into the cluster by default.\n const handleClusterClick = async (e: MapLibreGL.MapMouseEvent) => {\n const features = map.queryRenderedFeatures(e.point, {\n layers: [clusterLayerId],\n });\n const feature = features[0];\n if (!feature) return;\n const clusterId = feature.properties?.cluster_id as number;\n const pointCount = feature.properties?.point_count as number;\n const coordinates = (feature.geometry as GeoJSON.Point).coordinates as [number, number];\n\n if (onClusterClick) {\n onClusterClick(clusterId, coordinates, pointCount);\n } else {\n const source = map.getSource(sourceId) as MapLibreGL.GeoJSONSource;\n const zoom = await source.getClusterExpansionZoom(clusterId);\n map.easeTo({\n center: coordinates,\n zoom,\n });\n }\n };\n\n // Unclustered point click handler.\n const handlePointClick = (\n e: MapLibreGL.MapMouseEvent & {\n features?: MapLibreGL.MapGeoJSONFeature[];\n },\n ) => {\n const feature = e.features?.[0];\n if (!onPointClick || !feature) return;\n const coordinates = (feature.geometry as GeoJSON.Point).coordinates.slice() as [\n number,\n number,\n ];\n\n // Handle world copies.\n while (Math.abs(e.lngLat.lng - coordinates[0]) > 180) {\n coordinates[0] += e.lngLat.lng > coordinates[0] ? 360 : -360;\n }\n\n onPointClick(feature as unknown as GeoJSON.Feature<GeoJSON.Point, P>, coordinates);\n };\n\n // Cursor style handlers.\n const handleMouseEnterCluster = () => {\n map.getCanvas().style.cursor = \"pointer\";\n };\n const handleMouseLeaveCluster = () => {\n map.getCanvas().style.cursor = \"\";\n };\n const handleMouseEnterPoint = () => {\n if (onPointClick) {\n map.getCanvas().style.cursor = \"pointer\";\n }\n };\n const handleMouseLeavePoint = () => {\n map.getCanvas().style.cursor = \"\";\n };\n\n map.on(\"click\", clusterLayerId, handleClusterClick);\n map.on(\"click\", unclusteredLayerId, handlePointClick);\n map.on(\"mouseenter\", clusterLayerId, handleMouseEnterCluster);\n map.on(\"mouseleave\", clusterLayerId, handleMouseLeaveCluster);\n map.on(\"mouseenter\", unclusteredLayerId, handleMouseEnterPoint);\n map.on(\"mouseleave\", unclusteredLayerId, handleMouseLeavePoint);\n\n return () => {\n map.off(\"click\", clusterLayerId, handleClusterClick);\n map.off(\"click\", unclusteredLayerId, handlePointClick);\n map.off(\"mouseenter\", clusterLayerId, handleMouseEnterCluster);\n map.off(\"mouseleave\", clusterLayerId, handleMouseLeaveCluster);\n map.off(\"mouseenter\", unclusteredLayerId, handleMouseEnterPoint);\n map.off(\"mouseleave\", unclusteredLayerId, handleMouseLeavePoint);\n };\n }, [isLoaded, map, clusterLayerId, unclusteredLayerId, sourceId, onClusterClick, onPointClick]);\n\n return null;\n}\n"],"mappings":"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","names":["useEffect","useState","MapCanvas","useState","useEffect","MapLibreGL","createContext","use","useEffect","useMemo","useRef","useState","cn","useLocale","jsx","jsxs","createContext","use","useState","useRef","useEffect","MapLibreGL","cn","useLocale","useMemo","positionClasses","MapLibreGL","useEffect","useMemo","useRef","createPortal","X","cn","useLocale","jsx","jsxs","useLocale","useRef","useMemo","MapLibreGL","useEffect","createPortal","cn","X","useCallback","useEffect","useRef","useState","Spinner","cn","jsx","jsxs","cn","useState","useCallback","Spinner","useRef","useEffect","useEffect","useState","useState","useEffect","useEffect","useId","useMemo","useRef","useId","useMemo","useRef","useEffect","useEffect","useId","useMemo","useRef","useId","useMemo","useRef","useEffect","useEffect","useId","useMemo","useId","useMemo","useEffect"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@elabs-ai/components-maps",
3
- "version": "4.1.0",
3
+ "version": "4.2.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,8 +32,8 @@
32
32
  "maplibre-gl": "^5.15.0",
33
33
  "react": "^18.2.0 || ^19.0.0",
34
34
  "react-dom": "^18.2.0 || ^19.0.0",
35
- "@elabs-ai/components-tokens": "4.1.0",
36
- "@elabs-ai/components-ui": "4.1.0"
35
+ "@elabs-ai/components-tokens": "4.2.0",
36
+ "@elabs-ai/components-ui": "4.2.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@testing-library/jest-dom": "^6.6.3",
@@ -51,9 +51,9 @@
51
51
  "typescript": "^5.7.3",
52
52
  "vitest": "^3.0.2",
53
53
  "@elabs-ai/components-eslint-config": "0.1.0",
54
+ "@elabs-ai/components-tokens": "4.2.0",
54
55
  "@elabs-ai/components-typescript-config": "0.1.0",
55
- "@elabs-ai/components-tokens": "4.1.0",
56
- "@elabs-ai/components-ui": "4.1.0"
56
+ "@elabs-ai/components-ui": "4.2.0"
57
57
  },
58
58
  "scripts": {
59
59
  "build": "tsup && node ../../scripts/link-dist-css.mjs",
@@ -1,4 +1,4 @@
1
- import { cleanup, render, screen, waitFor } from "@testing-library/react";
1
+ import { act, cleanup, render, screen, waitFor } from "@testing-library/react";
2
2
  import { afterEach, describe, expect, it, vi } from "vitest";
3
3
 
4
4
  // maplibre-gl requires WebGL — mock the engine and assert the brand wrapper's
@@ -90,4 +90,35 @@ describe("MapCanvas", () => {
90
90
  }
91
91
  expect(() => render(<Bare />)).toThrow(/within a <MapCanvas>/);
92
92
  });
93
+
94
+ it("applies the CURRENT projection prop on a later styledata event, not the value from mount", async () => {
95
+ const globe = { type: "globe" } as const;
96
+ const mercator = { type: "mercator" } as const;
97
+ const { rerender } = render(<MapCanvas projection={globe} />);
98
+ const map = MockMap.instances[0]!;
99
+ // Let the mount-time styledata's own 100ms timeout settle first.
100
+ await act(async () => {
101
+ await new Promise((resolve) => setTimeout(resolve, 150));
102
+ });
103
+
104
+ const setProjectionSpy = vi.spyOn(map, "setProjection");
105
+ rerender(<MapCanvas projection={mercator} />);
106
+ // The dedicated "sync projection on prop change" effect also fires here —
107
+ // clear it so the assertion below isolates the `styledata` handler alone.
108
+ await act(async () => {
109
+ await new Promise((resolve) => setTimeout(resolve, 150));
110
+ });
111
+ setProjectionSpy.mockClear();
112
+
113
+ // A style reload (theme swap, `setStyle`) re-fires `styledata` on the SAME
114
+ // mount-only handler — it must read the current prop via a ref, not the
115
+ // value closed over when the handler was registered.
116
+ await act(async () => {
117
+ map.emit("styledata");
118
+ await new Promise((resolve) => setTimeout(resolve, 150));
119
+ });
120
+
121
+ expect(setProjectionSpy).toHaveBeenCalledWith(mercator);
122
+ expect(setProjectionSpy).not.toHaveBeenCalledWith(globe);
123
+ });
93
124
  });
@@ -13,7 +13,7 @@ import {
13
13
  useState,
14
14
  type ReactNode,
15
15
  } from "react";
16
- import { Spinner, StatePanel } from "@elabs-ai/components-ui";
16
+ import { Spinner, StatePanel, useLocale } from "@elabs-ai/components-ui";
17
17
  import { cn } from "@elabs-ai/components-ui/lib/cn";
18
18
 
19
19
  import { MapContext, type BasemapTheme } from "./map-context";
@@ -142,6 +142,7 @@ export const MapCanvas = forwardRef<MapCanvasRef, MapCanvasProps>(function MapCa
142
142
  },
143
143
  ref,
144
144
  ) {
145
+ const { t } = useLocale();
145
146
  const containerRef = useRef<HTMLDivElement>(null);
146
147
  const [mapInstance, setMapInstance] = useState<MapLibreGL.Map | null>(null);
147
148
  const [initFailed, setInitFailed] = useState(false);
@@ -157,6 +158,11 @@ export const MapCanvas = forwardRef<MapCanvasRef, MapCanvasProps>(function MapCa
157
158
  const onViewportChangeRef = useRef(onViewportChange);
158
159
  onViewportChangeRef.current = onViewportChange;
159
160
 
161
+ // Read from the mount-only `styledata` handler below so a `projection` prop
162
+ // change after mount isn't reapplied with the value captured at mount time.
163
+ const projectionRef = useRef(projection);
164
+ projectionRef.current = projection;
165
+
160
166
  const mapStyles = useMemo(() => {
161
167
  // Explicit styles win. Otherwise `blank` opts into the transparent
162
168
  // tile-less basemap; with neither, fall back to the Carto defaults.
@@ -221,8 +227,8 @@ export const MapCanvas = forwardRef<MapCanvasRef, MapCanvasProps>(function MapCa
221
227
  // race conditions on setStyle without force-updating every layer.
222
228
  styleTimeoutRef.current = setTimeout(() => {
223
229
  setIsStyleLoaded(true);
224
- if (projection) {
225
- map.setProjection(projection);
230
+ if (projectionRef.current) {
231
+ map.setProjection(projectionRef.current);
226
232
  }
227
233
  }, 100);
228
234
  };
@@ -329,8 +335,8 @@ export const MapCanvas = forwardRef<MapCanvasRef, MapCanvasProps>(function MapCa
329
335
  <div className={cn("relative h-full w-full", className)}>
330
336
  <StatePanel
331
337
  kind="error"
332
- title="Map unavailable"
333
- description="This browser can’t render WebGL maps."
338
+ title={t("maps.canvas.unavailableTitle")}
339
+ description={t("maps.canvas.unavailableDescription")}
334
340
  className="h-full"
335
341
  />
336
342
  </div>
@@ -78,4 +78,59 @@ describe("MapMarker", () => {
78
78
  unmount();
79
79
  expect(MockMarker.instances[0]!.addedTo).toBeNull();
80
80
  });
81
+
82
+ it("rebuilds the marker instance when `anchor` changes — MapLibre has no live setter for it", async () => {
83
+ const { rerender } = render(
84
+ <MapCanvas>
85
+ <MapMarker longitude={0} latitude={0} anchor="top">
86
+ <MapMarkerContent />
87
+ </MapMarker>
88
+ </MapCanvas>,
89
+ );
90
+ await waitFor(() => {
91
+ expect(MockMarker.instances).toHaveLength(1);
92
+ expect(MockMarker.instances[0]!.options.anchor).toBe("top");
93
+ });
94
+
95
+ rerender(
96
+ <MapCanvas>
97
+ <MapMarker longitude={0} latitude={0} anchor="bottom">
98
+ <MapMarkerContent />
99
+ </MapMarker>
100
+ </MapCanvas>,
101
+ );
102
+
103
+ await waitFor(() => {
104
+ expect(MockMarker.instances).toHaveLength(2);
105
+ expect(MockMarker.instances[1]!.options.anchor).toBe("bottom");
106
+ });
107
+ // The old instance is detached, not mutated in place.
108
+ expect(MockMarker.instances[0]!.addedTo).toBeNull();
109
+ expect(MockMarker.instances[1]!.addedTo).not.toBeNull();
110
+ });
111
+
112
+ it("never touches `document` during the render phase (SSR-safe)", async () => {
113
+ // `renderToStaticMarkup` never runs effects — only the plain render
114
+ // functions — which is exactly the environment a real SSR host runs in.
115
+ // Deleting `document` reproduces that host having none: the old
116
+ // `useMemo`-based marker construction called `document.createElement`
117
+ // straight out of the render function and would throw here.
118
+ const { renderToStaticMarkup } = await import("react-dom/server");
119
+ const originalDocument = globalThis.document;
120
+ // @ts-expect-error -- simulating a server environment with no DOM
121
+ delete globalThis.document;
122
+ try {
123
+ expect(() =>
124
+ renderToStaticMarkup(
125
+ <MapCanvas>
126
+ <MapMarker longitude={0} latitude={0}>
127
+ <MapMarkerContent />
128
+ </MapMarker>
129
+ </MapCanvas>,
130
+ ),
131
+ ).not.toThrow();
132
+ } finally {
133
+ globalThis.document = originalDocument;
134
+ }
135
+ });
81
136
  });
@@ -1,10 +1,11 @@
1
1
  "use client";
2
2
 
3
3
  import MapLibreGL, { type MarkerOptions, type PopupOptions } from "maplibre-gl";
4
- import { createContext, use, useEffect, useMemo, useRef, type ReactNode } from "react";
4
+ import { createContext, use, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
5
5
  import { createPortal } from "react-dom";
6
6
  import { X } from "lucide-react";
7
7
  import { cn } from "@elabs-ai/components-ui/lib/cn";
8
+ import { useLocale } from "@elabs-ai/components-ui";
8
9
 
9
10
  import { useMap } from "../map-canvas/map-context";
10
11
 
@@ -63,6 +64,7 @@ export function MapMarker({
63
64
  ...markerOptions
64
65
  }: MapMarkerProps) {
65
66
  const { map } = useMap();
67
+ const [marker, setMarker] = useState<MapLibreGL.Marker | null>(null);
66
68
 
67
69
  const callbacksRef = useRef({
68
70
  onClick,
@@ -81,12 +83,33 @@ export function MapMarker({
81
83
  onDragEnd,
82
84
  };
83
85
 
84
- const marker = useMemo(() => {
86
+ const { anchor, className, offset, rotation, rotationAlignment, pitchAlignment } = markerOptions;
87
+
88
+ // `longitude`/`latitude`/`draggable` at construction time only — the sync
89
+ // effect below corrects them on the very next commit, and are read from a
90
+ // ref here so they don't force a rebuild on every position change (see the
91
+ // `anchor`/`className` note below for what SHOULD force one).
92
+ const initialRef = useRef({ longitude, latitude, draggable });
93
+ initialRef.current = { longitude, latitude, draggable };
94
+
95
+ // Builds (and tears down) the actual MapLibre `Marker` instance. This is a
96
+ // real `useEffect`, never `useMemo`: `useMemo`'s initializer runs during
97
+ // RENDER — including on the server, where `document` doesn't exist — and
98
+ // React offers no guarantee it runs only once (it may discard and recompute
99
+ // a memoized value), which would silently leak marker instances/listeners.
100
+ //
101
+ // `anchor` and `className` have no MapLibre setter (`Marker` bakes both into
102
+ // its constructor), so they are the only options that must REBUILD the
103
+ // instance; every other option (position, draggable, offset, rotation,
104
+ // alignment) has a live setter and is kept in sync by the effect further
105
+ // below without ever recreating the marker.
106
+ useEffect(() => {
85
107
  const markerInstance = new MapLibreGL.Marker({
86
- ...markerOptions,
108
+ anchor,
109
+ className,
87
110
  element: document.createElement("div"),
88
- draggable,
89
- }).setLngLat([longitude, latitude]);
111
+ draggable: initialRef.current.draggable,
112
+ }).setLngLat([initialRef.current.longitude, initialRef.current.latitude]);
90
113
 
91
114
  const handleClick = (e: MouseEvent) => callbacksRef.current.onClick?.(e);
92
115
  const handleMouseEnter = (e: MouseEvent) => callbacksRef.current.onMouseEnter?.(e);
@@ -113,24 +136,25 @@ export function MapMarker({
113
136
  markerInstance.on("drag", handleDrag);
114
137
  markerInstance.on("dragend", handleDragEnd);
115
138
 
116
- return markerInstance;
117
- // eslint-disable-next-line react-hooks/exhaustive-deps -- instance created once; position/options are synced by the effect below
118
- }, []);
139
+ setMarker(markerInstance);
140
+ }, [anchor, className]);
119
141
 
142
+ // Attaches/detaches the CURRENT marker instance to the map — its own effect
143
+ // so both a `map` change and an `anchor`/`className`-driven rebuild above
144
+ // are handled the same way, without a double `remove()`.
120
145
  useEffect(() => {
121
- if (!map) return;
146
+ if (!map || !marker) return;
122
147
 
123
148
  marker.addTo(map);
124
149
 
125
150
  return () => {
126
151
  marker.remove();
127
152
  };
128
- // eslint-disable-next-line react-hooks/exhaustive-deps -- `marker` is stable (created once above)
129
- }, [map]);
130
-
131
- const { offset, rotation, rotationAlignment, pitchAlignment } = markerOptions;
153
+ }, [map, marker]);
132
154
 
133
155
  useEffect(() => {
156
+ if (!marker) return;
157
+
134
158
  const current = marker.getLngLat();
135
159
  if (current.lng !== longitude || current.lat !== latitude) {
136
160
  marker.setLngLat([longitude, latitude]);
@@ -160,6 +184,14 @@ export function MapMarker({
160
184
  }
161
185
  }, [marker, longitude, latitude, draggable, offset, rotation, rotationAlignment, pitchAlignment]);
162
186
 
187
+ // No marker yet (first client tick after mount, or the moment an
188
+ // `anchor`/`className` rebuild is in flight) — render nothing rather than
189
+ // a sub-component (`MapMarkerContent` et al.) reaching into a null marker,
190
+ // and nothing at all on the server.
191
+ if (!marker) {
192
+ return null;
193
+ }
194
+
163
195
  return <MarkerContext.Provider value={{ marker, map }}>{children}</MarkerContext.Provider>;
164
196
  }
165
197
 
@@ -189,11 +221,12 @@ function DefaultMarkerIcon() {
189
221
  }
190
222
 
191
223
  function PopupCloseButton({ onClick }: { onClick: () => void }) {
224
+ const { t } = useLocale();
192
225
  return (
193
226
  <button
194
227
  type="button"
195
228
  onClick={onClick}
196
- aria-label="Close popup"
229
+ aria-label={t("maps.popup.close")}
197
230
  className="absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset"
198
231
  >
199
232
  <X className="size-3.5" aria-hidden="true" />
@@ -5,6 +5,7 @@ import { useEffect, useMemo, useRef, type ReactNode } from "react";
5
5
  import { createPortal } from "react-dom";
6
6
  import { X } from "lucide-react";
7
7
  import { cn } from "@elabs-ai/components-ui/lib/cn";
8
+ import { useLocale } from "@elabs-ai/components-ui";
8
9
 
9
10
  import { useMap } from "../map-canvas/map-context";
10
11
 
@@ -37,6 +38,7 @@ export function MapPopup({
37
38
  ...popupOptions
38
39
  }: MapPopupProps) {
39
40
  const { map } = useMap();
41
+ const { t } = useLocale();
40
42
  const onCloseRef = useRef(onClose);
41
43
  onCloseRef.current = onClose;
42
44
  const container = useMemo(() => document.createElement("div"), []);
@@ -100,7 +102,7 @@ export function MapPopup({
100
102
  <button
101
103
  type="button"
102
104
  onClick={handleClose}
103
- aria-label="Close popup"
105
+ aria-label={t("maps.popup.close")}
104
106
  className="absolute top-1 right-1 z-10 inline-flex size-5 items-center justify-center rounded-sm text-foreground transition-colors duration-fast hover:bg-muted focus-ring-inset"
105
107
  >
106
108
  <X className="size-3.5" aria-hidden="true" />
@@ -55,6 +55,13 @@ export class MockMap {
55
55
  return this;
56
56
  }
57
57
 
58
+ /** Manually re-fire an event's handlers — e.g. a second `styledata` after a style reload. */
59
+ emit(event: string, layerId?: string, ...args: unknown[]) {
60
+ this.handlers.get(this.key(event, layerId))?.forEach((handler) => {
61
+ handler(...args);
62
+ });
63
+ }
64
+
58
65
  remove() {
59
66
  this.removed = true;
60
67
  }
@@ -124,9 +131,12 @@ export class MockMarker {
124
131
  addedTo: MockMap | null = null;
125
132
  popup: unknown = null;
126
133
  lngLat = { lng: 0, lat: 0 };
134
+ /** The full options object the component constructed the marker with. */
135
+ options: Record<string, any>;
127
136
 
128
- constructor(options: { element?: HTMLElement } = {}) {
137
+ constructor(options: { element?: HTMLElement } & Record<string, any> = {}) {
129
138
  this.element = options.element ?? document.createElement("div");
139
+ this.options = options;
130
140
  MockMarker.instances.push(this);
131
141
  }
132
142