@lugg/maps 0.2.0-alpha.2 → 0.2.0-alpha.20

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 (96) hide show
  1. package/README.md +17 -4
  2. package/android/src/main/java/com/luggmaps/LuggGoogleMapView.kt +31 -37
  3. package/android/src/main/java/com/luggmaps/LuggMapWrapperView.kt +6 -5
  4. package/android/src/main/java/com/luggmaps/LuggMarkerView.kt +136 -14
  5. package/android/src/main/java/com/luggmaps/LuggMarkerViewManager.kt +21 -0
  6. package/android/src/main/java/com/luggmaps/LuggPolylineView.kt +16 -0
  7. package/android/src/main/java/com/luggmaps/LuggPolylineViewManager.kt +22 -0
  8. package/android/src/main/java/com/luggmaps/core/PolylineAnimator.kt +157 -52
  9. package/ios/LuggAppleMapView.mm +154 -42
  10. package/ios/LuggGoogleMapView.mm +52 -22
  11. package/ios/LuggMarkerView.h +9 -0
  12. package/ios/LuggMarkerView.mm +79 -0
  13. package/ios/LuggPolylineView.h +4 -0
  14. package/ios/LuggPolylineView.mm +23 -0
  15. package/ios/core/GMSPolylineAnimator.h +3 -0
  16. package/ios/core/GMSPolylineAnimator.m +164 -41
  17. package/ios/core/MKPolylineAnimator.h +4 -0
  18. package/ios/core/MKPolylineAnimator.m +162 -43
  19. package/ios/core/PolylineAnimatorBase.h +14 -0
  20. package/ios/core/PolylineAnimatorBase.m +33 -0
  21. package/ios/extensions/MKMapView+Zoom.h +2 -0
  22. package/ios/extensions/MKMapView+Zoom.m +14 -4
  23. package/lib/module/MapProvider.js +13 -0
  24. package/lib/module/MapProvider.js.map +1 -0
  25. package/lib/module/MapProvider.types.js +4 -0
  26. package/lib/module/MapProvider.types.js.map +1 -0
  27. package/lib/module/MapProvider.web.js +20 -0
  28. package/lib/module/MapProvider.web.js.map +1 -0
  29. package/lib/module/MapView.js +2 -2
  30. package/lib/module/MapView.js.map +1 -1
  31. package/lib/module/MapView.web.js +272 -0
  32. package/lib/module/MapView.web.js.map +1 -0
  33. package/lib/module/components/Marker.js +10 -1
  34. package/lib/module/components/Marker.js.map +1 -1
  35. package/lib/module/components/Marker.web.js +33 -0
  36. package/lib/module/components/Marker.web.js.map +1 -0
  37. package/lib/module/components/Polyline.js +8 -3
  38. package/lib/module/components/Polyline.js.map +1 -1
  39. package/lib/module/components/Polyline.web.js +229 -0
  40. package/lib/module/components/Polyline.web.js.map +1 -0
  41. package/lib/module/components/index.js +2 -2
  42. package/lib/module/components/index.js.map +1 -1
  43. package/lib/module/components/index.web.js +5 -0
  44. package/lib/module/components/index.web.js.map +1 -0
  45. package/lib/module/fabric/LuggMarkerViewNativeComponent.ts +7 -1
  46. package/lib/module/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  47. package/lib/module/index.js +3 -2
  48. package/lib/module/index.js.map +1 -1
  49. package/lib/module/index.web.js +6 -0
  50. package/lib/module/index.web.js.map +1 -0
  51. package/lib/typescript/src/MapProvider.d.ts +8 -0
  52. package/lib/typescript/src/MapProvider.d.ts.map +1 -0
  53. package/lib/typescript/src/MapProvider.types.d.ts +16 -0
  54. package/lib/typescript/src/MapProvider.types.d.ts.map +1 -0
  55. package/lib/typescript/src/MapProvider.web.d.ts +11 -0
  56. package/lib/typescript/src/MapProvider.web.d.ts.map +1 -0
  57. package/lib/typescript/src/MapView.d.ts +1 -1
  58. package/lib/typescript/src/MapView.d.ts.map +1 -1
  59. package/lib/typescript/src/MapView.types.d.ts +2 -2
  60. package/lib/typescript/src/MapView.types.d.ts.map +1 -1
  61. package/lib/typescript/src/MapView.web.d.ts +3 -0
  62. package/lib/typescript/src/MapView.web.d.ts.map +1 -0
  63. package/lib/typescript/src/components/Marker.d.ts +21 -0
  64. package/lib/typescript/src/components/Marker.d.ts.map +1 -1
  65. package/lib/typescript/src/components/Marker.web.d.ts +3 -0
  66. package/lib/typescript/src/components/Marker.web.d.ts.map +1 -0
  67. package/lib/typescript/src/components/Polyline.d.ts +32 -0
  68. package/lib/typescript/src/components/Polyline.d.ts.map +1 -1
  69. package/lib/typescript/src/components/Polyline.web.d.ts +3 -0
  70. package/lib/typescript/src/components/Polyline.web.d.ts.map +1 -0
  71. package/lib/typescript/src/components/index.web.d.ts +5 -0
  72. package/lib/typescript/src/components/index.web.d.ts.map +1 -0
  73. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts +4 -1
  74. package/lib/typescript/src/fabric/LuggMarkerViewNativeComponent.d.ts.map +1 -1
  75. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts +7 -0
  76. package/lib/typescript/src/fabric/LuggPolylineViewNativeComponent.d.ts.map +1 -1
  77. package/lib/typescript/src/index.d.ts +3 -1
  78. package/lib/typescript/src/index.d.ts.map +1 -1
  79. package/lib/typescript/src/index.web.d.ts +7 -0
  80. package/lib/typescript/src/index.web.d.ts.map +1 -0
  81. package/package.json +15 -2
  82. package/src/MapProvider.tsx +10 -0
  83. package/src/MapProvider.types.ts +16 -0
  84. package/src/MapProvider.web.tsx +14 -0
  85. package/src/MapView.tsx +2 -2
  86. package/src/MapView.types.ts +2 -2
  87. package/src/MapView.web.tsx +337 -0
  88. package/src/components/Marker.tsx +37 -3
  89. package/src/components/Marker.web.tsx +33 -0
  90. package/src/components/Polyline.tsx +38 -1
  91. package/src/components/Polyline.web.tsx +287 -0
  92. package/src/components/index.web.ts +4 -0
  93. package/src/fabric/LuggMarkerViewNativeComponent.ts +7 -1
  94. package/src/fabric/LuggPolylineViewNativeComponent.ts +8 -0
  95. package/src/index.ts +8 -1
  96. package/src/index.web.ts +17 -0
