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

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 (222) 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/build.gradle +6 -2
  6. package/android/gradle.properties +4 -4
  7. package/android/src/main/cpp/ClusterEngineJNI.cpp +198 -0
  8. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMap.kt +397 -0
  9. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapConfig.kt +53 -0
  10. package/android/src/main/{java → kotlin}/com/margelo/nitro/nitromap/NitroMapPackage.kt +4 -4
  11. package/android/src/main/kotlin/com/margelo/nitro/nitromap/NitroMapView.kt +73 -0
  12. package/android/src/main/kotlin/com/margelo/nitro/nitromap/UserLocationManager.kt +295 -0
  13. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusterIconRenderer.kt +111 -0
  14. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/ClusteringManager.kt +104 -0
  15. package/android/src/main/kotlin/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +166 -0
  16. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerIconFactory.kt +303 -0
  17. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/MarkerSelectionHandler.kt +72 -0
  18. package/android/src/main/kotlin/com/margelo/nitro/nitromap/markers/PriceMarkerRenderer.kt +159 -0
  19. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderFactory.kt +24 -0
  20. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +128 -0
  21. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapDelegate.kt +317 -0
  22. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Clustering.kt +524 -0
  23. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Markers.kt +358 -0
  24. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+Overlays.kt +272 -0
  25. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider+UserLocation.kt +296 -0
  26. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/GoogleMapProvider.kt +815 -0
  27. package/android/src/main/kotlin/com/margelo/nitro/nitromap/providers/google/MarkerTagData.kt +19 -0
  28. package/ios/Location/NitroLocationManager.swift +116 -0
  29. package/ios/MarkerRenderer/MarkerIconFactory.swift +1 -3
  30. package/ios/MarkerRenderer/PriceMarkerRenderer.swift +10 -6
  31. package/ios/NitroMap.swift +279 -13
  32. package/ios/NitroMapConfig/NitroMapConfig.swift +45 -0
  33. package/ios/Providers/{GoogleMapDelegate.swift → Google/GoogleMapDelegate.swift} +48 -23
  34. package/ios/Providers/Google/GoogleMapProvider+Camera.swift +180 -0
  35. package/ios/Providers/Google/GoogleMapProvider+Clustering.swift +541 -0
  36. package/ios/Providers/Google/GoogleMapProvider+Markers.swift +270 -0
  37. package/ios/Providers/Google/GoogleMapProvider+Overlays.swift +245 -0
  38. package/ios/Providers/Google/GoogleMapProvider+UserLocation.swift +180 -0
  39. package/ios/Providers/Google/GoogleMapProvider.swift +342 -0
  40. package/ios/Providers/MapProviderFactory.swift +17 -0
  41. package/ios/Providers/MapProviderProtocol.swift +48 -1
  42. package/ios/Shared/ClusterConfig+Factory.swift +2 -2
  43. package/ios/Shared/MapStyleProvider.swift +6 -4
  44. package/ios/Shared/MarkerSelectionHandler.swift +4 -1
  45. package/ios/Utils/ColorValueExtension.swift +46 -67
  46. package/lib/module/components/ImageMarker.js +39 -29
  47. package/lib/module/components/ImageMarker.js.map +1 -1
  48. package/lib/module/components/Marker.js +118 -0
  49. package/lib/module/components/Marker.js.map +1 -0
  50. package/lib/module/components/NitroCircle.js +92 -0
  51. package/lib/module/components/NitroCircle.js.map +1 -0
  52. package/lib/module/components/NitroMap.js +216 -76
  53. package/lib/module/components/NitroMap.js.map +1 -1
  54. package/lib/module/components/NitroPolygon.js +137 -0
  55. package/lib/module/components/NitroPolygon.js.map +1 -0
  56. package/lib/module/components/NitroPolyline.js +115 -0
  57. package/lib/module/components/NitroPolyline.js.map +1 -0
  58. package/lib/module/components/PriceMarker.js +16 -29
  59. package/lib/module/components/PriceMarker.js.map +1 -1
  60. package/lib/module/context/NitroMapContext.js.map +1 -1
  61. package/lib/module/hooks/useNitroCircle.js +18 -0
  62. package/lib/module/hooks/useNitroCircle.js.map +1 -0
  63. package/lib/module/hooks/useNitroMarker.js +26 -9
  64. package/lib/module/hooks/useNitroMarker.js.map +1 -1
  65. package/lib/module/hooks/useNitroOverlay.js +59 -0
  66. package/lib/module/hooks/useNitroOverlay.js.map +1 -0
  67. package/lib/module/hooks/useNitroPolygon.js +18 -0
  68. package/lib/module/hooks/useNitroPolygon.js.map +1 -0
  69. package/lib/module/hooks/useNitroPolyline.js +18 -0
  70. package/lib/module/hooks/useNitroPolyline.js.map +1 -0
  71. package/lib/module/index.js +5 -0
  72. package/lib/module/index.js.map +1 -1
  73. package/lib/module/types/overlay.js +4 -0
  74. package/lib/module/types/overlay.js.map +1 -0
  75. package/lib/module/types/theme.js +4 -0
  76. package/lib/module/types/theme.js.map +1 -0
  77. package/lib/module/utils/colors.js +41 -13
  78. package/lib/module/utils/colors.js.map +1 -1
  79. package/lib/module/utils/validation.js +45 -0
  80. package/lib/module/utils/validation.js.map +1 -0
  81. package/lib/typescript/src/components/ImageMarker.d.ts.map +1 -1
  82. package/lib/typescript/src/components/Marker.d.ts +34 -0
  83. package/lib/typescript/src/components/Marker.d.ts.map +1 -0
  84. package/lib/typescript/src/components/NitroCircle.d.ts +70 -0
  85. package/lib/typescript/src/components/NitroCircle.d.ts.map +1 -0
  86. package/lib/typescript/src/components/NitroMap.d.ts +60 -3
  87. package/lib/typescript/src/components/NitroMap.d.ts.map +1 -1
  88. package/lib/typescript/src/components/NitroPolygon.d.ts +86 -0
  89. package/lib/typescript/src/components/NitroPolygon.d.ts.map +1 -0
  90. package/lib/typescript/src/components/NitroPolyline.d.ts +84 -0
  91. package/lib/typescript/src/components/NitroPolyline.d.ts.map +1 -0
  92. package/lib/typescript/src/components/PriceMarker.d.ts +0 -5
  93. package/lib/typescript/src/components/PriceMarker.d.ts.map +1 -1
  94. package/lib/typescript/src/context/NitroMapContext.d.ts +2 -0
  95. package/lib/typescript/src/context/NitroMapContext.d.ts.map +1 -1
  96. package/lib/typescript/src/hooks/useNitroCircle.d.ts +7 -0
  97. package/lib/typescript/src/hooks/useNitroCircle.d.ts.map +1 -0
  98. package/lib/typescript/src/hooks/useNitroMarker.d.ts +20 -0
  99. package/lib/typescript/src/hooks/useNitroMarker.d.ts.map +1 -1
  100. package/lib/typescript/src/hooks/useNitroOverlay.d.ts +26 -0
  101. package/lib/typescript/src/hooks/useNitroOverlay.d.ts.map +1 -0
  102. package/lib/typescript/src/hooks/useNitroPolygon.d.ts +7 -0
  103. package/lib/typescript/src/hooks/useNitroPolygon.d.ts.map +1 -0
  104. package/lib/typescript/src/hooks/useNitroPolyline.d.ts +7 -0
  105. package/lib/typescript/src/hooks/useNitroPolyline.d.ts.map +1 -0
  106. package/lib/typescript/src/index.d.ts +15 -2
  107. package/lib/typescript/src/index.d.ts.map +1 -1
  108. package/lib/typescript/src/specs/NitroMap.nitro.d.ts +248 -6
  109. package/lib/typescript/src/specs/NitroMap.nitro.d.ts.map +1 -1
  110. package/lib/typescript/src/types/map.d.ts +34 -4
  111. package/lib/typescript/src/types/map.d.ts.map +1 -1
  112. package/lib/typescript/src/types/marker.d.ts +24 -36
  113. package/lib/typescript/src/types/marker.d.ts.map +1 -1
  114. package/lib/typescript/src/types/overlay.d.ts +84 -0
  115. package/lib/typescript/src/types/overlay.d.ts.map +1 -0
  116. package/lib/typescript/src/types/theme.d.ts +93 -0
  117. package/lib/typescript/src/types/theme.d.ts.map +1 -0
  118. package/lib/typescript/src/utils/colors.d.ts +6 -8
  119. package/lib/typescript/src/utils/colors.d.ts.map +1 -1
  120. package/lib/typescript/src/utils/validation.d.ts +12 -0
  121. package/lib/typescript/src/utils/validation.d.ts.map +1 -0
  122. package/nitrogen/generated/android/c++/JCircleData.hpp +94 -0
  123. package/nitrogen/generated/android/c++/JClusterConfig.hpp +5 -7
  124. package/nitrogen/generated/android/c++/JCoordinateRing.hpp +77 -0
  125. package/nitrogen/generated/android/c++/JFunc_void_UserLocationChangeEvent.hpp +79 -0
  126. package/nitrogen/generated/android/c++/JFunc_void_UserTrackingMode.hpp +77 -0
  127. package/nitrogen/generated/android/c++/JFunc_void_std__string.hpp +76 -0
  128. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +332 -21
  129. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +53 -2
  130. package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +3 -6
  131. package/nitrogen/generated/android/c++/JMarkerData.hpp +15 -3
  132. package/nitrogen/generated/android/c++/JPolygonData.hpp +133 -0
  133. package/nitrogen/generated/android/c++/JPolylineData.hpp +113 -0
  134. package/nitrogen/generated/android/c++/JUserLocationChangeEvent.hpp +70 -0
  135. package/nitrogen/generated/android/c++/JUserTrackingMode.hpp +62 -0
  136. package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +72 -4
  137. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CircleData.kt +62 -0
  138. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +4 -4
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/CoordinateRing.kt +38 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserLocationChangeEvent.kt +80 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_UserTrackingMode.kt +80 -0
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_std__string.kt +80 -0
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +228 -2
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +1 -2
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +12 -3
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolygonData.kt +62 -0
  147. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PolylineData.kt +62 -0
  148. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/UserLocationChangeEvent.kt +47 -0
  149. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/{ClusterAnimationStyle.kt → UserTrackingMode.kt} +6 -8
  150. package/nitrogen/generated/android/nitromapOnLoad.cpp +6 -0
  151. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +24 -0
  152. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +178 -17
  153. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +18 -3
  154. package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +252 -16
  155. package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +90 -5
  156. package/nitrogen/generated/ios/swift/CircleData.swift +143 -0
  157. package/nitrogen/generated/ios/swift/ClusterConfig.swift +22 -15
  158. package/nitrogen/generated/ios/swift/CoordinateRing.swift +48 -0
  159. package/nitrogen/generated/ios/swift/Func_void_UserLocationChangeEvent.swift +47 -0
  160. package/nitrogen/generated/ios/swift/Func_void_UserTrackingMode.swift +47 -0
  161. package/nitrogen/generated/ios/swift/Func_void_std__string.swift +47 -0
  162. package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +35 -1
  163. package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +582 -8
  164. package/nitrogen/generated/ios/swift/MarkerAnimation.swift +4 -8
  165. package/nitrogen/generated/ios/swift/MarkerData.swift +54 -2
  166. package/nitrogen/generated/ios/swift/PolygonData.swift +167 -0
  167. package/nitrogen/generated/ios/swift/PolylineData.swift +155 -0
  168. package/nitrogen/generated/ios/swift/UserLocationChangeEvent.swift +69 -0
  169. package/nitrogen/generated/ios/swift/UserTrackingMode.swift +44 -0
  170. package/nitrogen/generated/shared/c++/CircleData.hpp +113 -0
  171. package/nitrogen/generated/shared/c++/ClusterConfig.hpp +5 -8
  172. package/nitrogen/generated/shared/c++/CoordinateRing.hpp +77 -0
  173. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +53 -2
  174. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +75 -6
  175. package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +4 -8
  176. package/nitrogen/generated/shared/c++/MarkerData.hpp +14 -2
  177. package/nitrogen/generated/shared/c++/PolygonData.hpp +117 -0
  178. package/nitrogen/generated/shared/c++/PolylineData.hpp +114 -0
  179. package/nitrogen/generated/shared/c++/UserLocationChangeEvent.hpp +88 -0
  180. package/nitrogen/generated/shared/c++/UserTrackingMode.hpp +80 -0
  181. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +216 -12
  182. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +23 -1
  183. package/nitrogen/generated/shared/json/NitroMapConfig.json +18 -1
  184. package/package.json +36 -5
  185. package/src/components/ImageMarker.tsx +58 -42
  186. package/src/components/Marker.tsx +161 -0
  187. package/src/components/NitroCircle.tsx +183 -0
  188. package/src/components/NitroMap.tsx +328 -78
  189. package/src/components/NitroPolygon.tsx +229 -0
  190. package/src/components/NitroPolyline.tsx +208 -0
  191. package/src/components/PriceMarker.tsx +23 -48
  192. package/src/context/NitroMapContext.tsx +4 -0
  193. package/src/hooks/useNitroCircle.ts +25 -0
  194. package/src/hooks/useNitroMarker.ts +49 -10
  195. package/src/hooks/useNitroOverlay.ts +68 -0
  196. package/src/hooks/useNitroPolygon.ts +25 -0
  197. package/src/hooks/useNitroPolyline.ts +25 -0
  198. package/src/index.tsx +28 -2
  199. package/src/specs/NitroMap.nitro.ts +294 -5
  200. package/src/types/map.ts +36 -4
  201. package/src/types/marker.ts +24 -44
  202. package/src/types/overlay.ts +87 -0
  203. package/src/types/theme.ts +101 -0
  204. package/src/utils/colors.ts +48 -16
  205. package/src/utils/validation.ts +69 -0
  206. package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +0 -108
  207. package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +0 -63
  208. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +0 -408
  209. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +0 -68
  210. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +0 -176
  211. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +0 -252
  212. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +0 -252
  213. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +0 -195
  214. package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +0 -912
  215. package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +0 -70
  216. package/cpp/QuadTree.hpp +0 -246
  217. package/ios/NitroMapConfig/HybridNitroMapConfig.swift +0 -33
  218. package/ios/Providers/GoogleMapProvider+Camera.swift +0 -164
  219. package/ios/Providers/GoogleMapProvider.swift +0 -924
  220. package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +0 -68
  221. package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +0 -52
  222. package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +0 -88
