diff --git a/README.md b/README.md index 422ba83..fa497e8 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,33 @@ -# rn-mapbox-toolkit +# 🌍 rn-mapbox-toolkit -React native binding for mapbox +**React native binding for mapbox** -## Installation +--- +## 🚀 Installation -```sh +```bash npm install rn-mapbox-toolkit ``` +--- -## Usage \ No newline at end of file +## 🧑‍💻 Usage + +Inside `example/App.tsx` multiple screen exist with diverse example for each component + +```tsx +// See example/App.tsx for full usage demonstration +``` + +--- + +## ✅ Features + +| Feature | Android | iOS | +| --------------------------- | :-----: | :-: | +| MapView & event | ✅ | ❌ | +| Camera | ✅ | ❌ | + + +--- \ No newline at end of file diff --git a/android/src/main/java/com/rnmapboxtoolkit/fabric/RnMapboxToolkitCamera.kt b/android/src/main/java/com/rnmapboxtoolkit/fabric/RnMapboxToolkitCamera.kt index 586cf6e..1937729 100644 --- a/android/src/main/java/com/rnmapboxtoolkit/fabric/RnMapboxToolkitCamera.kt +++ b/android/src/main/java/com/rnmapboxtoolkit/fabric/RnMapboxToolkitCamera.kt @@ -1,6 +1,9 @@ package com.rnmapboxtoolkit.fabric import android.annotation.SuppressLint +import android.util.Log import com.facebook.react.uimanager.ThemedReactContext +import com.mapbox.maps.CameraBoundsOptions +import com.mapbox.maps.MapboxMap import com.mapbox.maps.plugin.animation.CameraAnimatorsFactory import com.mapbox.maps.plugin.animation.easeTo import com.mapbox.maps.plugin.animation.flyTo @@ -15,6 +18,19 @@ class RnMapboxToolkitCamera(context: ThemedReactContext) : AbstractMapFeature(co const val TAG = "RnMapboxToolkitCamera" } + /** + * Default settings for bounds for avoid each new state is preserved when props change + */ + private var pendingBounds = PendingBounds() + + data class PendingBounds( + var maxPitch: Double? = null, + var minPitch: Double? = null, + var minZoom: Double? = null, + var maxZoom: Double? = null + ) + + override fun addToMap(mapView: RnMapboxToolkitView) { super.addToMap(mapView) } @@ -53,7 +69,9 @@ class RnMapboxToolkitCamera(context: ThemedReactContext) : AbstractMapFeature(co animationJson.toAnimationOptions() } - mMapView?.getMapboxMap()?.flyTo(cameraOptions, animOptions) + withMapView { mapView -> + mapView.getMapboxMap()?.flyTo(cameraOptions, animOptions) + } } fun easeTo(cameraOptions: String, animationOptions: String?) { @@ -65,6 +83,43 @@ class RnMapboxToolkitCamera(context: ThemedReactContext) : AbstractMapFeature(co animationJson.toAnimationOptions() } - mMapView?.getMapboxMap()?.easeTo(cameraOptions, animOptions) + withMapView { mapView -> + mapView.getMapboxMap()?.easeTo(cameraOptions, animOptions) + } + } + + fun setMaxPitch(maxPitch: Double) { + pendingBounds.maxPitch = maxPitch + updateBounds() + } + + fun setMinPitch(minPitch: Double) { + pendingBounds.minPitch = minPitch + updateBounds() + } + + fun setMinZoom(minZoom: Double) { + pendingBounds.minZoom = minZoom + updateBounds() + } + + fun setMaxZoom(maxZoom: Double) { + pendingBounds.maxZoom = maxZoom + updateBounds() + } + + private fun updateBounds() { + val builder = CameraBoundsOptions.Builder() + + pendingBounds.maxPitch?.let { builder.maxPitch(it) } + pendingBounds.minPitch?.let { builder.minPitch(it) } + pendingBounds.minZoom?.let { builder.minZoom(it) } + pendingBounds.maxZoom?.let { builder.maxZoom(it) } + + withMapView { it -> + it.getMapboxMap()?.setBounds(builder.build()) + } } } + + diff --git a/android/src/main/java/com/rnmapboxtoolkit/fabric/RnMapboxToolkitCameraManager.kt b/android/src/main/java/com/rnmapboxtoolkit/fabric/RnMapboxToolkitCameraManager.kt index bbae64c..f8cbe5d 100644 --- a/android/src/main/java/com/rnmapboxtoolkit/fabric/RnMapboxToolkitCameraManager.kt +++ b/android/src/main/java/com/rnmapboxtoolkit/fabric/RnMapboxToolkitCameraManager.kt @@ -30,6 +30,34 @@ class RnMapboxToolkitCameraManager : return RnMapboxToolkitCamera(context) } + override fun setMinZoom( + view: RnMapboxToolkitCamera?, + minZoom: Double + ) { + view?.setMinZoom(minZoom) + } + + override fun setMaxZoom( + view: RnMapboxToolkitCamera?, + maxZoom: Double + ) { + view?.setMaxZoom(maxZoom) + } + + override fun setMinPitch( + view: RnMapboxToolkitCamera?, + minPitch: Double + ) { + view?.setMinPitch(minPitch) + } + + override fun setMaxPitch( + view: RnMapboxToolkitCamera?, + maxPitch: Double + ) { + view?.setMaxPitch(maxPitch) + } + override fun flyTo(view: RnMapboxToolkitCamera, cameraOptions: String, animationOptions: String?) { view.flyTo(cameraOptions, animationOptions) } diff --git a/example/android/app/src/main/java/rnmapboxtoolkit/example/MainActivity.kt b/example/android/app/src/main/java/rnmapboxtoolkit/example/MainActivity.kt index 7c31f14..c3c0b85 100644 --- a/example/android/app/src/main/java/rnmapboxtoolkit/example/MainActivity.kt +++ b/example/android/app/src/main/java/rnmapboxtoolkit/example/MainActivity.kt @@ -1,5 +1,6 @@ package rnmapboxtoolkit.example +import android.os.Bundle; import com.facebook.react.ReactActivity import com.facebook.react.ReactActivityDelegate import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.fabricEnabled @@ -19,4 +20,9 @@ class MainActivity : ReactActivity() { */ override fun createReactActivityDelegate(): ReactActivityDelegate = DefaultReactActivityDelegate(this, mainComponentName, fabricEnabled) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(null) + } + } diff --git a/example/package.json b/example/package.json index f8fe30c..984ffd5 100644 --- a/example/package.json +++ b/example/package.json @@ -11,9 +11,13 @@ }, "dependencies": { "@react-native/new-app-screen": "0.81.0", + "@react-navigation/elements": "^2.6.4", + "@react-navigation/native": "^7.1.17", + "@react-navigation/native-stack": "^7.3.26", "react": "19.1.0", "react-native": "0.81.0", - "react-native-safe-area-context": "^5.5.2" + "react-native-safe-area-context": "^5.6.1", + "react-native-screens": "^4.16.0" }, "devDependencies": { "@babel/core": "^7.25.2", diff --git a/example/src/App.tsx b/example/src/App.tsx index 662faa7..bbf3b04 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,119 +1,26 @@ -import React from 'react'; -import { Button, StyleSheet } from 'react-native'; -import { - Camera, - type CameraRef, - MapView, - type MapViewRef, -} from 'rn-mapbox-toolkit'; +import { NavigationContainer } from '@react-navigation/native'; +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import ScreenList from './GroupScreen'; +import MapSettings from './screens/MapSettings'; +import MapCamera from './screens/MapCamera'; +import MapEventsListener from './screens/MapEventsListener'; -export default function App() { - const mapRef = React.useRef(null); - const cameraRef = React.useRef(null); - - const handleCameraZoom = async () => { - try { - const zoom = await cameraRef.current?.getZoomLevel(); - console.log(zoom); - } catch (error) { - console.error('An error occured', error); - } - }; - - const handleGetZoom = async () => { - try { - const zoomLevel = await mapRef.current?.getZoomLevel(); - console.log(zoomLevel); - } catch (error) { - console.error('An error occured', error); - } - }; - - const handleFlyTo = async () => { - try { - await cameraRef.current?.flyTo( - { - center: { longitude: 2.333333, latitude: 48.866667 }, - }, - { - duration: 5000, - startDelay: 2000, - } - ); - } catch (error) { - console.error('An error occured', error); - } - }; +const Stack = createNativeStackNavigator(); +export default function App() { return ( <> - - console.log( - 'onMapIdle', - JSON.stringify(e.nativeEvent.properties, null, 2) - ) - } - onMapLoaded={() => console.log('onMapLoaded trigger')} - onStyleDataLoaded={(e) => console.log(e.nativeEvent.properties.type)} - onStyleLoaded={() => console.log('onStyleLoaded trigger')} - onMapLoadingError={(e) => - console.log('onMapLoadingError', e.nativeEvent.properties.type) - } - onSourceAdded={(e) => - console.log('onSourceAdded', e.nativeEvent.properties.sourceId) - } - onStyleImageMissing={(e) => - console.log('onStyleImageMissing', e.nativeEvent.properties.imageId) - } - // onRenderFrameStarted={() => console.log('onRenderFrameStarted')} - // onRenderFrameFinished={() => console.log('onRenderFrameFinished')} - onSourceRemoved={(e) => - console.log('onSourceRemoved', e.nativeEvent.properties.sourceId) - } - onMapClick={(e) => console.log('onMapClick', e.nativeEvent.properties)} - onMapLongClick={(e) => - console.log('onMapLongClick', e.nativeEvent.properties) - } - > - - - -