@lugg/maps 0.2.0-alpha.9 → 1.0.0-beta.1

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 (184) hide show
  1. package/README.md +21 -20
  2. package/android/build.gradle +1 -1
  3. package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +74 -0
  4. package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +38 -0
  5. package/android/src/main/java/com/luggmaps/LuggMapView.kt +290 -0
  6. package/android/src/main/java/com/luggmaps/{LuggGoogleMapViewManager.kt → LuggMapViewManager.kt} +99 -36
  7. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +11 -14
  8. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +187 -21
  9. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +28 -0
  10. package/android/src/main/java/com/luggmaps/LuggPackage.kt +8 -4
  11. package/android/src/main/java/com/luggmaps/LuggPolygonView.kt +85 -0
  12. package/android/src/main/java/com/luggmaps/LuggPolygonViewManager.kt +100 -0
  13. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +16 -0
  14. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +22 -0
  15. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +874 -0
  16. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +60 -0
  17. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +89 -24
  18. package/android/src/main/java/com/luggmaps/events/LongPressEvent.kt +29 -0
  19. package/android/src/main/java/com/luggmaps/events/MarkerDragEvent.kt +41 -0
  20. package/android/src/main/java/com/luggmaps/events/MarkerPressEvent.kt +34 -0
  21. package/android/src/main/java/com/luggmaps/events/PolygonPressEvent.kt +12 -0
  22. package/android/src/main/java/com/luggmaps/events/PressEvent.kt +29 -0
  23. package/ios/LuggCalloutView.h +23 -0
  24. package/ios/LuggCalloutView.mm +145 -0
  25. package/ios/LuggMapView.h +9 -0
  26. package/ios/LuggMapView.mm +343 -0
  27. package/ios/LuggMarkerView.h +15 -0
  28. package/ios/LuggMarkerView.mm +136 -2
  29. package/ios/LuggPolygonView.h +31 -0
  30. package/ios/LuggPolygonView.mm +151 -0
  31. package/ios/LuggPolylineView.h +4 -0
  32. package/ios/LuggPolylineView.mm +23 -0
  33. package/ios/core/AppleMapProvider.h +15 -0
  34. package/ios/core/AppleMapProvider.mm +1173 -0
  35. package/ios/core/GMSPolylineAnimator.h +3 -0
  36. package/ios/core/GMSPolylineAnimator.m +101 -15
  37. package/ios/core/GoogleMapProvider.h +13 -0
  38. package/ios/core/GoogleMapProvider.mm +798 -0
  39. package/ios/core/LuggAnnotationView.h +12 -0
  40. package/ios/core/LuggAnnotationView.mm +55 -0
  41. package/ios/core/MKPolylineAnimator.h +4 -0
  42. package/ios/core/MKPolylineAnimator.m +167 -42
  43. package/ios/core/MapProviderDelegate.h +84 -0
  44. package/ios/core/PolylineAnimatorBase.h +24 -1
  45. package/ios/core/PolylineAnimatorBase.m +82 -19
  46. package/ios/events/LongPressEvent.h +29 -0
  47. package/ios/events/MarkerDragEvent.h +69 -0
  48. package/ios/events/MarkerPressEvent.h +29 -0
  49. package/ios/events/PolygonPressEvent.h +20 -0
  50. package/ios/events/PressEvent.h +29 -0
  51. package/ios/extensions/MKMapView+Zoom.h +2 -0
  52. package/ios/extensions/MKMapView+Zoom.m +14 -4
  53. package/lib/module/MapProvider.js +2 -4
  54. package/lib/module/MapProvider.js.map +1 -1
  55. package/lib/module/MapProvider.web.js +14 -7
  56. package/lib/module/MapProvider.web.js.map +1 -1
  57. package/lib/module/MapView.js +33 -16
  58. package/lib/module/MapView.js.map +1 -1
  59. package/lib/module/MapView.web.js +296 -181
  60. package/lib/module/MapView.web.js.map +1 -1
  61. package/lib/module/components/GeoJson.js +159 -0
  62. package/lib/module/components/GeoJson.js.map +1 -0
  63. package/lib/module/components/GeoJson.types.js +4 -0
  64. package/lib/module/components/GeoJson.types.js.map +1 -0
  65. package/lib/module/components/Marker.js +49 -8
  66. package/lib/module/components/Marker.js.map +1 -1
  67. package/lib/module/components/Marker.types.js +4 -0
  68. package/lib/module/components/Marker.types.js.map +1 -0
  69. package/lib/module/components/Marker.web.js +159 -24
  70. package/lib/module/components/Marker.web.js.map +1 -1
  71. package/lib/module/components/Polygon.js +46 -0
  72. package/lib/module/components/Polygon.js.map +1 -0
  73. package/lib/module/components/Polygon.types.js +4 -0
  74. package/lib/module/components/Polygon.types.js.map +1 -0
  75. package/lib/module/components/Polygon.web.js +87 -0
  76. package/lib/module/components/Polygon.web.js.map +1 -0
  77. package/lib/module/components/Polyline.js +17 -4
  78. package/lib/module/components/Polyline.js.map +1 -1
  79. package/lib/module/components/Polyline.types.js +4 -0
  80. package/lib/module/components/Polyline.types.js.map +1 -0
  81. package/lib/module/components/Polyline.web.js +108 -31
  82. package/lib/module/components/Polyline.web.js.map +1 -1
  83. package/lib/module/components/index.js +4 -2
  84. package/lib/module/components/index.js.map +1 -1
  85. package/lib/module/components/index.web.js +2 -0
  86. package/lib/module/components/index.web.js.map +1 -1
  87. package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +20 -0
  88. package/lib/module/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +43 -7
  89. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +49 -1
  90. package/lib/module/fabric/LuggPolygonViewNativeComponent.ts +25 -0
  91. package/lib/module/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  92. package/lib/module/index.js +4 -1
  93. package/lib/module/index.js.map +1 -1
  94. package/lib/module/index.web.js +4 -1
  95. package/lib/module/index.web.js.map +1 -1
  96. package/lib/typescript/src/MapProvider.d.ts +1 -1
  97. package/lib/typescript/src/MapProvider.d.ts.map +1 -1
  98. package/lib/typescript/src/MapProvider.web.d.ts +23 -1
  99. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  100. package/lib/typescript/src/MapView.d.ts +4 -4
  101. package/lib/typescript/src/MapView.d.ts.map +1 -1
  102. package/lib/typescript/src/MapView.types.d.ts +35 -9
  103. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  104. package/lib/typescript/src/MapView.web.d.ts +2 -11
  105. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  106. package/lib/typescript/src/components/GeoJson.d.ts +4 -0
  107. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -0
  108. package/lib/typescript/src/components/GeoJson.types.d.ts +89 -0
  109. package/lib/typescript/src/components/GeoJson.types.d.ts.map +1 -0
  110. package/lib/typescript/src/components/Marker.d.ts +6 -33
  111. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  112. package/lib/typescript/src/components/Marker.types.d.ts +100 -0
  113. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -0
  114. package/lib/typescript/src/components/Marker.web.d.ts +2 -5
  115. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  116. package/lib/typescript/src/components/Polygon.d.ts +10 -0
  117. package/lib/typescript/src/components/Polygon.d.ts.map +1 -0
  118. package/lib/typescript/src/components/Polygon.types.d.ts +33 -0
  119. package/lib/typescript/src/components/Polygon.types.d.ts.map +1 -0
  120. package/lib/typescript/src/components/Polygon.web.d.ts +3 -0
  121. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -0
  122. package/lib/typescript/src/components/Polyline.d.ts +6 -21
  123. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  124. package/lib/typescript/src/components/Polyline.types.d.ts +53 -0
  125. package/lib/typescript/src/components/Polyline.types.d.ts.map +1 -0
  126. package/lib/typescript/src/components/Polyline.web.d.ts +2 -5
  127. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  128. package/lib/typescript/src/components/index.d.ts +8 -2
  129. package/lib/typescript/src/components/index.d.ts.map +1 -1
  130. package/lib/typescript/src/components/index.web.d.ts +6 -2
  131. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  132. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +13 -0
  133. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts.map +1 -0
  134. package/lib/typescript/src/fabric/{LuggGoogleMapViewNativeComponent.d.ts → LuggMapViewNativeComponent.d.ts} +30 -4
  135. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -0
  136. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +45 -1
  137. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  138. package/lib/typescript/src/fabric/LuggPolygonViewNativeComponent.d.ts +18 -0
  139. package/lib/typescript/src/fabric/LuggPolygonViewNativeComponent.d.ts.map +1 -0
  140. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts +7 -0
  141. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts.map +1 -1
  142. package/lib/typescript/src/index.d.ts +11 -3
  143. package/lib/typescript/src/index.d.ts.map +1 -1
  144. package/lib/typescript/src/index.web.d.ts +9 -1
  145. package/lib/typescript/src/index.web.d.ts.map +1 -1
  146. package/lib/typescript/src/types.d.ts +11 -0
  147. package/lib/typescript/src/types.d.ts.map +1 -1
  148. package/package.json +11 -9
  149. package/src/MapProvider.tsx +1 -3
  150. package/src/MapProvider.web.tsx +22 -3
  151. package/src/MapView.tsx +32 -26
  152. package/src/MapView.types.ts +43 -9
  153. package/src/MapView.web.tsx +368 -210
  154. package/src/components/GeoJson.tsx +221 -0
  155. package/src/components/GeoJson.types.ts +118 -0
  156. package/src/components/Marker.tsx +75 -38
  157. package/src/components/Marker.types.ts +102 -0
  158. package/src/components/Marker.web.tsx +197 -19
  159. package/src/components/Polygon.tsx +52 -0
  160. package/src/components/Polygon.types.ts +33 -0
  161. package/src/components/Polygon.web.tsx +104 -0
  162. package/src/components/Polyline.tsx +23 -22
  163. package/src/components/Polyline.types.ts +55 -0
  164. package/src/components/Polyline.web.tsx +151 -37
  165. package/src/components/index.ts +17 -2
  166. package/src/components/index.web.ts +6 -2
  167. package/src/fabric/LuggCalloutViewNativeComponent.ts +20 -0
  168. package/src/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +43 -7
  169. package/src/fabric/LuggMarkerViewNativeComponent.ts +49 -1
  170. package/src/fabric/LuggPolygonViewNativeComponent.ts +25 -0
  171. package/src/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  172. package/src/index.ts +30 -1
  173. package/src/index.web.ts +16 -1
  174. package/src/types.ts +13 -0
  175. package/android/src/main/java/com/luggmaps/LuggGoogleMapView.kt +0 -511
  176. package/ios/LuggAppleMapView.h +0 -16
  177. package/ios/LuggAppleMapView.mm +0 -592
  178. package/ios/LuggGoogleMapView.h +0 -13
  179. package/ios/LuggGoogleMapView.mm +0 -455
  180. package/lib/module/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
  181. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts +0 -54
  182. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts.map +0 -1
  183. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts.map +0 -1
  184. package/src/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
