@lugg/maps 0.2.0-alpha.8 → 1.0.0-beta.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 (173) hide show
  1. package/README.md +20 -19
  2. package/android/src/main/java/com/luggmaps/LuggMapView.kt +290 -0
  3. package/android/src/main/java/com/luggmaps/{LuggGoogleMapViewManager.kt → LuggMapViewManager.kt} +99 -36
  4. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +13 -5
  5. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +157 -13
  6. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +28 -0
  7. package/android/src/main/java/com/luggmaps/LuggPackage.kt +1 -4
  8. package/android/src/main/java/com/luggmaps/LuggPolygonView.kt +85 -0
  9. package/android/src/main/java/com/luggmaps/LuggPolygonViewManager.kt +100 -0
  10. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +16 -0
  11. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +22 -0
  12. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +698 -0
  13. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +60 -0
  14. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +89 -24
  15. package/android/src/main/java/com/luggmaps/events/LongPressEvent.kt +29 -0
  16. package/android/src/main/java/com/luggmaps/events/MarkerDragEvent.kt +41 -0
  17. package/android/src/main/java/com/luggmaps/events/MarkerPressEvent.kt +34 -0
  18. package/android/src/main/java/com/luggmaps/events/PolygonPressEvent.kt +12 -0
  19. package/android/src/main/java/com/luggmaps/events/PressEvent.kt +29 -0
  20. package/ios/LuggMapView.h +9 -0
  21. package/ios/LuggMapView.mm +343 -0
  22. package/ios/LuggMarkerView.h +13 -0
  23. package/ios/LuggMarkerView.mm +119 -0
  24. package/ios/LuggPolygonView.h +31 -0
  25. package/ios/LuggPolygonView.mm +151 -0
  26. package/ios/LuggPolylineView.h +4 -0
  27. package/ios/LuggPolylineView.mm +23 -0
  28. package/ios/core/AppleMapProvider.h +15 -0
  29. package/ios/core/AppleMapProvider.mm +1029 -0
  30. package/ios/core/GMSPolylineAnimator.h +3 -0
  31. package/ios/core/GMSPolylineAnimator.m +101 -15
  32. package/ios/core/GoogleMapProvider.h +13 -0
  33. package/ios/core/GoogleMapProvider.mm +689 -0
  34. package/ios/core/MKPolylineAnimator.h +4 -0
  35. package/ios/core/MKPolylineAnimator.m +167 -42
  36. package/ios/core/MapProviderDelegate.h +84 -0
  37. package/ios/core/PolylineAnimatorBase.h +24 -1
  38. package/ios/core/PolylineAnimatorBase.m +82 -19
  39. package/ios/events/LongPressEvent.h +29 -0
  40. package/ios/events/MarkerDragEvent.h +69 -0
  41. package/ios/events/MarkerPressEvent.h +29 -0
  42. package/ios/events/PolygonPressEvent.h +20 -0
  43. package/ios/events/PressEvent.h +29 -0
  44. package/ios/extensions/MKMapView+Zoom.h +2 -0
  45. package/ios/extensions/MKMapView+Zoom.m +14 -4
  46. package/lib/module/MapProvider.js +2 -4
  47. package/lib/module/MapProvider.js.map +1 -1
  48. package/lib/module/MapProvider.web.js +12 -7
  49. package/lib/module/MapProvider.web.js.map +1 -1
  50. package/lib/module/MapView.js +33 -16
  51. package/lib/module/MapView.js.map +1 -1
  52. package/lib/module/MapView.web.js +315 -217
  53. package/lib/module/MapView.web.js.map +1 -1
  54. package/lib/module/components/GeoJson.js +159 -0
  55. package/lib/module/components/GeoJson.js.map +1 -0
  56. package/lib/module/components/GeoJson.types.js +4 -0
  57. package/lib/module/components/GeoJson.types.js.map +1 -0
  58. package/lib/module/components/Marker.js +28 -4
  59. package/lib/module/components/Marker.js.map +1 -1
  60. package/lib/module/components/Marker.types.js +4 -0
  61. package/lib/module/components/Marker.types.js.map +1 -0
  62. package/lib/module/components/Marker.web.js +98 -27
  63. package/lib/module/components/Marker.web.js.map +1 -1
  64. package/lib/module/components/Polygon.js +46 -0
  65. package/lib/module/components/Polygon.js.map +1 -0
  66. package/lib/module/components/Polygon.types.js +4 -0
  67. package/lib/module/components/Polygon.types.js.map +1 -0
  68. package/lib/module/components/Polygon.web.js +87 -0
  69. package/lib/module/components/Polygon.web.js.map +1 -0
  70. package/lib/module/components/Polyline.js +17 -4
  71. package/lib/module/components/Polyline.js.map +1 -1
  72. package/lib/module/components/Polyline.types.js +4 -0
  73. package/lib/module/components/Polyline.types.js.map +1 -0
  74. package/lib/module/components/Polyline.web.js +108 -31
  75. package/lib/module/components/Polyline.web.js.map +1 -1
  76. package/lib/module/components/index.js +4 -2
  77. package/lib/module/components/index.js.map +1 -1
  78. package/lib/module/components/index.web.js +2 -0
  79. package/lib/module/components/index.web.js.map +1 -1
  80. package/lib/module/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +43 -7
  81. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +49 -1
  82. package/lib/module/fabric/LuggPolygonViewNativeComponent.ts +25 -0
  83. package/lib/module/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  84. package/lib/module/index.js +4 -1
  85. package/lib/module/index.js.map +1 -1
  86. package/lib/module/index.web.js +4 -1
  87. package/lib/module/index.web.js.map +1 -1
  88. package/lib/typescript/src/MapProvider.d.ts +1 -1
  89. package/lib/typescript/src/MapProvider.d.ts.map +1 -1
  90. package/lib/typescript/src/MapProvider.web.d.ts +17 -1
  91. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -1
  92. package/lib/typescript/src/MapView.d.ts +4 -4
  93. package/lib/typescript/src/MapView.d.ts.map +1 -1
  94. package/lib/typescript/src/MapView.types.d.ts +35 -9
  95. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  96. package/lib/typescript/src/MapView.web.d.ts +2 -11
  97. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  98. package/lib/typescript/src/components/GeoJson.d.ts +4 -0
  99. package/lib/typescript/src/components/GeoJson.d.ts.map +1 -0
  100. package/lib/typescript/src/components/GeoJson.types.d.ts +79 -0
  101. package/lib/typescript/src/components/GeoJson.types.d.ts.map +1 -0
  102. package/lib/typescript/src/components/Marker.d.ts +6 -33
  103. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  104. package/lib/typescript/src/components/Marker.types.d.ts +74 -0
  105. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -0
  106. package/lib/typescript/src/components/Marker.web.d.ts +2 -5
  107. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  108. package/lib/typescript/src/components/Polygon.d.ts +10 -0
  109. package/lib/typescript/src/components/Polygon.d.ts.map +1 -0
  110. package/lib/typescript/src/components/Polygon.types.d.ts +33 -0
  111. package/lib/typescript/src/components/Polygon.types.d.ts.map +1 -0
  112. package/lib/typescript/src/components/Polygon.web.d.ts +3 -0
  113. package/lib/typescript/src/components/Polygon.web.d.ts.map +1 -0
  114. package/lib/typescript/src/components/Polyline.d.ts +6 -21
  115. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  116. package/lib/typescript/src/components/Polyline.types.d.ts +53 -0
  117. package/lib/typescript/src/components/Polyline.types.d.ts.map +1 -0
  118. package/lib/typescript/src/components/Polyline.web.d.ts +2 -5
  119. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -1
  120. package/lib/typescript/src/components/index.d.ts +8 -2
  121. package/lib/typescript/src/components/index.d.ts.map +1 -1
  122. package/lib/typescript/src/components/index.web.d.ts +6 -2
  123. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  124. package/lib/typescript/src/fabric/{LuggGoogleMapViewNativeComponent.d.ts → LuggMapViewNativeComponent.d.ts} +30 -4
  125. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -0
  126. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +45 -1
  127. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  128. package/lib/typescript/src/fabric/LuggPolygonViewNativeComponent.d.ts +18 -0
  129. package/lib/typescript/src/fabric/LuggPolygonViewNativeComponent.d.ts.map +1 -0
  130. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts +7 -0
  131. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts.map +1 -1
  132. package/lib/typescript/src/index.d.ts +11 -3
  133. package/lib/typescript/src/index.d.ts.map +1 -1
  134. package/lib/typescript/src/index.web.d.ts +9 -1
  135. package/lib/typescript/src/index.web.d.ts.map +1 -1
  136. package/lib/typescript/src/types.d.ts +11 -0
  137. package/lib/typescript/src/types.d.ts.map +1 -1
  138. package/package.json +10 -9
  139. package/src/MapProvider.tsx +1 -3
  140. package/src/MapProvider.web.tsx +12 -3
  141. package/src/MapView.tsx +32 -26
  142. package/src/MapView.types.ts +43 -9
  143. package/src/MapView.web.tsx +376 -244
  144. package/src/components/GeoJson.tsx +221 -0
  145. package/src/components/GeoJson.types.ts +107 -0
  146. package/src/components/Marker.tsx +45 -36
  147. package/src/components/Marker.types.ts +75 -0
  148. package/src/components/Marker.web.tsx +116 -28
  149. package/src/components/Polygon.tsx +52 -0
  150. package/src/components/Polygon.types.ts +33 -0
  151. package/src/components/Polygon.web.tsx +104 -0
  152. package/src/components/Polyline.tsx +23 -22
  153. package/src/components/Polyline.types.ts +55 -0
  154. package/src/components/Polyline.web.tsx +151 -37
  155. package/src/components/index.ts +16 -2
  156. package/src/components/index.web.ts +6 -2
  157. package/src/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +43 -7
  158. package/src/fabric/LuggMarkerViewNativeComponent.ts +49 -1
  159. package/src/fabric/LuggPolygonViewNativeComponent.ts +25 -0
  160. package/src/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  161. package/src/index.ts +28 -1
  162. package/src/index.web.ts +15 -1
  163. package/src/types.ts +13 -0
  164. package/android/src/main/java/com/luggmaps/LuggGoogleMapView.kt +0 -511
  165. package/ios/LuggAppleMapView.h +0 -16
  166. package/ios/LuggAppleMapView.mm +0 -592
  167. package/ios/LuggGoogleMapView.h +0 -13
  168. package/ios/LuggGoogleMapView.mm +0 -455
  169. package/lib/module/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
  170. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts +0 -54
  171. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts.map +0 -1
  172. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts.map +0 -1
  173. package/src/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
