@lugg/maps 1.0.0-beta.0 → 1.0.0-beta.10

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 (186) hide show
  1. package/README.md +4 -1
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +79 -0
  4. package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +39 -0
  5. package/android/src/main/java/com/luggmaps/LuggCircleView.kt +85 -0
  6. package/android/src/main/java/com/luggmaps/LuggCircleViewManager.kt +79 -0
  7. package/android/src/main/java/com/luggmaps/LuggGroundOverlayView.kt +81 -0
  8. package/android/src/main/java/com/luggmaps/LuggGroundOverlayViewManager.kt +82 -0
  9. package/android/src/main/java/com/luggmaps/LuggMapView.kt +49 -0
  10. package/android/src/main/java/com/luggmaps/LuggMapViewManager.kt +31 -0
  11. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +0 -11
  12. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +99 -51
  13. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +13 -0
  14. package/android/src/main/java/com/luggmaps/LuggPackage.kt +11 -1
  15. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +1 -22
  16. package/android/src/main/java/com/luggmaps/LuggTileOverlayView.kt +92 -0
  17. package/android/src/main/java/com/luggmaps/LuggTileOverlayViewManager.kt +80 -0
  18. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +628 -18
  19. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +14 -0
  20. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +12 -7
  21. package/android/src/main/java/com/luggmaps/events/CirclePressEvent.kt +12 -0
  22. package/android/src/main/java/com/luggmaps/events/GroundOverlayPressEvent.kt +12 -0
  23. package/android/src/main/java/com/luggmaps/events/TileOverlayPressEvent.kt +12 -0
  24. package/android/src/main/java/com/luggmaps/extensions/ViewExtensions.kt +11 -0
  25. package/ios/LuggCalloutView.h +23 -0
  26. package/ios/LuggCalloutView.mm +155 -0
  27. package/ios/LuggCircleView.h +31 -0
  28. package/ios/LuggCircleView.mm +133 -0
  29. package/ios/LuggGroundOverlayView.h +30 -0
  30. package/ios/LuggGroundOverlayView.mm +125 -0
  31. package/ios/LuggMapView.mm +84 -2
  32. package/ios/LuggMarkerView.h +5 -0
  33. package/ios/LuggMarkerView.mm +40 -2
  34. package/ios/LuggTileOverlayView.h +31 -0
  35. package/ios/LuggTileOverlayView.mm +130 -0
  36. package/ios/core/AppleMapProvider.mm +838 -34
  37. package/ios/core/GMSPolylineAnimator.m +6 -3
  38. package/ios/core/GoogleMapProvider.mm +506 -19
  39. package/ios/core/LuggAnnotationView.h +12 -0
  40. package/ios/core/LuggAnnotationView.mm +55 -0
  41. package/ios/core/MapProviderDelegate.h +20 -1
  42. package/ios/events/CirclePressEvent.h +20 -0
  43. package/ios/events/GroundOverlayPressEvent.h +20 -0
  44. package/ios/events/TileOverlayPressEvent.h +20 -0
  45. package/lib/module/MapProvider.web.js +7 -2
  46. package/lib/module/MapProvider.web.js.map +1 -1
  47. package/lib/module/MapView.js +12 -1
  48. package/lib/module/MapView.js.map +1 -1
  49. package/lib/module/MapView.web.js +36 -20
  50. package/lib/module/MapView.web.js.map +1 -1
  51. package/lib/module/components/Circle.js +46 -0
  52. package/lib/module/components/Circle.js.map +1 -0
  53. package/lib/module/components/Circle.types.js +4 -0
  54. package/lib/module/components/Circle.types.js.map +1 -0
  55. package/lib/module/components/Circle.web.js +79 -0
  56. package/lib/module/components/Circle.web.js.map +1 -0
  57. package/lib/module/components/GeoJson.js +3 -3
  58. package/lib/module/components/GeoJson.js.map +1 -1
  59. package/lib/module/components/GroundOverlay.js +45 -0
  60. package/lib/module/components/GroundOverlay.js.map +1 -0
  61. package/lib/module/components/GroundOverlay.types.js +4 -0
  62. package/lib/module/components/GroundOverlay.types.js.map +1 -0
  63. package/lib/module/components/GroundOverlay.web.js +77 -0
  64. package/lib/module/components/GroundOverlay.web.js.map +1 -0
  65. package/lib/module/components/Marker.js +41 -6
  66. package/lib/module/components/Marker.js.map +1 -1
  67. package/lib/module/components/Marker.web.js +103 -26
  68. package/lib/module/components/Marker.web.js.map +1 -1
  69. package/lib/module/components/Polygon.web.js +3 -3
  70. package/lib/module/components/Polygon.web.js.map +1 -1
  71. package/lib/module/components/Polyline.web.js +6 -12
  72. package/lib/module/components/Polyline.web.js.map +1 -1
  73. package/lib/module/components/TileOverlay.js +44 -0
  74. package/lib/module/components/TileOverlay.js.map +1 -0
  75. package/lib/module/components/TileOverlay.types.js +4 -0
  76. package/lib/module/components/TileOverlay.types.js.map +1 -0
  77. package/lib/module/components/TileOverlay.web.js +69 -0
  78. package/lib/module/components/TileOverlay.web.js.map +1 -0
  79. package/lib/module/components/index.js +3 -0
  80. package/lib/module/components/index.js.map +1 -1
  81. package/lib/module/components/index.web.js +3 -0
  82. package/lib/module/components/index.web.js.map +1 -1
  83. package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +20 -0
  84. package/lib/module/fabric/LuggCircleViewNativeComponent.ts +25 -0
  85. package/lib/module/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  86. package/lib/module/fabric/LuggMapViewNativeComponent.ts +13 -5
  87. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  88. package/lib/module/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  89. package/lib/module/index.js +3 -0
  90. package/lib/module/index.js.map +1 -1
  91. package/lib/module/index.web.js +3 -0
  92. package/lib/module/index.web.js.map +1 -1
  93. package/lib/typescript/src/MapProvider.web.d.ts +9 -2
  94. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  95. package/lib/typescript/src/MapView.d.ts +1 -1
  96. package/lib/typescript/src/MapView.d.ts.map +1 -1
  97. package/lib/typescript/src/MapView.types.d.ts +36 -1
  98. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  99. package/lib/typescript/src/MapView.web.d.ts +1 -1
  100. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  101. package/lib/typescript/src/components/Circle.d.ts +10 -0
  102. package/lib/typescript/src/components/Circle.d.ts.map +1 -0
  103. package/lib/typescript/src/components/Circle.types.d.ts +33 -0
  104. package/lib/typescript/src/components/Circle.types.d.ts.map +1 -0
  105. package/lib/typescript/src/components/Circle.web.d.ts +3 -0
  106. package/lib/typescript/src/components/Circle.web.d.ts.map +1 -0
  107. package/lib/typescript/src/components/GeoJson.d.ts +2 -1
  108. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -1
  109. package/lib/typescript/src/components/GeoJson.types.d.ts +11 -1
  110. package/lib/typescript/src/components/GeoJson.types.d.ts.map +1 -1
  111. package/lib/typescript/src/components/GroundOverlay.d.ts +10 -0
  112. package/lib/typescript/src/components/GroundOverlay.d.ts.map +1 -0
  113. package/lib/typescript/src/components/GroundOverlay.types.d.ts +35 -0
  114. package/lib/typescript/src/components/GroundOverlay.types.d.ts.map +1 -0
  115. package/lib/typescript/src/components/GroundOverlay.web.d.ts +3 -0
  116. package/lib/typescript/src/components/GroundOverlay.web.d.ts.map +1 -0
  117. package/lib/typescript/src/components/Marker.d.ts +6 -3
  118. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  119. package/lib/typescript/src/components/Marker.types.d.ts +40 -2
  120. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
  121. package/lib/typescript/src/components/Marker.web.d.ts +3 -2
  122. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  123. package/lib/typescript/src/components/Polygon.web.d.ts +1 -1
  124. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -1
  125. package/lib/typescript/src/components/Polyline.web.d.ts +1 -1
  126. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  127. package/lib/typescript/src/components/TileOverlay.d.ts +10 -0
  128. package/lib/typescript/src/components/TileOverlay.d.ts.map +1 -0
  129. package/lib/typescript/src/components/TileOverlay.types.d.ts +36 -0
  130. package/lib/typescript/src/components/TileOverlay.types.d.ts.map +1 -0
  131. package/lib/typescript/src/components/TileOverlay.web.d.ts +3 -0
  132. package/lib/typescript/src/components/TileOverlay.web.d.ts.map +1 -0
  133. package/lib/typescript/src/components/index.d.ts +7 -1
  134. package/lib/typescript/src/components/index.d.ts.map +1 -1
  135. package/lib/typescript/src/components/index.web.d.ts +7 -1
  136. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  137. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +13 -0
  138. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts.map +1 -0
  139. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts +18 -0
  140. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts.map +1 -0
  141. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts +21 -0
  142. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts.map +1 -0
  143. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts +10 -5
  144. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -1
  145. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +8 -1
  146. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  147. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts +21 -0
  148. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts.map +1 -0
  149. package/lib/typescript/src/index.d.ts +10 -4
  150. package/lib/typescript/src/index.d.ts.map +1 -1
  151. package/lib/typescript/src/index.web.d.ts +9 -3
  152. package/lib/typescript/src/index.web.d.ts.map +1 -1
  153. package/lib/typescript/src/types.d.ts +0 -4
  154. package/lib/typescript/src/types.d.ts.map +1 -1
  155. package/package.json +6 -2
  156. package/src/MapProvider.web.tsx +15 -3
  157. package/src/MapView.tsx +12 -1
  158. package/src/MapView.types.ts +118 -1
  159. package/src/MapView.web.tsx +379 -322
  160. package/src/components/Circle.tsx +52 -0
  161. package/src/components/Circle.types.ts +33 -0
  162. package/src/components/Circle.web.tsx +95 -0
  163. package/src/components/GeoJson.tsx +3 -3
  164. package/src/components/GeoJson.types.ts +12 -1
  165. package/src/components/GroundOverlay.tsx +47 -0
  166. package/src/components/GroundOverlay.types.ts +36 -0
  167. package/src/components/GroundOverlay.web.tsx +81 -0
  168. package/src/components/Marker.tsx +59 -5
  169. package/src/components/Marker.types.ts +42 -2
  170. package/src/components/Marker.web.tsx +235 -99
  171. package/src/components/Polygon.web.tsx +93 -89
  172. package/src/components/Polyline.web.tsx +235 -235
  173. package/src/components/TileOverlay.tsx +44 -0
  174. package/src/components/TileOverlay.types.ts +37 -0
  175. package/src/components/TileOverlay.web.tsx +97 -0
  176. package/src/components/index.ts +11 -0
  177. package/src/components/index.web.ts +10 -1
  178. package/src/fabric/LuggCalloutViewNativeComponent.ts +20 -0
  179. package/src/fabric/LuggCircleViewNativeComponent.ts +25 -0
  180. package/src/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  181. package/src/fabric/LuggMapViewNativeComponent.ts +13 -5
  182. package/src/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  183. package/src/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  184. package/src/index.ts +14 -1
  185. package/src/index.web.ts +17 -1
  186. package/src/types.ts +0 -5
