Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.mapbox.geojson.Point
import com.mapbox.maps.ViewAnnotationAnchor
import com.mapbox.maps.ViewAnnotationOptions
import com.mapbox.maps.viewannotation.viewAnnotationOptions
import com.facebook.react.uimanager.PointerEvents
import com.rnmapbox.rnmbx.components.AbstractMapFeature
import com.rnmapbox.rnmbx.components.RemovalReason
import com.rnmapbox.rnmbx.components.mapview.RNMBXMapView
Expand All @@ -33,6 +34,7 @@ class RNMBXMarkerView(context: Context?, private val mManager: RNMBXMarkerViewMa
private var mAllowOverlap = false
private var mAllowOverlapWithPuck = false
private var mIsSelected = false
private var mPointerEvents: PointerEvents = PointerEvents.AUTO

fun setCoordinate(point: Point?) {
mCoordinate = point
Expand Down Expand Up @@ -63,8 +65,14 @@ class RNMBXMarkerView(context: Context?, private val mManager: RNMBXMarkerViewMa

// region View methods

fun setContentPointerEvents(pointerEvents: PointerEvents) {
mPointerEvents = pointerEvents
(mView as? RNMBXMarkerViewContent)?.setExternalPointerEvents(pointerEvents)
}

override fun addView(childView: View, childPosition: Int) {
mView = childView
(childView as? RNMBXMarkerViewContent)?.setExternalPointerEvents(mPointerEvents)
// Note: Do not call this method on `super`. The view is added manually.
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,18 @@ import android.view.View.MeasureSpec
import android.view.ViewGroup
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.PointerEvents
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.views.view.ReactViewGroup
import com.rnmapbox.rnmbx.components.camera.BaseEvent

class RNMBXMarkerViewContent(context: Context): ReactViewGroup(context) {
var inAdd: Boolean = false
private var externalPointerEvents: PointerEvents = PointerEvents.AUTO

fun setExternalPointerEvents(pointerEvents: PointerEvents) {
externalPointerEvents = pointerEvents
}

// Track last reported translation to avoid feedback loop:
// Mapbox sets setTranslationX(512) → we fire event → JS sets transform:[{translateX:512}]
Expand All @@ -29,6 +35,9 @@ class RNMBXMarkerViewContent(context: Context): ReactViewGroup(context) {
}

override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
if (externalPointerEvents == PointerEvents.NONE) {
return false
}
// On ACTION_DOWN, tell the parent MapView not to intercept subsequent MOVE/UP
// events for pan/zoom recognition — that would send CANCEL to child Pressables
// and suppress onPress. Android resets the disallow flag on each new DOWN, so
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import com.facebook.react.bridge.ReactApplicationContext
import com.rnmapbox.rnmbx.components.AbstractEventEmitter
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.common.MapBuilder
import com.facebook.react.uimanager.PointerEvents
import com.facebook.react.uimanager.ReactStylesDiffMap
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.uimanager.ViewProps
import com.facebook.react.viewmanagers.RNMBXMarkerViewManagerDelegate
import com.facebook.react.viewmanagers.RNMBXMarkerViewManagerInterface
import com.mapbox.maps.ScreenCoordinate
Expand Down Expand Up @@ -72,6 +75,17 @@ class RNMBXMarkerViewManager(reactApplicationContext: ReactApplicationContext) :
markerView.setIsSelected(isSelected.asBoolean())
}

override fun updateProperties(viewToUpdate: RNMBXMarkerView, props: ReactStylesDiffMap) {
super.updateProperties(viewToUpdate, props)
// The codegen delegate does not forward the standard `pointerEvents` ViewProp — it falls
// to BaseViewManagerDelegate which ignores it. Intercept it here so the marker content
// view (which lives in a separate Mapbox view hierarchy) can honour it.
if (props.hasKey(ViewProps.POINTER_EVENTS)) {
val pe = PointerEvents.parsePointerEvents(props.getString(ViewProps.POINTER_EVENTS))
viewToUpdate.setContentPointerEvents(pe)
}
}

override fun createViewInstance(reactContext: ThemedReactContext): RNMBXMarkerView {
return RNMBXMarkerView(reactContext, this)
}
Expand Down
25 changes: 22 additions & 3 deletions example/src/examples/Annotations/MarkerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -274,12 +274,20 @@ const ShowMarkerView = () => {
React.useState<GeoJSON.Position[]>(INITIAL_COORDINATES);
const [allowOverlapWithPuck, setAllowOverlapWithPuck] =
React.useState<boolean>(false);
const [pointerEventsNone, setPointerEventsNone] =
React.useState<boolean>(false);

const [mapMoveCount, setMapMoveCount] = React.useState(0);

const onPressMap = (e: GeoJSON.Feature) => {
const geometry = e.geometry as GeoJSON.Point;
setPointList((pl) => [...pl, geometry.coordinates]);
};

const onRegionDidChange = React.useCallback(() => {
setMapMoveCount((c) => c + 1);
}, []);

return (
<>
<Button
Expand All @@ -290,7 +298,15 @@ const ShowMarkerView = () => {
}
onPress={() => setAllowOverlapWithPuck((prev) => !prev)}
/>
<Mapbox.MapView onPress={onPressMap} style={styles.matchParent}>
<Button
title={
pointerEventsNone
? 'pointerEvents="none" – tap/drag passes to map'
: 'pointerEvents="auto" – marker handles touches'
}
onPress={() => setPointerEventsNone((prev) => !prev)}
/>
<Mapbox.MapView onPress={onPressMap} onRegionDidChange={onRegionDidChange} style={styles.matchParent}>
<Mapbox.Camera
defaultSettings={{
zoomLevel: 16,
Expand All @@ -305,6 +321,7 @@ const ShowMarkerView = () => {
<Mapbox.MarkerView
coordinate={pointList[0]!}
allowOverlapWithPuck={allowOverlapWithPuck}
pointerEvents={pointerEventsNone ? 'none' : 'auto'}
>
<AnnotationContent title={'this is a marker view'} />
</Mapbox.MarkerView>
Expand All @@ -331,6 +348,7 @@ const ShowMarkerView = () => {
</Mapbox.MapView>

<Bubble>
<Text>Map moved: {mapMoveCount} times</Text>
<Text>Tap on map to add a point annotation</Text>
</Bubble>
</>
Expand All @@ -344,11 +362,12 @@ export default ShowMarkerView;
/** @type ExampleWithMetadata['metadata'] */
const metadata = {
title: 'Marker View',
tags: ['PointAnnotation', 'MarkerView', 'Slider', 'Interactive'],
tags: ['PointAnnotation', 'MarkerView', 'Slider', 'Interactive', 'pointerEvents'],
docs: `
Shows marker view and point annotations, including an interactive marker with
sliders, switch, counter, text input, and pressable button to verify complex
touch interactions inside a MarkerView.
touch interactions inside a MarkerView. Toggle pointerEvents="none" to make
the marker transparent to all touch events, passing them through to the map.
`,
};
ShowMarkerView.metadata = metadata;
13 changes: 10 additions & 3 deletions src/components/MarkerView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ const MarkerView = ({
isSelected = false,
coordinate,
style,
pointerEvents,
children,
}: Props) => {
// Android new-arch (Fabric) fix: UIManager.measure reads from the Fabric shadow
Expand All @@ -98,9 +99,14 @@ const MarkerView = ({
// we don't trigger a re-render for no-op native position re-applications.
const lastTranslateRef = useRef<{ x: number; y: number } | null>(null);

const handleTouchEnd = useCallback((e: { stopPropagation: () => void }) => {
e.stopPropagation();
}, []);
const handleTouchEnd = useCallback(
(e: { stopPropagation: () => void }) => {
if (pointerEvents !== 'none' && pointerEvents !== 'box-none') {
e.stopPropagation();
}
},
[pointerEvents],
);

const handleAnnotationPosition = useCallback(
(e: NativeSyntheticEvent<{ x: number; y: number }>) => {
Expand Down Expand Up @@ -150,6 +156,7 @@ const MarkerView = ({
allowOverlap={allowOverlap}
allowOverlapWithPuck={allowOverlapWithPuck}
isSelected={isSelected}
pointerEvents={pointerEvents}
onTouchEnd={handleTouchEnd}
>
<RNMBXMakerViewContentComponent
Expand Down
Loading