@@ -1,33 +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,
8
+ useState,
7
9
  type CSSProperties,
8
- type ReactElement,
9
- type ReactNode,
10
10
  } from 'react';
11
- import type { NativeSyntheticEvent, ViewStyle } from 'react-native';
11
+ import type { NativeSyntheticEvent } from 'react-native';
12
12
  import { View } from 'react-native';
13
- 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';
14
21
  import { Marker } from './components/Marker.web';
15
- import { Polyline } from './components/Polyline.web';
22
+ import { MapContext } from './MapProvider.web';
16
23
 
17
24
  import type {
18
25
  MapViewProps,
19
26
  MapViewRef,
20
27
  MoveCameraOptions,
21
28
  FitCoordinatesOptions,
29
+ SetEdgeInsetsOptions,
22
30
  CameraEventPayload,
23
31
  } from './MapView.types';
24
- import type { Coordinate } from './types';
25
-
26
- // Map-specific component types that render inside the Google Map
27
- const MAP_COMPONENT_TYPES = new Set([Marker, Polyline]);
28
-
29
- const isMapComponent = (child: ReactElement): boolean =>
30
- MAP_COMPONENT_TYPES.has(child.type as typeof Marker | typeof Polyline);
32
+ import type { Coordinate, EdgeInsets } from './types';
31
33
 
