import * as React from 'react'; /** * Coordinate tuple — `[lng, lat]`. Always tuples in the public API; each * adapter normalizes internally to whatever shape its provider expects * (Google takes `{ lat, lng }` objects, Mapbox/MapLibre take tuples). */ type Coords = [lng: number, lat: number]; type MapAppearance = "light" | "dark" | "satellite" | "auto"; /** * Which built-in map UI (zoom buttons today; compass/scale could join) * renders on the map. `"auto"` (default) shows zoom buttons whenever the * map is `interactive`; `"zoom"` forces them on regardless; `"none"` * hides every tool. Attribution is NOT a tool — it's a license * requirement and always renders. */ type MapTools = "auto" | "zoom" | "none"; /** * Corner the tools dock to. One vocabulary across providers — each * adapter maps it to its native enum (Leaflet `topleft`, MapLibre/Mapbox * `top-left`, Google `ControlPosition.*`). */ type MapToolsPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right"; type MapErrorCode = "sdk-missing" | "api-key-missing" | "provider-init-failed" | "style-load-failed" | "tile-load-failed"; type MapError = { code: MapErrorCode; message: string; cause?: unknown; }; /** * Imperative handle exposed via `ref` and `onLoad`. Use for fly-to * animations, fit-bounds, and the `.instance` escape hatch when you * need the provider-native map object. */ type MapHandle = { /** Pan + zoom to a marker by id, or directly to coords. */ flyTo: (idOrCoords: string | Coords, opts?: { zoom?: number; durationMs?: number; }) => void; panTo: (coords: Coords, opts?: { durationMs?: number; }) => void; fitBounds: (coords: Coords[], opts?: { paddingPx?: number; durationMs?: number; }) => void; getCenter: () => Coords; getZoom: () => number; getBounds: () => [Coords, Coords]; /** * Provider-native instance. Cast to `mapboxgl.Map`, `maplibregl.Map`, * or `google.maps.Map` to reach features the wrapper doesn't expose * (3D extrusions, custom layers, drawing tools, heatmaps). * Touching this makes your code provider-specific. */ readonly instance: unknown; }; type MapBaseProps = { center: Coords; zoom: number; /** [southwest, northeast] — takes precedence over center/zoom when set. */ bounds?: [Coords, Coords]; /** Default `"auto"` (follows GradeThemeProvider mode). */ appearance?: MapAppearance; /** Default `true`. `false` disables pan/zoom/rotate (static display). */ interactive?: boolean; /** Which built-in map UI shows. Default `"auto"` — zoom buttons follow * `interactive`. `"none"` for chrome-free maps (hero backdrops, * posters); attribution always stays (license). */ tools?: MapTools; /** Corner the tools dock to. Default `"top-left"`. Handy when the * layout puts a search bar or legend over the default corner. */ toolsPosition?: MapToolsPosition; /** Controlled hovered marker id — pairs with `onHoveredIdChange` for list↔map sync. */ hoveredId?: string | null; onHoveredIdChange?: (id: string | null) => void; onLoad?: (handle: MapHandle) => void; onError?: (error: MapError) => void; className?: string; style?: React.CSSProperties; children?: React.ReactNode; }; /** * Discriminated union — TS enforces the right config fields per provider. * * @example * // Zero-config public demo (MapLibre + MapTiler demo key): * * * // Mapbox: * * * // Google: * */ type MapProps = MapBaseProps & ({ provider?: "maplibre"; /** Override the default MapTiler style URL entirely. */ styleUrl?: string; /** Your MapTiler key. Omit to use the Grade demo key (referrer-locked to gradeui.com). */ tilerKey?: string; } | { provider: "mapbox"; accessToken: string; /** Override the appearance-derived Mapbox style URL. */ styleUrl?: string; } | { provider: "google"; apiKey: string; /** Optional Google Cloud Map ID for cloud-based styling. */ mapId?: string; }); type MapMarkerProps = { /** Required — used by `flyTo(id)` and `hoveredId` matching. */ id: string; at: Coords; /** Default `"bottom"` — pin tip at the coord. */ anchor?: "center" | "bottom"; className?: string; children?: React.ReactNode; onClick?: (e: { id: string; coords: Coords; native: MouseEvent; }) => void; }; type AdapterCallbacks = { onLoad: () => void; onError: (error: MapError) => void; onMarkerHover: (id: string | null) => void; onMarkerClick: (id: string, coords: Coords, native: MouseEvent) => void; }; type AdapterOpts = { center: Coords; zoom: number; bounds?: [Coords, Coords]; appearance: "light" | "dark" | "satellite"; interactive: boolean; tools: MapTools; toolsPosition: MapToolsPosition; styleUrl?: string; tilerKey?: string; accessToken?: string; apiKey?: string; mapId?: string; }; type MarkerHandle = { /** DOM element the adapter created for this marker. `` portals its children into here. */ element: HTMLElement; /** Last-known coords, used by `flyTo(id)`. Updated via `setPosition`. */ coords: Coords; setHovered: (hovered: boolean) => void; setPosition: (coords: Coords) => void; remove: () => void; }; type AdapterInstance = { setCenter: (coords: Coords) => void; setZoom: (zoom: number) => void; setBounds: (sw: Coords, ne: Coords) => void; setAppearance: (appearance: "light" | "dark" | "satellite") => void; setInteractive: (enabled: boolean) => void; flyTo: (coords: Coords, opts?: { zoom?: number; durationMs?: number; }) => void; panTo: (coords: Coords, opts?: { durationMs?: number; }) => void; fitBounds: (coords: Coords[], opts?: { paddingPx?: number; durationMs?: number; }) => void; getCenter: () => Coords; getZoom: () => number; getBounds: () => [Coords, Coords]; addMarker: (id: string, coords: Coords, anchor: "center" | "bottom") => MarkerHandle; destroy: () => void; instance: unknown; }; type AdapterFactory = (container: HTMLElement, opts: AdapterOpts, callbacks: AdapterCallbacks) => Promise; export type { AdapterFactory as A, Coords as C, MapProps as M, MapHandle as a, MapMarkerProps as b, MapAppearance as c, MapError as d, MapErrorCode as e };