@@ -27,9 +27,10 @@ import type {
27
27
  MarkerPressEvent,
28
28
  MarkerColor,
29
29
  } from '../types/marker';
30
- import type { MapStyle } from '../types/map';
31
- import { getDefaultProvider } from '../modules';
32
- import { parseColor, Colors } from '../utils/colors';
30
+ import type { EdgePadding, MapProvider, MapStyle } from '../types/map';
31
+ import type { ThemeForProvider } from '../types/theme';
32
+ import { IsNitroMapInitialized } from '../modules';
33
+ import { parseColor, areColorsEqual, Colors } from '../utils/colors';
33
34
 
34
35
  // Re-export all types
35
36
  export * from '../specs/NitroMap.nitro';
@@ -45,18 +46,118 @@ const NitroMapHostComponent = getHostComponent<
45
46
  NitroMapMethods
46
47
  >('NitroMap', () => NitroMapConfig);
47
48
 
48
- // Extended props with style and children
49
- export interface NitroMapProps
50
- extends Omit<NitroMapPropsSpec, 'style' | 'hybridRef'> {
49
+ // Sentinel region meaning "not set / cleared".
50
+ // Fabric sends `null` when a previously-set optional prop is removed from JSX,
51
+ // and the Nitro SDK's JSIConverter only handles `undefined`, not `null` — crash.
52
+ // React also strips `undefined` prop values (treats them as absent).
53
+ // Workaround: always pass region/initialRegion and use a sentinel with negative
54
+ // deltas that the native side recognizes as "no-op".
55
+ const REGION_UNSET = {
56
+ latitude: 0,
57
+ longitude: 0,
58
+ latitudeDelta: -1,
59
+ longitudeDelta: -1,
60
+ };
61
+
62
+ // Default "disabled" cluster config — always passed to native so the prop is
63
+ // never removed (same Fabric null workaround as REGION_UNSET).
64
+ const CLUSTER_CONFIG_DISABLED = {
65
+ enabled: false,
66
+ strategy: 'supercluster' as const,
67
+ radius: 80,
68
+ minimumClusterSize: 2,
69
+ maxZoom: 20,
70
+ backgroundColor: Colors.blue,
71
+ textColor: Colors.white,
72
+ borderWidth: 2,
73
+ borderColor: Colors.white,
74
+ animatesClusters: true,
75
+ animateOnReappear: true,
76
+ animationDuration: 0.3,
77
+ realtimeClustering: false,
78
+ renderBuffer: 0,
79
+ throttleInterval: 150,
80
+ };
81
+
82
+ // Extended props with style and children.
83
+ // minZoom, maxZoom, mapPadding are non-optional in the Nitro spec (ADR-009)
84
+ // but optional for consumers — we apply defaults in the component.
85
+ //
86
+ // Generic parameter P narrows the `theme` prop to the correct type for
87
+ // the chosen provider. Defaults to 'google' when provider is omitted.
88
+ export interface NitroMapProps<P extends MapProvider = 'google'>
89
+ extends Omit<
90
+ NitroMapPropsSpec,
91
+ | 'style'
92
+ | 'hybridRef'
93
+ | 'minZoom'
94
+ | 'maxZoom'
95
+ | 'mapPadding'
96
+ | 'customMapStyle'
97
+ | 'provider'
98
+ | 'userLocationImage'
99
+ | 'userLocationSize'
100
+ > {
51
101
  style?: StyleProp<ViewStyle>;
52
102
  children?: React.ReactNode;
103
+
104
+ /**
105
+ * Map provider to use. Typed as generic `P` so that `theme` narrows
106
+ * to the correct provider-specific type.
107
+ * @default 'google'
108
+ */
109
+ provider?: P;
110
+ /** Minimum zoom level (1 = world). @default 1 */
111
+ minZoom?: number;
112
+ /** Maximum zoom level (22 = building). @default 22 */
113
+ maxZoom?: number;
114
+ /** URL or asset name for custom user location icon. @default '' */
115
+ userLocationImage?: string;
116
+ /** Size of the custom user location icon in points. @default 40 */
117
+ userLocationSize?: number;
118
+ /**
119
+ * Padding around map edges to offset center and controls.
120
+ * Pass a single number for uniform padding on all sides,
121
+ * or an EdgePadding object for per-side control.
122
+ * @default {0,0,0,0}
123
+ */
124
+ mapPadding?: number | EdgePadding;
125
+
126
+ /**
127
+ * Provider-aware map theme. The accepted type depends on the `provider` prop:
128
+ * - `'google'` → `GoogleMapTheme` (JSON style array)
129
+ * - `'apple'` → `AppleMapTheme` (configuration object) — Phase 3
130
+ * - `'yandex'` → `YandexMapTheme` (style URI / night mode) — Phase 4
131
+ *
132
+ * @example
133
+ * ```tsx
134
+ * // Google — JSON style array
135
+ * <NitroMap provider="google" theme={[
136
+ * { featureType: 'water', stylers: [{ color: '#0e171d' }] },
137
+ * ]} />
138
+ *
139
+ * // Apple (future) — configuration object
140
+ * <NitroMap provider="apple" theme={{
141
+ * colorScheme: 'dark',
142
+ * emphasisStyle: 'muted',
143
+ * }} />
144
+ * ```
145
+ */
146
+ theme?: ThemeForProvider[P];
147
+
148
+ /**
149
+ * @deprecated Use `theme` instead. `customMapStyle` only supports Google Maps
150
+ * JSON format and will be removed in a future version.
151
+ */
152
+ customMapStyle?: MapStyle;
53
153
  }
54
154
 
55
155
  // Ref type
56
156
  export type NitroMapRef = NitroMapMethods;
57
157
 
58
158
  /**
59
- * NitroMap - High-performance Google Maps component with native markers
159
+ * NitroMap - High-performance map component with native markers.
160
+ * Supports multiple providers (Google, Apple, Yandex) with type-safe theming.
60
161
  */
61
162
  const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
62
163
  (
@@ -65,6 +166,7 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
65
166
  children,
66
167
  provider,
67
168
  initialRegion,
169
+ region,
68
170
  showsUserLocation = false,
69
171
  zoomEnabled = true,
70
172
  scrollEnabled = true,
@@ -72,8 +174,13 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
72
174
  pitchEnabled = true,
73
175
  darkMode = false,
74
176
  mapType = 'standard',
75
- showsMyLocationButton = false,
76
177
  clusterConfig,
178
+ mapPadding,
179
+ showsTraffic = false,
180
+ showsBuildings = true,
181
+ showsCompass = true,
182
+ minZoom,
183
+ maxZoom,
77
184
  onPress,
78
185
  onLongPress,
79
186
  onMapReady,
@@ -84,77 +191,96 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
84
191
  onMarkerDrag,
85
192
  onMarkerDragEnd,
86
193
  onClusterPress,
194
+ onPolylinePress,
195
+ onPolygonPress,
196
+ onCirclePress,
197
+ onUserLocationChange,
198
+ onUserTrackingModeChange,
199
+ onUserLocationError,
200
+ onMapIdle,
87
201
  onError,
202
+ userTrackingMode = 'none',
203
+ userLocationImage = '',
204
+ userLocationSize = 40,
205
+ userLocationAnchor = { x: 0.5, y: 0.5 },
206
+ theme,
207
+ customMapStyle,
88
208
  ...rest
89
209
  },
90
210
  ref
91
211
  ) => {
212
+ // Dev-mode warning for unimplemented providers
213
+ if (__DEV__ && provider && provider !== 'google') {
214
+ console.warn(
215
+ `[NitroMap] Provider "${provider}" is not yet implemented. ` +
216
+ 'Falling back to Google Maps. See ROADMAP for provider status.'
217
+ );
218
+ }
219
+
220
+ // Dev-mode deprecation warning for customMapStyle
221
+ if (__DEV__ && customMapStyle && !theme) {
222
+ console.warn(
223
+ '[NitroMap] `customMapStyle` is deprecated. Use `theme` instead.'
224
+ );
225
+ }
226
+
227
+ // Resolve theme → native bridge prop.
228
+ // `theme` takes precedence over deprecated `customMapStyle`.
229
+ // Currently only Google is implemented — theme is passed as customMapStyle.
230
+ // When Apple/Yandex are added, this will route to the correct native prop.
231
+ const resolvedMapStyle: MapStyle | undefined = useMemo(() => {
232
+ if (theme) {
233
+ // Google provider: theme is already MapStyleElement[] (GoogleMapTheme)
234
+ // Apple/Yandex: will be handled by their respective native props
235
+ const resolvedProvider = provider ?? 'google';
236
+ if (resolvedProvider === 'google') {
237
+ return theme as MapStyle;
238
+ }
239
+ // Future providers: return undefined here, pass theme via separate native prop
240
+ return undefined;
241
+ }
242
+ return customMapStyle;
243
+ }, [theme, customMapStyle, provider]);
244
+
92
245
  // Native ref
93
246
  const nativeRef = useRef<NitroMapMethods | null>(null);
94
247
 
95
248
  // Marker handlers registry
96
249
  const markerHandlersRef = useRef<Map<string, MarkerHandlers>>(new Map());
97
250
 
251
+ // Active marker ID tracking for duplicate detection (__DEV__ only)
252
+ const activeMarkerIdsRef = useRef<Set<string>>(new Set());
253
+
98
254
  // Track if map is ready
99
255
  const [isReady, setIsReady] = useState(false);
100
256
 
101
- // Expose methods via ref
102
- useImperativeHandle(ref, () => ({
103
- animateToRegion: (region, duration) => {
104
- nativeRef.current?.animateToRegion(region, duration);
105
- },
106
- fitToCoordinates: (coordinates, edgePadding, animated) => {
107
- nativeRef.current?.fitToCoordinates(coordinates, edgePadding, animated);
108
- },
109
- animateCamera: (camera, duration) => {
110
- nativeRef.current?.animateCamera(camera, duration);
111
- },
112
- getCamera: () => {
113
- if (!nativeRef.current) {
114
- return Promise.reject(new Error('Map not ready'));
115
- }
116
- return nativeRef.current.getCamera();
117
- },
118
- setCamera: (camera) => {
119
- nativeRef.current?.setCamera(camera);
120
- },
121
- getMapBoundaries: () => {
122
- if (!nativeRef.current) {
123
- return Promise.reject(new Error('Map not ready'));
124
- }
125
- return nativeRef.current.getMapBoundaries();
126
- },
127
- addMarker: (marker) => {
128
- nativeRef.current?.addMarker(marker);
129
- },
130
- addMarkers: (markers) => {
131
- nativeRef.current?.addMarkers(markers);
132
- },
133
- updateMarker: (marker) => {
134
- nativeRef.current?.updateMarker(marker);
135
- },
136
- removeMarker: (id) => {
137
- nativeRef.current?.removeMarker(id);
138
- },
139
- clearMarkers: () => {
140
- nativeRef.current?.clearMarkers();
141
- },
142
- setClusteringEnabled: (enabled) => {
143
- nativeRef.current?.setClusteringEnabled(enabled);
144
- },
145
- refreshClusters: () => {
146
- nativeRef.current?.refreshClusters();
147
- },
148
- selectMarker: (id: string) => {
149
- nativeRef.current?.selectMarker(id);
150
- },
151
- setMapStyle: (mapStyle: MapStyle) => {
152
- nativeRef.current?.setMapStyle(mapStyle);
153
- },
154
- setIsDarkMode: (enabled: boolean) => {
155
- nativeRef.current?.setIsDarkMode(enabled);
156
- },
157
- }));
257
+ // Expose all NitroMapMethods via ref by forwarding calls to the native ref.
258
+ // Uses a Proxy to avoid maintaining a 1:1 list of ~25 wrapper methods.
259
+ useImperativeHandle(ref, () => {
260
+ const handler: ProxyHandler<NitroMapMethods> = {
261
+ get(_target, prop: string) {
262
+ return (...args: unknown[]) => {
263
+ const native = nativeRef.current;
264
+ if (!native) {
265
+ // Methods that return Promises should reject; void methods silently no-op.
266
+ if (prop === 'getCamera' || prop === 'getMapBoundaries') {
267
+ return Promise.reject(new Error('Map not ready'));
268
+ }
269
+ if (
270
+ prop === 'pointForCoordinate' ||
271
+ prop === 'coordinateForPoint'
272
+ ) {
273
+ throw new Error('Map not ready');
274
+ }
275
+ return undefined;
276
+ }
277
+
278
+ return (native as any)[prop]?.(...args);
279
+ };
280
+ },
281
+ };
282
+ return new Proxy({} as NitroMapMethods, handler);
283
+ });
158
284
 
159
285
  // Handle hybridRef callback
160
286
 
@@ -164,9 +290,16 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
164
290
  onMapReady?.();
165
291
  }, [onMapReady]);
