| import React, { useRef, useCallback, useEffect } from 'react'; |
| import { StyleSheet } from 'react-native'; |
| import { WebView } from 'react-native-webview'; |
| import type { LatLng } from '../../types/location'; |
| import { getLeafletMapHtml } from './LeafletMapHtml'; |
| import type { LeafletMapMarker, LeafletMapPolyline } from './LeafletMapHtml'; |
|
|
| export interface LeafletMapProps { |
| |
| center: LatLng; |
| |
| zoom?: number; |
| |
| markers?: LeafletMapMarker[]; |
| |
| polylines?: LeafletMapPolyline[]; |
| |
| showCurrentLocation?: boolean; |
| |
| interactive?: boolean; |
| |
| onMarkerTap?: (data: { lat: number; lng: number; title?: string; type?: string }) => void; |
| |
| onMapReady?: () => void; |
| |
| onMapMove?: (data: { lat: number; lng: number; zoom: number }) => void; |
| |
| style?: object; |
| } |
|
|
| |
| |
| |
| |
| |
| export function LeafletMap({ |
| center, |
| zoom = 14, |
| markers = [], |
| polylines = [], |
| showCurrentLocation = false, |
| interactive = true, |
| onMarkerTap, |
| onMapReady, |
| onMapMove, |
| style, |
| }: LeafletMapProps) { |
| const webViewRef = useRef<WebView>(null); |
|
|
| |
| const html = getLeafletMapHtml({ |
| centerLat: center.lat, |
| centerLng: center.lng, |
| zoom, |
| markers, |
| polylines, |
| showCurrentLocation, |
| interactive, |
| }); |
|
|
| const handleMessage = useCallback( |
| (event: { nativeEvent: { data: string } }) => { |
| try { |
| const msg = JSON.parse(event.nativeEvent.data); |
| switch (msg.event) { |
| case 'map_ready': |
| onMapReady?.(); |
| break; |
| case 'marker_tap': |
| onMarkerTap?.(msg.data); |
| break; |
| case 'map_move': |
| onMapMove?.(msg.data); |
| break; |
| } |
| } catch { |
| |
| } |
| }, |
| [onMapReady, onMarkerTap, onMapMove], |
| ); |
|
|
| |
| |
| const runJS = useCallback((js: string) => { |
| webViewRef.current?.injectJavaScript(js); |
| }, []); |
|
|
| |
| |
| useEffect(() => { |
| |
| }, [center.lat, center.lng]); |
|
|
| return ( |
| <WebView |
| ref={webViewRef} |
| source={{ html }} |
| style={[styles.webview, style]} |
| originWhitelist={['*']} |
| onMessage={handleMessage} |
| javaScriptEnabled |
| domStorageEnabled |
| scrollEnabled={false} |
| bounces={false} |
| showsHorizontalScrollIndicator={false} |
| showsVerticalScrollIndicator={false} |
| cacheEnabled |
| cacheMode="LOAD_DEFAULT" |
| androidLayerType="hardware" |
| // @ts-expect-error – mixed content mode needed for OSM tiles |
| mixedContentMode="always" |
| pointerEvents={interactive ? 'auto' : 'none'} |
| /> |
| ); |
| } |
|
|
| const styles = StyleSheet.create({ |
| webview: { |
| flex: 1, |
| backgroundColor: 'transparent', |
| }, |
| }); |
|
|