@lugg/maps 0.2.0-alpha.3 → 0.2.0-alpha.30

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 (124) hide show
  1. package/README.md +21 -3
  2. package/android/src/main/java/com/luggmaps/LuggMapView.kt +265 -0
  3. package/android/src/main/java/com/luggmaps/{LuggGoogleMapViewManager.kt → LuggMapViewManager.kt} +75 -36
  4. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +6 -5
  5. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +136 -14
  6. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +21 -0
  7. package/android/src/main/java/com/luggmaps/LuggPackage.kt +1 -4
  8. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +16 -0
  9. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +22 -0
  10. package/android/src/main/java/com/luggmaps/{LuggGoogleMapView.kt → core/GoogleMapProvider.kt} +251 -241
  11. package/android/src/main/java/com/luggmaps/core/MapProviderDelegate.kt +55 -0
  12. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +163 -52
  13. package/ios/LuggMapView.h +9 -0
  14. package/ios/LuggMapView.mm +312 -0
  15. package/ios/LuggMarkerView.h +9 -0
  16. package/ios/LuggMarkerView.mm +79 -0
  17. package/ios/LuggPolylineView.h +4 -0
  18. package/ios/LuggPolylineView.mm +23 -0
  19. package/ios/core/AppleMapProvider.h +15 -0
  20. package/ios/core/AppleMapProvider.mm +664 -0
  21. package/ios/core/GMSPolylineAnimator.h +3 -0
  22. package/ios/core/GMSPolylineAnimator.m +175 -40
  23. package/ios/core/GoogleMapProvider.h +13 -0
  24. package/ios/core/GoogleMapProvider.mm +513 -0
  25. package/ios/core/MKPolylineAnimator.h +4 -0
  26. package/ios/core/MKPolylineAnimator.m +224 -68
  27. package/ios/core/MapProviderDelegate.h +72 -0
  28. package/ios/core/PolylineAnimatorBase.h +24 -1
  29. package/ios/core/PolylineAnimatorBase.m +82 -19
  30. package/ios/extensions/MKMapView+Zoom.h +2 -0
  31. package/ios/extensions/MKMapView+Zoom.m +14 -4
  32. package/lib/module/MapProvider.js +13 -0
  33. package/lib/module/MapProvider.js.map +1 -0
  34. package/lib/module/MapProvider.types.js +4 -0
  35. package/lib/module/MapProvider.types.js.map +1 -0
  36. package/lib/module/MapProvider.web.js +20 -0
  37. package/lib/module/MapProvider.web.js.map +1 -0
  38. package/lib/module/MapView.js +29 -16
  39. package/lib/module/MapView.js.map +1 -1
  40. package/lib/module/MapView.web.js +290 -0
  41. package/lib/module/MapView.web.js.map +1 -0
  42. package/lib/module/components/Marker.js +19 -2
  43. package/lib/module/components/Marker.js.map +1 -1
  44. package/lib/module/components/Marker.web.js +33 -0
  45. package/lib/module/components/Marker.web.js.map +1 -0
  46. package/lib/module/components/Polyline.js +17 -4
  47. package/lib/module/components/Polyline.js.map +1 -1
  48. package/lib/module/components/Polyline.web.js +229 -0
  49. package/lib/module/components/Polyline.web.js.map +1 -0
  50. package/lib/module/components/index.js +2 -2
  51. package/lib/module/components/index.js.map +1 -1
  52. package/lib/module/components/index.web.js +5 -0
  53. package/lib/module/components/index.web.js.map +1 -0
  54. package/lib/module/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +19 -7
  55. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +7 -1
  56. package/lib/module/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  57. package/lib/module/index.js +4 -2
  58. package/lib/module/index.js.map +1 -1
  59. package/lib/module/index.web.js +7 -0
  60. package/lib/module/index.web.js.map +1 -0
  61. package/lib/typescript/src/MapProvider.d.ts +8 -0
  62. package/lib/typescript/src/MapProvider.d.ts.map +1 -0
  63. package/lib/typescript/src/MapProvider.types.d.ts +16 -0
  64. package/lib/typescript/src/MapProvider.types.d.ts.map +1 -0
  65. package/lib/typescript/src/MapProvider.web.d.ts +11 -0
  66. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -0
  67. package/lib/typescript/src/MapView.d.ts +4 -4
  68. package/lib/typescript/src/MapView.d.ts.map +1 -1
  69. package/lib/typescript/src/MapView.types.d.ts +23 -7
  70. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  71. package/lib/typescript/src/MapView.web.d.ts +3 -0
  72. package/lib/typescript/src/MapView.web.d.ts.map +1 -0
  73. package/lib/typescript/src/components/Marker.d.ts +25 -1
  74. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  75. package/lib/typescript/src/components/Marker.web.d.ts +3 -0
  76. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -0
  77. package/lib/typescript/src/components/Polyline.d.ts +36 -1
  78. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  79. package/lib/typescript/src/components/Polyline.web.d.ts +3 -0
  80. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -0
  81. package/lib/typescript/src/components/index.d.ts +4 -2
  82. package/lib/typescript/src/components/index.d.ts.map +1 -1
  83. package/lib/typescript/src/components/index.web.d.ts +5 -0
  84. package/lib/typescript/src/components/index.web.d.ts.map +1 -0
  85. package/lib/typescript/src/fabric/{LuggGoogleMapViewNativeComponent.d.ts → LuggMapViewNativeComponent.d.ts} +8 -4
  86. package/lib/typescript/src/fabric/LuggMapViewNativeComponent.d.ts.map +1 -0
  87. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +4 -1
  88. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  89. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts +7 -0
  90. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts.map +1 -1
  91. package/lib/typescript/src/index.d.ts +8 -3
  92. package/lib/typescript/src/index.d.ts.map +1 -1
  93. package/lib/typescript/src/index.web.d.ts +10 -0
  94. package/lib/typescript/src/index.web.d.ts.map +1 -0
  95. package/lib/typescript/src/types.d.ts +4 -0
  96. package/lib/typescript/src/types.d.ts.map +1 -1
  97. package/package.json +17 -3
  98. package/src/MapProvider.tsx +10 -0
  99. package/src/MapProvider.types.ts +16 -0
  100. package/src/MapProvider.web.tsx +14 -0
  101. package/src/MapView.tsx +28 -26
  102. package/src/MapView.types.ts +24 -7
  103. package/src/MapView.web.tsx +364 -0
  104. package/src/components/Marker.tsx +50 -4
  105. package/src/components/Marker.web.tsx +33 -0
  106. package/src/components/Polyline.tsx +51 -2
  107. package/src/components/Polyline.web.tsx +287 -0
  108. package/src/components/index.ts +8 -2
  109. package/src/components/index.web.ts +4 -0
  110. package/src/fabric/{LuggGoogleMapViewNativeComponent.ts → LuggMapViewNativeComponent.ts} +19 -7
  111. package/src/fabric/LuggMarkerViewNativeComponent.ts +7 -1
  112. package/src/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  113. package/src/index.ts +18 -2
  114. package/src/index.web.ts +20 -0
  115. package/src/types.ts +5 -0
  116. package/ios/LuggAppleMapView.h +0 -16
  117. package/ios/LuggAppleMapView.mm +0 -580
  118. package/ios/LuggGoogleMapView.h +0 -13
  119. package/ios/LuggGoogleMapView.mm +0 -454
  120. package/lib/module/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
  121. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts +0 -54
  122. package/lib/typescript/src/fabric/LuggAppleMapViewNativeComponent.d.ts.map +0 -1
  123. package/lib/typescript/src/fabric/LuggGoogleMapViewNativeComponent.d.ts.map +0 -1
  124. package/src/fabric/LuggAppleMapViewNativeComponent.ts +0 -83