32
34
  const createSyntheticEvent = <T,>(nativeEvent: T): NativeSyntheticEvent<T> =>
33
35
  ({
@@ -48,69 +50,31 @@ const createSyntheticEvent = <T,>(nativeEvent: T): NativeSyntheticEvent<T> =>
48
50
  type: '',
49
51
  } as unknown as NativeSyntheticEvent<T>);
50
52
 
51
- interface MapControllerProps {
52
- onMapReady: (map: google.maps.Map) => void;
53
- onCameraMove?: (event: NativeSyntheticEvent<CameraEventPayload>) => void;
54
- onCameraIdle?: (event: NativeSyntheticEvent<CameraEventPayload>) => void;
55
- onReady?: () => void;
56
- userLocationEnabled?: boolean;
57
- }
58
-
59
- function MapController({
60
- onMapReady,
61
- onCameraMove,
62
- onCameraIdle,
63
- onReady,
64
- userLocationEnabled,
65
- }: MapControllerProps) {
66
- const map = useMap();
67
- const readyFired = useRef(false);
68
- const userLocationMarker =
69
- useRef<google.maps.marker.AdvancedMarkerElement | null>(null);
70
-
71
- useEffect(() => {
72
- if (!map) return;
73
- onMapReady(map);
53
+ const userLocationDotStyle: CSSProperties = {
54
+ width: 16,
55
+ height: 16,
56
+ backgroundColor: '#4285F4',
57
+ border: '2px solid white',
58
+ borderRadius: '50%',
59
+ boxShadow: '0 1px 4px rgba(0,0,0,0.3)',
60
+ };
74
61
 
75
- if (!readyFired.current) {
76
- readyFired.current = true;
77
- onReady?.();
78
- }
79
- }, [map, onMapReady, onReady]);
62
+ const UserLocationMarker = ({ enabled }: { enabled?: boolean }) => {
63
+ const [coordinate, setCoordinate] = useState<Coordinate | null>(null);
80
64
 
81
65
  useEffect(() => {
82
- if (!map || !userLocationEnabled) {
83
- if (userLocationMarker.current) {
84
- userLocationMarker.current.map = null;
85
- userLocationMarker.current = null;
86
- }
66
+ if (!enabled) {
67
+ setCoordinate(null);
87
68
  return;
88
69
  }
89
70
 
90
71
  let watchId: number | null = null;
91
72
 
92
73
  const updateLocation = (position: GeolocationPosition) => {
93
- const { latitude, longitude } = position.coords;
94
- const pos = { lat: latitude, lng: longitude };
95
-
96
- if (!userLocationMarker.current) {
97
- const dot = document.createElement('div');
98
- dot.style.width = '16px';
99
- dot.style.height = '16px';
100
- dot.style.backgroundColor = '#4285F4';
101
- dot.style.border = '2px solid white';
102
- dot.style.borderRadius = '50%';
103
- dot.style.boxShadow = '0 1px 4px rgba(0,0,0,0.3)';
104
-
105
- userLocationMarker.current =
106
- new google.maps.marker.AdvancedMarkerElement({
107
- map,
108
- position: pos,
109
- content: dot,
110
- });
111
- } else {
112
- userLocationMarker.current.position = pos;
113
- }
74
+ setCoordinate({
75
+ latitude: position.coords.latitude,
76
+ longitude: position.coords.longitude,
77
+ });
114
78
  };
115
79
 
116
80
  navigator.geolocation.getCurrentPosition(updateLocation, () => {});
@@ -120,198 +84,366 @@ function MapController({
120
84
  if (watchId !== null) {
121
85
  navigator.geolocation.clearWatch(watchId);
122
86
  }
123
- if (userLocationMarker.current) {
124
- userLocationMarker.current.map = null;
125
- userLocationMarker.current = null;
126
- }
127
87
  };
128
- }, [map, userLocationEnabled]);
129
-
130
- useEffect(() => {
131
- if (!map) return;
88
+ }, [enabled]);
89
+
90
+ if (!coordinate) return null;
91
+
92
+ return (
93
+ <Marker coordinate={coordinate} anchor={{ x: 0.5, y: 0.5 }}>
94
+ <div style={userLocationDotStyle} />
95
+ </Marker>
96
+ );
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);
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
+ }
132
138
 
133
- const createPayload = (gesture: boolean): CameraEventPayload => {
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;
134
166
  const center = map.getCenter();
135
- return {
136
- coordinate: {
137
- latitude: center?.lat() ?? 0,
138
- longitude: center?.lng() ?? 0,
139
- },
140
- zoom: map.getZoom() ?? 0,
141
- gesture,
142
- };
143
- };
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
+ }
144
189
 
145
- let isDragging = false;
190
+ prevEdgeInsets.current = newEdgeInsets;
191
+ },
192
+ [map, initialZoom, offsetCenter]
193
+ );
146
194
 
