@lugg/maps 1.0.0-beta.5 → 1.0.0-beta.6

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