@elabs-ai/components-maps 4.0.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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +121 -0
  3. package/dist/index.css +11 -0
  4. package/dist/index.css.map +1 -0
  5. package/dist/index.d.ts +442 -0
  6. package/dist/index.js +1475 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +66 -0
  9. package/src/index.ts +37 -0
  10. package/src/lib/arc-math.test.ts +41 -0
  11. package/src/lib/arc-math.ts +45 -0
  12. package/src/lib/merge-hover-paint.test.ts +36 -0
  13. package/src/lib/merge-hover-paint.ts +21 -0
  14. package/src/lib/use-token-color.ts +23 -0
  15. package/src/map-arc/index.ts +9 -0
  16. package/src/map-arc/map-arc.stories.tsx +58 -0
  17. package/src/map-arc/map-arc.tsx +294 -0
  18. package/src/map-canvas/index.ts +8 -0
  19. package/src/map-canvas/map-canvas-webgl-fallback.test.tsx +30 -0
  20. package/src/map-canvas/map-canvas.stories.tsx +54 -0
  21. package/src/map-canvas/map-canvas.test.tsx +93 -0
  22. package/src/map-canvas/map-canvas.tsx +349 -0
  23. package/src/map-canvas/map-context.ts +32 -0
  24. package/src/map-canvas/maps.css +15 -0
  25. package/src/map-canvas/use-resolved-basemap-theme.ts +77 -0
  26. package/src/map-cluster-layer/index.ts +1 -0
  27. package/src/map-cluster-layer/map-cluster-layer.stories.tsx +56 -0
  28. package/src/map-cluster-layer/map-cluster-layer.tsx +292 -0
  29. package/src/map-controls/index.ts +1 -0
  30. package/src/map-controls/map-controls.stories.tsx +43 -0
  31. package/src/map-controls/map-controls.test.tsx +56 -0
  32. package/src/map-controls/map-controls.tsx +220 -0
  33. package/src/map-geojson/index.ts +9 -0
  34. package/src/map-geojson/map-geojson.stories.tsx +124 -0
  35. package/src/map-geojson/map-geojson.tsx +274 -0
  36. package/src/map-marker/index.ts +12 -0
  37. package/src/map-marker/map-marker.stories.tsx +71 -0
  38. package/src/map-marker/map-marker.test.tsx +81 -0
  39. package/src/map-marker/map-marker.tsx +373 -0
  40. package/src/map-popup/index.ts +1 -0
  41. package/src/map-popup/map-popup.tsx +113 -0
  42. package/src/map-route/index.ts +1 -0
  43. package/src/map-route/map-route.stories.tsx +56 -0
  44. package/src/map-route/map-route.tsx +143 -0
  45. package/src/test-utils/maplibre-mock.ts +249 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Manuel Reimitz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ <!-- brand-ui:gen:readme:start -->