@@ -0,0 +1,287 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useMapContext } from '../MapProvider.web';
3
+ import type { PolylineProps, PolylineEasing } from './Polyline';
4
+
5
+ const DEFAULT_DURATION = 2150;
6
+
7
+ function applyEasing(t: number, easing: PolylineEasing = 'linear'): number {
8
+ switch (easing) {
9
+ case 'easeIn':
10
+ return t * t;
11
+ case 'easeOut':
12
+ return t * (2 - t);
13
+ case 'easeInOut':
14
+ return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
15
+ default:
16
+ return t;
17
+ }
18
+ }
19
+
20
+ function interpolateColor(color1: string, color2: string, t: number): string {
21
+ const hex = (c: string) => parseInt(c, 16);
22
+ const r1 = hex(color1.slice(1, 3));
23
+ const g1 = hex(color1.slice(3, 5));
24
+ const b1 = hex(color1.slice(5, 7));
25
+ const r2 = hex(color2.slice(1, 3));
26
+ const g2 = hex(color2.slice(3, 5));
27
+ const b2 = hex(color2.slice(5, 7));
28
+
29
+ const r = Math.round(r1 + (r2 - r1) * t);
30
+ const g = Math.round(g1 + (g2 - g1) * t);
31
+ const b = Math.round(b1 + (b2 - b1) * t);
32
+
33
+ return `#${r.toString(16).padStart(2, '0')}${g
34
+ .toString(16)
35
+ .padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
36
+ }
37
+
38
+ function getGradientColor(colors: string[], position: number): string {
39
+ if (colors.length === 0) return '#000000';
40
+ if (colors.length === 1 || position <= 0) return colors[0]!;
41
+ if (position >= 1) return colors[colors.length - 1]!;
42
+
43
+ const scaledPos = position * (colors.length - 1);
44
+ const index = Math.floor(scaledPos);
45
+ const t = scaledPos - index;
46
+
47
+ return interpolateColor(colors[index]!, colors[index + 1]!, t);
48
+ }
49
+
50
+ export function Polyline({
51
+ coordinates,
52
+ strokeColors,
53
+ strokeWidth = 1,
54
+ animated,
55
+ animatedOptions,
56
+ zIndex,
57
+ }: PolylineProps) {
58
+ const resolvedZIndex = zIndex ?? (animated ? 1 : 0);
59
+ const { map, isDragging } = useMapContext();
60
+ const polylinesRef = useRef<google.maps.Polyline[]>([]);
61
+ const animationRef = useRef<number>(0);
62
+ const isPausedRef = useRef(false);
63
+
64
+ const colors = useMemo(
65
+ () =>
66
+ strokeColors && strokeColors.length > 0
67
+ ? (strokeColors as string[])
68
+ : ['#000000'],
69
+ [strokeColors]
70
+ );
71
+
72
+ const hasGradient = colors.length > 1;
73
+
74
+ // Refs for animation loop access
75
+ const propsRef = useRef({
76
+ map,
77
+ colors,
78
+ strokeWidth,
79
+ hasGradient,
80
+ zIndex: resolvedZIndex,
81
+ });
82
+ const [mapReady, setMapReady] = useState(!!map);
83
+
84
+ useEffect(() => {
85
+ propsRef.current = {
86
+ map,
87
+ colors,
88
+ strokeWidth,
89
+ hasGradient,
90
+ zIndex: resolvedZIndex,
91
+ };
92
+ if (map && !mapReady) setMapReady(true);
93
+ }, [map, colors, strokeWidth, hasGradient, resolvedZIndex, mapReady]);
94
+
95
+ const updatePath = useCallback((path: google.maps.LatLngLiteral[]) => {
96
+ const {
97
+ map: currentMap,
98
+ colors: currentColors,
99
+ strokeWidth: currentStrokeWidth,
100
+ hasGradient: currentHasGradient,
101
+ zIndex: currentZIndex,
102
+ } = propsRef.current;
103
+ if (!currentMap || path.length < 2) return;
104
+
105
+ const neededSegments = currentHasGradient ? path.length - 1 : 1;
106
+ const existing = polylinesRef.current;
107
+
108
+ // Update or create segments
109
+ for (let i = 0; i < neededSegments; i++) {
110
+ const segmentPath = currentHasGradient ? [path[i]!, path[i + 1]!] : path;
111
+ const color = currentHasGradient
112
+ ? getGradientColor(currentColors, i / (path.length - 1))
113
+ : currentColors[0]!;
114
+
115
+ const segment = existing[i];
116
+ if (segment) {
117
+ segment.setPath(segmentPath);
118
+ segment.setOptions({ strokeColor: color });
119
+ } else {
120
+ existing.push(
121
+ new google.maps.Polyline({
122
+ path: segmentPath,
123
+ strokeColor: color,
124
+ strokeWeight: currentStrokeWidth,
125
+ strokeOpacity: 1,
126
+ zIndex: currentZIndex,
127
+ map: currentMap,
128
+ })
129
+ );
130
+ }
131
+ }
132
+
133
+ // Remove extra segments
134
+ for (let i = neededSegments; i < existing.length; i++) {
135
+ existing[i]?.setMap(null);
136
+ }
137
+ existing.length = neededSegments;
138
+ }, []);
139
+
140
+ // Cleanup on unmount
141
+ useEffect(() => {
142
+ const polylines = polylinesRef.current;
143
+ return () => {
144
+ cancelAnimationFrame(animationRef.current);
145
+ polylines.forEach((p) => p.setMap(null));
146
+ };
147
+ }, []);
148
+
149
+ // Pause/resume animation during drag
150
+ useEffect(() => {
151
+ isPausedRef.current = isDragging;
152
+ }, [isDragging]);
153
+
154
+ // Main effect
155
+ useEffect(() => {
156
+ if (!propsRef.current.map || coordinates.length === 0) return;
157
+
158
+ const fullPath = coordinates.map((c) => ({
159
+ lat: c.latitude,
160
+ lng: c.longitude,
161
+ }));
162
+
163
+ cancelAnimationFrame(animationRef.current);
164
+
165
+ if (!animated) {
166
+ updatePath(fullPath);
167
+ return;
168
+ }
169
+
170
+ const duration = animatedOptions?.duration ?? DEFAULT_DURATION;
171
+ const easing = animatedOptions?.easing ?? 'linear';
172
+ const trailLength = Math.max(
173
+ 0.01,
174
+ Math.min(1, animatedOptions?.trailLength ?? 1)
175
+ );
176
+ const delay = animatedOptions?.delay ?? 0;
177
+
178
+ const totalPoints = fullPath.length;
179
+ const useTrailMode = trailLength < 1;
180
+ const cycleDuration = useTrailMode ? duration : duration * 2;
181
+
182
+ let startTime: number | null = null;
183
+ let delayRemaining = delay;
184
+
185
+ const animate = (time: number) => {
186
+ if (isPausedRef.current) {
187
+ startTime = null;
188
+ animationRef.current = requestAnimationFrame(animate);
189
+ return;
190
+ }
191
+
192
+ if (startTime === null) {
193
+ startTime = time;
194
+ }
195
+
196
+ const elapsed = time - startTime;
197
+
198
+ if (delayRemaining > 0) {
199
+ if (elapsed < delayRemaining) {
200
+ animationRef.current = requestAnimationFrame(animate);
201
+ return;
202
+ }
203
+ startTime = time - (elapsed - delayRemaining);
204
+ delayRemaining = 0;
205
+ }
206
+
207
+ const rawProgress = ((time - startTime) % cycleDuration) / duration;
208
+ const maxProgress = useTrailMode ? 1 : 2;
209
+ const clampedProgress = Math.min(rawProgress, maxProgress);
210
+ const easedProgress =
211
+ applyEasing(clampedProgress / maxProgress, easing) * maxProgress;
212
+
213
+ let startIdx: number;
214
+ let endIdx: number;
215
+
216
+ if (useTrailMode) {
217
+ endIdx = easedProgress * totalPoints;
218
+ startIdx = Math.max(0, endIdx - trailLength * totalPoints);
219
+ } else {
220
+ startIdx = easedProgress <= 1 ? 0 : (easedProgress - 1) * totalPoints;
221
+ endIdx = easedProgress <= 1 ? easedProgress * totalPoints : totalPoints;
222
+ }
223
+
224
+ if (rawProgress >= maxProgress) {
225
+ delayRemaining = delay;
226
+ startTime = time;
227
+ }
228
+
229
+ const partialPath: google.maps.LatLngLiteral[] = [];
230
+ const startFloor = Math.floor(startIdx);
231
+ const endFloor = Math.floor(endIdx);
232
+
233
+ // Start point (interpolated)
234
+ if (startFloor < totalPoints) {
235
+ const frac = startIdx - startFloor;
236
+ const from = fullPath[startFloor]!;
237
+ const to = fullPath[Math.min(startFloor + 1, totalPoints - 1)]!;
238
+ partialPath.push(
239
+ frac > 0
240
+ ? {
241
+ lat: from.lat + (to.lat - from.lat) * frac,
242
+ lng: from.lng + (to.lng - from.lng) * frac,
243
+ }
244
+ : from
245
+ );
246
+ }
247
+
248
+ // Middle points
249
+ for (
250
+ let i = startFloor + 1;
251
+ i <= Math.min(endFloor, totalPoints - 1);
252
+ i++
253
+ ) {
254
+ partialPath.push(fullPath[i]!);
255
+ }
256
+
257
+ // End point (interpolated)
258
+ if (endFloor < totalPoints - 1) {
259
+ const frac = endIdx - endFloor;
260
+ const from = fullPath[endFloor]!;
261
+ const to = fullPath[endFloor + 1]!;
262
+ if (frac > 0) {
263
+ partialPath.push({
264
+ lat: from.lat + (to.lat - from.lat) * frac,
265
+ lng: from.lng + (to.lng - from.lng) * frac,
266
+ });
267
+ }
268
+ }
269
+
270
+ updatePath(partialPath);
271
+ animationRef.current = requestAnimationFrame(animate);
272
+ };
273
+
274
+ animationRef.current = requestAnimationFrame(animate);
275
+
276
+ return () => cancelAnimationFrame(animationRef.current);
277
+ }, [
278
+ coordinates,
279
+ animated,
280
+ animatedOptions,
281
+ hasGradient,
282
+ updatePath,
283
+ mapReady,
284
+ ]);
285
+
286
+ return null;
287
+ }
@@ -0,0 +1,4 @@
1
+ export { Marker } from './Marker.web';
2
+ export { Polyline } from './Polyline.web';
3
+ export type { MarkerProps } from './Marker';
4
+ export type { PolylineProps } from './Polyline';
@@ -1,6 +1,9 @@
1
1
  import { codegenNativeComponent } from 'react-native';
2
2
  import type { ViewProps, HostComponent } from 'react-native';
3
- import type { Double } from 'react-native/Libraries/Types/CodegenTypes';
3
+ import type {
4
+ Double,
5
+ WithDefault,
6
+ } from 'react-native/Libraries/Types/CodegenTypes';
4
7
 
5
8
  export interface Coordinate {
6
9
  latitude: Double;
@@ -18,6 +21,9 @@ export interface NativeProps extends ViewProps {
18
21
  title?: string;
19
22
  description?: string;
20
23
  anchor?: Point;
24
+ rotate?: WithDefault<Double, 0>;
25
+ scale?: WithDefault<Double, 1>;
26
+ rasterize?: WithDefault<boolean, true>;
21
27
  }
22
28
 
23
29
  export default codegenNativeComponent<NativeProps>(
@@ -7,11 +7,19 @@ export interface Coordinate {
7
7
  longitude: Double;
8
8
  }
9
9
 
10
+ export interface AnimatedOptions {
11
+ duration?: Double;
12
+ easing?: string;
13
+ trailLength?: Double;
14
+ delay?: Double;
15
+ }
16
+
10
17
  export interface NativeProps extends ViewProps {
11
18
  coordinates: ReadonlyArray<Coordinate>;
12
19
  strokeColors?: ReadonlyArray<ColorValue>;
13
20
  strokeWidth?: Double;
14
21
  animated?: boolean;
22
+ animatedOptions?: AnimatedOptions;
15
23
  }
16
24
 
17
25
  export default codegenNativeComponent<NativeProps>(
package/src/index.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  export { MapView } from './MapView';
2
+ export { MapProvider } from './MapProvider';
3
+ export type { MapProviderProps } from './MapProvider.types';
2
4
  export * from './components';
3
5
  export type {
4
6
  MapViewProps,
@@ -7,4 +9,9 @@ export type {
7
9
  FitCoordinatesOptions,
8
10
  CameraEventPayload,
9
11
  } from './MapView.types';
10
- export type { MapProvider, Coordinate, Point, EdgeInsets } from './types';
12
+ export type {
13
+ MapProvider as MapProviderType,
14
+ Coordinate,
15
+ Point,
16
+ EdgeInsets,
17
+ } from './types';
@@ -0,0 +1,17 @@
1
+ export { MapView } from './MapView.web';
2
+ export { MapProvider } from './MapProvider.web';
3
+ export type { MapProviderProps } from './MapProvider.types';
4
+ export * from './components/index.web';
5
+ export type {
6
+ MapViewProps,
7
+ MapViewRef,
8
+ MoveCameraOptions,
9
+ FitCoordinatesOptions,
10
+ CameraEventPayload,
11
+ } from './MapView.types';
12
+ export type {
13
+ MapProvider as MapProviderType,
14
+ Coordinate,
15
+ Point,
16
+ EdgeInsets,
17
+ } from './types';