@maydon_tech/react-native-nitro-maps 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (217) hide show
  1. package/LICENSE +1 -1
  2. package/NitroMap.podspec +1 -1
  3. package/README.md +82 -9
  4. package/android/CMakeLists.txt +4 -1
  5. package/android/gradle.properties +4 -4
  6. package/android/src/main/cpp/ClusterEngineJNI.cpp +198 -0
  7. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMap.kt +397 -0
  8. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapConfig.kt +53 -0
  9. package/android/src/main/{java → kotlin}/com/margelo/nitro/nitromap/NitroMapPackage.kt +4 -4
  10. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapView.kt +73 -0
  11. package/android/src/main/kotlin/com/margelo/nitro/nitromap/UserLocationManager.kt +295 -0
  12. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusterIconRenderer.kt +111 -0
  13. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusteringManager.kt +104 -0
  14. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +166 -0
  15. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerIconFactory.kt +303 -0
  16. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerSelectionHandler.kt +72 -0
  17. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/PriceMarkerRenderer.kt +159 -0
  18. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderFactory.kt +24 -0
  19. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +128 -0
  20. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapDelegate.kt +317 -0
  21. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Clustering.kt +524 -0
  22. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Markers.kt +358 -0
  23. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Overlays.kt +272 -0
  24. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+UserLocation.kt +296 -0
  25. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider.kt +815 -0
  26. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/MarkerTagData.kt +19 -0
  27. package/ios/Location/NitroLocationManager.swift +116 -0
  28. package/ios/MarkerRenderer/MarkerIconFactory.swift +1 -3
  29. package/ios/MarkerRenderer/PriceMarkerRenderer.swift +10 -6
  30. package/ios/NitroMap.swift +279 -13
  31. package/ios/NitroMapConfig/NitroMapConfig.swift +45 -0
  32. package/ios/Providers/{GoogleMapDelegate.swift → Google/GoogleMapDelegate.swift} +48 -23
  33. package/ios/Providers/Google/GoogleMapProvider+Camera.swift +180 -0
  34. package/ios/Providers/Google/GoogleMapProvider+Clustering.swift +541 -0
  35. package/ios/Providers/Google/GoogleMapProvider+Markers.swift +270 -0
  36. package/ios/Providers/Google/GoogleMapProvider+Overlays.swift +245 -0
  37. package/ios/Providers/Google/GoogleMapProvider+UserLocation.swift +180 -0
  38. package/ios/Providers/Google/GoogleMapProvider.swift +342 -0
  39. package/ios/Providers/MapProviderFactory.swift +17 -0
  40. package/ios/Providers/MapProviderProtocol.swift +48 -1
  41. package/ios/Shared/ClusterConfig+Factory.swift +2 -2
  42. package/ios/Shared/MapStyleProvider.swift +6 -4
  43. package/ios/Shared/MarkerSelectionHandler.swift +4 -1
  44. package/ios/Utils/ColorValueExtension.swift +46 -67
  45. package/lib/module/components/ImageMarker.js +39 -29
  46. package/lib/module/components/ImageMarker.js.map +1 -1
  47. package/lib/module/components/Marker.js +118 -0
  48. package/lib/module/components/Marker.js.map +1 -0
  49. package/lib/module/components/NitroCircle.js +92 -0
  50. package/lib/module/components/NitroCircle.js.map +1 -0
  51. package/lib/module/components/NitroMap.js +216 -76
  52. package/lib/module/components/NitroMap.js.map +1 -1
  53. package/lib/module/components/NitroPolygon.js +135 -0
  54. package/lib/module/components/NitroPolygon.js.map +1 -0
  55. package/lib/module/components/NitroPolyline.js +115 -0
  56. package/lib/module/components/NitroPolyline.js.map +1 -0
  57. package/lib/module/components/PriceMarker.js +16 -29
  58. package/lib/module/components/PriceMarker.js.map +1 -1
  59. package/lib/module/context/NitroMapContext.js.map +1 -1
  60. package/lib/module/hooks/useNitroCircle.js +18 -0
  61. package/lib/module/hooks/useNitroCircle.js.map +1 -0
  62. package/lib/module/hooks/useNitroMarker.js +26 -9
  63. package/lib/module/hooks/useNitroMarker.js.map +1 -1
  64. package/lib/module/hooks/useNitroOverlay.js +59 -0
  65. package/lib/module/hooks/useNitroOverlay.js.map +1 -0
  66. package/lib/module/hooks/useNitroPolygon.js +18 -0
  67. package/lib/module/hooks/useNitroPolygon.js.map +1 -0
  68. package/lib/module/hooks/useNitroPolyline.js +18 -0
  69. package/lib/module/hooks/useNitroPolyline.js.map +1 -0
  70. package/lib/module/index.js +5 -0
  71. package/lib/module/index.js.map +1 -1
  72. package/lib/module/types/overlay.js +4 -0
  73. package/lib/module/types/overlay.js.map +1 -0
  74. package/lib/module/types/theme.js +4 -0
  75. package/lib/module/types/theme.js.map +1 -0
  76. package/lib/module/utils/colors.js +41 -13
  77. package/lib/module/utils/colors.js.map +1 -1
  78. package/lib/module/utils/validation.js +45 -0
  79. package/lib/module/utils/validation.js.map +1 -0
  80. package/lib/typescript/src/components/ImageMarker.d.ts.map +1 -1
  81. package/lib/typescript/src/components/Marker.d.ts +34 -0
  82. package/lib/typescript/src/components/Marker.d.ts.map +1 -0
  83. package/lib/typescript/src/components/NitroCircle.d.ts +70 -0
  84. package/lib/typescript/src/components/NitroCircle.d.ts.map +1 -0
  85. package/lib/typescript/src/components/NitroMap.d.ts +60 -3
  86. package/lib/typescript/src/components/NitroMap.d.ts.map +1 -1
  87. package/lib/typescript/src/components/NitroPolygon.d.ts +86 -0
  88. package/lib/typescript/src/components/NitroPolygon.d.ts.map +1 -0
  89. package/lib/typescript/src/components/NitroPolyline.d.ts +84 -0
  90. package/lib/typescript/src/components/NitroPolyline.d.ts.map +1 -0
  91. package/lib/typescript/src/components/PriceMarker.d.ts +0 -5
  92. package/lib/typescript/src/components/PriceMarker.d.ts.map +1 -1
  93. package/lib/typescript/src/context/NitroMapContext.d.ts +2 -0
  94. package/lib/typescript/src/context/NitroMapContext.d.ts.map +1 -1
  95. package/lib/typescript/src/hooks/useNitroCircle.d.ts +7 -0
  96. package/lib/typescript/src/hooks/useNitroCircle.d.ts.map +1 -0
  97. package/lib/typescript/src/hooks/useNitroMarker.d.ts +20 -0
  98. package/lib/typescript/src/hooks/useNitroMarker.d.ts.map +1 -1
  99. package/lib/typescript/src/hooks/useNitroOverlay.d.ts +26 -0
  100. package/lib/typescript/src/hooks/useNitroOverlay.d.ts.map +1 -0
  101. package/lib/typescript/src/hooks/useNitroPolygon.d.ts +7 -0
  102. package/lib/typescript/src/hooks/useNitroPolygon.d.ts.map +1 -0
  103. package/lib/typescript/src/hooks/useNitroPolyline.d.ts +7 -0
  104. package/lib/typescript/src/hooks/useNitroPolyline.d.ts.map +1 -0
  105. package/lib/typescript/src/index.d.ts +15 -2
  106. package/lib/typescript/src/index.d.ts.map +1 -1
  107. package/lib/typescript/src/specs/NitroMap.nitro.d.ts +248 -6
  108. package/lib/typescript/src/specs/NitroMap.nitro.d.ts.map +1 -1
  109. package/lib/typescript/src/types/map.d.ts +34 -4
  110. package/lib/typescript/src/types/map.d.ts.map +1 -1
  111. package/lib/typescript/src/types/marker.d.ts +24 -36
  112. package/lib/typescript/src/types/marker.d.ts.map +1 -1
  113. package/lib/typescript/src/types/overlay.d.ts +75 -0
  114. package/lib/typescript/src/types/overlay.d.ts.map +1 -0
  115. package/lib/typescript/src/types/theme.d.ts +93 -0
  116. package/lib/typescript/src/types/theme.d.ts.map +1 -0
  117. package/lib/typescript/src/utils/colors.d.ts +6 -8
  118. package/lib/typescript/src/utils/colors.d.ts.map +1 -1
  119. package/lib/typescript/src/utils/validation.d.ts +12 -0
  120. package/lib/typescript/src/utils/validation.d.ts.map +1 -0
  121. package/nitrogen/generated/android/c++/JCircleData.hpp +94 -0
  122. package/nitrogen/generated/android/c++/JClusterConfig.hpp +5 -7
  123. package/nitrogen/generated/android/c++/JFunc_void_UserLocationChangeEvent.hpp +79 -0
  124. package/nitrogen/generated/android/c++/JFunc_void_UserTrackingMode.hpp +77 -0
  125. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  126. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +328 -21
  127. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +53 -2
  128. package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +3 -6
  129. package/nitrogen/generated/android/c++/JMarkerData.hpp +15 -3
  130. package/nitrogen/generated/android/c++/JPolygonData.hpp +149 -0
  131. package/nitrogen/generated/android/c++/JPolylineData.hpp +113 -0
  132. package/nitrogen/generated/android/c++/JUserLocationChangeEvent.hpp +70 -0
  133. package/nitrogen/generated/android/c++/JUserTrackingMode.hpp +62 -0
  134. package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +72 -4
  135. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CircleData.kt +62 -0
  136. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +4 -4
  137. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserLocationChangeEvent.kt +80 -0
  138. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserTrackingMode.kt +80 -0
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_std__string.kt +80 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +228 -2
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +1 -2
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +12 -3
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolygonData.kt +62 -0
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolylineData.kt +62 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/UserLocationChangeEvent.kt +47 -0
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/{ClusterAnimationStyle.kt → UserTrackingMode.kt} +6 -8
  147. package/nitrogen/generated/android/nitromapOnLoad.cpp +6 -0
  148. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +24 -0
  149. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +175 -17
  150. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +15 -3
  151. package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +249 -16
  152. package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +90 -5
  153. package/nitrogen/generated/ios/swift/CircleData.swift +143 -0
  154. package/nitrogen/generated/ios/swift/ClusterConfig.swift +22 -15
  155. package/nitrogen/generated/ios/swift/Func_void_UserLocationChangeEvent.swift +47 -0
  156. package/nitrogen/generated/ios/swift/Func_void_UserTrackingMode.swift +47 -0
  157. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  158. package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +35 -1
  159. package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +582 -8
  160. package/nitrogen/generated/ios/swift/MarkerAnimation.swift +4 -8
  161. package/nitrogen/generated/ios/swift/MarkerData.swift +54 -2
  162. package/nitrogen/generated/ios/swift/PolygonData.swift +179 -0
  163. package/nitrogen/generated/ios/swift/PolylineData.swift +155 -0
  164. package/nitrogen/generated/ios/swift/UserLocationChangeEvent.swift +69 -0
  165. package/nitrogen/generated/ios/swift/UserTrackingMode.swift +44 -0
  166. package/nitrogen/generated/shared/c++/CircleData.hpp +113 -0
  167. package/nitrogen/generated/shared/c++/ClusterConfig.hpp +5 -8
  168. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +53 -2
  169. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +75 -6
  170. package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +4 -8
  171. package/nitrogen/generated/shared/c++/MarkerData.hpp +14 -2
  172. package/nitrogen/generated/shared/c++/PolygonData.hpp +114 -0
  173. package/nitrogen/generated/shared/c++/PolylineData.hpp +114 -0
  174. package/nitrogen/generated/shared/c++/UserLocationChangeEvent.hpp +88 -0
  175. package/nitrogen/generated/shared/c++/UserTrackingMode.hpp +80 -0
  176. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +216 -12
  177. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +23 -1
  178. package/nitrogen/generated/shared/json/NitroMapConfig.json +18 -1
  179. package/package.json +36 -5
  180. package/src/components/ImageMarker.tsx +58 -42
  181. package/src/components/Marker.tsx +161 -0
  182. package/src/components/NitroCircle.tsx +183 -0
  183. package/src/components/NitroMap.tsx +328 -78
  184. package/src/components/NitroPolygon.tsx +229 -0
  185. package/src/components/NitroPolyline.tsx +208 -0
  186. package/src/components/PriceMarker.tsx +23 -48
  187. package/src/context/NitroMapContext.tsx +4 -0
  188. package/src/hooks/useNitroCircle.ts +25 -0
  189. package/src/hooks/useNitroMarker.ts +49 -10
  190. package/src/hooks/useNitroOverlay.ts +68 -0
  191. package/src/hooks/useNitroPolygon.ts +25 -0
  192. package/src/hooks/useNitroPolyline.ts +25 -0
  193. package/src/index.tsx +23 -2
  194. package/src/specs/NitroMap.nitro.ts +294 -5
  195. package/src/types/map.ts +36 -4
  196. package/src/types/marker.ts +24 -44
  197. package/src/types/overlay.ts +77 -0
  198. package/src/types/theme.ts +101 -0
  199. package/src/utils/colors.ts +48 -16
  200. package/src/utils/validation.ts +69 -0
  201. package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +0 -108
  202. package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +0 -63
  203. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +0 -408
  204. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +0 -68
  205. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +0 -176
  206. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +0 -252
  207. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +0 -252
  208. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +0 -195
  209. package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +0 -912
  210. package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +0 -70
  211. package/cpp/QuadTree.hpp +0 -246
  212. package/ios/NitroMapConfig/HybridNitroMapConfig.swift +0 -33
  213. package/ios/Providers/GoogleMapProvider+Camera.swift +0 -164
  214. package/ios/Providers/GoogleMapProvider.swift +0 -924
  215. package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +0 -68
  216. package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +0 -52
  217. package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +0 -88