166
292
 
293
+ // Cleanup marker handlers on unmount to prevent memory leaks
294
+ useEffect(() => {
295
+ const handlers = markerHandlersRef.current;
296
+ return () => {
297
+ handlers.clear();
298
+ };
299
+ }, []);
300
+
167
301
  // Safety guard: warn if NitroMapInitialize() was not called before rendering
168
302
  useEffect(() => {
169
- const { IsNitroMapInitialized } = require('../modules');
170
303
  if (!IsNitroMapInitialized()) {
171
304
  onError?.({
172
305
  code: 'NOT_INITIALIZED',
@@ -175,6 +308,7 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
175
308
  'Call it in your app entry point or a useEffect before the map mounts.',
176
309
  });
177
310
  }
311
+ // onError is excluded — we only check initialization once on mount.
178
312
  // eslint-disable-next-line react-hooks/exhaustive-deps
179
313
  }, []);
180
314
 
@@ -226,7 +360,9 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
226
360
  [onClusterPress]
227
361
  );
228
362
 
229
- // Context value - memoized to prevent unnecessary re-renders
363
+ // Context value uses useRef to create a stable reference that won't
364
+ // trigger re-renders on children. mapRef is mutated in the hybridRef
365
+ // callback, but consumers access it lazily (in effects, not during render).
230
366
  const contextValue = useRef({
231
367
  mapRef: null as NitroMapMethods | null,
232
368
 
@@ -241,6 +377,20 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
241
377
  getMarkerHandler: (id: string) => {
242
378
  return markerHandlersRef.current.get(id);
243
379
  },
380
+
381
+ trackMarkerId: (id: string) => {
382
+ if (__DEV__ && activeMarkerIdsRef.current.has(id)) {
383
+ console.warn(
384
+ `[NitroMap] Duplicate marker ID "${id}" detected. ` +
385
+ 'Each marker must have a unique ID. The previous marker with this ID will be replaced.'
386
+ );
387
+ }
388
+ activeMarkerIdsRef.current.add(id);
389
+ },
390
+
391
+ untrackMarkerId: (id: string) => {
392
+ activeMarkerIdsRef.current.delete(id);
393
+ },
244
394
  }).current;