@@ -0,0 +1,364 @@
1
+ import {
2
+ forwardRef,
3
+ useCallback,
4
+ useEffect,
5
+ useId,
6
+ useImperativeHandle,
7
+ useRef,
8
+ useState,
9
+ type CSSProperties,
10
+ } from 'react';
11
+ import type { NativeSyntheticEvent } from 'react-native';
12
+ import { View } from 'react-native';
13
+ import {
14
+ ColorScheme,
15
+ Map,
16
+ useMap,
17
+ type MapCameraChangedEvent,
18
+ type MapEvent,
19
+ } from '@vis.gl/react-google-maps';
20
+ import { Marker } from './components/Marker.web';
21
+ import { MapContext } from './MapProvider.web';
22
+
23
+ import type {
24
+ MapViewProps,
25
+ MapViewRef,
26
+ MoveCameraOptions,
27
+ FitCoordinatesOptions,
28
+ SetEdgeInsetsOptions,
29
+ CameraEventPayload,
30
+ } from './MapView.types';
31
+ import type { Coordinate, EdgeInsets } from './types';
32
+
33
+ const createSyntheticEvent = <T,>(nativeEvent: T): NativeSyntheticEvent<T> =>
34
+ ({
35
+ nativeEvent,
36
+ currentTarget: null,
37
+ target: null,
38
+ bubbles: false,
39
+ cancelable: false,
40
+ defaultPrevented: false,
41
+ eventPhase: 0,
42
+ isTrusted: true,
43
+ preventDefault: () => {},
44
+ stopPropagation: () => {},
45
+ isDefaultPrevented: () => false,
46
+ isPropagationStopped: () => false,
47
+ persist: () => {},
48
+ timeStamp: Date.now(),
49
+ type: '',
50
+ } as unknown as NativeSyntheticEvent<T>);
51
+
52
+ const userLocationDotStyle: CSSProperties = {
53
+ width: 16,
54
+ height: 16,
55
+ backgroundColor: '#4285F4',
56
+ border: '2px solid white',
57
+ borderRadius: '50%',
58
+ boxShadow: '0 1px 4px rgba(0,0,0,0.3)',
59
+ };
60
+
61
+ function UserLocationMarker({ enabled }: { enabled?: boolean }) {
62
+ const [coordinate, setCoordinate] = useState<Coordinate | null>(null);
63
+
64
+ useEffect(() => {
65
+ if (!enabled) {
66
+ setCoordinate(null);
67
+ return;
68
+ }
69
+
70
+ let watchId: number | null = null;
71
+
72
+ const updateLocation = (position: GeolocationPosition) => {
73
+ setCoordinate({
74
+ latitude: position.coords.latitude,
75
+ longitude: position.coords.longitude,
76
+ });
77
+ };
78
+
79
+ navigator.geolocation.getCurrentPosition(updateLocation, () => {});
80
+ watchId = navigator.geolocation.watchPosition(updateLocation, () => {});
81
+
82
+ return () => {
83
+ if (watchId !== null) {
84
+ navigator.geolocation.clearWatch(watchId);
85
+ }
86
+ };
87
+ }, [enabled]);
88
+
89
+ if (!coordinate) return null;
90
+
91
+ return (
92
+ <Marker coordinate={coordinate} anchor={{ x: 0.5, y: 0.5 }}>
93
+ <div style={userLocationDotStyle} />
94
+ </Marker>
95
+ );
96
+ }
97
+
98
+ export const MapView = forwardRef<MapViewRef, MapViewProps>(function MapView(
99
+ props,
100
+ ref
101
+ ) {
102
+ const {
103
+ mapId = 'DEMO_MAP_ID',
104
+ initialCoordinate,
105
+ initialZoom = 10,
106
+ minZoom,
107
+ maxZoom,
108
+ zoomEnabled = true,
109
+ scrollEnabled = true,
110
+ pitchEnabled = true,
111
+ edgeInsets,
112
+ userLocationEnabled,
113
+ theme = 'system',
114
+ onCameraMove,
115
+ onCameraIdle,
116
+ onReady,
117
+ children,
118
+ style,
119
+ } = props;
120
+
121
+ const id = useId();
122
+ const map = useMap(id);
123
+ const containerRef = useRef<View>(null);
124
+ const readyFired = useRef(false);
125
+ const [isDragging, setIsDragging] = useState(false);
126
+ const wasGesture = useRef(false);
127
+ const prevEdgeInsets = useRef(edgeInsets);
128
+
129
+ const offsetCenter = useCallback(
130
+ (coord: Coordinate, zoom: number, insets?: EdgeInsets, reverse = false) => {
131
+ const p = insets ?? prevEdgeInsets.current;
132
+ if (!p) {
133
+ return { lat: coord.latitude, lng: coord.longitude };
134
+ }
135
+
136
+ const dir = reverse ? -1 : 1;
137
+ const scale = 256 * Math.pow(2, zoom);
138
+ const offsetX = (dir * ((p.right ?? 0) - (p.left ?? 0))) / 2;
139
+ const offsetY = (dir * ((p.bottom ?? 0) - (p.top ?? 0))) / 2;
140
+
141
+ const latRad = (coord.latitude * Math.PI) / 180;
142
+ const x = ((coord.longitude + 180) / 360) * scale + offsetX;
143
+ const y =
144
+ ((1 - Math.log(Math.tan(latRad) + 1 / Math.cos(latRad)) / Math.PI) /
145
+ 2) *
146
+ scale +
147
+ offsetY;
148
+
149
+ const lng = (x / scale) * 360 - 180;
150
+ const lat =
151
+ (Math.atan(Math.sinh(Math.PI * (1 - (2 * y) / scale))) * 180) / Math.PI;
152
+
153
+ return { lat, lng };
154
+ },
155
+ []
156
+ );
157
+
158
+ const applyEdgeInsets = useCallback(
159
+ (newEdgeInsets: EdgeInsets, duration?: number) => {
160
+ if (!map) return;
161
+
162
+ const prev = prevEdgeInsets.current;
163
+ const center = map.getCenter();
164
+ const zoom = map.getZoom() ?? initialZoom;
165
+
166
+ if (center) {
167
+ const logicalCenter = offsetCenter(
168
+ { latitude: center.lat(), longitude: center.lng() },
169
+ zoom,
170
+ prev,
171
+ true
172
+ );
173
+ const newCenter = offsetCenter(
174
+ { latitude: logicalCenter.lat, longitude: logicalCenter.lng },
175
+ zoom,
176
+ newEdgeInsets,
177
+ false
178
+ );
179
+
180
+ if (duration === 0) {
181
+ map.moveCamera({ center: newCenter, zoom });
182
+ } else {
183
+ map.panTo(newCenter);
184
+ }
185
+ }
186
+
187
+ prevEdgeInsets.current = newEdgeInsets;
188
+ },
189
+ [map, initialZoom, offsetCenter]
190
+ );
191
+
192
+ useImperativeHandle(
193
+ ref,
194
+ () => ({
195
+ moveCamera(coordinate: Coordinate, options?: MoveCameraOptions) {
196
+ if (!map) return;
197
+
198
+ const { zoom = 0, duration = -1 } = options ?? {};
199
+ const targetZoom = zoom || map.getZoom() || initialZoom;
200
+ const center = offsetCenter(coordinate, targetZoom, undefined, false);
201
+
202
+ if (duration === 0) {
203
+ map.moveCamera({ center, zoom: targetZoom });
204
+ } else {
205
+ const currentZoom = map.getZoom();
206
+ const zoomChanged = zoom !== 0 && zoom !== currentZoom;
207
+
208
+ if (zoomChanged) {
209
+ map.setZoom(zoom);
210
+ }
211
+ map.panTo(center);
212
+ }
213
+ },
214
+
215
+ fitCoordinates(
216
+ coordinates: Coordinate[],
217
+ options?: FitCoordinatesOptions
218
+ ) {
219
+ const first = coordinates[0];
220
+ if (!map || !first) return;
221
+
222
+ const { padding: fitPadding, duration = -1 } = options ?? {};
223
+
224
+ if (coordinates.length === 1) {
225
+ this.moveCamera(first, { zoom: initialZoom, duration });
226
+ return;
227
+ }
228
+
229
+ const bounds = new google.maps.LatLngBounds();
230
+ coordinates.forEach((coord) => {
231
+ bounds.extend({ lat: coord.latitude, lng: coord.longitude });
232
+ });
233
+
234
+ const ei = prevEdgeInsets.current;
235
+ map.fitBounds(bounds, {
236
+ top: (ei?.top ?? 0) + (fitPadding?.top ?? 0),
237
+ left: (ei?.left ?? 0) + (fitPadding?.left ?? 0),
238
+ bottom: (ei?.bottom ?? 0) + (fitPadding?.bottom ?? 0),
239
+ right: (ei?.right ?? 0) + (fitPadding?.right ?? 0),
240
+ });
241
+ },
242
+
243
+ setEdgeInsets(newEdgeInsets: EdgeInsets, options?: SetEdgeInsetsOptions) {
244
+ applyEdgeInsets(newEdgeInsets, options?.duration);
245
+ },
246
+ }),
247
+ [map, initialZoom, offsetCenter, applyEdgeInsets]
248
+ );
249
+
250
+ useEffect(() => {
251
+ if (map && !readyFired.current) {
252
+ readyFired.current = true;
253
+ onReady?.();
254
+ }
255
+ }, [map, onReady]);
256
+
257
+ useEffect(() => {
258
+ if (!map || !edgeInsets) return;
259
+
260
+ const prev = prevEdgeInsets.current;
261
+ const changed =
262
+ prev?.top !== edgeInsets.top ||
263
+ prev?.left !== edgeInsets.left ||
264
+ prev?.bottom !== edgeInsets.bottom ||
265
+ prev?.right !== edgeInsets.right;
266
+
267
+ if (changed) {
268
+ applyEdgeInsets(edgeInsets);
269
+ }
270
+ }, [map, edgeInsets, applyEdgeInsets]);
271
+
272
+ const handleDragStart = () => {
273
+ setIsDragging(true);
274
+ wasGesture.current = true;
275
+ };
276
+
277
+ const handleDragEnd = () => {
278
+ setIsDragging(false);
279
+ };
280
+
281
+ const handleCameraChanged = (event: MapCameraChangedEvent) => {
282
+ const logicalCenter = offsetCenter(
283
+ { latitude: event.detail.center.lat, longitude: event.detail.center.lng },
284
+ event.detail.zoom,
285
+ undefined,
286
+ true
287
+ );
288
+ const payload: CameraEventPayload = {
289
+ coordinate: {
290
+ latitude: logicalCenter.lat,
291
+ longitude: logicalCenter.lng,
292
+ },
293
+ zoom: event.detail.zoom,
294
+ gesture: isDragging,
295
+ };
296
+ onCameraMove?.(createSyntheticEvent(payload));
297
+ };
298
+
299
+ const handleIdle = (event: MapEvent) => {
300
+ const center = event.map.getCenter();
301
+ const zoom = event.map.getZoom() ?? 0;
302
+ const logicalCenter = offsetCenter(
303
+ { latitude: center?.lat() ?? 0, longitude: center?.lng() ?? 0 },
304
+ zoom,
305
+ undefined,
306
+ true
307
+ );
308
+ const payload: CameraEventPayload = {
309
+ coordinate: {
310
+ latitude: logicalCenter.lat,
311
+ longitude: logicalCenter.lng,
312
+ },
313
+ zoom,
314
+ gesture: wasGesture.current,
315
+ };
316
+ onCameraIdle?.(createSyntheticEvent(payload));
317
+ wasGesture.current = false;
318
+ };
319
+
320
+ const gestureHandling =
321
+ scrollEnabled === false && zoomEnabled === false
322
+ ? 'none'
323
+ : scrollEnabled === false
324
+ ? 'cooperative'
325
+ : 'auto';
326
+
327
+ const colorScheme =
328
+ theme === 'dark'
329
+ ? ColorScheme.DARK
330
+ : theme === 'light'
331
+ ? ColorScheme.LIGHT
332
+ : ColorScheme.FOLLOW_SYSTEM;
333
+
334
+ const defaultCenter = initialCoordinate
335
+ ? { lat: initialCoordinate.latitude, lng: initialCoordinate.longitude }
336
+ : undefined;
337
+
338
+ return (
339
+ <MapContext.Provider value={{ map, isDragging }}>
340
+ <View ref={containerRef} style={style}>
341
+ <Map
342
+ id={id}
343
+ mapId={mapId}
344
+ defaultCenter={defaultCenter}
345
+ defaultZoom={initialZoom}
346
+ minZoom={minZoom}
347
+ maxZoom={maxZoom}
348
+ gestureHandling={gestureHandling}
349
+ colorScheme={colorScheme}
350
+ disableDefaultUI
351
+ isFractionalZoomEnabled
352
+ tilt={pitchEnabled === false ? 0 : undefined}
353
+ onDragstart={handleDragStart}
354
+ onDragend={handleDragEnd}
355
+ onCameraChanged={handleCameraChanged}
356
+ onIdle={handleIdle}
357
+ >
358
+ <UserLocationMarker enabled={userLocationEnabled} />
359
+ {children}
360
+ </Map>
361
+ </View>
362
+ </MapContext.Provider>
363
+ );
364
+ });
@@ -25,25 +25,71 @@ export interface MarkerProps {
25
25
  * Anchor point for custom marker views
26
26
  */
27
27
  anchor?: Point;
28
+ /**
29
+ * Z-index for marker ordering. Higher values render on top.
30
+ */
31
+ zIndex?: number;
32
+ /**
33
+ * Rotation angle in degrees clockwise from north.
34
+ * @default 0
35
+ */
36
+ rotate?: number;
37
+ /**
38
+ * Scale factor for the marker.
39
+ * @default 1
40
+ */
41
+ scale?: number;
42
+ /**
43
+ * Rasterize custom marker view to bitmap for better performance.
44
+ * Set to false if you need live view updates (e.g., animations).
45
+ * @platform ios, android
46
+ * @default true
47
+ */
48
+ rasterize?: boolean;
28
49
  /**
29
50
  * Custom marker view
30
51
  */
31
52
  children?: ReactNode;
32
53
  }