@@ -0,0 +1,229 @@
1
+ // src/components/NitroPolygon.tsx
2
+ import { memo, useCallback, useEffect, useRef } from 'react';
3
+ import { useNitroPolygon } from '../hooks/useNitroPolygon';
4
+ import type { Coordinate } from '../types/map';
5
+ import type { MarkerColor } from '../types/marker';
6
+ import {
7
+ parseColor,
8
+ areColorsEqual,
9
+ type ColorValue,
10
+ Colors,
11
+ } from '../utils/colors';
12
+ import { validateCoordinates } from '../utils/validation';
13
+ import type { PolygonData } from '../types/overlay';
14
+
15
+ /**
16
+ * Props for the NitroPolygon component
17
+ */
18
+ export interface NitroPolygonProps {
19
+ /** Unique identifier for the polygon */
20
+ id?: string;
21
+
22
+ /**
23
+ * Array of geographic coordinates that form the polygon boundary.
24
+ * At least 3 coordinates are required to render a visible polygon.
25
+ * The polygon is automatically closed (first and last points are connected).
26
+ * @required
27
+ */
28
+ coordinates: Coordinate[];
29
+
30
+ /**
31
+ * Optional array of holes — each hole is an array of coordinates
32
+ * that will be cut out from the polygon fill.
33
+ * @default []
34
+ */
35
+ holes?: Coordinate[][];
36
+
37
+ /**
38
+ * Fill color of the polygon (hex string like "#00000040" or MarkerColor object).
39
+ * Use 8-char hex for alpha: "#RRGGBBAA"
40
+ * @default MarkerColor { r: 0, g: 0, b: 0, a: 77 } (~30% opacity black)
41
+ */
42
+ fillColor?: ColorValue;
43
+
44
+ /**
45
+ * Stroke color of the polygon border (hex string or MarkerColor object).
46
+ * @default '#000000'
47
+ */
48
+ strokeColor?: ColorValue;
49
+
50
+ /**
51
+ * Stroke width of the polygon border in pixels.
52
+ * @default 1
53
+ */
54
+ strokeWidth?: number;
55
+
56
+ /**
57
+ * Z-index for controlling overlay stacking order.
58
+ * Higher values render on top.
59
+ * @default 0
60
+ */
61
+ zIndex?: number;
62
+
63
+ /**
64
+ * Whether this polygon responds to tap events.
65
+ * When true, `onPolygonPress` on `<NitroMap>` fires with this polygon's ID.
66
+ * @default false
67
+ */
68
+ tappable?: boolean;
69
+
70
+ /**
71
+ * Accessibility label read by screen readers (VoiceOver / TalkBack).
72
+ */
73
+ accessibilityLabel?: string;
74
+ }
75
+
76
+ // ID generator — monotonic counter, unique within a JS runtime session
77
+ let polygonIdCounter = 0;
78
+ const generatePolygonId = (): string => `polygon_${++polygonIdCounter}`;
79
+
80
+ // Default colors
81
+ const defaultFillColor: MarkerColor = { r: 0, g: 0, b: 0, a: 77 }; // ~30% opacity black
82
+ const defaultStrokeColor: MarkerColor = Colors.black;
83
+
84
+ // ============ Custom Memo Comparison ============
85
+ const arePropsEqual = (
86
+ prev: NitroPolygonProps,
87
+ next: NitroPolygonProps
88
+ ): boolean => {
89
+ // Compare primitives
90
+ if (
91
+ prev.id !== next.id ||
92
+ prev.strokeWidth !== next.strokeWidth ||
93
+ prev.zIndex !== next.zIndex ||
94
+ prev.tappable !== next.tappable ||
95
+ prev.accessibilityLabel !== next.accessibilityLabel
96
+ ) {
97
+ return false;
98
+ }
99
+
100
+ if (!areColorsEqual(prev.fillColor, next.fillColor)) return false;
101
+ if (!areColorsEqual(prev.strokeColor, next.strokeColor)) return false;
102
+
103
+ // Compare coordinates array (deep)
104
+ const prevCoords = prev.coordinates;
105
+ const nextCoords = next.coordinates;
106
+ if (prevCoords.length !== nextCoords.length) return false;
107
+ for (let i = 0; i < prevCoords.length; i++) {
108
+ if (
109
+ prevCoords[i]!.latitude !== nextCoords[i]!.latitude ||
110
+ prevCoords[i]!.longitude !== nextCoords[i]!.longitude
111
+ ) {
112
+ return false;
113
+ }
114
+ }
115
+
116
+ // Compare holes
117
+ const prevHoles = prev.holes ?? [];
118
+ const nextHoles = next.holes ?? [];
119
+ if (prevHoles.length !== nextHoles.length) return false;
120
+ for (let i = 0; i < prevHoles.length; i++) {
121
+ const ph = prevHoles[i]!;
122
+ const nh = nextHoles[i]!;
123
+ if (ph.length !== nh.length) return false;
124
+ for (let j = 0; j < ph.length; j++) {
125
+ if (
126
+ ph[j]!.latitude !== nh[j]!.latitude ||
127
+ ph[j]!.longitude !== nh[j]!.longitude
128
+ ) {
129
+ return false;
130
+ }
131
+ }
132
+ }
133
+
134
+ return true;
135
+ };
136
+
137
+ /**
138
+ * NitroPolygon - Draw a closed polygon shape on the map
139
+ *
140
+ * @example Basic polygon
141
+ * ```tsx
142
+ * <NitroPolygon
143
+ * coordinates={[
144
+ * { latitude: 41.29, longitude: 69.24 },
145
+ * { latitude: 41.31, longitude: 69.28 },
146
+ * { latitude: 41.33, longitude: 69.26 },
147
+ * ]}
148
+ * fillColor="#4285F440"
149
+ * strokeColor="#4285F4"
150
+ * strokeWidth={2}
151
+ * />
152
+ * ```
153
+ *
154
+ * @example Polygon with a hole
155
+ * ```tsx
156
+ * <NitroPolygon
157
+ * coordinates={outerBoundary}
158
+ * holes={[innerHole]}
159
+ * fillColor="#FF000040"
160
+ * strokeColor="#FF0000"
161
+ * />
162
+ * ```
163
+ */
164
+ export const NitroPolygon = memo(function NitroPolygon({
165
+ id,
166
+ coordinates,
167
+ holes = [],
168
+ fillColor,
169
+ strokeColor,
170
+ strokeWidth = 1,
171
+ zIndex = 0,
172
+ tappable = false,
173
+ accessibilityLabel,
174
+ }: NitroPolygonProps) {
175
+ useEffect(() => {
176
+ if (__DEV__) {
177
+ if (coordinates.length < 3) {
178
+ console.warn(
179
+ 'NitroPolygon: at least 3 coordinates are required to render a visible polygon. ' +
180
+ `Received ${coordinates.length}.`
181
+ );
182
+ }
183
+ validateCoordinates(coordinates, 'NitroPolygon');
184
+ holes.forEach((hole, i) =>
185
+ validateCoordinates(hole, `NitroPolygon hole[${i}]`)
186
+ );
187
+ }
188
+ // eslint-disable-next-line react-hooks/exhaustive-deps
189
+ }, []);
190
+
191
+ // Generate stable ID
192
+ const polygonId = useRef(id || generatePolygonId()).current;
193
+
194
+ // Build polygon data
195
+ const buildPolygonData = useCallback(
196
+ (): PolygonData => ({
197
+ id: polygonId,
198
+ coordinates,
199
+ holes,
200
+ fillColor: parseColor(fillColor) ?? defaultFillColor,
201
+ strokeColor: parseColor(strokeColor) ?? defaultStrokeColor,
202
+ strokeWidth,
203
+ zIndex,
204
+ tappable,
205
+ accessibilityLabel,
206
+ }),
207
+ [
208
+ polygonId,
209
+ coordinates,
210
+ holes,
211
+ fillColor,
212
+ strokeColor,
213
+ strokeWidth,
214
+ zIndex,
215
+ tappable,
216
+ accessibilityLabel,
217
+ ]
218
+ );
219
+
220
+ // Use polygon lifecycle hook
221
+ useNitroPolygon(buildPolygonData);
222
+
223
+ // Render nothing — polygon is rendered natively
224
+ return null;
225
+ },
226
+ arePropsEqual);
227
+
228
+ export { arePropsEqual as arePolygonPropsEqual };
229
+ export default NitroPolygon;
@@ -0,0 +1,208 @@
1
+ // src/components/NitroPolyline.tsx
2
+ import { memo, useCallback, useEffect, useRef } from 'react';
3
+ import { useNitroPolyline } from '../hooks/useNitroPolyline';
4
+ import type { Coordinate } from '../types/map';
5
+ import {
6
+ parseColor,
7
+ areColorsEqual,
8
+ type ColorValue,
9
+ Colors,
10
+ } from '../utils/colors';
11
+ import { validateCoordinates } from '../utils/validation';
12
+ import type { PolylineData } from '../types/overlay';
13
+
14
+ /**
15
+ * Props for the NitroPolyline component
16
+ */
17
+ export interface NitroPolylineProps {
18
+ /** Unique identifier for the polyline */
19
+ id?: string;
20
+
21
+ /**
22
+ * Array of geographic coordinates that form the polyline path.
23
+ * At least 2 coordinates are required to render a visible line.
24
+ * @required
25
+ */
26
+ coordinates: Coordinate[];
27
+
28
+ /**
29
+ * Stroke color of the polyline (hex string like "#4285F4" or MarkerColor object).
30
+ * @default '#000000'
31
+ */
32
+ strokeColor?: ColorValue;
33
+
34
+ /**
35
+ * Stroke width in pixels.
36
+ * @default 1
37
+ */
38
+ strokeWidth?: number;
39
+
40
+ /**
41
+ * Whether the line follows Earth's curvature (great circle path).
42
+ * When true, the line curves on the Mercator projection to represent
43
+ * the shortest path on the sphere. Noticeable on long-distance lines.
44
+ * @default false
45
+ */
46
+ geodesic?: boolean;
47
+
48
+ /**
49
+ * Whether the line is rendered as dashed.
50
+ * @default false
51
+ */
52
+ dashed?: boolean;
53
+
54
+ /**
55
+ * Z-index for controlling overlay stacking order.
56
+ * Higher values render on top.
57
+ * @default 0
58
+ */
59
+ zIndex?: number;
60
+
61
+ /**
62
+ * Whether this polyline responds to tap events.
63
+ * When true, `onPolylinePress` on `<NitroMap>` fires with this polyline's ID.
64
+ * @default false
65
+ */
66
+ tappable?: boolean;
67
+
68
+ /**
69
+ * Accessibility label read by screen readers (VoiceOver / TalkBack).
70
+ */
71
+ accessibilityLabel?: string;
72
+ }
73
+
74
+ // ID generator — monotonic counter, unique within a JS runtime session
75
+ let polylineIdCounter = 0;
76
+ const generatePolylineId = (): string => `polyline_${++polylineIdCounter}`;
77
+
78
+ // Default stroke color (black)
79
+ const defaultStrokeColor = Colors.black;
80
+
81
+ // ============ Custom Memo Comparison ============
82
+ const arePropsEqual = (
83
+ prev: NitroPolylineProps,
84
+ next: NitroPolylineProps
85
+ ): boolean => {
86
+ // Compare primitives
87
+ if (
88
+ prev.id !== next.id ||
89
+ prev.strokeWidth !== next.strokeWidth ||
90
+ prev.geodesic !== next.geodesic ||
91
+ prev.dashed !== next.dashed ||
92
+ prev.zIndex !== next.zIndex ||
93
+ prev.tappable !== next.tappable ||
94
+ prev.accessibilityLabel !== next.accessibilityLabel
95
+ ) {
96
+ return false;
97
+ }
98
+
99
+ // Compare strokeColor
100
+ if (!areColorsEqual(prev.strokeColor, next.strokeColor)) {
101
+ return false;
102
+ }
103
+
104
+ // Compare coordinates array (deep)
105
+ const prevCoords = prev.coordinates;
106
+ const nextCoords = next.coordinates;
107
+ if (prevCoords.length !== nextCoords.length) return false;
108
+ for (let i = 0; i < prevCoords.length; i++) {
109
+ if (
110
+ prevCoords[i]!.latitude !== nextCoords[i]!.latitude ||
111
+ prevCoords[i]!.longitude !== nextCoords[i]!.longitude
112
+ ) {
113
+ return false;
114
+ }
115
+ }
116
+
117
+ return true;
118
+ };
119
+
120
+ /**
121
+ * NitroPolyline - Draw a line connecting multiple geographic points on the map
122
+ *
123
+ * @example Basic route line
124
+ * ```tsx
125
+ * <NitroPolyline
126
+ * coordinates={[
127
+ * { latitude: 41.29, longitude: 69.24 },
128
+ * { latitude: 41.31, longitude: 69.28 },
129
+ * ]}
130
+ * strokeColor="#4285F4"
131
+ * strokeWidth={4}
132
+ * />
133
+ * ```
134
+ *
135
+ * @example Geodesic dashed line
136
+ * ```tsx
137
+ * <NitroPolyline
138
+ * coordinates={routeCoordinates}
139
+ * strokeColor="#FF0000"
140
+ * strokeWidth={2}
141
+ * geodesic={true}
142
+ * dashed={true}
143
+ * />
144
+ * ```
145
+ */
146
+ export const NitroPolyline = memo(function NitroPolyline({
147
+ id,
148
+ coordinates,
149
+ strokeColor,
150
+ strokeWidth = 1,
151
+ geodesic = false,
152
+ dashed = false,
153
+ zIndex = 0,
154
+ tappable = false,
155
+ accessibilityLabel,
156
+ }: NitroPolylineProps) {
157
+ useEffect(() => {
158
+ if (__DEV__) {
159
+ if (coordinates.length < 2) {
160
+ console.warn(
161
+ 'NitroPolyline: at least 2 coordinates are required to render a visible line. ' +
162
+ `Received ${coordinates.length}.`
163
+ );
164
+ }
165
+ validateCoordinates(coordinates, 'NitroPolyline');
166
+ }
167
+ // eslint-disable-next-line react-hooks/exhaustive-deps
168
+ }, []);
169
+
170
+ // Generate stable ID
171
+ const polylineId = useRef(id || generatePolylineId()).current;
172
+
173
+ // Build polyline data
174
+ const buildPolylineData = useCallback(
175
+ (): PolylineData => ({
176
+ id: polylineId,
177
+ coordinates,
178
+ strokeColor: parseColor(strokeColor) ?? defaultStrokeColor,
179
+ strokeWidth,
180
+ geodesic,
181
+ dashed,
182
+ zIndex,
183
+ tappable,
184
+ accessibilityLabel,
185
+ }),
186
+ [
187
+ polylineId,
188
+ coordinates,
189
+ strokeColor,
190
+ strokeWidth,
191
+ geodesic,
192
+ dashed,
193
+ zIndex,
194
+ tappable,
195
+ accessibilityLabel,
196
+ ]
197
+ );
198
+
199
+ // Use polyline lifecycle hook
200
+ useNitroPolyline(buildPolylineData);
201
+
202
+ // Render nothing — polyline is rendered natively
203
+ return null;
204
+ },
205
+ arePropsEqual);
206
+
207
+ export { arePropsEqual as arePolylinePropsEqual };
208
+ export default NitroPolyline;
@@ -1,11 +1,11 @@
1
1
  // src/components/PriceMarker.tsx