245
395
 
246
396
  // Memoize style to prevent new array on every render
@@ -268,8 +418,8 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
268
418
  borderColor:
269
419
  parseColor(clusterConfig.borderColor) ?? defaultBorderColor,
270
420
  animatesClusters: clusterConfig.animatesClusters ?? true,
421
+ animateOnReappear: clusterConfig.animateOnReappear ?? true,
271
422
  animationDuration: clusterConfig.animationDuration ?? 0.3,
272
- animationStyle: clusterConfig.animationStyle ?? 'default',
273
423
  realtimeClustering: clusterConfig.realtimeClustering ?? false,
274
424
  renderBuffer: clusterConfig.renderBuffer ?? 0,
275
425
  throttleInterval: clusterConfig.throttleInterval ?? 150,
@@ -299,6 +449,13 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
299
449
  onMarkerDrag: callback(handleMarkerDrag),
300
450
  onMarkerDragEnd: callback(handleMarkerDragEnd),
301
451
  onClusterPress: callback(handleClusterPress),
452
+ onPolylinePress: callback(onPolylinePress),
453
+ onPolygonPress: callback(onPolygonPress),
454
+ onCirclePress: callback(onCirclePress),
455
+ onUserLocationChange: callback(onUserLocationChange),
456
+ onUserTrackingModeChange: callback(onUserTrackingModeChange),
457
+ onUserLocationError: callback(onUserLocationError),
458
+ onMapIdle: callback(onMapIdle),
302
459
  onError: callback(onError),