2
+ <!-- Generated by scripts/gen-package-readmes.mjs — do not edit inside these markers. -->
3
+
4
+ # `@elabs-ai/components-maps`
5
+
6
+ > MapLibre GL maps: MapCanvas, markers, popups, controls, routes, arcs, GeoJSON, clusters.
7
+
8
+ Part of **brand-ui**, a source-owned, token-driven React component system.
9
+ These packages are **private** and are not published to any registry — they are
10
+ consumed from this workspace. See `docs/CONSUMING.md`.
11
+
12
+ ## Install
13
+
14
+ Inside this monorepo the packages resolve as workspace dependencies:
15
+
16
+ ```json
17
+ "@elabs-ai/components-maps": "workspace:*"
18
+ ```
19
+
20
+ ## Set up styling (do not skip)
21
+
22
+ Components are Tailwind v4 classes backed by semantic tokens. Two lines in
23
+ your CSS entry, or **everything renders unstyled** — the single most common
24
+ mistake:
25
+
26
+ ```css
27
+ @import "@elabs-ai/components-tokens/styles.css";
28
+ @source "../node_modules/@elabs-ai/components-maps/dist";
29
+ ```
30
+
31
+ The `@source` line is required because Tailwind ignores `node_modules`. Add
32
+ one per brand-ui package you render. Then wrap your app once:
33
+
34
+ ```tsx
35
+ import { ThemeProvider } from "@elabs-ai/components-tokens";
36
+
37
+ <ThemeProvider defaultTheme="light">{children}</ThemeProvider>;
38
+ ```
39
+
40
+ ## This package specifically
41
+
42
+ - `maplibre-gl` is a peer — it owns a WebGL context and global CSS.
43
+ - No CSS import needed: `MapCanvas` pulls in MapLibre's stylesheet and the brand overrides itself.
44
+
45
+ ## What's in it
46
+
47
+ 12 exported components — including `MapArc`, `MapCanvas`, `MapClusterLayer`, `MapControls`, `MapGeoJSON`.
48
+
49
+ Don't guess the API — ask the CLI:
50
+
51
+ ```bash
52
+ pnpm add -D @elabs-ai/components-cli
53
+ pnpm exec brand-ui search <query> # find a component
54
+ pnpm exec brand-ui docs <Name> # its real props, from source
55
+ ```
56
+
57
+ ## Using an AI coding agent?
58
+
59
+ For Claude Code, install the plugin from this repo's checkout:
60
+
61
+ ```
62
+ /plugin marketplace add .
63
+ /plugin install brand-ui
64
+ ```
65
+
66
+ `brand-ui docs <Name>` returns intent, composition, state→token mappings and
67
+ anti-patterns — tell your agent to run it instead of guessing a prop. The CLI
68
+ also runs as an MCP server (`brand-ui mcp`).
69
+
70
+ ## Full guide
71
+
72
+ Tailwind and Next.js wiring, per-package extras, agent enablement and a
73
+ prompt for migrating an existing project: `docs/CONSUMING.md`.
74
+
75
+ ## License
76
+
77
+ UNLICENSED — private.
78
+
79
+ <!-- brand-ui:gen:readme:end -->
80
+
81
+ ---
82
+
83
+ ## Package detail
84
+
85
+ Token-driven MapLibre GL map components for brand-ui: `MapCanvas` (theme-aware
86
+ basemap), `MapMarker` (+ content/label/popup/tooltip parts), `MapPopup`,
87
+ `MapControls`, `MapRoute`, `MapArc`, `MapGeoJSON`, `MapClusterLayer`.
88
+
89
+ ```tsx
90
+ import { MapCanvas, MapControls, MapMarker, MapMarkerContent } from "@elabs-ai/components-maps";
91
+
92
+ <div className="h-96">
93
+ <MapCanvas center={[13.4, 52.52]} zoom={11}>
94
+ <MapMarker longitude={13.4} latitude={52.52}>
95
+ <MapMarkerContent />
96
+ </MapMarker>
97
+ <MapControls />
98
+ </MapCanvas>
99
+ </div>;
100
+ ```
101
+
102
+ - The basemap flavor (light/dark) follows the active theme's own
103
+ `color-scheme` (`resolveThemeIsDark`), so a theme you authored yourself
104
+ resolves correctly too; pass `theme` to pin it.
105
+ - Default layer paints (routes, arcs, clusters, GeoJSON fills) resolve
106
+ semantic tokens at runtime — WebGL can't read CSS variables — and re-resolve
107
+ on theme change.
108
+ - `<MapCanvas>` imports MapLibre's CSS and the brand popup overrides itself.
109
+ - MapLibre's attribution control is **disabled by default**. The default Carto
110
+ basemap serves ODbL-licensed OpenStreetMap data that requires the credit, so
111
+ either display it via `<AttributionPanel>`
112
+ (`@elabs-ai/components-ui`) somewhere the user can reach, or
113
+ re-enable the in-map control with `attributionControl={{ compact: true }}`
114
+ (it wins through the prop spread). `styles` / `blank` avoid the requirement
115
+ entirely by changing the tiles.
116
+ - Use `blank` for tile-less data-viz canvases (choropleths, arcs, dot maps).
117
+
118
+ ## Attribution
119
+
120
+ Adapted from [mapcn](https://github.com/AnmolSaini16/mapcn) (MIT License,
121
+ © 2025 Anmoldeep Singh). `maplibre-gl` is BSD-3-Clause.
package/dist/index.css ADDED
@@ -0,0 +1,11 @@
1
+ /* src/map-canvas/maps.css */
2
+ .maplibregl-popup-content {
3
+ background: transparent;
4
+ box-shadow: none;
5
+ padding: 0;
6
+ border-radius: 0;
7
+ }
8
+ .maplibregl-popup-tip {
9
+ display: none;
10
+ }
11
+ /*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/map-canvas/maps.css"],"sourcesContent":["/*\n * MapLibre popup chrome reset — @elabs-ai/components-maps popups paint their own surface\n * (bg-popover, border, shadow), so MapLibre's white bubble + tip must not\n * render underneath. Imported by <MapCanvas> alongside maplibre-gl's CSS.\n */\n.maplibregl-popup-content {\n background: transparent;\n box-shadow: none;\n padding: 0;\n border-radius: 0;\n}\n\n.maplibregl-popup-tip {\n display: none;\n}\n"],"mappings":";AAKA,CAAC;AACC,cAAY;AACZ,cAAY;AACZ,WAAS;AACT,iBAAe;AACjB;AAEA,CAAC;AACC,WAAS;AACX;","names":[]}
@@ -0,0 +1,442 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import MapLibreGL, { MarkerOptions, PopupOptions } from 'maplibre-gl';
4
+ export { LngLatBoundsLike, LngLatLike, MapLayerMouseEvent, MapMouseEvent, MapOptions, ProjectionSpecification, StyleSpecification } from 'maplibre-gl';
5
+
6
+ /** Light-or-dark flavor of the active basemap (derived from the brand theme). */
7
+ type BasemapTheme = "light" | "dark";
8
+ interface MapContextValue {
9
+ /** The live MapLibre map instance (`null` until the map has mounted). */
10
+ map: MapLibreGL.Map | null;
11
+ /** True once the map AND its style are fully loaded — gate layer operations on this. */
12
+ isLoaded: boolean;
13
+ /** Which basemap flavor is active. */
14
+ resolvedTheme: BasemapTheme;
15
+ /**
16
+ * Changes whenever the active brand theme changes. Layer components use it
17
+ * as a dependency key to re-resolve semantic token colors for WebGL paint.
18
+ */
19
+ themeKey: string;
20
+ }
21
+ /** Access the map instance + load state from any descendant of `<MapCanvas>`. */
22
+ declare function useMap(): MapContextValue;
23
+
24
+ /** Map viewport state. */
25
+ interface MapViewport {
26
+ /** Center coordinates [longitude, latitude]. */
27
+ center: [number, number];
28
+ /** Zoom level. */
29
+ zoom: number;
30
+ /** Bearing (rotation) in degrees. */
31
+ bearing: number;
32
+ /** Pitch (tilt) in degrees. */
33
+ pitch: number;
34
+ }
35
+ type MapStyleOption = string | MapLibreGL.StyleSpecification;
36
+ /** The imperative handle exposed by `<MapCanvas ref>`: the MapLibre map itself. */
37
+ type MapCanvasRef = MapLibreGL.Map;
38
+ type MapCanvasProps = {
39
+ children?: ReactNode;
40
+ /** Additional CSS classes for the map container. */
41
+ className?: string;
42
+ /**
43
+ * Basemap flavor. If not provided, it is derived from the active brand theme
44
+ * (`data-theme` + that theme's own `color-scheme`), then a `dark`/`light` root class,
45
+ * then the OS preference.
46
+ */
47
+ theme?: BasemapTheme;
48
+ /** Custom map styles for light and dark themes. Overrides the default Carto styles. */
49
+ styles?: {
50
+ light?: MapStyleOption;
51
+ dark?: MapStyleOption;
52
+ };
53
+ /**
54
+ * Use a transparent, tile-less basemap instead of the default Carto street
55
+ * basemap — a blank canvas. Used alone it renders nothing; add your own
56
+ * layers on top (`<MapGeoJSON>`, `<MapArc>`, markers, …). Ideal for data
57
+ * visualizations. Ignored when an explicit `styles` prop is provided.
58
+ */
59
+ blank?: boolean;
60
+ /** Map projection type. Use `{ type: "globe" }` for a 3D globe view. */
61
+ projection?: MapLibreGL.ProjectionSpecification;
62
+ /**
63
+ * Controlled viewport. When provided together with `onViewportChange`, the
64
+ * map becomes controlled and the viewport is driven by this prop.
65
+ */
66
+ viewport?: Partial<MapViewport>;
67
+ /**
68
+ * Callback fired continuously as the viewport changes (pan, zoom, rotate,
69
+ * pitch). Use standalone to observe changes, or with `viewport` for
70
+ * controlled mode.
71
+ */
72
+ onViewportChange?: (viewport: MapViewport) => void;
73
+ /** Show a loading overlay on the map (e.g. while the app fetches map data). */
74
+ loading?: boolean;
75
+ } & Omit<MapLibreGL.MapOptions, "container" | "style">;
76
+ /**
77
+ * The root map surface — a token/theme-aware MapLibre GL canvas. Compose the
78
+ * other `@elabs-ai/components-maps` components (markers, popups, controls, layers) as
79
+ * children; they reach the map through context (`useMap`).
80
+ *
81
+ * The ref exposes the raw MapLibre `Map` instance for imperative work
82
+ * (`flyTo`, `fitBounds`, …).
83
+ */
84
+ declare const MapCanvas: react.ForwardRefExoticComponent<{
85
+ children?: ReactNode;
86
+ /** Additional CSS classes for the map container. */
87
+ className?: string;
88
+ /**
89
+ * Basemap flavor. If not provided, it is derived from the active brand theme
90
+ * (`data-theme` + that theme's own `color-scheme`), then a `dark`/`light` root class,
91
+ * then the OS preference.
92
+ */
93
+ theme?: BasemapTheme;
94
+ /** Custom map styles for light and dark themes. Overrides the default Carto styles. */
95
+ styles?: {
96
+ light?: MapStyleOption;
97
+ dark?: MapStyleOption;
98
+ };
99
+ /**
100
+ * Use a transparent, tile-less basemap instead of the default Carto street
101
+ * basemap — a blank canvas. Used alone it renders nothing; add your own
102
+ * layers on top (`<MapGeoJSON>`, `<MapArc>`, markers, …). Ideal for data
103
+ * visualizations. Ignored when an explicit `styles` prop is provided.
104
+ */
105
+ blank?: boolean;
106
+ /** Map projection type. Use `{ type: "globe" }` for a 3D globe view. */
107
+ projection?: MapLibreGL.ProjectionSpecification;
108
+ /**
109
+ * Controlled viewport. When provided together with `onViewportChange`, the
110
+ * map becomes controlled and the viewport is driven by this prop.
111
+ */
112
+ viewport?: Partial<MapViewport>;
113
+ /**
114
+ * Callback fired continuously as the viewport changes (pan, zoom, rotate,
115
+ * pitch). Use standalone to observe changes, or with `viewport` for
116
+ * controlled mode.
117
+ */
118
+ onViewportChange?: (viewport: MapViewport) => void;
119
+ /** Show a loading overlay on the map (e.g. while the app fetches map data). */
120
+ loading?: boolean;
121
+ } & Omit<MapLibreGL.MapOptions, "container" | "style"> & react.RefAttributes<MapLibreGL.Map>>;
122
+
123
+ type MapMarkerProps = {
124
+ /** Longitude coordinate for the marker position. */
125
+ longitude: number;
126
+ /** Latitude coordinate for the marker position. */
127
+ latitude: number;
128
+ /** Marker sub-components (MapMarkerContent, MapMarkerPopup, MapMarkerTooltip, MapMarkerLabel). */
129
+ children: ReactNode;
130
+ /** Callback when the marker is clicked. */
131
+ onClick?: (e: MouseEvent) => void;
132
+ /** Callback when the mouse enters the marker. */
133
+ onMouseEnter?: (e: MouseEvent) => void;
134
+ /** Callback when the mouse leaves the marker. */
135
+ onMouseLeave?: (e: MouseEvent) => void;
136
+ /** Callback when a drag starts (requires `draggable`). */
137
+ onDragStart?: (lngLat: {
138
+ lng: number;
139
+ lat: number;
140
+ }) => void;
141
+ /** Callback during a drag (requires `draggable`). */
142
+ onDrag?: (lngLat: {
143
+ lng: number;
144
+ lat: number;
145
+ }) => void;
146
+ /** Callback when a drag ends (requires `draggable`). */
147
+ onDragEnd?: (lngLat: {
148
+ lng: number;
149
+ lat: number;
150
+ }) => void;
151
+ } & Omit<MarkerOptions, "element">;
152
+ /**
153
+ * A marker anchored at a lng/lat. Compose the pieces you need:
154
+ * `MapMarkerContent` (the visual), `MapMarkerLabel`, `MapMarkerPopup` (opens
155
+ * on click) and `MapMarkerTooltip` (shows on hover).
156
+ */
157
+ declare function MapMarker({ longitude, latitude, children, onClick, onMouseEnter, onMouseLeave, onDragStart, onDrag, onDragEnd, draggable, ...markerOptions }: MapMarkerProps): react.JSX.Element;
158
+ interface MapMarkerContentProps {
159
+ /** Custom marker content. Defaults to a primary-colored dot. */
160
+ children?: ReactNode;
161
+ /** Additional CSS classes for the marker container. */
162
+ className?: string;
163
+ }
164
+ /** The marker's visual, portaled into the MapLibre marker element. */
165
+ declare function MapMarkerContent({ children, className }: MapMarkerContentProps): react.ReactPortal;
166
+ type MapMarkerPopupProps = {
167
+ /** Popup content. */
168
+ children: ReactNode;
169
+ /** Additional CSS classes for the popup container. */
170
+ className?: string;
171
+ /** Show a close button in the popup (default: false). */
172
+ closeButton?: boolean;
173
+ } & Omit<PopupOptions, "className" | "closeButton">;
174
+ /** A popup attached to the marker — MapLibre toggles it on marker click. */
175
+ declare function MapMarkerPopup({ children, className, closeButton, ...popupOptions }: MapMarkerPopupProps): react.ReactPortal;
176
+ type MapMarkerTooltipProps = {
177
+ /** Tooltip content. */
178
+ children: ReactNode;
179
+ /** Additional CSS classes for the tooltip container. */
180
+ className?: string;
181
+ } & Omit<PopupOptions, "className" | "closeButton" | "closeOnClick">;
182
+ /** A hover tooltip attached to the marker. */
183
+ declare function MapMarkerTooltip({ children, className, ...popupOptions }: MapMarkerTooltipProps): react.ReactPortal;
184
+ interface MapMarkerLabelProps {
185
+ /** Label text content. */
186
+ children: ReactNode;
187
+ /** Additional CSS classes for the label. */
188
+ className?: string;
189
+ /** Position of the label relative to the marker (default: "top"). */
190
+ position?: "top" | "bottom";
191
+ }
192
+ /**
193
+ * A small always-visible text label above or below the marker. Portaled into
194
+ * the marker element so it anchors to the marker whether composed as a sibling
195
+ * of `MapMarkerContent` or nested inside it (the three-theme sweep caught the
196
+ * sibling composition rendering the label against the map container instead).
197
+ */
198
+ declare function MapMarkerLabel({ children, className, position }: MapMarkerLabelProps): react.ReactPortal;
199
+
200
+ type MapPopupProps = {
201
+ /** Longitude coordinate for the popup position. */
202
+ longitude: number;
203
+ /** Latitude coordinate for the popup position. */
204
+ latitude: number;
205
+ /** Callback when the popup is closed. */
206
+ onClose?: () => void;
207
+ /** Popup content. */
208
+ children: ReactNode;
209
+ /** Additional CSS classes for the popup container. */
210
+ className?: string;
211
+ /** Show a close button in the popup (default: false). */
212
+ closeButton?: boolean;
213
+ } & Omit<PopupOptions, "className" | "closeButton">;
214
+ /**
215
+ * A standalone popup anchored at a lng/lat (not attached to a marker) —
216
+ * typically rendered conditionally from app state (e.g. after a layer click).
217
+ */
218
+ declare function MapPopup({ longitude, latitude, onClose, children, className, closeButton, ...popupOptions }: MapPopupProps): react.ReactPortal;
219
+
220
+ interface MapControlsProps {
221
+ /** Position of the controls on the map (default: "bottom-right"). */
222
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
223
+ /** Show zoom in/out buttons (default: true). */
224
+ showZoom?: boolean;
225
+ /** Show a compass button to reset bearing/pitch (default: false). */
226
+ showCompass?: boolean;
227
+ /** Show a locate button to fly to the user's location (default: false). */
228
+ showLocate?: boolean;
229
+ /** Show a fullscreen toggle button (default: false). */
230
+ showFullscreen?: boolean;
231
+ /** Additional CSS classes for the controls container. */
232
+ className?: string;
233
+ /** Callback with user coordinates when located. */
234
+ onLocate?: (coords: {
235
+ longitude: number;
236
+ latitude: number;
237
+ }) => void;
238
+ /** Callback when geolocation fails or is denied. */
239
+ onLocateError?: (error: GeolocationPositionError) => void;
240
+ }
241
+ /** Branded zoom / compass / locate / fullscreen controls. Render inside `<MapCanvas>`. */
242
+ declare function MapControls({ position, showZoom, showCompass, showLocate, showFullscreen, className, onLocate, onLocateError, }: MapControlsProps): react.JSX.Element;
243
+
244
+ interface MapRouteProps {
245
+ /** Optional unique identifier for the route layer. */
246
+ id?: string;
247
+ /** Array of [longitude, latitude] coordinate pairs defining the route. */
248
+ coordinates: [number, number][];
249
+ /** Line color as a CSS color value. Defaults to the theme's `--primary` token. */
250
+ color?: string;
251
+ /** Line width in pixels (default: 3). */
252
+ width?: number;
253
+ /** Line opacity from 0 to 1 (default: 0.8). */
254
+ opacity?: number;
255
+ /** Dash pattern [dash length, gap length] for dashed lines. */
256
+ dashArray?: [number, number];
257
+ /** Callback when the route line is clicked. */
258
+ onClick?: () => void;
259
+ /** Callback when the mouse enters the route line. */
260
+ onMouseEnter?: () => void;
261
+ /** Callback when the mouse leaves the route line. */
262
+ onMouseLeave?: () => void;
263
+ /** Whether the route is interactive — shows a pointer cursor on hover (default: true). */
264
+ interactive?: boolean;
265
+ }
266
+ /** A GeoJSON line layer for routes/paths. Renders nothing itself — it draws on the map. */
267
+ declare function MapRoute({ id: propId, coordinates, color, width, opacity, dashArray, onClick, onMouseEnter, onMouseLeave, interactive, }: MapRouteProps): null;
268
+
269
+ /** A single arc to render inside `<MapArc data={...}>`. */
270
+ type MapArcDatum = {
271
+ /** Unique identifier for this arc. Required for hover state tracking and event payloads. */
272
+ id: string | number;
273
+ /** Start coordinate as [longitude, latitude]. */
274
+ from: [number, number];
275
+ /** End coordinate as [longitude, latitude]. */
276
+ to: [number, number];
277
+ };
278
+ /** Event payload passed to MapArc interaction callbacks. */
279
+ type MapArcEvent<T extends MapArcDatum = MapArcDatum> = {
280
+ /** The arc datum that was hovered or clicked. */
281
+ arc: T;
282
+ /** Longitude of the cursor at the time of the event. */
283
+ longitude: number;
284
+ /** Latitude of the cursor at the time of the event. */
285
+ latitude: number;
286
+ /** The underlying MapLibre mouse event for advanced use cases. */
287
+ originalEvent: MapLibreGL.MapMouseEvent;
288
+ };
289
+ type MapArcLinePaint = NonNullable<MapLibreGL.LineLayerSpecification["paint"]>;
290
+ type MapArcLineLayout = NonNullable<MapLibreGL.LineLayerSpecification["layout"]>;
291
+ type MapArcProps<T extends MapArcDatum = MapArcDatum> = {
292
+ /** Array of arcs to render. Each arc must have a unique `id`. */
293
+ data: T[];
294
+ /** Optional unique identifier prefix for the arc source/layers. Auto-generated if not provided. */
295
+ id?: string;
296
+ /**
297
+ * How far each arc bows away from a straight line. `0` renders straight
298
+ * lines; higher values bend further; negative values bend to the opposite
299
+ * side. Arcs cross the antimeridian via the shorter direction. (default: 0.2)
300
+ */
301
+ curvature?: number;
302
+ /** Number of samples used to render each curve. Higher = smoother. (default: 64) */
303
+ samples?: number;
304
+ /**
305
+ * MapLibre paint properties for the arc layer. Merged on top of theme-aware
306
+ * defaults (`line-color` = the `--primary` token, `line-width: 2`,
307
+ * `line-opacity: 0.85`). Any value can be a MapLibre expression for
308
+ * per-feature styling; every field on each arc datum (besides `from`/`to`)
309
+ * is exposed via `["get", ...]`.
310
+ */
311
+ paint?: MapArcLinePaint;
312
+ /** MapLibre layout properties for the arc layer. Defaults to rounded joins/caps. */
313
+ layout?: MapArcLineLayout;
314
+ /**
315
+ * Paint properties applied to the arc currently under the cursor. Each key
316
+ * is merged into `paint` as a `case` expression keyed on per-feature hover
317
+ * state, so only the hovered arc changes appearance.
318
+ */
319
+ hoverPaint?: MapArcLinePaint;
320
+ /** Callback when an arc is clicked. */
321
+ onClick?: (e: MapArcEvent<T>) => void;
322
+ /**
323
+ * Callback fired when the hovered arc changes. Receives the cursor's
324
+ * lng/lat at the moment of entry, and `null` when the cursor leaves the
325
+ * last hovered arc.
326
+ */
327
+ onHover?: (e: MapArcEvent<T> | null) => void;
328
+ /** Whether arcs respond to mouse events (default: true). */
329
+ interactive?: boolean;
330
+ /** Optional MapLibre layer id to insert the arc layers before (z-order control). */
331
+ beforeId?: string;
332
+ };
333
+ /**
334
+ * Curved great-circle-style arcs between coordinate pairs (flight paths,
335
+ * network links). Pairs well with `<MapCanvas blank projection={{ type: "globe" }}>`.
336
+ */
337
+ declare function MapArc<T extends MapArcDatum = MapArcDatum>({ data, id: propId, curvature, samples, paint, layout, hoverPaint, onClick, onHover, interactive, beforeId, }: MapArcProps<T>): null;
338
+
339
+ /** Pure geometry for `<MapArc>` — kept engine-free so it can be unit-tested. */
340
+ /**
341
+ * Sample a quadratic Bézier between `from` and `to` in lng/lat space.
342
+ * `curvature` is how far the arc bows away from a straight line (0 = straight;
343
+ * negative bends to the opposite side). The destination longitude is unwrapped
344
+ * relative to the origin so arcs cross the antimeridian via the shorter
345
+ * great-circle direction — resulting longitudes may fall outside [-180, 180],
346
+ * which MapLibre renders correctly on the globe projection.
347
+ */
348
+ declare function buildArcCoordinates(from: [number, number], to: [number, number], curvature: number, samples: number): [number, number][];
349
+
350
+ type MapGeoJSONData<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = GeoJSON.FeatureCollection<GeoJSON.Geometry, P> | GeoJSON.Feature<GeoJSON.Geometry, P> | GeoJSON.Geometry | string;
351
+ type MapFillPaint = NonNullable<MapLibreGL.FillLayerSpecification["paint"]>;
352
+ type MapLinePaint = NonNullable<MapLibreGL.LineLayerSpecification["paint"]>;
353
+ /** A rendered feature with strongly-typed `properties`. */
354
+ type MapGeoJSONFeature<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = Omit<MapLibreGL.MapGeoJSONFeature, "properties"> & {
355
+ properties: P;
356
+ };
357
+ /** Event payload passed to MapGeoJSON interaction callbacks. */
358
+ type MapGeoJSONEvent<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = {
359
+ /** The feature under the cursor, with its typed GeoJSON properties. */
360
+ feature: MapGeoJSONFeature<P>;
361
+ /** Longitude of the cursor at the time of the event. */
362
+ longitude: number;
363
+ /** Latitude of the cursor at the time of the event. */
364
+ latitude: number;
365
+ /** The underlying MapLibre mouse event for advanced use cases. */
366
+ originalEvent: MapLibreGL.MapLayerMouseEvent;
367
+ };
368
+ type MapGeoJSONProps<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = {
369
+ /** GeoJSON data (FeatureCollection, Feature, Geometry) or a URL to fetch it from. */
370
+ data: MapGeoJSONData<P>;
371
+ /** Optional unique identifier prefix for the source/layers. Auto-generated if not provided. */
372
+ id?: string;
373
+ /**
374
+ * Feature property to promote to the feature `id`. Required for hover
375
+ * feature-state (`fillHoverPaint`) and stable `onHover`/`onClick` payloads.
376
+ */
377
+ promoteId?: string;
378
+ /**
379
+ * Paint for the polygon fill layer. Merged on top of a theme-aware neutral
380
+ * default (the `--border` token — a mid neutral that reads on the page
381
+ * surface in every theme). Pass `false` to omit the fill layer entirely
382
+ * (e.g. outlines only).
383
+ */
384
+ fillPaint?: MapFillPaint | false;
385
+ /**
386
+ * Paint for the outline layer. Merged on top of a hairline default
387
+ * (`line-color` = the `--background` token, `line-width` = 0.5) for thin
388
+ * separators. Override `line-color` if your container differs, or pass
389
+ * `false` to omit the layer.
390
+ */
391
+ linePaint?: MapLinePaint | false;
392
+ /**
393
+ * Paint merged onto the fill layer for the feature under the cursor, applied
394
+ * as a `case` expression keyed on hover feature-state. Requires `promoteId`.
395
+ */
396
+ fillHoverPaint?: MapFillPaint;
397
+ /** Callback when a feature is clicked. */
398
+ onClick?: (e: MapGeoJSONEvent<P>) => void;
399
+ /** Callback fired when the hovered feature changes; `null` when the cursor leaves. */
400
+ onHover?: (e: MapGeoJSONEvent<P> | null) => void;
401
+ /** Whether features respond to mouse events (default: false). */
402
+ interactive?: boolean;
403
+ /** Optional MapLibre layer id to insert the layers before (z-order control). */
404
+ beforeId?: string;
405
+ };
406
+ /**
407
+ * Renders arbitrary GeoJSON as fill + outline layers on the map. Composes like
408
+ * `MapRoute` / `MapArc` — drop it inside `<MapCanvas>` (typically with `blank`)
409
+ * for choropleths and region/data maps. For full control over expressions and
410
+ * multiple layers, manage layers directly via `useMap()` instead.
411
+ */
412
+ declare function MapGeoJSON<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties>({ data, id: propId, promoteId, fillPaint, linePaint, fillHoverPaint, onClick, onHover, interactive, beforeId, }: MapGeoJSONProps<P>): null;
413
+
414
+ type MapClusterLayerProps<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties> = {
415
+ /** GeoJSON FeatureCollection data or a URL to fetch GeoJSON from. */
416
+ data: string | GeoJSON.FeatureCollection<GeoJSON.Point, P>;
417
+ /** Maximum zoom level to cluster points on (default: 14). */
418
+ clusterMaxZoom?: number;
419
+ /** Radius of each cluster when clustering points, in pixels (default: 50). */
420
+ clusterRadius?: number;
421
+ /**
422
+ * Colors for cluster circles: [small, medium, large] based on point count.
423
+ * Defaults to the theme's `--success`/`--warning`/`--destructive` tokens.
424
+ */
425
+ clusterColors?: [string, string, string];
426
+ /** Point-count thresholds for the color/size steps: [medium, large] (default: [100, 750]). */
427
+ clusterThresholds?: [number, number];
428
+ /** Color for unclustered individual points. Defaults to the theme's `--primary` token. */
429
+ pointColor?: string;
430
+ /** Callback when an unclustered point is clicked. */
431
+ onPointClick?: (feature: GeoJSON.Feature<GeoJSON.Point, P>, coordinates: [number, number]) => void;
432
+ /** Callback when a cluster is clicked. If not provided, zooms into the cluster. */
433
+ onClusterClick?: (clusterId: number, coordinates: [number, number], pointCount: number) => void;
434
+ };
435
+ /**
436
+ * Clustered point rendering for large point datasets. Cluster circles step
437
+ * through the status tokens (success → warning → destructive) as the point
438
+ * count grows; strokes and count labels use the page surface for contrast.
439
+ */
440
+ declare function MapClusterLayer<P extends GeoJSON.GeoJsonProperties = GeoJSON.GeoJsonProperties>({ data, clusterMaxZoom, clusterRadius, clusterColors, clusterThresholds, pointColor, onPointClick, onClusterClick, }: MapClusterLayerProps<P>): null;
441
+
442
+ export { type BasemapTheme, MapArc, type MapArcDatum, type MapArcEvent, type MapArcLineLayout, type MapArcLinePaint, type MapArcProps, MapCanvas, type MapCanvasProps, type MapCanvasRef, MapClusterLayer, type MapClusterLayerProps, type MapContextValue, MapControls, type MapControlsProps, type MapFillPaint, MapGeoJSON, type MapGeoJSONData, type MapGeoJSONEvent, type MapGeoJSONFeature, type MapGeoJSONProps, type MapLinePaint, MapMarker, MapMarkerContent, type MapMarkerContentProps, MapMarkerLabel, type MapMarkerLabelProps, MapMarkerPopup, type MapMarkerPopupProps, type MapMarkerProps, MapMarkerTooltip, type MapMarkerTooltipProps, MapPopup, type MapPopupProps, MapRoute, type MapRouteProps, type MapStyleOption, type MapViewport, buildArcCoordinates, useMap };