2
- import { memo, useCallback, useMemo } from 'react';
2
+ import { memo, useCallback } from 'react';
3
3
  import {
4
4
  useNitroMarker,
5
+ useMarkerHandlers,
5
6
  type CommonMarkerProps,
6
7
  } from '../hooks/useNitroMarker';
7
- import type { MarkerColor } from '../types/marker';
8
- import { parseColor, type ColorValue } from '../utils/colors';
8
+ import { parseColor, areColorsEqual, type ColorValue } from '../utils/colors';
9
9
 
10
10
  /**
11
11
  * Props for the PriceMarker component
@@ -48,12 +48,6 @@ export interface PriceMarkerProps extends CommonMarkerProps {
48
48
  */
49
49
  fontSize?: number;
50
50
 
51
- /**
52
- * Corner radius in pixels (only for simple price style)
53
- * @default 8
54
- */
55
- cornerRadius?: number;
56
-
57
51
  /**
58
52
  * Background color when marker is selected (hex string like "#FF0000" or MarkerColor object)
59
53
  */
@@ -99,11 +93,13 @@ const arePropsEqual = (
99
93
  prevProps.zIndex !== nextProps.zIndex ||
100
94
  prevProps.clusteringEnabled !== nextProps.clusteringEnabled ||
101
95
  prevProps.animation !== nextProps.animation ||
96
+ prevProps.animationDuration !== nextProps.animationDuration ||
97
+ prevProps.animateOnReappear !== nextProps.animateOnReappear ||
102
98
  prevProps.fontSize !== nextProps.fontSize ||
103
- prevProps.cornerRadius !== nextProps.cornerRadius ||
104
99
  prevProps.paddingHorizontal !== nextProps.paddingHorizontal ||
105
100
  prevProps.paddingVertical !== nextProps.paddingVertical ||
106
- prevProps.shadowOpacity !== nextProps.shadowOpacity
101
+ prevProps.shadowOpacity !== nextProps.shadowOpacity ||
102
+ prevProps.accessibilityLabel !== nextProps.accessibilityLabel
107
103
  ) {
108
104
  return false;
109
105
  }