303
460
  }),
304
461
  [
@@ -312,6 +469,13 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
312
469
  handleMarkerDrag,
313
470
  handleMarkerDragEnd,
314
471
  handleClusterPress,
472
+ onPolylinePress,
473
+ onPolygonPress,
474
+ onCirclePress,
475
+ onUserLocationChange,
476
+ onUserTrackingModeChange,
477
+ onUserLocationError,
478
+ onMapIdle,
315
479
  onError,
316
480
  ]
317
481
  );
@@ -321,8 +485,9 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
321
485
  <NitroMapHostComponent
322
486
  style={combinedStyle}
323
487
  hybridRef={hybridRefCallback}
324
- provider={provider ?? getDefaultProvider()}
325
- initialRegion={initialRegion}
488
+ provider={provider ?? 'google'}
489
+ initialRegion={initialRegion ?? REGION_UNSET}
490
+ region={region ?? REGION_UNSET}
326
491
  showsUserLocation={showsUserLocation}
327
492
  zoomEnabled={zoomEnabled}
328
493
  scrollEnabled={scrollEnabled}
@@ -330,8 +495,27 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
330
495
  pitchEnabled={pitchEnabled}
331
496
  mapType={mapType}
332
497
  darkMode={darkMode}
333
- showsMyLocationButton={showsMyLocationButton}
334
- clusterConfig={parsedClusterConfig}
498
+ clusterConfig={parsedClusterConfig ?? CLUSTER_CONFIG_DISABLED}
499
+ mapPadding={
500
+ typeof mapPadding === 'number'
501
+ ? {
502
+ top: mapPadding,
503
+ right: mapPadding,
504
+ bottom: mapPadding,
505
+ left: mapPadding,
506
+ }
507
+ : mapPadding ?? { top: 0, right: 0, bottom: 0, left: 0 }
508
+ }
509
+ showsCompass={showsCompass}
510
+ showsTraffic={showsTraffic}
511
+ showsBuildings={showsBuildings}
512
+ minZoom={minZoom ?? 1}
513
+ maxZoom={maxZoom ?? 22}
514
+ userTrackingMode={userTrackingMode}
515
+ userLocationImage={userLocationImage}
516
+ userLocationSize={userLocationSize}
517
+ userLocationAnchor={userLocationAnchor}
518
+ customMapStyle={resolvedMapStyle ?? []}
335
519
  onPress={memoizedCallbacks.onPress}
