@rnmapbox/maps 10.3.0-rc.0 → 10.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/android/src/main/java/com/rnmapbox/rnmbx/components/annotation/RNMBXMarkerViewContent.kt +55 -0
  2. package/android/src/main/java/com/rnmapbox/rnmbx/components/annotation/RNMBXMarkerViewContentManager.kt +7 -2
  3. package/android/src/main/java/com/rnmapbox/rnmbx/components/annotation/RNMBXMarkerViewManager.kt +0 -2
  4. package/android/src/main/java/com/rnmapbox/rnmbx/components/location/RNMBXNativeUserLocationManager.kt +25 -24
  5. package/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/NativeMapViewModule.kt +3 -2
  6. package/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapView.kt +57 -39
  7. package/android/src/main/java/com/rnmapbox/rnmbx/components/mapview/RNMBXMapViewManager.kt +0 -9
  8. package/android/src/main/java/com/rnmapbox/rnmbx/components/styles/atmosphere/RNMBXAtmosphere.kt +4 -4
  9. package/android/src/main/java/com/rnmapbox/rnmbx/components/styles/atmosphere/RNMBXAtmosphereManager.kt +2 -1
  10. package/android/src/main/java/com/rnmapbox/rnmbx/components/styles/light/RNMBXLightManager.kt +2 -1
  11. package/android/src/main/java/com/rnmapbox/rnmbx/components/styles/sources/RNMBXSource.kt +2 -6
  12. package/android/src/main/java/com/rnmapbox/rnmbx/components/styles/terrain/RNMBXTerrainManager.kt +2 -1
  13. package/android/src/main/java/com/rnmapbox/rnmbx/events/FeatureClickEvent.java +5 -6
  14. package/android/src/main/java/com/rnmapbox/rnmbx/modules/RNMBXModule.kt +1 -0
  15. package/android/src/main/java/com/rnmapbox/rnmbx/utils/ConvertUtils.kt +0 -30
  16. package/android/src/main/java/com/rnmapbox/rnmbx/utils/extensions/Dynamic.kt +3 -1
  17. package/android/src/main/java/com/rnmapbox/rnmbx/utils/extensions/ReadableArray.kt +16 -14
  18. package/ios/RNMBX/RNMBXMapViewModule.mm +1 -1
  19. package/ios/RNMBX/RNMBXModule.swift +1 -0
  20. package/lib/module/Mapbox.native.js.map +1 -1
  21. package/lib/module/components/MapView.js +95 -113
  22. package/lib/module/components/MapView.js.map +1 -1
  23. package/lib/module/components/MarkerView.js +93 -76
  24. package/lib/module/components/MarkerView.js.map +1 -1
  25. package/lib/module/modules/offline/offlineManager.js +2 -12
  26. package/lib/module/modules/offline/offlineManager.js.map +1 -1
  27. package/lib/module/specs/RNMBXMarkerViewContentNativeComponent.ts +13 -1
  28. package/lib/typescript/src/Mapbox.native.d.ts +1 -1
  29. package/lib/typescript/src/Mapbox.native.d.ts.map +1 -1
  30. package/lib/typescript/src/components/Camera.d.ts +2 -2
  31. package/lib/typescript/src/components/Camera.d.ts.map +1 -1
  32. package/lib/typescript/src/components/MapView.d.ts +53 -41
  33. package/lib/typescript/src/components/MapView.d.ts.map +1 -1
  34. package/lib/typescript/src/components/MarkerView.d.ts +10 -17
  35. package/lib/typescript/src/components/MarkerView.d.ts.map +1 -1
  36. package/lib/typescript/src/modules/offline/offlineManager.d.ts.map +1 -1
  37. package/lib/typescript/src/specs/RNMBXMarkerViewContentNativeComponent.d.ts +6 -0
  38. package/lib/typescript/src/specs/RNMBXMarkerViewContentNativeComponent.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/setup-jest.js +1 -1
  41. package/src/Mapbox.native.ts +5 -1
  42. package/src/components/Camera.tsx +2 -2
  43. package/src/components/MapView.tsx +137 -154
  44. package/src/components/MarkerView.tsx +118 -95
  45. package/src/modules/offline/offlineManager.ts +2 -14
  46. package/src/specs/RNMBXMarkerViewContentNativeComponent.ts +13 -1
