@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
@@ -6,6 +6,7 @@
6
6
  "validAttributes": {
7
7
  "provider": true,
8
8
  "initialRegion": true,
9
+ "region": true,
9
10
  "showsUserLocation": true,
10
11
  "zoomEnabled": true,
11
12
  "scrollEnabled": true,
@@ -13,9 +14,18 @@
13
14
  "pitchEnabled": true,
14
15
  "mapType": true,
15
16
  "customMapStyle": true,
16
- "showsMyLocationButton": true,
17
17
  "clusterConfig": true,
18
+ "mapPadding": true,
19
+ "showsTraffic": true,
20
+ "showsBuildings": true,
21
+ "showsCompass": true,
22
+ "minZoom": true,
23
+ "maxZoom": true,
18
24
  "darkMode": true,
25
+ "userTrackingMode": true,
26
+ "userLocationImage": true,
27
+ "userLocationSize": true,
28
+ "userLocationAnchor": true,
19
29
  "onPress": true,
20
30
  "onLongPress": true,
21
31
  "onMapReady": true,
@@ -26,7 +36,14 @@
26
36
  "onMarkerDrag": true,
27
37
  "onMarkerDragEnd": true,
28
38
  "onClusterPress": true,
39
+ "onPolylinePress": true,
40
+ "onPolygonPress": true,
41
+ "onCirclePress": true,
29
42
  "onError": true,
43
+ "onUserLocationChange": true,
44
+ "onUserTrackingModeChange": true,
45
+ "onUserLocationError": true,
46
+ "onMapIdle": true,
30
47
  "hybridRef": true
31
48
  }
32
49
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@maydon_tech/react-native-nitro-maps",
3
- "version": "0.1.4",
3
+ "version": "0.2.0",
4
4
  "description": "A high-performance React Native map library powered by Nitro Modules with support for Apple Maps, Google Maps, and Yandex Maps. Features native clustering, custom markers, and seamless cross-platform integration.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -43,7 +43,8 @@
43
43
  "typecheck": "tsc",
44
44
  "lint": "eslint \"**/*.{js,ts,tsx}\"",
45
45
  "test": "jest",
46
- "release": "release-it --only-version"
46
+ "release": "release-it --only-version",
47
+ "prepublishOnly": "bob build && tsc"
47
48
  },
48
49
  "keywords": [
49
50
  "react-native",
@@ -68,8 +69,10 @@
68
69
  "@react-native/babel-preset": "0.83.0",
69
70
  "@react-native/eslint-config": "0.83.0",
70
71
  "@release-it/conventional-changelog": "^10.0.1",
72
+ "@testing-library/react-native": "^13.3.3",
71
73
  "@types/jest": "^29.5.14",
72
74
  "@types/react": "^19.2.0",
75
+ "@types/react-test-renderer": "^19",
73
76
  "commitlint": "^19.8.1",
74
77
  "del-cli": "^6.0.0",
75
78
  "eslint": "^9.35.0",
@@ -83,13 +86,15 @@
83
86
  "react-native": "0.83.0",
84
87
  "react-native-builder-bob": "^0.40.17",
85
88
  "react-native-nitro-modules": "^0.31.10",
89
+ "react-test-renderer": "19.2.0",
86
90
  "release-it": "^19.0.4",
87
91
  "turbo": "^2.5.6",
88
92
  "typescript": "^5.9.2"
89
93
  },
94
+ "react-native": "./lib/module/index.js",
90
95
  "peerDependencies": {
91
- "react": "*",
92
- "react-native": "*",
96
+ "react": ">=18.2.0",
97
+ "react-native": ">=0.72.0",
93
98
  "react-native-nitro-modules": "^0.31.10"
94
99
  },
95
100
  "workspaces": [
@@ -133,10 +138,36 @@
133
138
  },
134
139
  "jest": {
135
140
  "preset": "react-native",
141
+ "setupFiles": [
142
+ "./jest.setup.js"
143
+ ],
136
144
  "modulePathIgnorePatterns": [
137
145
  "<rootDir>/example/node_modules",
138
146
  "<rootDir>/lib/"
139
- ]
147
+ ],
148
+ "transformIgnorePatterns": [
149
+ "node_modules/(?!((jest-)?react-native|@react-native(-community)?|react-native-nitro-modules)/)"
150
+ ],
151
+ "collectCoverageFrom": [
152
+ "src/utils/**/*.ts",
153
+ "src/hooks/**/*.ts",
154
+ "src/context/**/*.tsx",
155
+ "!src/**/*.nitro.ts"
156
+ ],
157
+ "coverageThreshold": {
158
+ "global": {
159
+ "branches": 70,
160
+ "functions": 40,
161
+ "lines": 65,
162
+ "statements": 65
163
+ },
164
+ "./src/utils/": {
165
+ "branches": 90,
166
+ "functions": 100,
167
+ "lines": 90,
168
+ "statements": 90
169
+ }
170
+ }
140
171
  },