336
520
  onLongPress={memoizedCallbacks.onLongPress}
337
521
  onMapReady={memoizedCallbacks.onMapReady}
@@ -342,6 +526,13 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
342
526
  onMarkerDrag={memoizedCallbacks.onMarkerDrag}
343
527
  onMarkerDragEnd={memoizedCallbacks.onMarkerDragEnd}
344
528
  onClusterPress={memoizedCallbacks.onClusterPress}
529
+ onPolylinePress={memoizedCallbacks.onPolylinePress}
530
+ onPolygonPress={memoizedCallbacks.onPolygonPress}
531
+ onCirclePress={memoizedCallbacks.onCirclePress}
532
+ onUserLocationChange={memoizedCallbacks.onUserLocationChange}
533
+ onUserTrackingModeChange={memoizedCallbacks.onUserTrackingModeChange}
534
+ onUserLocationError={memoizedCallbacks.onUserLocationError}
535
+ onMapIdle={memoizedCallbacks.onMapIdle}
345
536
  onError={memoizedCallbacks.onError}
346
537
  {...rest}
347
538
  />
@@ -352,7 +543,7 @@ const NitroMapInner = forwardRef<NitroMapRef, NitroMapProps>(
352
543
  }
353
544
  );
354
545
 
355
- NitroMapInner.displayName = 'NitroMap';
546
+ NitroMapInner.displayName = 'NitroMapInner';
356
547
 
