Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions __tests__/interface.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ describe('Public Interface', () => {
'StyleSheet',
'Light',
'PointAnnotation',
'PointAnnotationManager',
'MarkerView',
'Annotation',
'Callout',
Expand Down
2 changes: 2 additions & 0 deletions android/src/main/java/com/rnmapbox/rnmbx/RNMBXPackage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.rnmapbox.rnmbx.components.annotation.RNMBXCalloutManager
import com.rnmapbox.rnmbx.components.annotation.RNMBXMarkerViewContentManager
import com.rnmapbox.rnmbx.components.annotation.RNMBXMarkerViewManager
import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotationManager
import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotationManagerViewManager
import com.rnmapbox.rnmbx.components.annotation.RNMBXPointAnnotationModule
import com.rnmapbox.rnmbx.components.camera.RNMBXCameraManager
import com.rnmapbox.rnmbx.components.camera.RNMBXCameraModule
Expand Down Expand Up @@ -135,6 +136,7 @@ class RNMBXPackage : TurboReactPackage() {
managers.add(RNMBXMarkerViewManager(reactApplicationContext))
managers.add(RNMBXMarkerViewContentManager(reactApplicationContext))
managers.add(RNMBXPointAnnotationManager(reactApplicationContext, getViewTagResolver(reactApplicationContext, "RNMBXPointAnnotationManager")))
managers.add(RNMBXPointAnnotationManagerViewManager(reactApplicationContext))
managers.add(RNMBXCalloutManager())
managers.add(RNMBXNativeUserLocationManager())
managers.add(RNMBXCustomLocationProviderManager())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.rnmapbox.rnmbx.components.annotation

import android.content.Context
import com.rnmapbox.rnmbx.components.AbstractMapFeature
import com.rnmapbox.rnmbx.components.RemovalReason
import com.rnmapbox.rnmbx.components.mapview.RNMBXMapView

class RNMBXPointAnnotationManagerView(context: Context) : AbstractMapFeature(context) {
var slot: String? = null
set(value) {
field = value
applySlot()
}

private fun applySlot() {
withMapView { mapView ->
mapView.pointAnnotations?.manager?.slot = slot
}
}

override fun addToMap(mapView: RNMBXMapView) {
super.addToMap(mapView)
applySlot()
}

override fun removeFromMap(mapView: RNMBXMapView, reason: RemovalReason): Boolean {
mapView.pointAnnotations?.manager?.slot = null
return super.removeFromMap(mapView, reason)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.rnmapbox.rnmbx.components.annotation

import com.facebook.react.bridge.Dynamic
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.common.MapBuilder
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.viewmanagers.RNMBXPointAnnotationManagerManagerInterface
import com.rnmapbox.rnmbx.components.AbstractEventEmitter

class RNMBXPointAnnotationManagerViewManager(context: ReactApplicationContext) :
AbstractEventEmitter<RNMBXPointAnnotationManagerView>(context),
RNMBXPointAnnotationManagerManagerInterface<RNMBXPointAnnotationManagerView> {
override fun customEvents(): Map<String, String>? {
return MapBuilder.builder<String, String>().build()
}

override fun getName(): String {
return REACT_CLASS
}

override fun createViewInstance(context: ThemedReactContext): RNMBXPointAnnotationManagerView {
return RNMBXPointAnnotationManagerView(context)
}

companion object {
const val REACT_CLASS = "RNMBXPointAnnotationManager"
}

@ReactProp(name = "slot")
override fun setSlot(view: RNMBXPointAnnotationManagerView, value: Dynamic) {
view.slot = if (value.isNull) null else value.asString()
}
}
24 changes: 24 additions & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -6529,6 +6529,30 @@
"relPath": "src/components/PointAnnotation.tsx",
"name": "PointAnnotation"
},
"PointAnnotationManager": {
"description": "Configures the shared PointAnnotation manager for the parent MapView.\nWrap PointAnnotation components as children.",
"displayName": "PointAnnotationManager",
"methods": [],
"props": [
{
"name": "slot",
"required": false,
"type": "Slot \\| (string & {})",
"default": "none",
"description": "The slot in the style layer stack to position the annotation layer.\nUse with Mapbox Standard style to control layer ordering."
},
{
"name": "children",
"required": false,
"type": "ReactNode",
"default": "none",
"description": "FIX ME NO DESCRIPTION"
}
],
"fileNameWithExt": "PointAnnotationManager.tsx",
"relPath": "src/components/PointAnnotationManager.tsx",
"name": "PointAnnotationManager"
},
"Rain": {
"description": "",
"displayName": "Rain",
Expand Down
14 changes: 14 additions & 0 deletions docs/examples.json
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,20 @@
"relPath": "Annotations/PointAnnotationAnchors.js",
"name": "PointAnnotationAnchors"
},
{
"metadata": {
"title": "PointAnnotationManager Slot",
"tags": [
"PointAnnotationManager",
"PointAnnotation",
"slot"
],
"docs": "\nDemonstrates using PointAnnotationManager to position annotations\nin different slots of the Mapbox Standard style.\n "
},
"fullPath": "example/src/examples/Annotations/PointAnnotationManagerSlot.tsx",
"relPath": "Annotations/PointAnnotationManagerSlot.tsx",
"name": "PointAnnotationManagerSlot"
},
{
"metadata": {
"title": "Show Point Annotations",
Expand Down
89 changes: 89 additions & 0 deletions example/src/examples/Annotations/MarkerViewDisappear.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { Camera, MapView, MarkerView } from '@rnmapbox/maps';
import { useEffect, useState } from 'react';
import { Platform, Pressable, StyleSheet, Text, View } from 'react-native';

const POSITIONS = Object.fromEntries([
['id1', [-1.202582, 43.36005] as [number, number]],
['id2', [-1.303701, 43.384357] as [number, number]],
]);

type Rider = {
id: string;
name: string;
order: number;
};

const RIDERS: Rider[] = [
{ id: 'id2', name: 'Favorite Rider', order: 100 },
{ id: 'id1', name: 'Normal Rider', order: 10 },
];

export const Test = () => {
const [selectedId, setSelectedId] = useState<string | null>(null);
const [positions, setPositions] = useState<Record<string, [number, number]>>({});

useEffect(() => {
const interval = setInterval(() => {
setPositions(POSITIONS);
}, 1000);
return () => clearInterval(interval);
}, []);

const labels = RIDERS.map(rider => ({
...rider,
order: rider.id === selectedId ? 10000 : rider.order,
selected: rider.id === selectedId,
})).sort((a, b) => a.order - b.order);

return (
<View style={StyleSheet.absoluteFill}>
<MapView
style={{ flex: 1 }}
onPress={() => {
if (Platform.OS !== 'web') setSelectedId(null);
}}>
<Camera centerCoordinate={[-1.21, 43.38]} zoomLevel={10} />

{labels.map(label => {
const position = positions[label.id];
if (!position) return null;

return (
<MarkerView
key={label.id}
id={label.id}
coordinate={position}
anchor={{ x: 0, y: 1 }}
allowOverlap={false}
isSelected={label.selected}>
<Pressable onPress={() => setSelectedId(label.id)}>
<View
style={{
borderRadius: 16,
borderWidth: 2,
borderColor: label.selected ? '#fff' : '#4CAF50',
backgroundColor: label.selected ? '#fff' : '#4CAF50',
}}
collapsable={false}>
<Text style={{ fontSize: 16, color: label.selected ? '#4CAF50' : '#fff' }}>
{label.name}
</Text>
</View>
</Pressable>
</MarkerView>
);
})}
</MapView>
</View>
);
};

export default Test;

/** @type ExampleWithMetadata['metadata'] */
const metadata = {
title: 'MarkerView Disappear (Issue 4206)',
tags: ['MarkerView', 'bug', 'isSelected'],
docs: 'Exact reproducer from issue #4206: MarkerViews disappear on Android when width/height is 0 during addViewAnnotation.',
};
Test.metadata = metadata;
91 changes: 91 additions & 0 deletions example/src/examples/Annotations/PointAnnotationManagerSlot.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import { useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import {
Camera,
MapView,
PointAnnotation,
PointAnnotationManager,
} from '@rnmapbox/maps';
import { Button } from '@rneui/base';

import type { ExampleWithMetadata } from '../common/ExampleMetadata'; // exclude-from-doc

const styles = StyleSheet.create({
map: { flex: 1 },
pin: {
width: 30,
height: 30,
borderRadius: 15,
justifyContent: 'center',
alignItems: 'center',
},
label: { color: 'white', fontWeight: 'bold', fontSize: 12 },
buttons: {
flexDirection: 'row',
justifyContent: 'center',
padding: 8,
gap: 8,
},
});

const COORDS: [number, number][] = [
[-74.00597, 40.71427],
[-74.0065, 40.7128],
[-74.0045, 40.7155],
];

const PointAnnotationManagerSlot = () => {
const [slot, setSlot] = useState<string>('middle');

return (
<>
<MapView style={styles.map} styleURL="mapbox://styles/mapbox/standard">
<Camera
defaultSettings={{
centerCoordinate: [-74.00597, 40.71427],
zoomLevel: 15,
pitch: 45,
}}
/>
<PointAnnotationManager slot={slot}>
{COORDS.map((coord, i) => (
<PointAnnotation key={`pin-${i}`} id={`pin-${i}`} coordinate={coord}>
<View style={[styles.pin, { backgroundColor: 'dodgerblue' }]}>
<Text style={styles.label}>{i + 1}</Text>
</View>
</PointAnnotation>
))}
</PointAnnotationManager>
</MapView>
<View style={styles.buttons}>
<Button
title="bottom"
onPress={() => setSlot('bottom')}
color={slot === 'bottom' ? 'primary' : 'grey'}
/>
<Button
title="middle"
onPress={() => setSlot('middle')}
color={slot === 'middle' ? 'primary' : 'grey'}
/>
<Button
title="top"
onPress={() => setSlot('top')}
color={slot === 'top' ? 'primary' : 'grey'}
/>
</View>
</>
);
};

export default PointAnnotationManagerSlot;

const metadata: ExampleWithMetadata['metadata'] = {
title: 'PointAnnotationManager Slot',
tags: ['PointAnnotationManager', 'PointAnnotation', 'slot'],
docs: `
Demonstrates using PointAnnotationManager to position annotations
in different slots of the Mapbox Standard style.
`,
};
PointAnnotationManagerSlot.metadata = metadata;
1 change: 1 addition & 0 deletions example/src/examples/Annotations/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export { default as CustomCallout } from './CustomCallout';
export { default as Heatmap } from './Heatmap';
export { default as MarkerView } from './MarkerView';
export { default as PointAnnotationAnchors } from './PointAnnotationAnchors';
export { default as PointAnnotationManagerSlot } from './PointAnnotationManagerSlot';
export { default as ShowPointAnnotation } from './ShowPointAnnotation';

export const metadata = {
Expand Down
15 changes: 15 additions & 0 deletions ios/RNMBX/RNMBXPointAnnotationManagerComponentView.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#ifdef __cplusplus

#import <UIKit/UIKit.h>

#import <React/RCTUIManager.h>
#import <React/RCTViewComponentView.h>

NS_ASSUME_NONNULL_BEGIN

@interface RNMBXPointAnnotationManagerComponentView : RCTViewComponentView

@end

NS_ASSUME_NONNULL_END
#endif // __cplusplus
Loading
Loading