@lugg/maps 0.2.0-alpha.9 → 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 +283 -181
  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 +345 -211
  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,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,356 @@ 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
- };
142
-
143
- let isDragging = false;
271
+ if (!map || !edgeInsets) return;
144
272
 
145
- const dragStartListener = map.addListener('dragstart', () => {
146
- isDragging = true;
147
- });
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;
148
279
 
149
- const dragEndListener = map.addListener('dragend', () => {
150
- isDragging = false;
151
- });
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
+ );
152
313
 
153
- const centerListener = map.addListener('center_changed', () => {
154
- onCameraMove?.(createSyntheticEvent(createPayload(isDragging)));
155
- });
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
+ );
156
333
 
157
- const idleListener = map.addListener('idle', () => {
158
- onCameraIdle?.(createSyntheticEvent(createPayload(false)));
159
- });
334
+ const handleMouseUp = useCallback(() => {
335
+ if (longPressTimer.current) {
336
+ clearTimeout(longPressTimer.current);
337
+ longPressTimer.current = null;
338
+ }
339
+ }, []);
160
340
 
341
+ useEffect(() => {
342
+ if (!map || !onLongPress) return;
343
+ const listeners = [
344
+ map.addListener('mousedown', handleMouseDown),
345
+ map.addListener('mouseup', handleMouseUp),
346
+ ];
161
347
  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);
348
+ listeners.forEach((l) => google.maps.event.removeListener(l));
349
+ handleMouseUp();
166
350
  };
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
- };
182
-
183
- private mapInstance: google.maps.Map | null = null;
351
+ }, [map, onLongPress, handleMouseDown, handleMouseUp]);
184
352
 
185
- private handleMapReady = (map: google.maps.Map) => {
186
- this.mapInstance = map;
353
+ const handleDragStart = () => {
354
+ handleMouseUp();
355
+ setIsDragging(true);
356
+ wasGesture.current = true;
187
357
  };
188
358
 
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
- }
359
+ const handleDragEnd = () => {
360
+ setIsDragging(false);
361
+ };
204
362
 
205
- fitCoordinates(coordinates: Coordinate[], options?: FitCoordinatesOptions) {
206
- const map = this.mapInstance;
207
- const first = coordinates[0];
208
- if (!map || !first) return;
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,
377
+ };
378
+ onCameraMove?.(createSyntheticEvent(payload));
379
+ };
209
380
 
210
- const { padding, duration = -1 } = options ?? {};
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,
397
+ };
398
+ onCameraIdle?.(createSyntheticEvent(payload));
399
+ wasGesture.current = false;
400
+ };
211
401
 
212
- if (coordinates.length === 1) {
213
- const zoom = this.props.initialZoom ?? 10;
214
- this.moveCamera(first, { zoom, duration });
215
- return;
216
- }
402
+ const gestureHandling =
403
+ scrollEnabled === false && zoomEnabled === false
404
+ ? 'none'
405
+ : scrollEnabled === false
406
+ ? 'cooperative'
407
+ : 'auto';
217
408
 
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
- };
409
+ const colorScheme =
410
+ theme === 'dark'
411
+ ? ColorScheme.DARK
412
+ : theme === 'light'
413
+ ? ColorScheme.LIGHT
414
+ : ColorScheme.FOLLOW_SYSTEM;
281
415
 
282
- const mapStyle: CSSProperties = {
283
- width: '100%',
284
- height: '100%',
285
- };
416
+ const defaultCenter = initialCoordinate
417
+ ? { lat: initialCoordinate.latitude, lng: initialCoordinate.longitude }
418
+ : undefined;
286
419
 
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}
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>
312
446
  </View>
313
- );
314
- }
315
- }
447
+ </MapContext.Provider>
448
+ );
449
+ });