@@ -1,34 +1,35 @@
1
1
  import {
2
- Children,
3
- Component,
4
- isValidElement,
2
+ forwardRef,
3
+ useCallback,
5
4
  useEffect,
5
+ useId,
6
+ useImperativeHandle,
6
7
  useRef,
7
8
  useState,
8
9
  type CSSProperties,
9
- type ReactElement,
10
- type ReactNode,
11
10
  } from 'react';
12
- import type { NativeSyntheticEvent, ViewStyle } from 'react-native';
11
+ import type { NativeSyntheticEvent } from 'react-native';
13
12
  import { View } from 'react-native';
14
- import { Map, useMap } from '@vis.gl/react-google-maps';
13
+ import {
14
+ ColorScheme,
15
+ Map,
16
+ useMap,
17
+ type MapCameraChangedEvent,
18
+ type MapEvent,
19
+ type MapMouseEvent,
20
+ } from '@vis.gl/react-google-maps';
15
21
  import { Marker } from './components/Marker.web';
16
- import { Polyline } from './components/Polyline.web';
22
+ import { MapContext } from './MapProvider.web';
17
23
 
18
24
  import type {
19
25
  MapViewProps,
20
26
  MapViewRef,
21
27
  MoveCameraOptions,
22
28
  FitCoordinatesOptions,
29
+ SetEdgeInsetsOptions,
23
30
  CameraEventPayload,
24
31
  } from './MapView.types';