33
54
 
34
- export class Marker extends React.Component<MarkerProps> {
55
+ export class Marker extends React.PureComponent<MarkerProps> {
56
+ private getStyle(zIndex: number | undefined) {
57
+ if (zIndex == null) return styles.marker;
58
+ if (zIndex !== this._cachedZIndex) {
59
+ this._cachedZIndex = zIndex;
60
+ this._cachedStyle = [{ zIndex }, styles.marker];
61
+ }
62
+ return this._cachedStyle!;
63
+ }
64
+
65
+ private _cachedZIndex: number | undefined;
66
+ private _cachedStyle: any;
67
+
35
68
  render() {
36
- const { name, coordinate, title, description, anchor, children } =
37
- this.props;
69
+ const {
70
+ name,
71
+ coordinate,
72
+ title,
73
+ description,
74
+ anchor,
75
+ zIndex,
76
+ rotate = 0,
77
+ scale = 1,
78
+ rasterize = true,
79
+ children,
80
+ } = this.props;
38
81
 
39
82
  return (
40
83
  <LuggMarkerViewNativeComponent
41
- style={styles.marker}
84
+ style={this.getStyle(zIndex)}
42
85
  name={name}
43
86
  coordinate={coordinate}
44
87
  title={title}
45
88
  description={description}
46
89
  anchor={anchor}
90
+ rotate={rotate}
91
+ scale={scale}
92
+ rasterize={rasterize}
47
93
  >
48
94
  {children}
49
95
  </LuggMarkerViewNativeComponent>
@@ -0,0 +1,33 @@
1
+ import { AdvancedMarker } from '@vis.gl/react-google-maps';
2
+ import type { MarkerProps } from './Marker';
3
+
4
+ const toWebAnchor = (value: number) => `-${value * 100}%`;
5
+
6
+ export function Marker({
7
+ coordinate,
8
+ title,
9
+ anchor,
10
+ zIndex,
11
+ rotate,
12
+ scale,
13
+ children,
14
+ }: MarkerProps) {
15
+ const transforms: string[] = [];
16
+ if (rotate) transforms.push(`rotate(${rotate}deg)`);
17
+ if (scale && scale !== 1) transforms.push(`scale(${scale})`);
18
+
19
+ return (
20
+ <AdvancedMarker
21
+ position={{ lat: coordinate.latitude, lng: coordinate.longitude }}
22
+ title={title}
23
+ zIndex={zIndex}
24
+ anchorLeft={anchor ? toWebAnchor(anchor.x) : undefined}
25
+ anchorTop={anchor ? toWebAnchor(anchor.y) : undefined}
26
+ style={
27
+ transforms.length > 0 ? { transform: transforms.join(' ') } : undefined
28
+ }
29
+ >
30
+ {children}
31
+ </AdvancedMarker>
32
+ );
33
+ }
@@ -4,6 +4,32 @@ import { StyleSheet } from 'react-native';
4
4
  import LuggPolylineViewNativeComponent from '../fabric/LuggPolylineViewNativeComponent';
5
5
  import type { Coordinate } from '../types';
6
6
 
7
+ export type PolylineEasing = 'linear' | 'easeIn' | 'easeOut' | 'easeInOut';
8
+
9
+ export interface PolylineAnimatedOptions {
10
+ /**
11
+ * Animation duration in milliseconds
12
+ * @default 2150
13
+ */
14
+ duration?: number;
15
+ /**
16
+ * Easing function for the animation
17
+ * @default 'linear'
18
+ */
19
+ easing?: PolylineEasing;
20
+ /**
21
+ * Portion of the line visible as trail (0-1)
22
+ * 1.0 = full snake effect, 0.2 = short worm
23
+ * @default 1.0
24
+ */
25
+ trailLength?: number;
26
+ /**
27
+ * Delay before animation starts in milliseconds
28
+ * @default 0
29
+ */
30
+ delay?: number;
31
+ }
32
+
7
33
  export interface PolylineProps {
8
34
  /**
9
35
  * Array of coordinates forming the polyline
@@ -21,24 +47,47 @@ export interface PolylineProps {
21
47
  * Animate the polyline with a snake effect
22
48
  */
23
49
  animated?: boolean;
50
+ /**
51
+ * Animation configuration options
52
+ */
53
+ animatedOptions?: PolylineAnimatedOptions;
54
+ /**
55
+ * Z-index for layering polylines
56
+ */
57
+ zIndex?: number;
24
58
  }
25
59
 
26
- export class Polyline extends React.Component<PolylineProps> {
60
+ export class Polyline extends React.PureComponent<PolylineProps> {
61
+ private getStyle(zIndex: number | undefined) {
62
+ if (zIndex == null) return styles.polyline;
63
+ if (zIndex !== this._cachedZIndex) {
64
+ this._cachedZIndex = zIndex;
65
+ this._cachedStyle = [{ zIndex }, styles.polyline];
66
+ }
67
+ return this._cachedStyle!;
68
+ }
69
+
70
+ private _cachedZIndex: number | undefined;
71
+ private _cachedStyle: any;
72
+
27
73
  render() {
28
74
  const {
29
75
  coordinates,
30
76
  strokeColors,
31
77
  strokeWidth,
32
78
  animated = false,
79
+ animatedOptions,
80
+ zIndex,
33
81
  } = this.props;
34
82
 
35
83
  return (
36
84
  <LuggPolylineViewNativeComponent
37
- style={styles.polyline}
85
+ style={this.getStyle(zIndex)}
38
86
  coordinates={coordinates}
39
87
  strokeColors={strokeColors}
40
88
  strokeWidth={strokeWidth}
41
89
  animated={animated}
90
+ animatedOptions={animatedOptions}
42
91
  />
43
92
  );
44
93
  }