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

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 (153) hide show
  1. package/README.md +3 -0
  2. package/android/src/main/java/com/luggmaps/LuggCalloutView.kt +4 -2
  3. package/android/src/main/java/com/luggmaps/LuggCalloutViewManager.kt +4 -3
  4. package/android/src/main/java/com/luggmaps/LuggCircleView.kt +85 -0
  5. package/android/src/main/java/com/luggmaps/LuggCircleViewManager.kt +79 -0
  6. package/android/src/main/java/com/luggmaps/LuggGroundOverlayView.kt +81 -0
  7. package/android/src/main/java/com/luggmaps/LuggGroundOverlayViewManager.kt +82 -0
  8. package/android/src/main/java/com/luggmaps/LuggMapView.kt +49 -0
  9. package/android/src/main/java/com/luggmaps/LuggMapViewManager.kt +31 -0
  10. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +23 -0
  11. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +13 -0
  12. package/android/src/main/java/com/luggmaps/LuggPackage.kt +4 -1
  13. package/android/src/main/java/com/luggmaps/LuggTileOverlayView.kt +92 -0
  14. package/android/src/main/java/com/luggmaps/LuggTileOverlayViewManager.kt +80 -0
  15. package/android/src/main/java/com/luggmaps/core/GoogleMapProvider.kt +451 -17
  16. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +14 -0
  17. package/android/src/main/java/com/luggmaps/events/CirclePressEvent.kt +12 -0
  18. package/android/src/main/java/com/luggmaps/events/GroundOverlayPressEvent.kt +12 -0
  19. package/android/src/main/java/com/luggmaps/events/TileOverlayPressEvent.kt +12 -0
  20. package/android/src/main/java/com/luggmaps/extensions/ViewExtensions.kt +11 -0
  21. package/ios/LuggCalloutView.h +1 -1
  22. package/ios/LuggCalloutView.mm +21 -11
  23. package/ios/LuggCircleView.h +31 -0
  24. package/ios/LuggCircleView.mm +133 -0
  25. package/ios/LuggGroundOverlayView.h +30 -0
  26. package/ios/LuggGroundOverlayView.mm +125 -0
  27. package/ios/LuggMapView.mm +84 -2
  28. package/ios/LuggMarkerView.h +3 -0
  29. package/ios/LuggMarkerView.mm +23 -0
  30. package/ios/LuggTileOverlayView.h +31 -0
  31. package/ios/LuggTileOverlayView.mm +130 -0
  32. package/ios/core/AppleMapProvider.mm +595 -17
  33. package/ios/core/GoogleMapProvider.mm +390 -28
  34. package/ios/core/MapProviderDelegate.h +20 -1
  35. package/ios/events/CirclePressEvent.h +20 -0
  36. package/ios/events/GroundOverlayPressEvent.h +20 -0
  37. package/ios/events/TileOverlayPressEvent.h +20 -0
  38. package/lib/module/MapView.js +11 -0
  39. package/lib/module/MapView.js.map +1 -1
  40. package/lib/module/MapView.web.js +3 -0
  41. package/lib/module/MapView.web.js.map +1 -1
  42. package/lib/module/components/Circle.js +46 -0
  43. package/lib/module/components/Circle.js.map +1 -0
  44. package/lib/module/components/Circle.types.js +4 -0
  45. package/lib/module/components/Circle.types.js.map +1 -0
  46. package/lib/module/components/Circle.web.js +79 -0
  47. package/lib/module/components/Circle.web.js.map +1 -0
  48. package/lib/module/components/GroundOverlay.js +45 -0
  49. package/lib/module/components/GroundOverlay.js.map +1 -0
  50. package/lib/module/components/GroundOverlay.types.js +4 -0
  51. package/lib/module/components/GroundOverlay.types.js.map +1 -0
  52. package/lib/module/components/GroundOverlay.web.js +77 -0
  53. package/lib/module/components/GroundOverlay.web.js.map +1 -0
  54. package/lib/module/components/Marker.js +22 -4
  55. package/lib/module/components/Marker.js.map +1 -1
  56. package/lib/module/components/Marker.web.js +20 -7
  57. package/lib/module/components/Marker.web.js.map +1 -1
  58. package/lib/module/components/TileOverlay.js +44 -0
  59. package/lib/module/components/TileOverlay.js.map +1 -0
  60. package/lib/module/components/TileOverlay.types.js +4 -0
  61. package/lib/module/components/TileOverlay.types.js.map +1 -0
  62. package/lib/module/components/TileOverlay.web.js +69 -0
  63. package/lib/module/components/TileOverlay.web.js.map +1 -0
  64. package/lib/module/components/index.js +3 -0
  65. package/lib/module/components/index.js.map +1 -1
  66. package/lib/module/components/index.web.js +3 -0
  67. package/lib/module/components/index.web.js.map +1 -1
  68. package/lib/module/fabric/LuggCalloutViewNativeComponent.ts +1 -1
  69. package/lib/module/fabric/LuggCircleViewNativeComponent.ts +25 -0
  70. package/lib/module/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  71. package/lib/module/fabric/LuggMapViewNativeComponent.ts +9 -1
  72. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  73. package/lib/module/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  74. package/lib/module/index.js +3 -0
  75. package/lib/module/index.js.map +1 -1
  76. package/lib/module/index.web.js +3 -0
  77. package/lib/module/index.web.js.map +1 -1
  78. package/lib/typescript/src/MapView.d.ts.map +1 -1
  79. package/lib/typescript/src/MapView.types.d.ts +36 -1
  80. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  81. package/lib/typescript/src/MapView.web.d.ts.map +1 -1
  82. package/lib/typescript/src/components/Circle.d.ts +10 -0
  83. package/lib/typescript/src/components/Circle.d.ts.map +1 -0
  84. package/lib/typescript/src/components/Circle.types.d.ts +33 -0
  85. package/lib/typescript/src/components/Circle.types.d.ts.map +1 -0
  86. package/lib/typescript/src/components/Circle.web.d.ts +3 -0
  87. package/lib/typescript/src/components/Circle.web.d.ts.map +1 -0
  88. package/lib/typescript/src/components/GroundOverlay.d.ts +10 -0
  89. package/lib/typescript/src/components/GroundOverlay.d.ts.map +1 -0
  90. package/lib/typescript/src/components/GroundOverlay.types.d.ts +35 -0
  91. package/lib/typescript/src/components/GroundOverlay.types.d.ts.map +1 -0
  92. package/lib/typescript/src/components/GroundOverlay.web.d.ts +3 -0
  93. package/lib/typescript/src/components/GroundOverlay.web.d.ts.map +1 -0
  94. package/lib/typescript/src/components/Marker.d.ts +6 -3
  95. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  96. package/lib/typescript/src/components/Marker.types.d.ts +18 -6
  97. package/lib/typescript/src/components/Marker.types.d.ts.map +1 -1
  98. package/lib/typescript/src/components/Marker.web.d.ts +3 -2
  99. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -1
  100. package/lib/typescript/src/components/TileOverlay.d.ts +10 -0
  101. package/lib/typescript/src/components/TileOverlay.d.ts.map +1 -0
  102. package/lib/typescript/src/components/TileOverlay.types.d.ts +36 -0
  103. package/lib/typescript/src/components/TileOverlay.types.d.ts.map +1 -0
  104. package/lib/typescript/src/components/TileOverlay.web.d.ts +3 -0
  105. package/lib/typescript/src/components/TileOverlay.web.d.ts.map +1 -0
  106. package/lib/typescript/src/components/index.d.ts +7 -1
  107. package/lib/typescript/src/components/index.d.ts.map +1 -1
  108. package/lib/typescript/src/components/index.web.d.ts +6 -0
  109. package/lib/typescript/src/components/index.web.d.ts.map +1 -1
  110. package/lib/typescript/src/fabric/LuggCalloutViewNativeComponent.d.ts +1 -1
  111. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts +18 -0
  112. package/lib/typescript/src/fabric/LuggCircleViewNativeComponent.d.ts.map +1 -0
  113. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts +21 -0
  114. package/lib/typescript/src/fabric/LuggGroundOverlayViewNativeComponent.d.ts.map +1 -0
  115. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts +6 -1
  116. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -1
  117. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +8 -1
  118. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  119. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts +21 -0
  120. package/lib/typescript/src/fabric/LuggTileOverlayViewNativeComponent.d.ts.map +1 -0
  121. package/lib/typescript/src/index.d.ts +9 -3
  122. package/lib/typescript/src/index.d.ts.map +1 -1
  123. package/lib/typescript/src/index.web.d.ts +7 -1
  124. package/lib/typescript/src/index.web.d.ts.map +1 -1
  125. package/lib/typescript/src/types.d.ts +0 -4
  126. package/lib/typescript/src/types.d.ts.map +1 -1
  127. package/package.json +5 -2
  128. package/src/MapView.tsx +11 -0
  129. package/src/MapView.types.ts +85 -1
  130. package/src/MapView.web.tsx +11 -0
  131. package/src/components/Circle.tsx +52 -0
  132. package/src/components/Circle.types.ts +33 -0
  133. package/src/components/Circle.web.tsx +93 -0
  134. package/src/components/GroundOverlay.tsx +47 -0
  135. package/src/components/GroundOverlay.types.ts +36 -0
  136. package/src/components/GroundOverlay.web.tsx +85 -0
  137. package/src/components/Marker.tsx +32 -6
  138. package/src/components/Marker.types.ts +19 -6
  139. package/src/components/Marker.web.tsx +191 -148
  140. package/src/components/TileOverlay.tsx +44 -0
  141. package/src/components/TileOverlay.types.ts +37 -0
  142. package/src/components/TileOverlay.web.tsx +95 -0
  143. package/src/components/index.ts +10 -0
  144. package/src/components/index.web.ts +9 -0
  145. package/src/fabric/LuggCalloutViewNativeComponent.ts +1 -1
  146. package/src/fabric/LuggCircleViewNativeComponent.ts +25 -0
  147. package/src/fabric/LuggGroundOverlayViewNativeComponent.ts +29 -0
  148. package/src/fabric/LuggMapViewNativeComponent.ts +9 -1
  149. package/src/fabric/LuggMarkerViewNativeComponent.ts +14 -2
  150. package/src/fabric/LuggTileOverlayViewNativeComponent.ts +30 -0
  151. package/src/index.ts +12 -1
  152. package/src/index.web.ts +15 -0
  153. package/src/types.ts +0 -5