@@ -1,15 +1,19 @@
1
- import React from 'react';
2
- import { NativeModules, Platform, type ViewProps } from 'react-native';
1
+ import React, { useCallback, useMemo, useRef, useState } from 'react';
2
+ import {
3
+ PixelRatio,
4
+ StyleSheet,
5
+ type NativeSyntheticEvent,
6
+ type ViewProps,
7
+ } from 'react-native';
3
8
 
4
9
  import RNMBXMakerViewContentComponent from '../specs/RNMBXMarkerViewContentNativeComponent';
5
10
  import NativeMarkerViewComponent from '../specs/RNMBXMarkerViewNativeComponent';
6
11
  import { type Position } from '../types/Position';
7
- import { toJSONString } from '../utils';
8
- import { makePoint } from '../utils/geoUtils';
9
12
 
10
- import PointAnnotation from './PointAnnotation';
13
+ // Device pixel ratio is constant for the lifetime of the app.
14
+ const PIXEL_RATIO = PixelRatio.get();
11
15
 
12
- const Mapbox = NativeModules.RNMBXModule;
16
+ const DEFAULT_ANCHOR = { x: 0.5, y: 0.5 };
13
17
 
14
18
  type Props = ViewProps & {
15
19
  /**
@@ -21,29 +25,28 @@ type Props = ViewProps & {
21
25
  * Any coordinate between (0, 0) and (1, 1), where (0, 0) is the top-left corner of
22
26
  * the view, and (1, 1) is the bottom-right corner. Defaults to the center at (0.5, 0.5).
23
27
  */
24
- anchor: {
28
+ anchor?: {
25
29
  x: number;
26
30
  y: number;
27
31
  };
28
32
 
29
33
  /**
30
- * @v10
31
- *
32
34
  * Whether or not nearby markers on the map should all be displayed. If false, adjacent
33
35
  * markers will 'collapse' and only one will be shown. Defaults to false.
34
36
  */
35
- allowOverlap: boolean;
37
+ allowOverlap?: boolean;
36
38
 
37
39
  /**
38
40
  * Whether or not nearby markers on the map should be hidden if close to a
39
41
  * UserLocation puck. Defaults to false.
40
42
  */
41
- allowOverlapWithPuck: boolean;
43
+ allowOverlapWithPuck?: boolean;
42
44
 
43
- isSelected: boolean;
45
+ isSelected?: boolean;
44
46
 
45
47
  /**
46
- * One or more valid React Native views.
48
+ * One or more valid React Native views. You can use Pressable, TouchableOpacity,
49
+ * etc. directly as children — onPress and touch feedback work correctly.
47
50
  */
48
51
  children: React.ReactElement;
49
52
  };
@@ -58,93 +61,113 @@ type Props = ViewProps & {
58
61
  * This is implemented with view annotations on [Android](https://docs.mapbox.com/android/maps/guides/annotations/view-annotations/)
59
62
  * and [iOS](https://docs.mapbox.com/ios/maps/guides/annotations/view-annotations).
60
63
  *
61
- * This component has no dedicated `onPress` method. Instead, you should handle gestures
62
- * with the React views passed in as `children`.
64
+ * This component has no dedicated `onPress` method. Instead, handle gestures
65
+ * with the React views passed in as `children` — Pressable, TouchableOpacity,
66
+ * etc. all work including their visual feedback (opacity, scale, etc.).
63
67
  */
64
- class MarkerView extends React.PureComponent<Props> {
65
- static defaultProps: Partial<Props> = {
66
- anchor: { x: 0.5, y: 0.5 },
67
- allowOverlap: false,
68
- allowOverlapWithPuck: false,
69
- isSelected: false,
70
- };
71
-
72
- static lastId = 0;
73
- __idForPointAnnotation?: string;
74
-
75
- _idForPointAnnotation(): string {
76
- if (this.__idForPointAnnotation === undefined) {
77
- MarkerView.lastId = MarkerView.lastId + 1;
78
- this.__idForPointAnnotation = `MV-${MarkerView.lastId}`;
79
- }
80
- return this.__idForPointAnnotation;
81
- }
82
-
83
- _getCoordinate(coordinate: Position): string | undefined {
84
- if (!coordinate) {
85
- return undefined;
86
- }
87
- return toJSONString(makePoint(coordinate));
68
+ const MarkerView = ({
69
+ anchor = DEFAULT_ANCHOR,
70
+ allowOverlap = false,
71
+ allowOverlapWithPuck = false,
72
+ isSelected = false,
73
+ coordinate,
74
+ style,
75
+ children,
76
+ }: Props) => {
77
+ // Android new-arch (Fabric) fix: UIManager.measure reads from the Fabric shadow
78
+ // tree, which doesn't include Mapbox's native setTranslationX/Y positioning.
79
+ // Strategy: intercept setTranslationX/Y on the native side (see
80
+ // RNMBXMarkerViewContent.kt), relay the values as an onAnnotationPosition event,
81
+ // then apply them as a React `transform` on RNMBXMarkerView so the shadow tree
82
+ // reflects the actual on-screen position. This makes
83
+ // Pressability._responderRegion correct and onPress / touch feedback work.
84
+ //
85
+ // Key details:
86
+ // • position:'absolute' on RNMBXMarkerView → all markers have Yoga pos (0,0)
87
+ // in MapView, so the only shadow-tree offset is the transform itself.
88
+ // • Transform goes on RNMBXMarkerView (not RNMBXMarkerViewContent) so Fabric
89
+ // never fights Mapbox's native positioning.
90
+ // • PIXEL_RATIO: Android translationX/Y is in device pixels; React transform
91
+ // expects logical pixels (points).
92
+ const [annotationTranslate, setAnnotationTranslate] = useState<{
93
+ x: number;
94
+ y: number;
95
+ } | null>(null);
96
+
97
+ // Mirror of Kotlin-side dedup: skip setState when position hasn't changed so
98
+ // we don't trigger a re-render for no-op native position re-applications.
99
+ const lastTranslateRef = useRef<{ x: number; y: number } | null>(null);
100
+
101
+ const handleTouchEnd = useCallback((e: { stopPropagation: () => void }) => {
102
+ e.stopPropagation();
103
+ }, []);
104
+
105
+ const handleAnnotationPosition = useCallback(
106
+ (e: NativeSyntheticEvent<{ x: number; y: number }>) => {
107
+ const x = e.nativeEvent.x / PIXEL_RATIO;
108
+ const y = e.nativeEvent.y / PIXEL_RATIO;
109
+ const last = lastTranslateRef.current;
110
+ if (last !== null && last.x === x && last.y === y) return;
111
+ lastTranslateRef.current = { x, y };
112
+ setAnnotationTranslate({ x, y });
113
+ },
114
+ [],
115
+ );
116
+
117
+ if (anchor.x < 0 || anchor.y < 0 || anchor.x > 1 || anchor.y > 1) {
118
+ console.warn(
119
+ `[MarkerView] Anchor with value (${anchor.x}, ${anchor.y}) should not be outside the range [(0, 0), (1, 1)]`,
120
+ );
88
121
  }
89
122
 
90
- render() {
91
- if (
92
- this.props.anchor.x < 0 ||
93
- this.props.anchor.y < 0 ||
94
- this.props.anchor.x > 1 ||
95
- this.props.anchor.y > 1
96
- ) {
97
- console.warn(
98
- `[MarkerView] Anchor with value (${this.props.anchor.x}, ${this.props.anchor.y}) should not be outside the range [(0, 0), (1, 1)]`,
99
- );
100
- }
101
-
102
- if (Platform.OS === 'ios' && !Mapbox.MapboxV10) {
103
- return (
104
- <PointAnnotation id={this._idForPointAnnotation()} {...this.props} />
105
- );
106
- }
107
-
108
- const { anchor = { x: 0.5, y: 0.5 } } = this.props;
109
-
110
- return (
111
- <RNMBXMarkerView
112
- style={[
113
- {
114
- flex: 0,
115
- alignSelf: 'flex-start',
116
- },
117
- this.props.style,
118
- ]}
119
- coordinate={[
120
- Number(this.props.coordinate[0]),
121
- Number(this.props.coordinate[1]),
122
- ]}
123
- anchor={anchor}
124
- allowOverlap={this.props.allowOverlap}
125
- allowOverlapWithPuck={this.props.allowOverlapWithPuck}
126
- isSelected={this.props.isSelected}
127
- onTouchEnd={(e) => {
128
- e.stopPropagation();
129
- }}
123
+ const nativeCoordinate = useMemo(
124
+ () => [Number(coordinate[0]), Number(coordinate[1])] as [number, number],
125
+ // eslint-disable-next-line react-hooks/exhaustive-deps
126
+ [coordinate[0], coordinate[1]],
127
+ );
128
+
129
+ const nativeStyle = useMemo(
130
+ () => [
131
+ styles.absolutePosition,
132
+ style,
133
+ annotationTranslate != null
134
+ ? {
135
+ transform: [
136
+ { translateX: annotationTranslate.x },
137
+ { translateY: annotationTranslate.y },
138
+ ],
139
+ }
140
+ : undefined,
141
+ ],
142
+ [style, annotationTranslate],
143
+ );
144
+
145
+ return (
146
+ <RNMBXMarkerView
147
+ style={nativeStyle}
148
+ coordinate={nativeCoordinate}
149
+ anchor={anchor}
150
+ allowOverlap={allowOverlap}
151
+ allowOverlapWithPuck={allowOverlapWithPuck}
152
+ isSelected={isSelected}
153
+ onTouchEnd={handleTouchEnd}
154
+ >
155
+ <RNMBXMakerViewContentComponent
156
+ collapsable={false}
157
+ style={styles.contentContainer}
158
+ onAnnotationPosition={handleAnnotationPosition}
130
159
  >
131
- <RNMBXMakerViewContentComponent
132
- collapsable={false}
133
- style={{ flex: 0, alignSelf: 'flex-start' }}
134
- onStartShouldSetResponder={(_event) => {
135
- return true;
136
- }}
137
- onTouchEnd={(e) => {
138
- e.stopPropagation();
139
- }}
140
- >
141
- {this.props.children}
142
- </RNMBXMakerViewContentComponent>
143
- </RNMBXMarkerView>
144
- );
145
- }
146
- }
160
+ {children}
161
+ </RNMBXMakerViewContentComponent>
162
+ </RNMBXMarkerView>
163
+ );
164
+ };
147
165
 
148
166
  const RNMBXMarkerView = NativeMarkerViewComponent;
149
167
 
168
+ const styles = StyleSheet.create({
169
+ absolutePosition: { position: 'absolute' },
170
+ contentContainer: { flex: 0, alignSelf: 'flex-start' },
171
+ });
172
+
150
173
  export default MarkerView;
@@ -182,14 +182,7 @@ class OfflineManager {
182
182
  * @return {void}
183
183
  */
184
184
  async invalidateAmbientCache(): Promise<void> {
185
- if (RNMBXModule.MapboxV10) {
186
- console.warn(
187
- 'RNMapbox: invalidateAmbientCache is not implemented on v10',
188
- );
189
- return;
190
- }
191
- await this._initialize();
192
- await MapboxOfflineManager.invalidateAmbientCache();
185
+ console.warn('RNMapbox: invalidateAmbientCache is not implemented on v10');
193
186
  }
194
187
 
195
188
  /**
@@ -204,12 +197,7 @@ class OfflineManager {
204
197
  * @return {void}
205
198
  */
206
199
  async clearAmbientCache(): Promise<void> {
207
- if (RNMBXModule.MapboxV10) {
208
- console.warn('RNMapbox: clearAmbientCache is not implemented on v10');
209
- return;
210
- }
211
- await this._initialize();
212
- await MapboxOfflineManager.clearAmbientCache();
200
+ console.warn('RNMapbox: clearAmbientCache is not implemented on v10');
213
201
  }
214
202
 
215
203
  /**
@@ -1,7 +1,19 @@
1
1
  import type { HostComponent, ViewProps } from 'react-native';
2
2
  import codegenNativeComponent from 'react-native/Libraries/Utilities/codegenNativeComponent';
3
+ // @ts-ignore - CI environment type resolution issue for CodegenTypes
4
+ import { DirectEventHandler, Float } from 'react-native/Libraries/Types/CodegenTypes';
3
5
 
4
- export interface NativeProps extends ViewProps {}
6
+ type OnAnnotationPositionEvent = {
7
+ x: Float;
8
+ y: Float;
9
+ };
10
+
11
+ export interface NativeProps extends ViewProps {
12
+ // Fired by native when Mapbox repositions the annotation via setTranslationX/Y.
13
+ // JS uses this to keep the Fabric shadow tree transform in sync so that
14
+ // UIManager.measure returns the correct on-screen position for Pressable hit-testing.
15
+ onAnnotationPosition?: DirectEventHandler<OnAnnotationPositionEvent>;
16
+ }
5
17
 
6
18
  // @ts-ignore-error - Codegen requires single cast but TypeScript prefers double cast
7
19
  export default codegenNativeComponent<NativeProps>(