147
- const dragStartListener = map.addListener('dragstart', () => {
148
- isDragging = true;
149
- });
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
+ );
150
262
 
151
- const dragEndListener = map.addListener('dragend', () => {
152
- isDragging = false;
153
- });
263
+ useEffect(() => {
264
+ if (map && !readyFired.current) {
265
+ readyFired.current = true;
266
+ onReady?.();
267
+ }
268
+ }, [map, onReady]);
154
269
 
155
- const centerListener = map.addListener('center_changed', () => {
156
- onCameraMove?.(createSyntheticEvent(createPayload(isDragging)));
157
- });
270
+ useEffect(() => {
271
+ if (!map || !edgeInsets) return;
158
272
 
159
- const idleListener = map.addListener('idle', () => {
160
- onCameraIdle?.(createSyntheticEvent(createPayload(false)));
161
- });
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;
162
279
 
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;
301
+ }
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
+ );
313
+
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(
324
+ createSyntheticEvent({
325
+ coordinate: { latitude: latLng.lat(), longitude: latLng.lng() },
326
+ point,
327
+ })
328
+ );
329
+ }, 500);
330
+ },
331
+ [onLongPress, getPoint]
332
+ );
333
+
334
+ const handleMouseUp = useCallback(() => {
335
+ if (longPressTimer.current) {
336
+ clearTimeout(longPressTimer.current);
337
+ longPressTimer.current = null;
338
+ }
339
+ }, []);
340
+
341
+ useEffect(() => {
342
+ if (!map || !onLongPress) return;
343
+ const listeners = [
344
+ map.addListener('mousedown', handleMouseDown),
345
+ map.addListener('mouseup', handleMouseUp),
346
+ ];
163
347
  return () => {
164
- google.maps.event.removeListener(dragStartListener);
165
- google.maps.event.removeListener(dragEndListener);
166
- google.maps.event.removeListener(centerListener);
167
- google.maps.event.removeListener(idleListener);
348
+ listeners.forEach((l) => google.maps.event.removeListener(l));
349
+ handleMouseUp();
168
350
  };
169
- }, [map, onCameraMove, onCameraIdle]);
170
-
171
- return null;
172
- }
173
-
174
- export class MapView extends Component<MapViewProps> implements MapViewRef {
175
- static defaultProps: Partial<MapViewProps> = {
176
- provider: 'google',
177
- mapId: 'DEMO_MAP_ID',
178
- initialZoom: 10,
179
- zoomEnabled: true,
180
- scrollEnabled: true,
181
- rotateEnabled: true,
182
- pitchEnabled: true,
183
- };
351
+ }, [map, onLongPress, handleMouseDown, handleMouseUp]);
184
352
 