25
- import type { Coordinate } from './types';
26
-
27
- // Map-specific component types that render inside the Google Map
28
- const MAP_COMPONENT_TYPES = new Set([Marker, Polyline]);
29
-
30
- const isMapComponent = (child: ReactElement): boolean =>
31
- MAP_COMPONENT_TYPES.has(child.type as typeof Marker | typeof Polyline);
32
+ import type { Coordinate, EdgeInsets } from './types';
32
33
 
33
34
  const createSyntheticEvent = <T,>(nativeEvent: T): NativeSyntheticEvent<T> =>
34
35
  ({
@@ -49,17 +50,6 @@ const createSyntheticEvent = <T,>(nativeEvent: T): NativeSyntheticEvent<T> =>
49
50
  type: '',
50
51
  } as unknown as NativeSyntheticEvent<T>);
51
52
 
52
- interface MapControllerProps {
53
- onMapReady: (map: google.maps.Map) => void;
54
- onCameraMove?: (event: NativeSyntheticEvent<CameraEventPayload>) => void;
55
- onCameraIdle?: (event: NativeSyntheticEvent<CameraEventPayload>) => void;
56
- onReady?: () => void;
57
- }
58
-
59
- interface UserLocationMarkerProps {
60
- enabled?: boolean;
61
- }
62
-
63
53
  const userLocationDotStyle: CSSProperties = {
64
54
  width: 16,
65
55
  height: 16,
@@ -69,7 +59,7 @@ const userLocationDotStyle: CSSProperties = {
69
59
  boxShadow: '0 1px 4px rgba(0,0,0,0.3)',
70
60
  };
71
61
 
72
- function UserLocationMarker({ enabled }: UserLocationMarkerProps) {
62
+ const UserLocationMarker = ({ enabled }: { enabled?: boolean }) => {
73
63
  const [coordinate, setCoordinate] = useState<Coordinate | null>(null);
74
64
 
75
65
  useEffect(() => {
@@ -104,212 +94,380 @@ function UserLocationMarker({ enabled }: UserLocationMarkerProps) {
104
94
  <div style={userLocationDotStyle} />
105
95
  </Marker>
106
96
  );
107
- }
108
-
109
- function MapController({
110
- onMapReady,
111
- onCameraMove,
112
- onCameraIdle,
113
- onReady,
114
- }: MapControllerProps) {
115
- const map = useMap();
97
+ };
98
+
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);
116
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
+ }
117
138
 
118
- useEffect(() => {
119
- if (!map) return;
120
- onMapReady(map);
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);
187
+ }
188
+ }
121
189
 
122
- if (!readyFired.current) {
190
+ prevEdgeInsets.current = newEdgeInsets;
191
+ },
192
+ [map, initialZoom, offsetCenter]
193
+ );
194
+
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
+ );
204
+
205
+ useImperativeHandle(
206
+ ref,
207
+ () => ({
208
+ moveCamera(coordinate: Coordinate, options?: MoveCameraOptions) {
209
+ if (!map) return;
210
+
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);
223
+ }
224
+ map.panTo(center);
225
+ }
226
+ },
227
+
228
+ fitCoordinates(
229
+ coordinates: Coordinate[],
230
+ options?: FitCoordinatesOptions
231
+ ) {
232
+ const first = coordinates[0];
233
+ if (!map || !first) return;
234
+
235
+ const { padding: fitPadding, duration = -1 } = options ?? {};
236
+
237
+ if (coordinates.length === 1) {
238
+ this.moveCamera(first, { zoom: initialZoom, duration });
239
+ return;
240
+ }
241
+
242
+ const bounds = new google.maps.LatLngBounds();
243
+ coordinates.forEach((coord) => {
244
+ bounds.extend({ lat: coord.latitude, lng: coord.longitude });
245
+ });
246
+
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
+ );
262
+
263
+ useEffect(() => {
264
+ if (map && !readyFired.current) {
123
265
  readyFired.current = true;
124
266
  onReady?.();
125
267
  }
126
- }, [map, onMapReady, onReady]);
268
+ }, [map, onReady]);
127
269
 