@@ -4,10 +4,71 @@ import type {
4
4
  MapProvider,
5
5
  Coordinate,
6
6
  EdgeInsets,
7
- MapTheme,
8
7
  PressEventPayload,
9
8
  } from './types';
10
9
 
10
+ export type MapType =
11
+ | 'standard'
12
+ | 'satellite'
13
+ | 'terrain'
14
+ | 'hybrid'
15
+ | 'muted-standard';
16
+
17
+ export type MapTheme = 'light' | 'dark' | 'system';
18
+
19
+ export type InsetAdjustment = 'automatic' | 'never';
20
+
21
+ export type PoiCategory =
22
+ | 'airport'
23
+ | 'amusement-park'
24
+ | 'aquarium'
25
+ | 'atm'
26
+ | 'bakery'
27
+ | 'bank'
28
+ | 'beach'
29
+ | 'brewery'
30
+ | 'cafe'
31
+ | 'campground'
32
+ | 'car-rental'
33
+ | 'ev-charger'
34
+ | 'fire-station'
35
+ | 'fitness-center'
36
+ | 'food-market'
37
+ | 'gas-station'
38
+ | 'hospital'
39
+ | 'hotel'
40
+ | 'laundry'
41
+ | 'library'
42
+ | 'marina'
43
+ | 'movie-theater'
44
+ | 'museum'
45
+ | 'national-park'
46
+ | 'nightlife'
47
+ | 'park'
48
+ | 'parking'
49
+ | 'pharmacy'
50
+ | 'police'
51
+ | 'post-office'
52
+ | 'public-transport'
53
+ | 'restaurant'
54
+ | 'restroom'
55
+ | 'school'
56
+ | 'stadium'
57
+ | 'store'
58
+ | 'theater'
59
+ | 'university'
60
+ | 'winery'
61
+ | 'zoo';
62
+
63
+ export interface PoiFilter {
64
+ /**
65
+ * Filter mode
66
+ * @default 'including'
67
+ */
68
+ mode?: 'including' | 'excluding';
69
+ categories: PoiCategory[];
70
+ }
71
+
11
72
  /**
12
73
  * Options for moving the camera
13
74
  */