@@ -1,9 +1,11 @@
1
1
  import {
2
2
  forwardRef,
3
+ memo,
3
4
  useCallback,
4
5
  useEffect,
5
6
  useId,
6
7
  useImperativeHandle,
8
+ useMemo,
7
9
  useRef,
8
10
  useState,
9
11
  type CSSProperties,
@@ -96,354 +98,409 @@ const UserLocationMarker = ({ enabled }: { enabled?: boolean }) => {
96
98
  );
97
99
  };
98
100
 
99
- export const MapView = forwardRef<MapViewRef, MapViewProps>(function MapView(
100
- props,
101
- ref
102
- ) {
103
- const {
104
- mapId = 'DEMO_MAP_ID',
105
- initialCoordinate,
106
- initialZoom = 10,
107
- minZoom,
108
- maxZoom,
109
- zoomEnabled = true,
110
- scrollEnabled = true,
111
- pitchEnabled = true,
112
- edgeInsets,
113
- userLocationEnabled,
114
- theme = 'system',
115
- onPress,
116
- onLongPress,
117
- onCameraMove,
118
- onCameraIdle,
119
- onReady,
120
- children,
121
- style,
122
- } = props;
123
-
124
- const id = useId();
125
- const map = useMap(id);
126
- const containerRef = useRef<View>(null);
127
- const readyFired = useRef(false);
128
- const [isDragging, setIsDragging] = useState(false);
129
- const wasGesture = useRef(false);
130
- const prevEdgeInsets = useRef(edgeInsets);
131
-
132
- const offsetCenter = useCallback(
133
- (coord: Coordinate, zoom: number, insets?: EdgeInsets, reverse = false) => {
134
- const p = insets ?? prevEdgeInsets.current;
135
- if (!p) {
136
- return { lat: coord.latitude, lng: coord.longitude };
137
- }
138
-
139
- const dir = reverse ? -1 : 1;
140
- const scale = 256 * Math.pow(2, zoom);
141
- const offsetX = (dir * ((p.right ?? 0) - (p.left ?? 0))) / 2;
142
- const offsetY = (dir * ((p.bottom ?? 0) - (p.top ?? 0))) / 2;
143
-
144
- const latRad = (coord.latitude * Math.PI) / 180;
145
- const x = ((coord.longitude + 180) / 360) * scale + offsetX;
146
- const y =
147
- ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) /
148
- 2) *
149
- scale +
150
- offsetY;
151
-
152
- const lng = (x / scale) * 360 - 180;
153
- const lat =
154
- (Math.atan(Math.sinh(Math.PI * (1 - (2 * y) / scale))) * 180) / Math.PI;
155
-
156
- return { lat, lng };
157
- },
158
- []
159
- );
160
-
161
- const applyEdgeInsets = useCallback(
162
- (newEdgeInsets: EdgeInsets, duration?: number) => {
163
- if (!map) return;
164
-
165
- const prev = prevEdgeInsets.current;
166
- const center = map.getCenter();
167
- const zoom = map.getZoom() ?? initialZoom;
168
-
169
- if (center) {
170
- const logicalCenter = offsetCenter(
171
- { latitude: center.lat(), longitude: center.lng() },
172
- zoom,
173
- prev,
174
- true
175
- );
176
- const newCenter = offsetCenter(
177
- { latitude: logicalCenter.lat, longitude: logicalCenter.lng },
178
- zoom,
179
- newEdgeInsets,
180
- false
181
- );
182
-
183
- if (duration === 0) {
184
- map.moveCamera({ center: newCenter, zoom });
185
- } else {
186
- map.panTo(newCenter);
101
+ export const MapView = memo(
102
+ forwardRef<MapViewRef, MapViewProps>(function MapView(props, ref) {
103
+ const {
104
+ mapType = 'standard',
105
+ mapId = 'DEMO_MAP_ID',
106
+ initialCoordinate,
107
+ initialZoom = 10,
108
+ minZoom,
109
+ maxZoom,
110
+ zoomEnabled = true,
111
+ scrollEnabled = true,
112
+ pitchEnabled = true,
113
+ edgeInsets,
114
+ userLocationEnabled,
115
+ theme = 'system',
116
+ onPress,
117
+ onLongPress,
118
+ onCameraMove,
119
+ onCameraIdle,
120
+ onReady,
121
+ children,
122
+ style,
123
+ } = props;
124
+
125
+ const id = useId();
126
+ const map = useMap(id);
127
+ const containerRef = useRef<View>(null);
128
+ const readyFired = useRef(false);
129
+ const isDraggingRef = useRef(false);
130
+ const wasGesture = useRef(false);
131
+ const prevEdgeInsets = useRef(edgeInsets);
132
+
133
+ const offsetCenter = useCallback(
134
+ (
135
+ coord: Coordinate,
136
+ zoom: number,
137
+ insets?: EdgeInsets,
138
+ reverse = false
139
+ ) => {
140
+ const p = insets ?? prevEdgeInsets.current;
141
+ if (!p) {
142
+ return { lat: coord.latitude, lng: coord.longitude };
187
143
  }
188
- }
189
-
190
- prevEdgeInsets.current = newEdgeInsets;
191
- },
192
- [map, initialZoom, offsetCenter]
193
- );
194
144
 
195
- const panToCoordinate = useCallback(
196
- (coordinate: Coordinate) => {
197
- if (!map) return;
198
- const zoom = map.getZoom() || initialZoom;
199
- const center = offsetCenter(coordinate, zoom, undefined, false);
200
- map.panTo(center);
201
- },
202
- [map, initialZoom, offsetCenter]
203
- );
145
+ const dir = reverse ? -1 : 1;
146
+ const scale = 256 * Math.pow(2, zoom);
147
+ const offsetX = (dir * ((p.right ?? 0) - (p.left ?? 0))) / 2;
148
+ const offsetY = (dir * ((p.bottom ?? 0) - (p.top ?? 0))) / 2;
149
+
150
+ const latRad = (coord.latitude * Math.PI) / 180;
151
+ const x = ((coord.longitude + 180) / 360) * scale + offsetX;
152
+ const y =
153
+ ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) /
154
+ 2) *
155
+ scale +
156
+ offsetY;
157
+
158
+ const lng = (x / scale) * 360 - 180;
159
+ const lat =
160
+ (Math.atan(Math.sinh(Math.PI * (1 - (2 * y) / scale))) * 180) /
161
+ Math.PI;
162
+
163
+ return { lat, lng };
164
+ },
165
+ []
166
+ );
204
167
 
205
- useImperativeHandle(
206
- ref,
207
- () => ({
208
- moveCamera(coordinate: Coordinate, options?: MoveCameraOptions) {
168
+ const applyEdgeInsets = useCallback(
169
+ (newEdgeInsets: EdgeInsets, duration?: number) => {
209
170
  if (!map) return;
210
171
 
211
- const { zoom = 0, duration = -1 } = options ?? {};
212
- const targetZoom = zoom || map.getZoom() || initialZoom;
213
- const center = offsetCenter(coordinate, targetZoom, undefined, false);
214
-
215
- if (duration === 0) {
216
- map.moveCamera({ center, zoom: targetZoom });
217
- } else {
218
- const currentZoom = map.getZoom();
219
- const zoomChanged = zoom !== 0 && zoom !== currentZoom;
220
-
221
- if (zoomChanged) {
222
- map.setZoom(zoom);
172
+ const prev = prevEdgeInsets.current;
173
+ const center = map.getCenter();
174
+ const zoom = map.getZoom() ?? initialZoom;
175
+
176
+ if (center) {
177
+ const logicalCenter = offsetCenter(
178
+ { latitude: center.lat(), longitude: center.lng() },
179
+ zoom,
180
+ prev,
181
+ true
182
+ );
183
+ const newCenter = offsetCenter(
184
+ { latitude: logicalCenter.lat, longitude: logicalCenter.lng },
185
+ zoom,
186
+ newEdgeInsets,
187
+ false
188
+ );
189
+
190
+ if (duration === 0) {
191
+ map.moveCamera({ center: newCenter, zoom });
192
+ } else {
193
+ map.panTo(newCenter);
223
194
  }
224
- map.panTo(center);
225
195
  }
196
+
197
+ prevEdgeInsets.current = newEdgeInsets;
226
198
  },
199
+ [map, initialZoom, offsetCenter]
200
+ );
227
201
 
228
- fitCoordinates(
229
- coordinates: Coordinate[],
230
- options?: FitCoordinatesOptions
231
- ) {
232
- const first = coordinates[0];
233
- if (!map || !first) return;
202
+ const panToCoordinate = useCallback(
203
+ (coordinate: Coordinate) => {
204
+ if (!map) return;
205
+ const zoom = map.getZoom() || initialZoom;
206
+ const center = offsetCenter(coordinate, zoom, undefined, false);
207
+ map.panTo(center);
208
+ },
209
+ [map, initialZoom, offsetCenter]
210
+ );
234
211
 
235
- const { padding: fitPadding, duration = -1 } = options ?? {};
212
+ useImperativeHandle(
213
+ ref,
214
+ () => ({
215
+ moveCamera(coordinate: Coordinate, options?: MoveCameraOptions) {
216
+ if (!map) return;
217
+
218
+ const { zoom = 0, duration = -1 } = options ?? {};
219
+ const targetZoom = zoom || map.getZoom() || initialZoom;
220
+ const center = offsetCenter(coordinate, targetZoom, undefined, false);
221
+
222
+ if (duration === 0) {
223
+ map.moveCamera({ center, zoom: targetZoom });
224
+ } else {
225
+ const currentZoom = map.getZoom();
226
+ const zoomChanged = zoom !== 0 && zoom !== currentZoom;
227
+
228
+ if (zoomChanged) {
229
+ map.setZoom(zoom);
230
+ }
231
+ map.panTo(center);
232
+ }
233
+ },
236
234
 
237
- if (coordinates.length === 1) {
238
- this.moveCamera(first, { zoom: initialZoom, duration });
239
- return;
240
- }
235
+ fitCoordinates(
236
+ coordinates: Coordinate[],
237
+ options?: FitCoordinatesOptions
238
+ ) {
239
+ const first = coordinates[0];
240
+ if (!map || !first) return;
241
241
 
242
- const bounds = new google.maps.LatLngBounds();
243
- coordinates.forEach((coord) => {
244
- bounds.extend({ lat: coord.latitude, lng: coord.longitude });
245
- });
242
+ const { padding: fitPadding, duration = -1 } = options ?? {};
246
243
 
247
- const ei = prevEdgeInsets.current;
248
- map.fitBounds(bounds, {
249
- top: (ei?.top ?? 0) + (fitPadding?.top ?? 0),
250
- left: (ei?.left ?? 0) + (fitPadding?.left ?? 0),
251
- bottom: (ei?.bottom ?? 0) + (fitPadding?.bottom ?? 0),
252
- right: (ei?.right ?? 0) + (fitPadding?.right ?? 0),
253
- });
254
- },
255
-
256
- setEdgeInsets(newEdgeInsets: EdgeInsets, options?: SetEdgeInsetsOptions) {
257
- applyEdgeInsets(newEdgeInsets, options?.duration);
258
- },
259
- }),
260
- [map, initialZoom, offsetCenter, applyEdgeInsets]
261
- );
244
+ if (coordinates.length === 1) {
245
+ this.moveCamera(first, { zoom: initialZoom, duration });
246
+ return;
247
+ }
262
248
 
263
- useEffect(() => {
264
- if (map && !readyFired.current) {
265
- readyFired.current = true;
266
- onReady?.();
267
- }
268
- }, [map, onReady]);
249
+ const bounds = new google.maps.LatLngBounds();
250
+ coordinates.forEach((coord) => {
251
+ bounds.extend({ lat: coord.latitude, lng: coord.longitude });
252
+ });
253
+
254
+ const ei = prevEdgeInsets.current;
255
+ map.fitBounds(bounds, {
256
+ top: (ei?.top ?? 0) + (fitPadding?.top ?? 0),
257
+ left: (ei?.left ?? 0) + (fitPadding?.left ?? 0),
258
+ bottom: (ei?.bottom ?? 0) + (fitPadding?.bottom ?? 0),
259
+ right: (ei?.right ?? 0) + (fitPadding?.right ?? 0),
260
+ });
261
+ },
262
+
263
+ setEdgeInsets(
264
+ newEdgeInsets: EdgeInsets,
265
+ options?: SetEdgeInsetsOptions
266
+ ) {
267
+ applyEdgeInsets(newEdgeInsets, options?.duration);
268
+ },
269
+ }),
270
+ [map, initialZoom, offsetCenter, applyEdgeInsets]
271
+ );
269
272
 
270
- useEffect(() => {
271
- if (!map || !edgeInsets) return;
273
+ useEffect(() => {
274
+ if (map && !readyFired.current) {
275
+ readyFired.current = true;
276
+ onReady?.();
277
+ }
278
+ }, [map, onReady]);
272
279
 
273
- const prev = prevEdgeInsets.current;
274
- const changed =
275
- prev?.top !== edgeInsets.top ||
276
- prev?.left !== edgeInsets.left ||
277
- prev?.bottom !== edgeInsets.bottom ||
278
- prev?.right !== edgeInsets.right;
280
+ useEffect(() => {
281
+ if (!map || !edgeInsets) return;
279
282
 
280
- if (changed) {
281
- applyEdgeInsets(edgeInsets);
282
- }
283
- }, [map, edgeInsets, applyEdgeInsets]);
284
-
285
- const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
286
- const longPressFired = useRef(false);
287
-
288
- const getPoint = useCallback((domEvent?: Event) => {
289
- const el = containerRef.current as unknown as HTMLElement | null;
290
- if (!domEvent || !el) return { x: 0, y: 0 };
291
- const rect = el.getBoundingClientRect();
292
- const e = domEvent as MouseEvent | Touch;
293
- return { x: e.clientX - rect.left, y: e.clientY - rect.top };
294
- }, []);
295
-
296
- const handleClick = useCallback(
297
- (event: MapMouseEvent) => {
298
- if (longPressFired.current) {
299
- longPressFired.current = false;
300
- return;
283
+ const prev = prevEdgeInsets.current;
284
+ const changed =
285
+ prev?.top !== edgeInsets.top ||
286
+ prev?.left !== edgeInsets.left ||
287
+ prev?.bottom !== edgeInsets.bottom ||
288
+ prev?.right !== edgeInsets.right;
289
+
290
+ if (changed) {
291
+ applyEdgeInsets(edgeInsets);
301
292
  }
302
- const latLng = event.detail.latLng;
303
- if (!onPress || !latLng) return;
304
- onPress(
305
- createSyntheticEvent({
306
- coordinate: { latitude: latLng.lat, longitude: latLng.lng },
307
- point: getPoint(event.domEvent),
308
- })
309
- );
310
- },
311
- [onPress, getPoint]
312
- );
293
+ }, [map, edgeInsets, applyEdgeInsets]);
294
+
295
+ const longPressTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
296
+ const longPressFired = useRef(false);
297
+
298
+ const calloutListeners = useRef(new Set<() => void>()).current;
299
+ const onCalloutClose = useCallback(
300
+ (listener: () => void) => {
301
+ calloutListeners.add(listener);
302
+ return () => calloutListeners.delete(listener);
303
+ },
304
+ [calloutListeners]
305
+ );
306
+ const closeCallouts = useCallback(
307
+ (except?: () => void) => {
308
+ calloutListeners.forEach((l) => {
309
+ if (l !== except) l();
310
+ });
311
+ },
312
+ [calloutListeners]
313
+ );
313
314
 
314
- const handleMouseDown = useCallback(
315
- (event: google.maps.MapMouseEvent) => {
316
- if (!onLongPress) return;
317
- const point = getPoint(event.domEvent);
318
- longPressFired.current = false;
319
- longPressTimer.current = setTimeout(() => {
320
- longPressFired.current = true;
321
- const latLng = event.latLng;
322
- if (!latLng) return;
323
- onLongPress(
315
+ const getPoint = useCallback((domEvent?: Event) => {
316
+ const el = containerRef.current as unknown as HTMLElement | null;
317
+ if (!domEvent || !el) return { x: 0, y: 0 };
318
+ const rect = el.getBoundingClientRect();
319
+ const e = domEvent as MouseEvent | Touch;
320
+ return { x: e.clientX - rect.left, y: e.clientY - rect.top };
321
+ }, []);
322
+
323
+ const handleClick = useCallback(
324
+ (event: MapMouseEvent) => {
325
+ if (longPressFired.current) {
326
+ longPressFired.current = false;
327
+ return;
328
+ }
329
+ closeCallouts();
330
+ const latLng = event.detail.latLng;
331
+ if (!onPress || !latLng) return;
332
+ onPress(
324
333
  createSyntheticEvent({
325
- coordinate: { latitude: latLng.lat(), longitude: latLng.lng() },
326
- point,
334
+ coordinate: { latitude: latLng.lat, longitude: latLng.lng },
335
+ point: getPoint(event.domEvent),
327
336
  })
328
337
  );
329
- }, 500);
330
- },
331
- [onLongPress, getPoint]
332
- );
338
+ },
339
+ [onPress, getPoint, closeCallouts]
340
+ );
333
341
 
334
- const handleMouseUp = useCallback(() => {
335
- if (longPressTimer.current) {
336
- clearTimeout(longPressTimer.current);
337
- longPressTimer.current = null;
338
- }
339
- }, []);
342
+ const handleMouseDown = useCallback(
343
+ (event: google.maps.MapMouseEvent) => {
344
+ if (!onLongPress) return;
345
+ const point = getPoint(event.domEvent);
346
+ longPressFired.current = false;
347
+ longPressTimer.current = setTimeout(() => {
348
+ longPressFired.current = true;
349
+ const latLng = event.latLng;
350
+ if (!latLng) return;
351
+ onLongPress(
352
+ createSyntheticEvent({
353
+ coordinate: { latitude: latLng.lat(), longitude: latLng.lng() },
354
+ point,
355
+ })
356
+ );
357
+ }, 500);
358
+ },
359
+ [onLongPress, getPoint]
360
+ );
340
361
 
341
- useEffect(() => {
342
- if (!map || !onLongPress) return;
343
- const listeners = [
344
- map.addListener('mousedown', handleMouseDown),
345
- map.addListener('mouseup', handleMouseUp),
346
- ];
347
- return () => {
348
- listeners.forEach((l) => google.maps.event.removeListener(l));
362
+ const handleMouseUp = useCallback(() => {
363
+ if (longPressTimer.current) {
364
+ clearTimeout(longPressTimer.current);
365
+ longPressTimer.current = null;
366
+ }
367
+ }, []);
368
+
369
+ useEffect(() => {
370
+ if (!map || !onLongPress) return;
371
+ const listeners = [
372
+ map.addListener('mousedown', handleMouseDown),
373
+ map.addListener('mouseup', handleMouseUp),
374
+ ];
375
+ return () => {
376
+ listeners.forEach((l) => google.maps.event.removeListener(l));
377
+ handleMouseUp();
378
+ };
379
+ }, [map, onLongPress, handleMouseDown, handleMouseUp]);
380
+
381
+ const handleDragStart = useCallback(() => {
349
382
  handleMouseUp();
350
- };
351
- }, [map, onLongPress, handleMouseDown, handleMouseUp]);
352
-
353
- const handleDragStart = () => {
354
- handleMouseUp();
355
- setIsDragging(true);
356
- wasGesture.current = true;
357
- };
358
-
359
- const handleDragEnd = () => {
360
- setIsDragging(false);
361
- };
362
-
363
- const handleCameraChanged = (event: MapCameraChangedEvent) => {
364
- const logicalCenter = offsetCenter(
365
- { latitude: event.detail.center.lat, longitude: event.detail.center.lng },
366
- event.detail.zoom,
367
- undefined,
368
- true
369
- );
370
- const payload: CameraEventPayload = {
371
- coordinate: {
372
- latitude: logicalCenter.lat,
373
- longitude: logicalCenter.lng,
383
+ isDraggingRef.current = true;
384
+ wasGesture.current = true;
385
+ }, [handleMouseUp]);
386
+
387
+ const handleDragEnd = useCallback(() => {
388
+ isDraggingRef.current = false;
389
+ }, []);
390
+
391
+ const handleCameraChanged = useCallback(
392
+ (event: MapCameraChangedEvent) => {
393
+ const logicalCenter = offsetCenter(
394
+ {
395
+ latitude: event.detail.center.lat,
396
+ longitude: event.detail.center.lng,
397
+ },
398
+ event.detail.zoom,
399
+ undefined,
400
+ true
401
+ );
402
+ const payload: CameraEventPayload = {
403
+ coordinate: {
404
+ latitude: logicalCenter.lat,
405
+ longitude: logicalCenter.lng,
406
+ },
407
+ zoom: event.detail.zoom,
408
+ gesture: isDraggingRef.current,
409
+ };
410
+ onCameraMove?.(createSyntheticEvent(payload));
374
411
  },
375
- zoom: event.detail.zoom,
376
- gesture: isDragging,
377
- };
378
- onCameraMove?.(createSyntheticEvent(payload));
379
- };
380
-
381
- const handleIdle = (event: MapEvent) => {
382
- const center = event.map.getCenter();
383
- const zoom = event.map.getZoom() ?? 0;
384
- const logicalCenter = offsetCenter(
385
- { latitude: center?.lat() ?? 0, longitude: center?.lng() ?? 0 },
386
- zoom,
387
- undefined,
388
- true
412
+ [offsetCenter, onCameraMove]
389
413
  );
390
- const payload: CameraEventPayload = {
391
- coordinate: {
392
- latitude: logicalCenter.lat,
393
- longitude: logicalCenter.lng,
414
+
415
+ const handleIdle = useCallback(
416
+ (event: MapEvent) => {
417
+ const center = event.map.getCenter();
418
+ const zoom = event.map.getZoom() ?? 0;
419
+ const logicalCenter = offsetCenter(
420
+ { latitude: center?.lat() ?? 0, longitude: center?.lng() ?? 0 },
421
+ zoom,
422
+ undefined,
423
+ true
424
+ );
425
+ const payload: CameraEventPayload = {
426
+ coordinate: {
427
+ latitude: logicalCenter.lat,
428
+ longitude: logicalCenter.lng,
429
+ },
430
+ zoom,
431
+ gesture: wasGesture.current,
432
+ };
433
+ onCameraIdle?.(createSyntheticEvent(payload));
434
+ wasGesture.current = false;
394
435
  },
395
- zoom,
396
- gesture: wasGesture.current,
397
- };
398
- onCameraIdle?.(createSyntheticEvent(payload));
399
- wasGesture.current = false;
400
- };
401
-
402
- const gestureHandling =
403
- scrollEnabled === false && zoomEnabled === false
404
- ? 'none'
405
- : scrollEnabled === false
406
- ? 'cooperative'
407
- : 'auto';
408
-
409
- const colorScheme =
410
- theme === 'dark'
411
- ? ColorScheme.DARK
412
- : theme === 'light'
413
- ? ColorScheme.LIGHT
414
- : ColorScheme.FOLLOW_SYSTEM;
415
-
416
- const defaultCenter = initialCoordinate
417
- ? { lat: initialCoordinate.latitude, lng: initialCoordinate.longitude }
418
- : undefined;
436
+ [offsetCenter, onCameraIdle]
437
+ );
419
438
 
420
- return (
421
- <MapContext.Provider
422
- value={{ map, isDragging, moveCamera: panToCoordinate }}
423
- >
424
- <View ref={containerRef} style={style}>
425
- <Map
426
- id={id}
427
- mapId={mapId}
428
- defaultCenter={defaultCenter}
429
- defaultZoom={initialZoom}
430
- minZoom={minZoom}
431
- maxZoom={maxZoom}
432
- gestureHandling={gestureHandling}
433
- colorScheme={colorScheme}
434
- disableDefaultUI
435
- isFractionalZoomEnabled
436
- tilt={pitchEnabled === false ? 0 : undefined}
437
- onClick={handleClick}
438
- onDragstart={handleDragStart}
439
- onDragend={handleDragEnd}
440
- onCameraChanged={handleCameraChanged}
441
- onIdle={handleIdle}
442
- >
443
- <UserLocationMarker enabled={userLocationEnabled} />
444
- {children}
445
- </Map>
446
- </View>
447
- </MapContext.Provider>
448
- );
449
- });
439
+ const mapTypeId =
440
+ mapType === 'satellite'
441
+ ? 'satellite'
442
+ : mapType === 'terrain'
443
+ ? 'terrain'
444
+ : mapType === 'hybrid'
445
+ ? 'hybrid'
446
+ : 'roadmap';
447
+
448
+ const gestureHandling =
449
+ scrollEnabled === false && zoomEnabled === false
450
+ ? 'none'
451
+ : scrollEnabled === false
452
+ ? 'cooperative'
453
+ : 'auto';
454
+
455
+ const colorScheme =
456
+ theme === 'dark'
457
+ ? ColorScheme.DARK
458
+ : theme === 'light'
459
+ ? ColorScheme.LIGHT
460
+ : ColorScheme.FOLLOW_SYSTEM;
461
+
462
+ const defaultCenter = initialCoordinate
463
+ ? { lat: initialCoordinate.latitude, lng: initialCoordinate.longitude }
464
+ : undefined;
465
+
466
+ return (
467
+ <MapContext.Provider
468
+ value={useMemo(
469
+ () => ({
470
+ map,
471
+ isDraggingRef,
472
+ moveCamera: panToCoordinate,
473
+ onCalloutClose,
474
+ closeCallouts,
475
+ }),
476
+ [map, panToCoordinate, onCalloutClose, closeCallouts]
477
+ )}
478
+ >
479
+ <View ref={containerRef} style={style}>
480
+ <Map
481
+ id={id}
482
+ mapId={mapId}
483
+ defaultCenter={defaultCenter}
484
+ defaultZoom={initialZoom}
485
+ minZoom={minZoom}
486
+ maxZoom={maxZoom}
487
+ mapTypeId={mapTypeId}
488
+ gestureHandling={gestureHandling}
489
+ colorScheme={colorScheme}
490
+ disableDefaultUI
491
+ isFractionalZoomEnabled
492
+ tilt={pitchEnabled === false ? 0 : undefined}
493
+ onClick={handleClick}
494
+ onDragstart={handleDragStart}
495
+ onDragend={handleDragEnd}
496
+ onCameraChanged={handleCameraChanged}
497
+ onIdle={handleIdle}
498
+ >
499
+ <UserLocationMarker enabled={userLocationEnabled} />
500
+ {children}
501
+ </Map>
502
+ </View>
503
+ </MapContext.Provider>
504
+ );
505
+ })
506
+ );