128
270
  useEffect(() => {
129
- if (!map) return;
130
-
131
- const createPayload = (gesture: boolean): CameraEventPayload => {
132
- const center = map.getCenter();
133
- return {
134
- coordinate: {
135
- latitude: center?.lat() ?? 0,
136
- longitude: center?.lng() ?? 0,
137
- },
138
- zoom: map.getZoom() ?? 0,
139
- gesture,
140
- };
141
- };
271
+ if (!map || !edgeInsets) return;
142
272
 
143
- let isDragging = false;
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;
144
279
 
145
- const dragStartListener = map.addListener('dragstart', () => {
146
- isDragging = true;
147
- });
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 calloutListeners = useRef(new Set<() => void>()).current;
289
+ const onCalloutClose = useCallback(
290
+ (listener: () => void) => {
291
+ calloutListeners.add(listener);
292
+ return () => calloutListeners.delete(listener);
293
+ },
294
+ [calloutListeners]
295
+ );
296
+ const closeCallouts = useCallback(
297
+ (except?: () => void) => {
298
+ calloutListeners.forEach((l) => {
299
+ if (l !== except) l();
300
+ });
301
+ },
302
+ [calloutListeners]
303
+ );
148
304
 
149
- const dragEndListener = map.addListener('dragend', () => {
150
- isDragging = false;
151
- });
305
+ const getPoint = useCallback((domEvent?: Event) => {
306
+ const el = containerRef.current as unknown as HTMLElement | null;
307
+ if (!domEvent || !el) return { x: 0, y: 0 };
308
+ const rect = el.getBoundingClientRect();
309
+ const e = domEvent as MouseEvent | Touch;
310
+ return { x: e.clientX - rect.left, y: e.clientY - rect.top };
311
+ }, []);
312
+
313
+ const handleClick = useCallback(
314
+ (event: MapMouseEvent) => {
315
+ if (longPressFired.current) {
316
+ longPressFired.current = false;
317
+ return;
318
+ }
319
+ closeCallouts();
320
+ const latLng = event.detail.latLng;
321
+ if (!onPress || !latLng) return;
322
+ onPress(
323
+ createSyntheticEvent({
324
+ coordinate: { latitude: latLng.lat, longitude: latLng.lng },
325
+ point: getPoint(event.domEvent),
326
+ })
327
+ );
328
+ },
329
+ [onPress, getPoint, closeCallouts]
330
+ );
152
331
 
153
- const centerListener = map.addListener('center_changed', () => {
154
- onCameraMove?.(createSyntheticEvent(createPayload(isDragging)));
155
- });
332
+ const handleMouseDown = useCallback(
333
+ (event: google.maps.MapMouseEvent) => {
334
+ if (!onLongPress) return;
335
+ const point = getPoint(event.domEvent);
336
+ longPressFired.current = false;
337
+ longPressTimer.current = setTimeout(() => {
338
+ longPressFired.current = true;
339
+ const latLng = event.latLng;
340
+ if (!latLng) return;
341
+ onLongPress(
342
+ createSyntheticEvent({
343
+ coordinate: { latitude: latLng.lat(), longitude: latLng.lng() },
344
+ point,
345
+ })
346
+ );
347
+ }, 500);
348
+ },
349
+ [onLongPress, getPoint]
350
+ );
156
351
 
157
- const idleListener = map.addListener('idle', () => {
158
- onCameraIdle?.(createSyntheticEvent(createPayload(false)));
159
- });
352
+ const handleMouseUp = useCallback(() => {
353
+ if (longPressTimer.current) {
354
+ clearTimeout(longPressTimer.current);
355
+ longPressTimer.current = null;
356
+ }
357
+ }, []);
160
358
 
359
+ useEffect(() => {
360
+ if (!map || !onLongPress) return;
361
+ const listeners = [
362
+ map.addListener('mousedown', handleMouseDown),
363
+ map.addListener('mouseup', handleMouseUp),
364
+ ];
161
365
  return () => {
162
- google.maps.event.removeListener(dragStartListener);
163
- google.maps.event.removeListener(dragEndListener);
164
- google.maps.event.removeListener(centerListener);
165
- google.maps.event.removeListener(idleListener);
366
+ listeners.forEach((l) => google.maps.event.removeListener(l));
367
+ handleMouseUp();
166
368
  };
167
- }, [map, onCameraMove, onCameraIdle]);
168
-
169
- return null;
170
- }
171
-
172
- export class MapView extends Component<MapViewProps> implements MapViewRef {
173
- static defaultProps: Partial<MapViewProps> = {
174
- provider: 'google',
175
- mapId: 'DEMO_MAP_ID',
176
- initialZoom: 10,
177
- zoomEnabled: true,
178
- scrollEnabled: true,
179
- rotateEnabled: true,
180
- pitchEnabled: true,
181
- };
369
+ }, [map, onLongPress, handleMouseDown, handleMouseUp]);
182
370
 