185
- private mapInstance: google.maps.Map | null = null;
186
-
187
- private handleMapReady = (map: google.maps.Map) => {
188
- this.mapInstance = map;
353
+ const handleDragStart = () => {
354
+ handleMouseUp();
355
+ setIsDragging(true);
356
+ wasGesture.current = true;
189
357
  };
190
358
 
191
- moveCamera(coordinate: Coordinate, options: MoveCameraOptions) {
192
- const map = this.mapInstance;
193
- if (!map) return;
194
-
195
- const { zoom, duration = -1 } = options;
196
- const center = { lat: coordinate.latitude, lng: coordinate.longitude };
197
-
198
- if (duration > 0) {
199
- map.panTo(center);
200
- map.setZoom(zoom);
201
- } else {
202
- map.setCenter(center);
203
- map.setZoom(zoom);
204
- }
205
- }
206
-
207
- fitCoordinates(coordinates: Coordinate[], options?: FitCoordinatesOptions) {
208
- const map = this.mapInstance;
209
- const first = coordinates[0];
210
- if (!map || !first) return;
211
-
212
- const { padding, duration = -1 } = options ?? {};
213
-
214
- if (coordinates.length === 1) {
215
- const zoom = this.props.initialZoom ?? 10;
216
- this.moveCamera(first, { zoom, duration });
217
- return;
218
- }
359
+ const handleDragEnd = () => {
360
+ setIsDragging(false);
361
+ };
219
362
 
220
- const bounds = new google.maps.LatLngBounds();
221
- coordinates.forEach((coord) => {
222
- bounds.extend({ lat: coord.latitude, lng: coord.longitude });
223
- });
224
-
225
- map.fitBounds(bounds, {
226
- top: padding?.top ?? 0,
227
- left: padding?.left ?? 0,
228
- bottom: padding?.bottom ?? 0,
229
- right: padding?.right ?? 0,
230
- });
231
- }
232
-
233
- render() {
234
- const {
235
- mapId,
236
- initialCoordinate,
237
- initialZoom,
238
- minZoom,
239
- maxZoom,
240
- zoomEnabled,
241
- scrollEnabled,
242
- pitchEnabled,
243
- padding,
244
- userLocationEnabled,
245
- onCameraMove,
246
- onCameraIdle,
247
- onReady,
248
- children,
249
- style,
250
- } = this.props;
251
-
252
- const gestureHandling =
253
- scrollEnabled === false && zoomEnabled === false
254
- ? 'none'
255
- : scrollEnabled === false
256
- ? 'none'
257
- : 'auto';
258
-
259
- const defaultCenter = initialCoordinate
260
- ? { lat: initialCoordinate.latitude, lng: initialCoordinate.longitude }
261
- : undefined;
262
-
263
- // Separate map children (Marker, Polyline) from overlay children (regular Views)
264
- const mapChildren: ReactNode[] = [];
265
- const overlayChildren: ReactNode[] = [];
266
-
267
- Children.forEach(children, (child) => {
268
- if (!isValidElement(child)) return;
269
- if (isMapComponent(child)) {
270
- mapChildren.push(child);
271
- } else {
272
- overlayChildren.push(child);
273
- }
274
- });
275
-
276
- const mapContainerStyle: ViewStyle = {
277
- position: 'absolute',
278
- top: padding?.top ?? 0,
279
- left: padding?.left ?? 0,
280
- right: padding?.right ?? 0,
281
- bottom: padding?.bottom ?? 0,
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,
374
+ },
375
+ zoom: event.detail.zoom,
376
+ gesture: isDragging,
282
377
  };
378
+ onCameraMove?.(createSyntheticEvent(payload));
379
+ };
283
380
 