357
548
  // ============ Custom Props Comparison ============
358
549
  // Deep compare map props to prevent unnecessary re-renders
@@ -364,17 +555,49 @@ const areMapPropsEqual = (
364
555
  if (
365
556
  prevProps.provider !== nextProps.provider ||
366
557
  prevProps.showsUserLocation !== nextProps.showsUserLocation ||
558
+ prevProps.userTrackingMode !== nextProps.userTrackingMode ||
559
+ prevProps.userLocationImage !== nextProps.userLocationImage ||
560
+ prevProps.userLocationSize !== nextProps.userLocationSize ||
561
+ prevProps.userLocationAnchor?.x !== nextProps.userLocationAnchor?.x ||
562
+ prevProps.userLocationAnchor?.y !== nextProps.userLocationAnchor?.y ||
367
563
  prevProps.zoomEnabled !== nextProps.zoomEnabled ||
368
564
  prevProps.scrollEnabled !== nextProps.scrollEnabled ||
369
565
  prevProps.rotateEnabled !== nextProps.rotateEnabled ||
370
566
  prevProps.pitchEnabled !== nextProps.pitchEnabled ||
371
567
  prevProps.darkMode !== nextProps.darkMode ||
372
568
  prevProps.mapType !== nextProps.mapType ||
373
- prevProps.showsMyLocationButton !== nextProps.showsMyLocationButton
569
+ prevProps.showsTraffic !== nextProps.showsTraffic ||
570
+ prevProps.showsBuildings !== nextProps.showsBuildings ||
571
+ prevProps.showsCompass !== nextProps.showsCompass ||
572
+ prevProps.minZoom !== nextProps.minZoom ||
573
+ prevProps.maxZoom !== nextProps.maxZoom ||
574
+ prevProps.theme !== nextProps.theme ||
575
+ prevProps.customMapStyle !== nextProps.customMapStyle
374
576
  ) {
375
577
  return false;
376
578
  }
377
579
 
580
+ // Compare mapPadding (handles number | EdgePadding union)
581
+ const prevPadding = prevProps.mapPadding;
582
+ const nextPadding = nextProps.mapPadding;
583
+ if (typeof prevPadding !== typeof nextPadding) {
584
+ return false;
585
+ }
586
+ if (typeof prevPadding === 'number') {
587
+ if (prevPadding !== nextPadding) return false;
588
+ } else if (prevPadding && nextPadding && typeof nextPadding === 'object') {
589
+ if (
590
+ prevPadding.top !== nextPadding.top ||
591
+ prevPadding.right !== nextPadding.right ||
592
+ prevPadding.bottom !== nextPadding.bottom ||
593
+ prevPadding.left !== nextPadding.left
594
+ ) {
595
+ return false;
596
+ }
597
+ } else if (prevPadding !== nextPadding) {
598
+ return false;
599
+ }
600
+
378
601
  // Compare initialRegion (deep)
379
602
  const prevRegion = prevProps.initialRegion;
380
603
  const nextRegion = nextProps.initialRegion;
@@ -402,11 +625,18 @@ const areMapPropsEqual = (
402
625
  prevCluster.minimumClusterSize !== nextCluster.minimumClusterSize ||
403
626
  prevCluster.maxZoom !== nextCluster.maxZoom ||
404
627
  prevCluster.animatesClusters !== nextCluster.animatesClusters ||
628
+ prevCluster.animateOnReappear !== nextCluster.animateOnReappear ||
405
629
  prevCluster.animationDuration !== nextCluster.animationDuration ||
406
- prevCluster.animationStyle !== nextCluster.animationStyle ||
630
+ prevCluster.borderWidth !== nextCluster.borderWidth ||
407
631
  prevCluster.realtimeClustering !== nextCluster.realtimeClustering ||
408
632
  prevCluster.renderBuffer !== nextCluster.renderBuffer ||
409
- prevCluster.throttleInterval !== nextCluster.throttleInterval
633
+ prevCluster.throttleInterval !== nextCluster.throttleInterval ||
634
+ !areColorsEqual(
635
+ prevCluster.backgroundColor,
636
+ nextCluster.backgroundColor
637
+ ) ||
638
+ !areColorsEqual(prevCluster.textColor, nextCluster.textColor) ||
639
+ !areColorsEqual(prevCluster.borderColor, nextCluster.borderColor)
410
640
  ) {
411
641
  return false;
412
642
  }
@@ -432,8 +662,28 @@ const areMapPropsEqual = (
432
662
  return true;
433
663
  };
434
664
 
665
+ // ============ Generic Component Type ============
666
+ // React's forwardRef + memo erase generics, so we define a generic
667
+ // function signature and cast the memoized component to it.
668
+ // This lets consumers write <NitroMap provider="apple" theme={...} />
669
+ // and get proper type narrowing on the `theme` prop.
670
+
671
+ /**
672
+ * Generic NitroMap component type that preserves provider-aware theme typing.
673
+ */
674
+ interface NitroMapComponent {
675
+ <P extends MapProvider = 'google'>(
676
+ props: NitroMapProps<P> & React.RefAttributes<NitroMapRef>
677
+ ): React.ReactElement | null;
678
+ displayName?: string;
679
+ }
680
+
435
681
  // Memoized NitroMap to prevent unnecessary re-renders
436
- const NitroMap = memo(NitroMapInner, areMapPropsEqual);
682
+ const NitroMapMemoized = memo(NitroMapInner, areMapPropsEqual);
683
+
684
+ // Cast to generic component type so consumers get provider-aware theme narrowing
685
+ const NitroMap = NitroMapMemoized as unknown as NitroMapComponent;
686
+ NitroMap.displayName = 'NitroMap';
437
687
 
438
688
  const styles = StyleSheet.create({
439
689
  default: {
@@ -441,5 +691,5 @@ const styles = StyleSheet.create({
441
691
  },
442
692
  });
443
693
 
444
- export { NitroMap };
694
+ export { NitroMap, areMapPropsEqual };
445
695
  export default NitroMap;