141
172
  "commitlint": {
142
173
  "extends": [
@@ -1,11 +1,31 @@
1
1
  // src/components/ImageMarker.tsx
2
- import { memo, useCallback, useMemo } from 'react';
2
+ import { memo, useCallback, useEffect } 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 { Colors, parseColor, type ColorValue } from '../utils/colors';
8
+ import {
9
+ Colors,
10
+ parseColor,
11
+ areColorsEqual,
12
+ type ColorValue,
13
+ } from '../utils/colors';
14
+
15
+ const MAX_IMAGE_DIMENSION = 512;
16
+ const MIN_IMAGE_DIMENSION = 1;
17
+
18
+ /** Clamp a dimension to [1, 512] and warn in __DEV__ if clamped */
19
+ function clampDimension(value: number, name: string): number {
20
+ if (value >= MIN_IMAGE_DIMENSION && value <= MAX_IMAGE_DIMENSION)
21
+ return value;
22
+ if (__DEV__) {
23
+ console.warn(
24
+ `[ImageMarker] ${name}=${value} clamped to [${MIN_IMAGE_DIMENSION}, ${MAX_IMAGE_DIMENSION}].`
25
+ );
26
+ }
27
+ return Math.max(MIN_IMAGE_DIMENSION, Math.min(MAX_IMAGE_DIMENSION, value));
28
+ }
9
29
 
10
30
  /**
11
31
  * Props for the ImageMarker component
@@ -73,7 +93,10 @@ const arePropsEqual = (
73
93
  prevProps.rotation !== nextProps.rotation ||
74
94
  prevProps.zIndex !== nextProps.zIndex ||
75
95
  prevProps.clusteringEnabled !== nextProps.clusteringEnabled ||
76
- prevProps.animation !== nextProps.animation
96
+ prevProps.animation !== nextProps.animation ||
97
+ prevProps.animationDuration !== nextProps.animationDuration ||
98
+ prevProps.animateOnReappear !== nextProps.animateOnReappear ||
99
+ prevProps.accessibilityLabel !== nextProps.accessibilityLabel
77
100
  ) {
78
101
  return false;
79
102
  }
@@ -92,28 +115,7 @@ const arePropsEqual = (
92
115
  return false;
93
116
  }
94
117
 
95
- const compareColors = (
96
- a: ColorValue | undefined,
97
- b: ColorValue | undefined
98
- ) => {
99
- if (!a && !b) return true;
100
- if (!a || !b) return false;
101
- // If both are strings, compare directly
102
- if (typeof a === 'string' && typeof b === 'string') return a === b;
103
- // If types differ, not equal
104
- if (typeof a !== typeof b) return false;
105
- // Both are MarkerColor objects
106
- const aColor = a as MarkerColor;
107
- const bColor = b as MarkerColor;
108
- return (
109
- aColor.r === bColor.r &&
110
- aColor.g === bColor.g &&
111
- aColor.b === bColor.b &&
112
- aColor.a === bColor.a
113
- );
114
- };
115
-
116
- if (!compareColors(prevProps.borderColor, nextProps.borderColor)) {
118
+ if (!areColorsEqual(prevProps.borderColor, nextProps.borderColor)) {
117
119
  return false;
118
120
  }
119
121
 
@@ -172,6 +174,9 @@ export const ImageMarker = memo(function ImageMarker({
172
174
  anchor = { x: 0.5, y: 0.5 },
173
175
  clusteringEnabled = true,
174
176
  animation = 'none',
177
+ animationDuration = 0.3,
178
+ animateOnReappear = true,
179
+ accessibilityLabel,
175
180
 
176
181
  // Events
177
182
  onPress,
@@ -179,18 +184,23 @@ export const ImageMarker = memo(function ImageMarker({
179
184
  onDrag,
180
185
  onDragEnd,
181
186
  }: ImageMarkerProps) {
182
- // Build marker config
183
-
184
- // Memoize handlers
185
- const handlers = useMemo(
186
- () => ({
187
- onPress,
188
- onDragStart,
189
- onDrag,
190
- onDragEnd,
191
- }),
192
- [onPress, onDragStart, onDrag, onDragEnd]
193
- );
187
+ // Dev-mode validation: at least one image source is required
188
+ useEffect(() => {
189
+ if (__DEV__ && !imageUrl && !imageBase64) {
190
+ console.warn(
191
+ 'ImageMarker: either imageUrl or imageBase64 must be provided. ' +
192
+ 'The marker will render without an image.'
193
+ );
194
+ }
195
+ // eslint-disable-next-line react-hooks/exhaustive-deps
196
+ }, []);
197
+
198
+ // Clamp dimensions to safe range
199
+ const clampedWidth = clampDimension(width, 'width');
200
+ const clampedHeight = clampDimension(height, 'height');
201
+
202
+ // Memoize handlers via shared hook
203
+ const handlers = useMarkerHandlers(onPress, onDragStart, onDrag, onDragEnd);
194
204
 
195
205
  // Build full marker data
196
206
  const buildMarkerData = useCallback(
@@ -210,14 +220,17 @@ export const ImageMarker = memo(function ImageMarker({
210
220
  image: {
211
221
  imageUrl,
212
222
  imageBase64,
213
- width,
214
- height,
223
+ width: clampedWidth,
224
+ height: clampedHeight,
215
225
  cornerRadius,
216
226
  borderWidth,
217
227
  borderColor: parseColor(borderColor) ?? Colors.gray,
218
228
  },
219
229
  },
230
+ accessibilityLabel,
220
231
  animation,
232
+ animationDuration,
233
+ animateOnReappear,
221
234
  }),
222
235
  [
223
236
  coordinate,
@@ -231,12 +244,15 @@ export const ImageMarker = memo(function ImageMarker({
231
244
  clusteringEnabled,
232
245
  imageUrl,
233
246
  imageBase64,
234
- width,
235
- height,
247
+ clampedWidth,
248
+ clampedHeight,
236
249
  cornerRadius,
237
250
  borderWidth,
238
251
  borderColor,
252
+ accessibilityLabel,
239
253
  animation,
254
+ animationDuration,
255
+ animateOnReappear,
240
256
  ]
241
257
  );
242
258
 
@@ -0,0 +1,161 @@
1
+ // src/components/Marker.tsx
2
+ import { memo, useCallback } from 'react';
3
+ import {
4
+ useNitroMarker,
5
+ useMarkerHandlers,
6
+ type CommonMarkerProps,
7
+ } from '../hooks/useNitroMarker';
8
+
9
+ /**
10
+ * Props for the Marker component (default Google Maps pin)
11
+ */
12
+ export type MarkerProps = CommonMarkerProps;
13
+
14
+ // ============ Custom Memo Comparison ============
15
+ const arePropsEqual = (
16
+ prevProps: MarkerProps,
17
+ nextProps: MarkerProps
18
+ ): boolean => {
19
+ // Compare primitive props
20
+ if (
21
+ prevProps.id !== nextProps.id ||
22
+ prevProps.title !== nextProps.title ||
23
+ prevProps.description !== nextProps.description ||
24
+ prevProps.draggable !== nextProps.draggable ||
25
+ prevProps.opacity !== nextProps.opacity ||
26
+ prevProps.rotation !== nextProps.rotation ||
27
+ prevProps.zIndex !== nextProps.zIndex ||
28
+ prevProps.clusteringEnabled !== nextProps.clusteringEnabled ||
29
+ prevProps.animation !== nextProps.animation ||
30
+ prevProps.animationDuration !== nextProps.animationDuration ||
31
+ prevProps.animateOnReappear !== nextProps.animateOnReappear ||
32
+ prevProps.accessibilityLabel !== nextProps.accessibilityLabel
33
+ ) {
34
+ return false;
35
+ }
36
+
37
+ // Compare coordinate
38
+ if (
39
+ prevProps.coordinate?.latitude !== nextProps.coordinate?.latitude ||
40
+ prevProps.coordinate?.longitude !== nextProps.coordinate?.longitude
41
+ ) {
42
+ return false;
43
+ }
44
+
45
+ // Compare anchor
46
+ if (
47
+ prevProps.anchor?.x !== nextProps.anchor?.x ||
48
+ prevProps.anchor?.y !== nextProps.anchor?.y
49
+ ) {
50
+ return false;
51
+ }
52
+
53
+ return true;
54
+ };
55
+
56
+ /**
57
+ * Marker - Display the default Google Maps pin on the map
58
+ *
59
+ * The simplest marker component. Uses the standard red Google Maps pin.
60
+ * For custom images, use `<ImageMarker>`. For price tags, use `<PriceMarker>`.
61
+ *
62
+ * @example Basic marker
63
+ * ```tsx
64
+ * <Marker
65
+ * coordinate={{ latitude: 41.29, longitude: 69.24 }}
66
+ * title="Hello World"
67
+ * description="This is a default marker"
68
+ * />
69
+ * ```
70
+ *
71
+ * @example Draggable marker
72
+ * ```tsx
73
+ * <Marker
74
+ * coordinate={{ latitude: 41.30, longitude: 69.25 }}
75
+ * draggable={true}
76
+ * onDragEnd={(coord) => console.log('Dropped at:', coord)}
77
+ * />
78
+ * ```
79
+ */
80
+ export const Marker = memo(function Marker({
81
+ // Required
82
+ coordinate,
83
+
84
+ // Identification
85
+ id,
86
+
87
+ // Common props
88
+ title,
89
+ description,
90
+ draggable = false,
91
+ opacity = 1,
92
+ rotation = 0,
93
+ zIndex = 0,
94
+ anchor = { x: 0.5, y: 1.0 },
95
+ clusteringEnabled = true,
96
+ animation = 'none',
97
+ animationDuration = 0.3,
98
+ animateOnReappear = true,
99
+ accessibilityLabel,
100
+
101
+ // Events
102
+ onPress,
103
+ onDragStart,
104
+ onDrag,
105
+ onDragEnd,
106
+ }: MarkerProps) {
107
+ // Memoize handlers via shared hook
108
+ const handlers = useMarkerHandlers(onPress, onDragStart, onDrag, onDragEnd);
109
+
110
+ // Build full marker data
111
+ const buildMarkerData = useCallback(
112
+ (markerId: string) => ({
113
+ id: markerId,
114
+ coordinate,
115
+ title,
116
+ description,
117
+ draggable,
118
+ opacity,
119
+ rotation,
120
+ zIndex,
121
+ anchor,
122
+ clusteringEnabled,
123
+ config: {
124
+ style: 'default' as const,
125
+ },
126
+ accessibilityLabel,
127
+ animation,
128
+ animationDuration,
129
+ animateOnReappear,
130
+ }),
131
+ [
132
+ coordinate,
133
+ title,
134
+ description,
135
+ draggable,
136
+ opacity,
137
+ rotation,
138
+ zIndex,
139
+ anchor,
140
+ clusteringEnabled,
141
+ accessibilityLabel,
142
+ animation,
143
+ animationDuration,
144
+ animateOnReappear,
145
+ ]
146
+ );
147
+
148
+ // Use shared marker hook
149
+ useNitroMarker({
150
+ idPrefix: 'marker',
151
+ providedId: id,
152
+ handlers,
153
+ buildMarkerData,
154
+ });
155
+
156
+ // Render nothing - marker is rendered natively
157
+ return null;
158
+ }, arePropsEqual);
159
+
160
+ export { arePropsEqual as areMarkerPropsEqual };
161
+ export default Marker;
@@ -0,0 +1,183 @@
1
+ // src/components/NitroCircle.tsx
2
+ import { memo, useCallback, useEffect, useRef } from 'react';
3
+ import { useNitroCircle } from '../hooks/useNitroCircle';
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 { validateCoordinate } from '../utils/validation';
13
+ import type { CircleData } from '../types/overlay';
14
+
15
+ /**
16
+ * Props for the NitroCircle component
17
+ */
18
+ export interface NitroCircleProps {
19
+ /** Unique identifier for the circle */
20
+ id?: string;
21
+
22
+ /**
23
+ * Center coordinate of the circle.
24
+ * @required
25
+ */
26
+ center: Coordinate;
27
+
28
+ /**
29
+ * Radius of the circle in meters.
30
+ * @required
31
+ */
32
+ radius: number;
33
+
34
+ /**
35
+ * Fill color of the circle (hex string like \"#00000040\" or MarkerColor object).
36
+ * Use 8-char hex for alpha: "#RRGGBBAA"
37
+ * @default MarkerColor { r: 0, g: 0, b: 0, a: 77 } (~30% opacity black)
38
+ */
39
+ fillColor?: ColorValue;
40
+
41
+ /**
42
+ * Stroke color of the circle border (hex string or MarkerColor object).
43
+ * @default '#000000'
44
+ */
45
+ strokeColor?: ColorValue;
46
+
47
+ /**
48
+ * Stroke width of the circle border in pixels.
49
+ * @default 1
50
+ */
51
+ strokeWidth?: number;
52
+
53
+ /**
54
+ * Z-index for controlling overlay stacking order.
55
+ * Higher values render on top.
56
+ * @default 0
57
+ */
58
+ zIndex?: number;
59
+
60
+ /**
61
+ * Whether this circle responds to tap events.
62
+ * When true, `onCirclePress` on `<NitroMap>` fires with this circle's ID.
63
+ * @default false
64
+ */
65
+ tappable?: boolean;
66
+
67
+ /**
68
+ * Accessibility label read by screen readers (VoiceOver / TalkBack).
69
+ */
70
+ accessibilityLabel?: string;
71
+ }
72
+
73
+ // ID generator — monotonic counter, unique within a JS runtime session
74
+ let circleIdCounter = 0;
75
+ const generateCircleId = (): string => `circle_${++circleIdCounter}`;
76
+
77
+ // Default colors
78
+ const defaultFillColor: MarkerColor = { r: 0, g: 0, b: 0, a: 77 };
79
+ const defaultStrokeColor: MarkerColor = Colors.black;
80
+
81
+ // ============ Custom Memo Comparison ============
82
+ const arePropsEqual = (
83
+ prev: NitroCircleProps,
84
+ next: NitroCircleProps
85
+ ): boolean => {
86
+ if (
87
+ prev.id !== next.id ||
88
+ prev.radius !== next.radius ||
89
+ prev.strokeWidth !== next.strokeWidth ||
90
+ prev.zIndex !== next.zIndex ||
91
+ prev.tappable !== next.tappable ||
92
+ prev.accessibilityLabel !== next.accessibilityLabel
93
+ ) {
94
+ return false;
95
+ }
96
+
97
+ // Compare center
98
+ if (
99
+ prev.center.latitude !== next.center.latitude ||
100
+ prev.center.longitude !== next.center.longitude
101
+ ) {
102
+ return false;
103
+ }
104
+
105
+ if (!areColorsEqual(prev.fillColor, next.fillColor)) return false;
106
+ if (!areColorsEqual(prev.strokeColor, next.strokeColor)) return false;
107
+
108
+ return true;
109
+ };
110
+
111
+ /**
112
+ * NitroCircle - Draw a circle overlay on the map
113
+ *
114
+ * @example Basic circle
115
+ * ```tsx
116
+ * <NitroCircle
117
+ * center={{ latitude: 41.31, longitude: 69.26 }}
118
+ * radius={500}
119
+ * fillColor="#4285F440"
120
+ * strokeColor="#4285F4"
121
+ * strokeWidth={2}
122
+ * />
123
+ * ```
124
+ */
125
+ export const NitroCircle = memo(function NitroCircle({
126
+ id,
127
+ center,
128
+ radius,
129
+ fillColor,
130
+ strokeColor,
131
+ strokeWidth = 1,
132
+ zIndex = 0,
133
+ tappable = false,
134
+ accessibilityLabel,
135
+ }: NitroCircleProps) {
136
+ useEffect(() => {
137
+ if (__DEV__) {
138
+ if (radius <= 0) {
139
+ console.warn(
140
+ 'NitroCircle: radius must be a positive number. ' +
141
+ `Received ${radius}.`
142
+ );
143
+ }
144
+ validateCoordinate(center, 'NitroCircle center');
145
+ }
146
+ // eslint-disable-next-line react-hooks/exhaustive-deps
147
+ }, []);
148
+
149
+ const circleId = useRef(id || generateCircleId()).current;
150
+
151
+ const buildCircleData = useCallback(
152
+ (): CircleData => ({
153
+ id: circleId,
154
+ center,
155
+ radius,
156
+ fillColor: parseColor(fillColor) ?? defaultFillColor,
157
+ strokeColor: parseColor(strokeColor) ?? defaultStrokeColor,
158
+ strokeWidth,
159
+ zIndex,
160
+ tappable,
161
+ accessibilityLabel,
162
+ }),
163
+ [
164
+ circleId,
165
+ center,
166
+ radius,
167
+ fillColor,
168
+ strokeColor,
169
+ strokeWidth,
170
+ zIndex,
171
+ tappable,
172
+ accessibilityLabel,
173
+ ]
174
+ );
175
+
176
+ useNitroCircle(buildCircleData);
177
+
178
+ return null;
179
+ },
180
+ arePropsEqual);
181
+
182
+ export { arePropsEqual as areCirclePropsEqual };
183
+ export default NitroCircle;