import type { RefObject, ReactElement } from 'react' import type { MapRef, ViewState, LayerProps, FillLayerSpecification, CircleLayerSpecification, LineLayerSpecification, SymbolLayerSpecification, ControlPosition, MarkerProps, } from 'react-map-gl/maplibre' import type { LngLatBoundsLike, LngLat, FilterSpecification } from 'maplibre-gl' export type { ControlPosition, MarkerProps } // ============================================================================ // Marker Types // ============================================================================ export interface MarkerData { id: string longitude: number latitude: number data?: Record } // ============================================================================ // Viewport Types // ============================================================================ export interface MapViewport extends Partial { longitude: number latitude: number zoom: number bearing?: number pitch?: number transitionDuration?: number } // ============================================================================ // Context Types // ============================================================================ export interface MapContextValue { mapRef: RefObject viewport: MapViewport setViewport: (viewport: Partial) => void initialViewport: MapViewport resetToInitial: () => void markers: MarkerData[] setMarkers: React.Dispatch> selectedMarker: MarkerData | null setSelectedMarker: (marker: MarkerData | null) => void hoveredFeature: GeoJSON.Feature | null setHoveredFeature: (feature: GeoJSON.Feature | null) => void isLoaded: boolean setIsLoaded: (loaded: boolean) => void } // ============================================================================ // Hook Return Types // ============================================================================ export interface MapControlActions { flyTo: (center: [number, number], zoom?: number, options?: { duration?: number }) => void easeTo: (center: [number, number], zoom?: number, options?: { duration?: number }) => void fitBounds: (bounds: LngLatBoundsLike, options?: { padding?: number; duration?: number; maxZoom?: number }) => void zoomIn: () => void zoomOut: () => void resetView: (initialViewport: MapViewport) => void getCenter: () => [number, number] | null getZoom: () => number | null getBounds: () => LngLatBoundsLike | null } export interface MarkerActions { markers: MarkerData[] addMarker: (marker: Omit) => string removeMarker: (id: string) => void updateMarker: (id: string, updates: Partial>) => void clearMarkers: () => void fitToMarkers: (paddingOrOpts?: number | { padding?: number; maxZoom?: number }) => void } // ============================================================================ // Event Types // ============================================================================ export interface MapEventHandlers { onClick?: (event: MapMouseEvent) => void onHover?: (event: MapMouseEvent) => void onMoveStart?: () => void onMoveEnd?: (viewport: MapViewport) => void onZoomStart?: () => void onZoomEnd?: (zoom: number) => void onLoad?: () => void } export interface MapMouseEvent { lngLat: LngLat point: { x: number; y: number } features?: GeoJSON.Feature[] originalEvent: MouseEvent } // ============================================================================ // Style Types // ============================================================================ // Single source of truth — derived from `MAP_STYLES` so new basemap keys // (e.g. OpenFreeMap `liberty`/`bright`/`positron`) flow through automatically. export type { MapStyleKey } from './styles' // ============================================================================ // Layer Types (from react-map-gl & maplibre-gl) // ============================================================================ export type { LayerProps, FillLayerSpecification, CircleLayerSpecification, LineLayerSpecification, SymbolLayerSpecification, FilterSpecification, } export interface ClusterLayerOptions { sourceId: string colors?: [string, string, string] radii?: [number, number, number] thresholds?: [number, number] hoverColor?: string } export interface PointLayerOptions { color?: string radius?: number strokeWidth?: number strokeColor?: string } export interface PolygonLayerOptions { fillColor?: string fillOpacity?: number strokeColor?: string strokeWidth?: number } // ============================================================================ // Line Layer Types // ============================================================================ export interface LineLayerOptions { id: string sourceId: string color?: string width?: number opacity?: number dashArray?: number[] lineCap?: 'butt' | 'round' | 'square' lineJoin?: 'bevel' | 'round' | 'miter' blur?: number minZoom?: number maxZoom?: number } export interface RouteLayerOptions extends Omit { sourceId: string } // ============================================================================ // Symbol Layer Types // ============================================================================ /** A data-driven expression (e.g. `['get', 'icon']`) or a static value. */ export type DataDrivenValue = T | unknown[] export interface SymbolLayerOptions { id: string sourceId: string /** * Registered image id (see `useMapImages`) OR a data-driven expression * selecting one per feature, e.g. `['get', 'icon']`. */ iconImage: DataDrivenValue iconSize?: DataDrivenValue iconAllowOverlap?: boolean /** Property name or expression for an optional text label. */ textField?: DataDrivenValue textSize?: DataDrivenValue textColor?: string textOffset?: [number, number] minZoom?: number maxZoom?: number } // ============================================================================ // Control Types // ============================================================================ export interface DrawControlProps { position?: ControlPosition displayControlsDefault?: boolean controls?: { point?: boolean line_string?: boolean polygon?: boolean trash?: boolean combine_features?: boolean uncombine_features?: boolean } defaultMode?: string onCreate?: (evt: { features: object[] }) => void onUpdate?: (evt: { features: object[]; action: string }) => void onDelete?: (evt: { features: object[] }) => void } export interface GeocoderControlProps { position: ControlPosition marker?: boolean | Omit placeholder?: string collapsed?: boolean clearOnBlur?: boolean showResultsWhileTyping?: boolean minLength?: number limit?: number language?: string countries?: string zoom?: number flyTo?: boolean | object onLoading?: (e: object) => void onResults?: (e: object) => void onResult?: (e: object) => void onError?: (e: object) => void } export interface CustomOverlayProps { children: React.ReactNode } // ============================================================================ // Legend Types // ============================================================================ export interface LegendItem { id: string label: string color?: string icon?: React.ReactNode type?: 'circle' | 'line' | 'fill' | 'symbol' visible?: boolean } export interface MapLegendProps { items: LegendItem[] position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' title?: string collapsible?: boolean defaultCollapsed?: boolean className?: string style?: React.CSSProperties onItemClick?: (item: LegendItem) => void } // ============================================================================ // Layer Switcher Types // ============================================================================ export interface LayerConfig { id: string label: string defaultVisible?: boolean group?: string } export interface LayerSwitcherProps { layers: LayerConfig[] position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' title?: string collapsible?: boolean defaultCollapsed?: boolean showToggleAll?: boolean className?: string style?: React.CSSProperties onChange?: (layerId: string, visible: boolean) => void }