284
- const mapStyle: CSSProperties = {
285
- width: '100%',
286
- height: '100%',
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
389
+ );
390
+ const payload: CameraEventPayload = {
391
+ coordinate: {
392
+ latitude: logicalCenter.lat,
393
+ longitude: logicalCenter.lng,
394
+ },
395
+ zoom,
396
+ gesture: wasGesture.current,
287
397
  };
398
+ onCameraIdle?.(createSyntheticEvent(payload));
399
+ wasGesture.current = false;
400
+ };
288
401
 
289
- return (
290
- <View style={style}>
291
- <View style={mapContainerStyle}>
292
- <Map
293
- mapId={mapId}
294
- defaultCenter={defaultCenter}
295
- defaultZoom={initialZoom}
296
- minZoom={minZoom}
297
- maxZoom={maxZoom}
298
- gestureHandling={gestureHandling}
299
- disableDefaultUI
300
- tilt={pitchEnabled === false ? 0 : undefined}
301
- style={mapStyle}
302
- >
303
- <MapController
304
- onMapReady={this.handleMapReady}
305
- onCameraMove={onCameraMove}
306
- onCameraIdle={onCameraIdle}
307
- onReady={onReady}
308
- userLocationEnabled={userLocationEnabled}
309
- />
310
- {mapChildren}
311
- </Map>
312
- </View>
313
- {overlayChildren}
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;
419
+
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>
314
446
  </View>
315
- );
316
- }
317
- }
447
+ </MapContext.Provider>
448
+ );
449
+ });