diff --git a/android/src/main/java/com/luggmaps/LuggMapView.kt b/android/src/main/java/com/luggmaps/LuggMapView.kt index ece4cb9..82a133c 100644 --- a/android/src/main/java/com/luggmaps/LuggMapView.kt +++ b/android/src/main/java/com/luggmaps/LuggMapView.kt @@ -220,9 +220,15 @@ class LuggMapView(private val reactContext: ThemedReactContext) : provider?.setTheme(value) } - fun setEdgeInsets(top: Int, left: Int, bottom: Int, right: Int) { + fun setEdgeInsets( + top: Int, + left: Int, + bottom: Int, + right: Int, + duration: Int = 0 + ) { edgeInsets = EdgeInsets(top, left, bottom, right) - provider?.setEdgeInsets(edgeInsets) + provider?.setEdgeInsets(edgeInsets, duration) } // endregion diff --git a/android/src/main/java/com/luggmaps/LuggMapViewManager.kt b/android/src/main/java/com/luggmaps/LuggMapViewManager.kt index d38d7e7..2fd966e 100644 --- a/android/src/main/java/com/luggmaps/LuggMapViewManager.kt +++ b/android/src/main/java/com/luggmaps/LuggMapViewManager.kt @@ -159,6 +159,23 @@ class LuggMapViewManager : view.moveCamera(latitude, longitude, zoom, duration.toInt()) } + override fun setEdgeInsets( + view: LuggMapView, + top: Double, + left: Double, + bottom: Double, + right: Double, + duration: Double + ) { + view.setEdgeInsets( + top.toFloat().dpToPx().toInt(), + left.toFloat().dpToPx().toInt(), + bottom.toFloat().dpToPx().toInt(), + right.toFloat().dpToPx().toInt(), + duration.toInt() + ) + } + override fun fitCoordinates( view: LuggMapView, coordinates: ReadableArray?, diff --git a/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt b/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt index f38cc5b..b043b76 100644 --- a/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +++ b/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt @@ -209,6 +209,19 @@ class GoogleMapProvider(private val context: Context) : } override fun setEdgeInsets(edgeInsets: EdgeInsets) { + val oldInsets = this.edgeInsets + this.edgeInsets = edgeInsets + applyEdgeInsets() + + val map = googleMap + if (map != null && oldInsets != edgeInsets) { + mapView?.post { + setEdgeInsets(edgeInsets, 0) + } + } + } + + override fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int) { val map = googleMap val oldInsets = this.edgeInsets this.edgeInsets = edgeInsets @@ -216,7 +229,11 @@ class GoogleMapProvider(private val context: Context) : if (map != null && oldInsets != edgeInsets) { val cameraUpdate = CameraUpdateFactory.newCameraPosition(map.cameraPosition) applyEdgeInsets() - map.moveCamera(cameraUpdate) + when { + duration < 0 -> map.animateCamera(cameraUpdate) + duration > 0 -> map.animateCamera(cameraUpdate, duration, null) + else -> map.moveCamera(cameraUpdate) + } } else { applyEdgeInsets() } diff --git a/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt b/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt index 553acad..ce9ff85 100644 --- a/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +++ b/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt @@ -30,6 +30,7 @@ interface MapProvider { fun setMinZoom(zoom: Double) fun setMaxZoom(zoom: Double) fun setEdgeInsets(edgeInsets: EdgeInsets) + fun setEdgeInsets(edgeInsets: EdgeInsets, duration: Int) // Children fun addMarkerView(markerView: LuggMarkerView) diff --git a/docs/MAPVIEW.md b/docs/MAPVIEW.md index e761669..9f573cb 100644 --- a/docs/MAPVIEW.md +++ b/docs/MAPVIEW.md @@ -56,7 +56,13 @@ mapRef.current?.fitCoordinates( { latitude: 37.7749, longitude: -122.4194 }, { latitude: 37.8049, longitude: -122.4094 }, ], - { edgeInsets: { top: 50, left: 50, bottom: 50, right: 50 }, duration: 500 } + { padding: { top: 50, left: 50, bottom: 50, right: 50 }, duration: 500 } +); + +// Set edge insets with animation +mapRef.current?.setEdgeInsets( + { top: 0, left: 0, bottom: 200, right: 0 }, + { duration: 300 } ); ``` @@ -81,11 +87,23 @@ Fit multiple coordinates in the visible map area. fitCoordinates(coordinates: Coordinate[], options?: FitCoordinatesOptions): void interface FitCoordinatesOptions { - edgeInsets?: EdgeInsets; + padding?: EdgeInsets; duration?: number; // milliseconds, -1 for default } ``` +### setEdgeInsets + +Programmatically update the map's edge insets with optional animation. + +```ts +setEdgeInsets(edgeInsets: EdgeInsets, options?: SetEdgeInsetsOptions): void + +interface SetEdgeInsetsOptions { + duration?: number; // milliseconds, -1 for default animation, 0 for instant +} +``` + ## Events ### onCameraMove diff --git a/docs/POLYLINE.md b/docs/POLYLINE.md index 9aaa734..a0a3f0a 100644 --- a/docs/POLYLINE.md +++ b/docs/POLYLINE.md @@ -38,6 +38,29 @@ import { MapView, Polyline } from '@lugg/maps'; | `coordinates` | `Coordinate[]` | **required** | Array of coordinates | | `strokeWidth` | `number` | - | Line width in points | | `strokeColors` | `ColorValue[]` | - | Gradient colors along the line | +| `animated` | `boolean` | `false` | Animate the polyline with a snake effect | +| `animatedOptions` | `PolylineAnimatedOptions` | - | Animation configuration options | +| `zIndex` | `number` | - | Z-index for layering polylines | + +## Animated Options + +```ts +interface PolylineAnimatedOptions { + duration?: number; // milliseconds, default 2150 + easing?: 'linear' | 'easeIn' | 'easeOut' | 'easeInOut'; // default 'linear' + trailLength?: number; // 0-1, default 1.0 (full snake effect) + delay?: number; // milliseconds, default 0 +} +``` + +```tsx + +``` ## Gradient Colors diff --git a/example/bare/ios/Podfile.lock b/example/bare/ios/Podfile.lock index 96db141..0c75b36 100644 --- a/example/bare/ios/Podfile.lock +++ b/example/bare/ios/Podfile.lock @@ -11,7 +11,7 @@ PODS: - hermes-engine (0.14.0): - hermes-engine/Pre-built (= 0.14.0) - hermes-engine/Pre-built (0.14.0) - - LuggMaps (0.2.0-alpha.24): + - LuggMaps (0.2.0-alpha.27): - boost - DoubleConversion - fast_float @@ -3050,7 +3050,7 @@ SPEC CHECKSUMS: glog: 5683914934d5b6e4240e497e0f4a3b42d1854183 GoogleMaps: 0608099d4870cac8754bdba9b6953db543432438 hermes-engine: 3515eff1a2de44b79dfa94a03d1adeed40f0dafe - LuggMaps: decd48fedf38073fcd53a74394624d5eaa763b2b + LuggMaps: 52b43077d9f8ca87c7c9c22771aa61fd93df5ae3 RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669 RCTDeprecation: 2b70c6e3abe00396cefd8913efbf6a2db01a2b36 RCTRequired: f3540eee8094231581d40c5c6d41b0f170237a81 diff --git a/example/shared/src/Home.tsx b/example/shared/src/Home.tsx index 9d2c30f..66c1e74 100644 --- a/example/shared/src/Home.tsx +++ b/example/shared/src/Home.tsx @@ -13,15 +13,15 @@ import { type CameraEventPayload, } from '@lugg/maps'; import { - type TrueSheet, + TrueSheet, TrueSheetProvider, + type DetentChangeEvent, } from '@lodev09/react-native-true-sheet'; import { ReanimatedTrueSheet, ReanimatedTrueSheetProvider, useReanimatedTrueSheet, } from '@lodev09/react-native-true-sheet/reanimated'; -import { useDerivedValue } from 'react-native-reanimated'; import { Button, Map } from './components'; import { randomFrom, randomLetter } from './utils'; @@ -33,35 +33,71 @@ import { } from './markers'; import { useLocationPermission } from './useLocationPermission'; +const bottomEdgeInsets = (bottom: number) => ({ + top: 0, + left: 0, + bottom, + right: 0, +}); + +export function Home() { + const apiKey = process.env.GOOGLE_MAPS_API_KEY; + + return ( + + + + + + + + ); +} + function HomeContent() { const mapRef = useRef(null); const sheetRef = useRef(null); const { height: screenHeight } = useWindowDimensions(); const locationPermission = useLocationPermission(); + const { animatedPosition } = useReanimatedTrueSheet(); const [provider, setProvider] = useState('apple'); const [showMap, setShowMap] = useState(true); const [markers, setMarkers] = useState(INITIAL_MARKERS); const [cameraPosition, setCameraPosition] = useState(); const [isIdle, setIsIdle] = useState(true); - const { animatedPosition } = useReanimatedTrueSheet(); + const getSheetBottom = useCallback( + (event: DetentChangeEvent) => screenHeight - event.nativeEvent.position, + [screenHeight] + ); - const animatedEdgeInsetsBottom = useDerivedValue( - () => screenHeight - animatedPosition.value + const handleMapReady = useCallback(() => { + const bottom = screenHeight - animatedPosition.value; + if (bottom > 0) { + mapRef.current?.setEdgeInsets(bottomEdgeInsets(bottom)); + } + }, [screenHeight, animatedPosition]); + + const handleSheetPresent = useCallback( + (event: DetentChangeEvent) => { + const bottom = getSheetBottom(event); + mapRef.current?.setEdgeInsets(bottomEdgeInsets(bottom)); + }, + [getSheetBottom] ); - const handleCameraMove = useCallback( - (event: { nativeEvent: CameraEventPayload }) => { - setCameraPosition(event.nativeEvent); - setIsIdle(false); + const handleDetentChange = useCallback( + (event: DetentChangeEvent) => { + const bottom = getSheetBottom(event); + mapRef.current?.setEdgeInsets(bottomEdgeInsets(bottom)); }, - [] + [getSheetBottom] ); - const handleCameraIdle = useCallback( - (event: { nativeEvent: CameraEventPayload }) => { + const handleCameraEvent = useCallback( + (event: { nativeEvent: CameraEventPayload }, idle: boolean) => { setCameraPosition(event.nativeEvent); - setIsIdle(true); + setIsIdle(idle); }, [] ); @@ -103,7 +139,7 @@ function HomeContent() { const fitAllMarkers = () => { const coordinates = markers.map((m) => m.coordinate); mapRef.current?.fitCoordinates(coordinates, { - edgeInsets: { + padding: { top: 60, left: 40, right: 40, @@ -112,93 +148,82 @@ function HomeContent() { }); }; - const apiKey = process.env.GOOGLE_MAPS_API_KEY; - return ( - - - - {showMap && ( - + + {showMap && ( + handleCameraEvent(e, false)} + onCameraIdle={(e) => handleCameraEvent(e, true)} + /> + )} + + + + {cameraPosition ? ( + <> + {cameraPosition.coordinate.latitude.toFixed(5)},{' '} + {cameraPosition.coordinate.longitude.toFixed(5)} (z + {cameraPosition.zoom.toFixed(1)}) + {isIdle + ? ` (idle${cameraPosition.gesture ? ', gesture' : ''})` + : cameraPosition.gesture + ? ' (gesture)' + : ''} + + ) : ( + 'Loading...' )} - - - - {cameraPosition ? ( - <> - {cameraPosition.coordinate.latitude.toFixed(5)},{' '} - {cameraPosition.coordinate.longitude.toFixed(5)} (z - {cameraPosition.zoom.toFixed(1)}) - {isIdle - ? ` (idle${cameraPosition.gesture ? ', gesture' : ''})` - : cameraPosition.gesture - ? ' (gesture)' - : ''} - - ) : ( - 'Loading...' - )} - - -