@@ -124,36 +120,14 @@ const arePropsEqual = (
124
120
  return false;
125
121
  }
126
122
 
127
- // Compare colors (handles both hex strings and MarkerColor objects)
128
- const compareColors = (
129
- a: ColorValue | undefined,
130
- b: ColorValue | undefined
131
- ) => {
132
- if (!a && !b) return true;
133
- if (!a || !b) return false;
134
- // If both are strings, compare directly
135
- if (typeof a === 'string' && typeof b === 'string') return a === b;
136
- // If types differ, not equal
137
- if (typeof a !== typeof b) return false;
138
- // Both are MarkerColor objects
139
- const aColor = a as MarkerColor;
140
- const bColor = b as MarkerColor;
141
- return (
142
- aColor.r === bColor.r &&
143
- aColor.g === bColor.g &&
144
- aColor.b === bColor.b &&
145
- aColor.a === bColor.a
146
- );
147
- };
148
-
149
123
  if (
150
- !compareColors(prevProps.backgroundColor, nextProps.backgroundColor) ||
151
- !compareColors(prevProps.textColor, nextProps.textColor) ||
152
- !compareColors(
124
+ !areColorsEqual(prevProps.backgroundColor, nextProps.backgroundColor) ||
125
+ !areColorsEqual(prevProps.textColor, nextProps.textColor) ||
126
+ !areColorsEqual(
153
127
  prevProps.selectedBackgroundColor,
154
128
  nextProps.selectedBackgroundColor
155
129
  ) ||
156
- !compareColors(prevProps.selectedTextColor, nextProps.selectedTextColor)
130
+ !areColorsEqual(prevProps.selectedTextColor, nextProps.selectedTextColor)
157
131
  ) {
158
132
  return false;
159
133
  }
@@ -197,7 +171,7 @@ export const PriceMarker = memo(function PriceMarker({
197
171
  backgroundColor,
198
172
  textColor,
199
173
  fontSize = 14,
200
- // cornerRadius prop kept for backwards compatibility but not used
174
+ // cornerRadius was removed not forwarded to native
201
175
  selectedBackgroundColor,
202
176
  selectedTextColor,
203
177
  paddingHorizontal,
@@ -214,6 +188,9 @@ export const PriceMarker = memo(function PriceMarker({
214
188
  anchor = { x: 0.5, y: 0.5 },
215
189
  clusteringEnabled = true,
216
190
  animation = 'none',
191
+ animationDuration = 0.3,
192
+ animateOnReappear = true,
193
+ accessibilityLabel,
217
194
 
218
195
  // Events
219
196
  onPress,
@@ -254,16 +231,8 @@ export const PriceMarker = memo(function PriceMarker({
254
231
  shadowOpacity,
255
232
  ]);
256
233
 
257
- // Memoize handlers
258
- const handlers = useMemo(
259
- () => ({
260
- onPress,
261
- onDragStart,
262
- onDrag,
263
- onDragEnd,
264
- }),
265
- [onPress, onDragStart, onDrag, onDragEnd]
266
- );
234
+ // Memoize handlers via shared hook
235
+ const handlers = useMarkerHandlers(onPress, onDragStart, onDrag, onDragEnd);
267
236
 
268
237
  // Build full marker data
269
238
  const buildMarkerData = useCallback(
@@ -279,7 +248,10 @@ export const PriceMarker = memo(function PriceMarker({
279
248
  anchor,
280
249
  clusteringEnabled,
281
250
  config: buildMarkerConfig(),
251
+ accessibilityLabel,
282
252
  animation,
253
+ animationDuration,
254
+ animateOnReappear,
283
255
  }),
284
256
  [
285
257
  coordinate,
@@ -292,7 +264,10 @@ export const PriceMarker = memo(function PriceMarker({
292
264
  anchor,
293
265
  clusteringEnabled,
294
266
  buildMarkerConfig,
267
+ accessibilityLabel,
295
268
  animation,
269
+ animationDuration,
270
+ animateOnReappear,
296
271
  ]
297
272
  );
298
273
 
@@ -24,6 +24,10 @@ export interface NitroMapContextValue {
24
24
 
25
25
  // Get handlers for a specific marker (used internally)
26
26
  getMarkerHandler: (markerId: string) => MarkerHandlers | undefined;
27
+
28
+ // Track active marker IDs for duplicate detection (__DEV__ only)
29
+ trackMarkerId: (markerId: string) => void;
30
+ untrackMarkerId: (markerId: string) => void;
27
31
  }
28
32
 
29
33
  // Create context with null default
@@ -0,0 +1,25 @@
1
+ // src/hooks/useNitroCircle.ts
2
+ import { useCallback } from 'react';
3
+ import { useNitroOverlay } from './useNitroOverlay';
4
+ import type { CircleData } from '../types/overlay';
5
+
6
+ /**
7
+ * Hook for circle lifecycle management.
8
+ * Adds circle on mount, updates on prop changes, removes on unmount.
9
+ */
10
+ export function useNitroCircle(buildCircleData: () => CircleData): void {
11
+ const getMethods = useCallback(
12
+ (mapRef: {
13
+ addCircle: (d: CircleData) => void;
14
+ updateCircle: (d: CircleData) => void;
15
+ removeCircle: (id: string) => void;
16
+ }) => ({
17
+ add: (data: CircleData) => mapRef.addCircle(data),
18
+ update: (data: CircleData) => mapRef.updateCircle(data),
19
+ remove: (id: string) => mapRef.removeCircle(id),
20
+ }),
21
+ []
22
+ );
23
+
24
+ useNitroOverlay(buildCircleData, getMethods);
25
+ }