183
- private mapInstance: google.maps.Map | null = null;
184
-
185
- private handleMapReady = (map: google.maps.Map) => {
186
- this.mapInstance = map;
371
+ const handleDragStart = () => {
372
+ handleMouseUp();
373
+ setIsDragging(true);
374
+ wasGesture.current = true;
187
375
  };
188
376
 
189
- moveCamera(coordinate: Coordinate, options: MoveCameraOptions) {
190
- const map = this.mapInstance;
191
- if (!map) return;
192
-
193
- const { zoom, duration = -1 } = options;
194
- const center = { lat: coordinate.latitude, lng: coordinate.longitude };
195
-
196
- if (duration === 0) {
197
- map.setCenter(center);
198
- map.setZoom(zoom);
199
- } else {
200
- map.panTo(center);
201
- map.setZoom(zoom);
202
- }
203
- }
377
+ const handleDragEnd = () => {
378
+ setIsDragging(false);
379
+ };
204
380
 
205
- fitCoordinates(coordinates: Coordinate[], options?: FitCoordinatesOptions) {
206
- const map = this.mapInstance;
207
- const first = coordinates[0];
208
- if (!map || !first) return;
381
+ const handleCameraChanged = (event: MapCameraChangedEvent) => {
382
+ const logicalCenter = offsetCenter(
383
+ { latitude: event.detail.center.lat, longitude: event.detail.center.lng },
384
+ event.detail.zoom,
385
+ undefined,
386
+ true
387
+ );
388
+ const payload: CameraEventPayload = {
389
+ coordinate: {
390
+ latitude: logicalCenter.lat,
391
+ longitude: logicalCenter.lng,
392
+ },
393
+ zoom: event.detail.zoom,
394
+ gesture: isDragging,
395
+ };
396
+ onCameraMove?.(createSyntheticEvent(payload));
397
+ };
209
398
 
210
- const { padding, duration = -1 } = options ?? {};
399
+ const handleIdle = (event: MapEvent) => {
400
+ const center = event.map.getCenter();
401
+ const zoom = event.map.getZoom() ?? 0;
402
+ const logicalCenter = offsetCenter(
403
+ { latitude: center?.lat() ?? 0, longitude: center?.lng() ?? 0 },
404
+ zoom,
405
+ undefined,
406
+ true
407
+ );
408
+ const payload: CameraEventPayload = {
409
+ coordinate: {
410
+ latitude: logicalCenter.lat,
411
+ longitude: logicalCenter.lng,
412
+ },
413
+ zoom,
414
+ gesture: wasGesture.current,
415
+ };
416
+ onCameraIdle?.(createSyntheticEvent(payload));
417
+ wasGesture.current = false;
418
+ };
211
419
 
212
- if (coordinates.length === 1) {
213
- const zoom = this.props.initialZoom ?? 10;
214
- this.moveCamera(first, { zoom, duration });
215
- return;
216
- }
420
+ const gestureHandling =
421
+ scrollEnabled === false && zoomEnabled === false
422
+ ? 'none'
423
+ : scrollEnabled === false
424
+ ? 'cooperative'
425
+ : 'auto';
217
426
 
218
- const bounds = new google.maps.LatLngBounds();
219
- coordinates.forEach((coord) => {
220
- bounds.extend({ lat: coord.latitude, lng: coord.longitude });
221
- });
222
-
223
- map.fitBounds(bounds, {
224
- top: padding?.top ?? 0,
225
- left: padding?.left ?? 0,
226
- bottom: padding?.bottom ?? 0,
227
- right: padding?.right ?? 0,
228
- });
229
- }
230
-
231
- render() {
232
- const {
233
- mapId,
234
- initialCoordinate,
235
- initialZoom,
236
- minZoom,
237
- maxZoom,
238
- zoomEnabled,
239
- scrollEnabled,
240
- pitchEnabled,
241
- padding,
242
- userLocationEnabled,
243
- onCameraMove,
244
- onCameraIdle,
245
- onReady,
246
- children,
247
- style,
248
- } = this.props;
249
-
250
- const gestureHandling =
251
- scrollEnabled === false && zoomEnabled === false
252
- ? 'none'
253
- : scrollEnabled === false
254
- ? 'none'
255
- : 'auto';
256
-
257
- const defaultCenter = initialCoordinate
258
- ? { lat: initialCoordinate.latitude, lng: initialCoordinate.longitude }
259
- : undefined;
260
-
261
- // Separate map children (Marker, Polyline) from overlay children (regular Views)
262
- const mapChildren: ReactNode[] = [];
263
- const overlayChildren: ReactNode[] = [];
264
-
265
- Children.forEach(children, (child) => {
266
- if (!isValidElement(child)) return;
267
- if (isMapComponent(child)) {
268
- mapChildren.push(child);
269
- } else {
270
- overlayChildren.push(child);
271
- }
272
- });
273
-
274
- const mapContainerStyle: ViewStyle = {
275
- position: 'absolute',
276
- top: padding?.top ?? 0,
277
- left: padding?.left ?? 0,
278
- right: padding?.right ?? 0,
279
- bottom: padding?.bottom ?? 0,
280
- };
427
+ const colorScheme =
428
+ theme === 'dark'
429
+ ? ColorScheme.DARK
430
+ : theme === 'light'
431
+ ? ColorScheme.LIGHT
432
+ : ColorScheme.FOLLOW_SYSTEM;
281
433
 
282
- const mapStyle: CSSProperties = {
283
- width: '100%',
284
- height: '100%',
285
- };
434
+ const defaultCenter = initialCoordinate
435
+ ? { lat: initialCoordinate.latitude, lng: initialCoordinate.longitude }
436
+ : undefined;
286
437
 
287
- return (
288
- <View style={style}>
289
- <View style={mapContainerStyle}>
290
- <Map
291
- mapId={mapId}
292
- defaultCenter={defaultCenter}
293
- defaultZoom={initialZoom}
294
- minZoom={minZoom}
295
- maxZoom={maxZoom}
296
- gestureHandling={gestureHandling}
297
- disableDefaultUI
298
- tilt={pitchEnabled === false ? 0 : undefined}
299
- style={mapStyle}
300
- >
301
- <MapController
302
- onMapReady={this.handleMapReady}
303
- onCameraMove={onCameraMove}
304
- onCameraIdle={onCameraIdle}
305
- onReady={onReady}
306
- />
307
- <UserLocationMarker enabled={userLocationEnabled} />
308
- {mapChildren}
309
- </Map>
310
- </View>
311
- {overlayChildren}
438
+ return (
439
+ <MapContext.Provider
440
+ value={{
441
+ map,
442
+ isDragging,
443
+ moveCamera: panToCoordinate,
444
+ onCalloutClose,
445
+ closeCallouts,
446
+ }}
447
+ >
448
+ <View ref={containerRef} style={style}>
449
+ <Map
450
+ id={id}
451
+ mapId={mapId}
452
+ defaultCenter={defaultCenter}
453
+ defaultZoom={initialZoom}
454
+ minZoom={minZoom}
455
+ maxZoom={maxZoom}
456
+ gestureHandling={gestureHandling}
457
+ colorScheme={colorScheme}
458
+ disableDefaultUI
459
+ isFractionalZoomEnabled
460
+ tilt={pitchEnabled === false ? 0 : undefined}
461
+ onClick={handleClick}
462
+ onDragstart={handleDragStart}
463
+ onDragend={handleDragEnd}
464
+ onCameraChanged={handleCameraChanged}
465
+ onIdle={handleIdle}
466
+ >
467
+ <UserLocationMarker enabled={userLocationEnabled} />
468
+ {children}
469
+ </Map>
312
470
  </View>
313
- );
314
- }
315
- }
471
+ </MapContext.Provider>
472
+ );
473
+ });