@@ -64,6 +125,11 @@ export interface MapViewProps extends ViewProps {
64
125
  * @default 'apple' on iOS, 'google' on Android
65
126
  */
66
127
  provider?: MapProvider;
128
+ /**
129
+ * Map type to display
130
+ * @default 'standard'
131
+ */
132
+ mapType?: MapType;
67
133
  /**
68
134
  * Map style ID (Google Maps) or configuration name (Apple Maps)
69
135
  */
@@ -121,11 +187,29 @@ export interface MapViewProps extends ViewProps {
121
187
  * @platform android
122
188
  */
123
189
  userLocationButtonEnabled?: boolean;
190
+ /**
191
+ * Show points of interest on the map (Apple Maps only).
192
+ * When false, hides all POIs regardless of poiFilter.
193
+ * @default true
194
+ * @platform ios
195
+ */
196
+ poiEnabled?: boolean;
197
+ /**
198
+ * Filter POI categories to show or hide (Apple Maps only).
199
+ * Only takes effect when poiEnabled is true.
200
+ * @platform ios
201
+ */
202
+ poiFilter?: PoiFilter;
124
203
  /**
125
204
  * Map color theme
126
205
  * @default 'system'
127
206
  */
128
207
  theme?: MapTheme;
208
+ /**
209
+ * Safe area inset adjustment behavior
210
+ * @default 'never'
211
+ */
212
+ insetAdjustment?: InsetAdjustment;
129
213
  /**
130
214
  * Called when the map is pressed
131
215
  */
@@ -101,6 +101,7 @@ export const MapView = forwardRef<MapViewRef, MapViewProps>(function MapView(
101
101
  ref
102
102
  ) {
103
103
  const {
104
+ mapType = 'standard',
104
105
  mapId = 'DEMO_MAP_ID',
105
106
  initialCoordinate,
106
107
  initialZoom = 10,
@@ -417,6 +418,15 @@ export const MapView = forwardRef<MapViewRef, MapViewProps>(function MapView(
417
418
  wasGesture.current = false;
418
419
  };
419
420
 
421
+ const mapTypeId =
422
+ mapType === 'satellite'
423
+ ? 'satellite'
424
+ : mapType === 'terrain'
425
+ ? 'terrain'
426
+ : mapType === 'hybrid'
427
+ ? 'hybrid'
428
+ : 'roadmap';
429
+
420
430
  const gestureHandling =
421
431
  scrollEnabled === false && zoomEnabled === false
422
432
  ? 'none'
@@ -453,6 +463,7 @@ export const MapView = forwardRef<MapViewRef, MapViewProps>(function MapView(
453
463
  defaultZoom={initialZoom}
454
464
  minZoom={minZoom}
455
465
  maxZoom={maxZoom}
466
+ mapTypeId={mapTypeId}
456
467
  gestureHandling={gestureHandling}
457
468
  colorScheme={colorScheme}
458
469
  disableDefaultUI
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import { StyleSheet } from 'react-native';
3
+ import LuggCircleViewNativeComponent from '../fabric/LuggCircleViewNativeComponent';
4
+ import type { CircleProps } from './Circle.types';
5
+
6
+ export type { CircleProps } from './Circle.types';
7
+
8
+ export class Circle extends React.PureComponent<CircleProps> {
9
+ private _cachedZIndex: number | undefined;
10
+ private _cachedStyle: any;
11
+
12
+ private getStyle(zIndex: number | undefined) {
13
+ if (zIndex == null) return styles.circle;
14
+ if (zIndex !== this._cachedZIndex) {
15
+ this._cachedZIndex = zIndex;
16
+ this._cachedStyle = [{ zIndex }, styles.circle];
17
+ }
18
+ return this._cachedStyle!;
19
+ }
20
+
21
+ render() {
22
+ const {
23
+ center,
24
+ radius,
25
+ strokeColor,
26
+ strokeWidth,
27
+ fillColor,
28
+ zIndex,
29
+ onPress,
30
+ } = this.props;
31
+
32
+ return (
33
+ <LuggCircleViewNativeComponent
34
+ style={this.getStyle(zIndex)}
35
+ center={center}
36
+ radius={radius}
37
+ strokeColor={strokeColor}
38
+ strokeWidth={strokeWidth}
39
+ fillColor={fillColor}
40
+ tappable={!!onPress}
41
+ onCirclePress={onPress}
42
+ />
43
+ );
44
+ }
45
+ }
46
+
47
+ const styles = StyleSheet.create({
48
+ circle: {
49
+ position: 'absolute',
50
+ pointerEvents: 'none',
51
+ },
52
+ });
@@ -0,0 +1,33 @@
1
+ import type { ColorValue } from 'react-native';
2
+ import type { Coordinate } from '../types';
3
+
4
+ export interface CircleProps {
5
+ /**
6
+ * Center coordinate of the circle
7
+ */
8
+ center: Coordinate;
9
+ /**
10
+ * Radius in meters
11
+ */
12
+ radius: number;
13
+ /**
14
+ * Stroke (outline) color
15
+ */
16
+ strokeColor?: ColorValue;
17
+ /**
18
+ * Stroke width in points
19
+ */
20
+ strokeWidth?: number;
21
+ /**
22
+ * Fill color of the circle
23
+ */
24
+ fillColor?: ColorValue;
25
+ /**
26
+ * Z-index for layering
27
+ */
28
+ zIndex?: number;
29
+ /**
30
+ * Called when the circle is tapped
31
+ */
32
+ onPress?: () => void;
33
+ }
@@ -0,0 +1,93 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ import { useMapContext } from '../MapProvider.web';
3
+ import type { CircleProps } from './Circle.types';
4
+
5
+ export const Circle = ({
6
+ center,
7
+ radius,
8
+ strokeColor = '#000000',
9
+ strokeWidth = 1,
10
+ fillColor = 'rgba(0, 0, 0, 0.3)',
11
+ zIndex = 0,
12
+ onPress,
13
+ }: CircleProps) => {
14
+ const { map } = useMapContext();
15
+ const circleRef = useRef<google.maps.Circle | null>(null);
16
+ const listenersRef = useRef<google.maps.MapsEventListener[]>([]);
17
+
18
+ const handleClick = useCallback(() => {
19
+ onPress?.();
20
+ }, [onPress]);
21
+
22
+ useEffect(() => {
23
+ return () => {
24
+ listenersRef.current.forEach((l) => l.remove());
25
+ listenersRef.current = [];
26
+ circleRef.current?.setMap(null);
27
+ circleRef.current = null;
28
+ };
29
+ }, []);
30
+
31
+ useEffect(() => {
32
+ const circle = circleRef.current;
33
+ if (!circle) return;
34
+
35
+ listenersRef.current.forEach((l) => l.remove());
36
+ listenersRef.current = [];
37
+
38
+ if (onPress) {
39
+ listenersRef.current.push(circle.addListener('click', handleClick));
40
+ }
41
+ circle.set('clickable', !!onPress);
42
+ }, [onPress, handleClick]);
43
+
44
+ useEffect(() => {
45
+ if (!map) {
46
+ circleRef.current?.setMap(null);
47
+ return;
48
+ }
49
+
50
+ const googleCenter = { lat: center.latitude, lng: center.longitude };
51
+
52
+ if (circleRef.current) {
53
+ circleRef.current.setCenter(googleCenter);
54
+ circleRef.current.setRadius(radius);
55
+ circleRef.current.setOptions({
56
+ strokeColor: strokeColor as string,
57
+ strokeWeight: strokeWidth,
58
+ fillColor: fillColor as string,
59
+ zIndex,
60
+ });
61
+ } else {
62
+ const circle = new google.maps.Circle({
63
+ center: googleCenter,
64
+ radius,
65
+ strokeColor: strokeColor as string,
66
+ strokeWeight: strokeWidth,
67
+ strokeOpacity: 1,
68
+ fillColor: fillColor as string,
69
+ fillOpacity: 1,
70
+ zIndex,
71
+ clickable: !!onPress,
72
+ map,
73
+ });
74
+ circleRef.current = circle;
75
+
76
+ if (onPress) {
77
+ listenersRef.current.push(circle.addListener('click', handleClick));
78
+ }
79
+ }
80
+ }, [
81
+ map,
82
+ center,
83
+ radius,
84
+ strokeColor,
85
+ strokeWidth,
86
+ fillColor,
87
+ zIndex,
88
+ onPress,
89
+ handleClick,
90
+ ]);
91
+
92
+ return null;
93
+ };
@@ -0,0 +1,47 @@
1
+ import React from 'react';
2
+ import { Image, StyleSheet } from 'react-native';
3
+ import LuggGroundOverlayViewNativeComponent from '../fabric/LuggGroundOverlayViewNativeComponent';
4
+ import type { GroundOverlayProps } from './GroundOverlay.types';
5
+
6
+ export type {
7
+ GroundOverlayProps,
8
+ GroundOverlayBounds,
9
+ } from './GroundOverlay.types';
10
+
11
+ export class GroundOverlay extends React.PureComponent<GroundOverlayProps> {
12
+ private _cachedZIndex: number | undefined;
13
+ private _cachedStyle: any;
14
+
15
+ private getStyle(zIndex: number | undefined) {
16
+ if (zIndex == null) return styles.overlay;
17
+ if (zIndex !== this._cachedZIndex) {
18
+ this._cachedZIndex = zIndex;
19
+ this._cachedStyle = [{ zIndex }, styles.overlay];
20
+ }
21
+ return this._cachedStyle!;
22
+ }
23
+
24
+ render() {
25
+ const { image, bounds, opacity, bearing, zIndex, onPress } = this.props;
26
+ const resolved = Image.resolveAssetSource(image);
27
+
28
+ return (
29
+ <LuggGroundOverlayViewNativeComponent
30
+ style={this.getStyle(zIndex)}
31
+ image={resolved?.uri ?? ''}
32
+ bounds={bounds}
33
+ opacity={opacity}
34
+ bearing={bearing}
35
+ tappable={!!onPress}
36
+ onGroundOverlayPress={onPress}
37
+ />
38
+ );
39
+ }
40
+ }
41
+
42
+ const styles = StyleSheet.create({
43
+ overlay: {
44
+ position: 'absolute',
45
+ pointerEvents: 'none',
46
+ },
47
+ });
@@ -0,0 +1,36 @@
1
+ import type { ImageSourcePropType } from 'react-native';
2
+ import type { Coordinate } from '../types';
3
+
4
+ export interface GroundOverlayBounds {
5
+ northeast: Coordinate;
6
+ southwest: Coordinate;
7
+ }
8
+
9
+ export interface GroundOverlayProps {
10
+ /**
11
+ * Image to overlay on the map
12
+ */
13
+ image: ImageSourcePropType;
14
+ /**
15
+ * Geographic bounds for the overlay
16
+ */
17
+ bounds: GroundOverlayBounds;
18
+ /**
19
+ * Opacity of the overlay (0-1)
20
+ */
21
+ opacity?: number;
22
+ /**
23
+ * Bearing (rotation) in degrees clockwise from north.
24
+ * Only supported on Google Maps.
25
+ * @platform google
26
+ */
27
+ bearing?: number;
28
+ /**
29
+ * Z-index for layering
30
+ */
31
+ zIndex?: number;
32
+ /**
33
+ * Called when the overlay is tapped
34
+ */
35
+ onPress?: () => void;
36
+ }
@@ -0,0 +1,85 @@
1
+ import { useCallback, useEffect, useRef } from 'react';
2
+ import { useMapContext } from '../MapProvider.web';
3
+ import type { GroundOverlayProps } from './GroundOverlay.types';
4
+
5
+ export const GroundOverlay = ({
6
+ image,
7
+ bounds,
8
+ opacity = 1,
9
+ zIndex = 0,
10
+ onPress,
11
+ }: GroundOverlayProps) => {
12
+ const { map } = useMapContext();
13
+ const overlayRef = useRef<google.maps.GroundOverlay | null>(null);
14
+ const listenersRef = useRef<google.maps.MapsEventListener[]>([]);
15
+
16
+ const handleClick = useCallback(() => {
17
+ onPress?.();
18
+ }, [onPress]);
19
+
20
+ // Cleanup on unmount
21
+ useEffect(() => {
22
+ return () => {
23
+ listenersRef.current.forEach((l) => l.remove());
24
+ listenersRef.current = [];
25
+ overlayRef.current?.setMap(null);
26
+ overlayRef.current = null;
27
+ };
28
+ }, []);
29
+
30
+ // Sync listeners
31
+ useEffect(() => {
32
+ const overlay = overlayRef.current;
33
+ if (!overlay) return;
34
+
35
+ listenersRef.current.forEach((l) => l.remove());
36
+ listenersRef.current = [];
37
+
38
+ if (onPress) {
39
+ listenersRef.current.push(overlay.addListener('click', handleClick));
40
+ }
41
+ overlay.set('clickable', !!onPress);
42
+ }, [onPress, handleClick]);
43
+
44
+ // Sync overlay with props
45
+ useEffect(() => {
46
+ if (!map) {
47
+ overlayRef.current?.setMap(null);
48
+ return;
49
+ }
50
+
51
+ const source =
52
+ typeof image === 'number'
53
+ ? null
54
+ : Array.isArray(image)
55
+ ? image[0]
56
+ : image;
57
+ const imageUrl = source?.uri ?? '';
58
+ if (!imageUrl) return;
59
+
60
+ const latLngBounds = new google.maps.LatLngBounds(
61
+ { lat: bounds.southwest.latitude, lng: bounds.southwest.longitude },
62
+ { lat: bounds.northeast.latitude, lng: bounds.northeast.longitude }
63
+ );
64
+
65
+ // GroundOverlay bounds are immutable — recreate if bounds change
66
+ overlayRef.current?.setMap(null);
67
+ listenersRef.current.forEach((l) => l.remove());
68
+ listenersRef.current = [];
69
+
70
+ const overlay = new google.maps.GroundOverlay(imageUrl, latLngBounds, {
71
+ opacity,
72
+ clickable: !!onPress,
73
+ map,
74
+ });
75
+
76
+ overlay.set('zIndex', zIndex);
77
+ overlayRef.current = overlay;
78
+
79
+ if (onPress) {
80
+ listenersRef.current.push(overlay.addListener('click', handleClick));
81
+ }
82
+ }, [map, image, bounds, opacity, zIndex, onPress, handleClick]);
83
+
84
+ return null;
85
+ };
@@ -1,17 +1,40 @@
1
1
  import React, { isValidElement } from 'react';
2
2
  import { StyleSheet } from 'react-native';
3
- import LuggMarkerViewNativeComponent from '../fabric/LuggMarkerViewNativeComponent';
3
+ import LuggMarkerViewNativeComponent, {
4
+ Commands,
5
+ } from '../fabric/LuggMarkerViewNativeComponent';
4
6
  import LuggCalloutViewNativeComponent from '../fabric/LuggCalloutViewNativeComponent';
5
- import type { MarkerProps } from './Marker.types';
7
+ import type { MarkerProps, MarkerRef } from './Marker.types';
8
+ import type { Point } from '../types';
6
9
 
7
10
  export type {
8
11
  CalloutOptions,
12
+ MarkerRef,
9
13
  MarkerProps,
10
14
  MarkerPressEvent,
11
15
  MarkerDragEvent,
12
16
  } from './Marker.types';
13
17
 
14
- export class Marker extends React.PureComponent<MarkerProps> {
18
+ const DEFAULT_ANCHOR: Point = { x: 0.5, y: 1 };
19
+
20
+ export class Marker
21
+ extends React.PureComponent<MarkerProps>
22
+ implements MarkerRef
23
+ {
24
+ private nativeRef = React.createRef<any>();
25
+
26
+ showCallout() {
27
+ const ref = this.nativeRef.current;
28
+ if (!ref) return;
29
+ Commands.showCallout(ref);
30
+ }
31
+
32
+ hideCallout() {
33
+ const ref = this.nativeRef.current;
34
+ if (!ref) return;
35
+ Commands.hideCallout(ref);
36
+ }
37
+
15
38
  private getStyle(zIndex: number | undefined) {
16
39
  if (zIndex == null) return styles.marker;
17
40
  if (zIndex !== this._cachedZIndex) {
@@ -35,6 +58,7 @@ export class Marker extends React.PureComponent<MarkerProps> {
35
58
  rotate = 0,
36
59
  scale = 1,
37
60
  rasterize = true,
61
+ centerOnPress = true,
38
62
  draggable = false,
39
63
  onPress,
40
64
  onDragStart,
@@ -51,19 +75,21 @@ export class Marker extends React.PureComponent<MarkerProps> {
51
75
  : React.createElement(callout)
52
76
  : null;
53
77
  const calloutBubbled = calloutOptions?.bubbled ?? true;
54
- const calloutAnchor = calloutOptions?.anchor;
78
+ const calloutOffset = calloutOptions?.offset;
55
79
 
56
80
  return (
57
81
  <LuggMarkerViewNativeComponent
82
+ ref={this.nativeRef}
58
83
  style={this.getStyle(zIndex)}
59
84
  name={name}
60
85
  coordinate={coordinate}
61
86
  title={title}
62
87
  description={description}
63
- anchor={anchor}
88
+ anchor={anchor ?? DEFAULT_ANCHOR}
64
89
  rotate={rotate}
65
90
  scale={scale}
66
91
  rasterize={rasterize}
92
+ centerOnPress={centerOnPress}
67
93
  draggable={draggable}
68
94
  onMarkerPress={onPress}
69
95
  onMarkerDragStart={onDragStart}
@@ -75,7 +101,7 @@ export class Marker extends React.PureComponent<MarkerProps> {
75
101
  <LuggCalloutViewNativeComponent
76
102
  style={calloutStyles.callout}
77
103
  bubbled={calloutBubbled}
78
- anchor={calloutAnchor}
104
+ offset={calloutOffset}
79
105
  >
80
106
  {calloutContent}
81
107
  </LuggCalloutViewNativeComponent>
@@ -14,13 +14,18 @@ export interface CalloutOptions {
14
14
  */
15
15
  bubbled?: boolean;
16
16
  /**
17
- * Anchor point for the callout relative to the marker position.
18
- * `{x: 0.5, y: 1}` places the bottom-center of the callout at the marker.
17
+ * Pixel offset for non-bubbled callouts from their default centered position above the marker.
18
+ * Positive `x` moves right, positive `y` moves down.
19
+ * Only applies when `bubbled` is `false`.
19
20
  *
20
- * @note Not supported on web.
21
- * @default {x: 0.5, y: 1}
21
+ * @default {x: 0, y: 0}
22
22
  */
23
- anchor?: Point;
23
+ offset?: Point;
24
+ }
25
+
26
+ export interface MarkerRef {
27
+ showCallout(): void;
28
+ hideCallout(): void;
24
29
  }
25
30
 
26
31
  export interface MarkerProps {
@@ -41,7 +46,10 @@ export interface MarkerProps {
41
46
  */
42
47
  description?: string;
43
48
  /**
44
- * Anchor point for custom marker views
49
+ * Anchor point for custom marker views.
50
+ * `{x: 0.5, y: 1}` places the bottom-center of the marker at the coordinate.
51
+ *
52
+ * @default {x: 0.5, y: 1}
45
53
  */
46
54
  anchor?: Point;
47
55
  /**
@@ -65,6 +73,11 @@ export interface MarkerProps {
65
73
  * @default true
66
74
  */
67
75
  rasterize?: boolean;
76
+ /**
77
+ * Whether the map centers on the marker when pressed.
78
+ * @default true
79
+ */
80
+ centerOnPress?: boolean;
68
81
  /**
69
82
  * Whether the marker can be dragged by the user.
70
83
  * @default false