@maydon_tech/react-native-nitro-maps 0.1.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 (283) hide show
  1. package/LICENSE +20 -0
  2. package/NitroMap.podspec +42 -0
  3. package/README.md +172 -0
  4. package/android/CMakeLists.txt +27 -0
  5. package/android/build.gradle +141 -0
  6. package/android/gradle.properties +5 -0
  7. package/android/src/main/AndroidManifest.xml +3 -0
  8. package/android/src/main/cpp/cpp-adapter.cpp +6 -0
  9. package/android/src/main/java/com/margelo/nitro/nitromap/ClusterIconGenerator.kt +108 -0
  10. package/android/src/main/java/com/margelo/nitro/nitromap/ColorUtils.kt +63 -0
  11. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMap.kt +408 -0
  12. package/android/src/main/java/com/margelo/nitro/nitromap/HybridNitroMapConfig.kt +68 -0
  13. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconCache.kt +176 -0
  14. package/android/src/main/java/com/margelo/nitro/nitromap/MarkerIconFactory.kt +252 -0
  15. package/android/src/main/java/com/margelo/nitro/nitromap/NitroMapPackage.kt +33 -0
  16. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/NitroClusterEngine.kt +252 -0
  17. package/android/src/main/java/com/margelo/nitro/nitromap/clustering/QuadTree.kt +195 -0
  18. package/android/src/main/java/com/margelo/nitro/nitromap/providers/GoogleMapProvider.kt +912 -0
  19. package/android/src/main/java/com/margelo/nitro/nitromap/providers/MapProviderInterface.kt +70 -0
  20. package/cpp/ClusterEngine.hpp +411 -0
  21. package/cpp/KDBush.hpp +238 -0
  22. package/cpp/QuadTree.hpp +246 -0
  23. package/ios/Clustering/ClusterEngineWrapper.h +58 -0
  24. package/ios/Clustering/ClusterEngineWrapper.mm +142 -0
  25. package/ios/Clustering/ClusterIconRenderer.swift +80 -0
  26. package/ios/Clustering/NitroClusterEngine.swift +117 -0
  27. package/ios/Clustering/NitroClusterIconGenerator.swift +35 -0
  28. package/ios/MarkerRenderer/MarkerIconFactory.swift +322 -0
  29. package/ios/MarkerRenderer/PriceMarkerRenderer.swift +140 -0
  30. package/ios/NitroMap.swift +332 -0
  31. package/ios/NitroMapConfig/HybridNitroMapConfig.swift +33 -0
  32. package/ios/Providers/GoogleMapDelegate.swift +310 -0
  33. package/ios/Providers/GoogleMapProvider+Camera.swift +164 -0
  34. package/ios/Providers/GoogleMapProvider.swift +924 -0
  35. package/ios/Providers/MapProviderProtocol.swift +78 -0
  36. package/ios/Shared/ClusterConfig+Factory.swift +58 -0
  37. package/ios/Shared/ClusteringManager.swift +211 -0
  38. package/ios/Shared/MapStyleProvider.swift +135 -0
  39. package/ios/Shared/MarkerSelectionHandler.swift +116 -0
  40. package/ios/Utils/ColorValueExtension.swift +54 -0
  41. package/lib/module/components/ImageMarker.js +146 -0
  42. package/lib/module/components/ImageMarker.js.map +1 -0
  43. package/lib/module/components/NitroMap.js +320 -0
  44. package/lib/module/components/NitroMap.js.map +1 -0
  45. package/lib/module/components/PriceMarker.js +165 -0
  46. package/lib/module/components/PriceMarker.js.map +1 -0
  47. package/lib/module/context/NitroMapContext.js +15 -0
  48. package/lib/module/context/NitroMapContext.js.map +1 -0
  49. package/lib/module/hooks/useNitroMarker.js +104 -0
  50. package/lib/module/hooks/useNitroMarker.js.map +1 -0
  51. package/lib/module/index.js +21 -0
  52. package/lib/module/index.js.map +1 -0
  53. package/lib/module/modules/index.js +4 -0
  54. package/lib/module/modules/index.js.map +1 -0
  55. package/lib/module/modules/module.js +30 -0
  56. package/lib/module/modules/module.js.map +1 -0
  57. package/lib/module/package.json +1 -0
  58. package/lib/module/specs/NitroMap.nitro.js +4 -0
  59. package/lib/module/specs/NitroMap.nitro.js.map +1 -0
  60. package/lib/module/specs/NitroMapConfig.nitro.js +4 -0
  61. package/lib/module/specs/NitroMapConfig.nitro.js.map +1 -0
  62. package/lib/module/types/map.js +2 -0
  63. package/lib/module/types/map.js.map +1 -0
  64. package/lib/module/types/marker.js +4 -0
  65. package/lib/module/types/marker.js.map +1 -0
  66. package/lib/module/utils/colors.js +147 -0
  67. package/lib/module/utils/colors.js.map +1 -0
  68. package/lib/typescript/package.json +1 -0
  69. package/lib/typescript/src/components/ImageMarker.d.ts +70 -0
  70. package/lib/typescript/src/components/ImageMarker.d.ts.map +1 -0
  71. package/lib/typescript/src/components/NitroMap.d.ts +14 -0
  72. package/lib/typescript/src/components/NitroMap.d.ts.map +1 -0
  73. package/lib/typescript/src/components/PriceMarker.d.ts +88 -0
  74. package/lib/typescript/src/components/PriceMarker.d.ts.map +1 -0
  75. package/lib/typescript/src/context/NitroMapContext.d.ts +16 -0
  76. package/lib/typescript/src/context/NitroMapContext.d.ts.map +1 -0
  77. package/lib/typescript/src/hooks/useNitroMarker.d.ts +78 -0
  78. package/lib/typescript/src/hooks/useNitroMarker.d.ts.map +1 -0
  79. package/lib/typescript/src/index.d.ts +12 -0
  80. package/lib/typescript/src/index.d.ts.map +1 -0
  81. package/lib/typescript/src/modules/index.d.ts +2 -0
  82. package/lib/typescript/src/modules/index.d.ts.map +1 -0
  83. package/lib/typescript/src/modules/module.d.ts +22 -0
  84. package/lib/typescript/src/modules/module.d.ts.map +1 -0
  85. package/lib/typescript/src/specs/NitroMap.nitro.d.ts +227 -0
  86. package/lib/typescript/src/specs/NitroMap.nitro.d.ts.map +1 -0
  87. package/lib/typescript/src/specs/NitroMapConfig.nitro.d.ts +10 -0
  88. package/lib/typescript/src/specs/NitroMapConfig.nitro.d.ts.map +1 -0
  89. package/lib/typescript/src/types/map.d.ts +154 -0
  90. package/lib/typescript/src/types/map.d.ts.map +1 -0
  91. package/lib/typescript/src/types/marker.d.ts +248 -0
  92. package/lib/typescript/src/types/marker.d.ts.map +1 -0
  93. package/lib/typescript/src/utils/colors.d.ts +82 -0
  94. package/lib/typescript/src/utils/colors.d.ts.map +1 -0
  95. package/nitro.json +21 -0
  96. package/nitrogen/generated/android/c++/JCamera.hpp +74 -0
  97. package/nitrogen/generated/android/c++/JClusterAnimationStyle.hpp +68 -0
  98. package/nitrogen/generated/android/c++/JClusterConfig.hpp +121 -0
  99. package/nitrogen/generated/android/c++/JClusterPressEvent.hpp +86 -0
  100. package/nitrogen/generated/android/c++/JClusterStrategy.hpp +59 -0
  101. package/nitrogen/generated/android/c++/JColorValue.cpp +26 -0
  102. package/nitrogen/generated/android/c++/JColorValue.hpp +70 -0
  103. package/nitrogen/generated/android/c++/JCoordinate.hpp +61 -0
  104. package/nitrogen/generated/android/c++/JEdgePadding.hpp +69 -0
  105. package/nitrogen/generated/android/c++/JFunc_void.hpp +75 -0
  106. package/nitrogen/generated/android/c++/JFunc_void_ClusterPressEvent.hpp +81 -0
  107. package/nitrogen/generated/android/c++/JFunc_void_MapError.hpp +78 -0
  108. package/nitrogen/generated/android/c++/JFunc_void_MapPressEvent.hpp +81 -0
  109. package/nitrogen/generated/android/c++/JFunc_void_MarkerDragEvent.hpp +80 -0
  110. package/nitrogen/generated/android/c++/JFunc_void_MarkerPressEvent.hpp +80 -0
  111. package/nitrogen/generated/android/c++/JFunc_void_RegionChangeEvent.hpp +79 -0
  112. package/nitrogen/generated/android/c++/JHybridNitroMapConfigSpec.cpp +59 -0
  113. package/nitrogen/generated/android/c++/JHybridNitroMapConfigSpec.hpp +66 -0
  114. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.cpp +593 -0
  115. package/nitrogen/generated/android/c++/JHybridNitroMapSpec.hpp +125 -0
  116. package/nitrogen/generated/android/c++/JImageMarkerConfig.hpp +86 -0
  117. package/nitrogen/generated/android/c++/JMapBoundaries.hpp +62 -0
  118. package/nitrogen/generated/android/c++/JMapError.hpp +61 -0
  119. package/nitrogen/generated/android/c++/JMapPressEvent.hpp +64 -0
  120. package/nitrogen/generated/android/c++/JMapProvider.hpp +62 -0
  121. package/nitrogen/generated/android/c++/JMapStyleElement.hpp +87 -0
  122. package/nitrogen/generated/android/c++/JMapStyler.hpp +78 -0
  123. package/nitrogen/generated/android/c++/JMapType.hpp +62 -0
  124. package/nitrogen/generated/android/c++/JMarkerAnimation.hpp +62 -0
  125. package/nitrogen/generated/android/c++/JMarkerColor.hpp +69 -0
  126. package/nitrogen/generated/android/c++/JMarkerConfig.hpp +77 -0
  127. package/nitrogen/generated/android/c++/JMarkerData.hpp +121 -0
  128. package/nitrogen/generated/android/c++/JMarkerDragEvent.hpp +63 -0
  129. package/nitrogen/generated/android/c++/JMarkerPressEvent.hpp +63 -0
  130. package/nitrogen/generated/android/c++/JMarkerStyle.hpp +62 -0
  131. package/nitrogen/generated/android/c++/JPoint.hpp +61 -0
  132. package/nitrogen/generated/android/c++/JPriceMarkerStyle.hpp +102 -0
  133. package/nitrogen/generated/android/c++/JRegion.hpp +69 -0
  134. package/nitrogen/generated/android/c++/JRegionChangeEvent.hpp +62 -0
  135. package/nitrogen/generated/android/c++/JVariant_String_MarkerColor.cpp +26 -0
  136. package/nitrogen/generated/android/c++/JVariant_String_MarkerColor.hpp +70 -0
  137. package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.cpp +144 -0
  138. package/nitrogen/generated/android/c++/views/JHybridNitroMapStateUpdater.hpp +49 -0
  139. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Camera.kt +50 -0
  140. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterAnimationStyle.kt +24 -0
  141. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterConfig.kt +80 -0
  142. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterPressEvent.kt +44 -0
  143. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ClusterStrategy.kt +21 -0
  144. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ColorValue.kt +59 -0
  145. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Coordinate.kt +41 -0
  146. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/EdgePadding.kt +47 -0
  147. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void.kt +80 -0
  148. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_ClusterPressEvent.kt +80 -0
  149. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_MapError.kt +80 -0
  150. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_MapPressEvent.kt +80 -0
  151. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_MarkerDragEvent.kt +80 -0
  152. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_MarkerPressEvent.kt +80 -0
  153. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Func_void_RegionChangeEvent.kt +80 -0
  154. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapConfigSpec.kt +61 -0
  155. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/HybridNitroMapSpec.kt +342 -0
  156. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/ImageMarkerConfig.kt +56 -0
  157. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapBoundaries.kt +41 -0
  158. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapError.kt +41 -0
  159. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapPressEvent.kt +41 -0
  160. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapProvider.kt +22 -0
  161. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapStyleElement.kt +44 -0
  162. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapStyler.kt +53 -0
  163. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MapType.kt +22 -0
  164. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerAnimation.kt +22 -0
  165. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerColor.kt +47 -0
  166. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerConfig.kt +44 -0
  167. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerData.kt +71 -0
  168. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerDragEvent.kt +41 -0
  169. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerPressEvent.kt +41 -0
  170. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/MarkerStyle.kt +22 -0
  171. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Point.kt +41 -0
  172. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/PriceMarkerStyle.kt +68 -0
  173. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Region.kt +47 -0
  174. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/RegionChangeEvent.kt +41 -0
  175. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/Variant_String_MarkerColor.kt +59 -0
  176. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/nitromapOnLoad.kt +35 -0
  177. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/views/HybridNitroMapManager.kt +50 -0
  178. package/nitrogen/generated/android/kotlin/com/margelo/nitro/nitromap/views/HybridNitroMapStateUpdater.kt +23 -0
  179. package/nitrogen/generated/android/nitromap+autolinking.cmake +87 -0
  180. package/nitrogen/generated/android/nitromap+autolinking.gradle +27 -0
  181. package/nitrogen/generated/android/nitromapOnLoad.cpp +70 -0
  182. package/nitrogen/generated/android/nitromapOnLoad.hpp +25 -0
  183. package/nitrogen/generated/ios/NitroMap+autolinking.rb +60 -0
  184. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.cpp +130 -0
  185. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Bridge.hpp +793 -0
  186. package/nitrogen/generated/ios/NitroMap-Swift-Cxx-Umbrella.hpp +132 -0
  187. package/nitrogen/generated/ios/NitroMapAutolinking.mm +41 -0
  188. package/nitrogen/generated/ios/NitroMapAutolinking.swift +40 -0
  189. package/nitrogen/generated/ios/c++/HybridNitroMapConfigSpecSwift.cpp +11 -0
  190. package/nitrogen/generated/ios/c++/HybridNitroMapConfigSpecSwift.hpp +84 -0
  191. package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.cpp +11 -0
  192. package/nitrogen/generated/ios/c++/HybridNitroMapSpecSwift.hpp +410 -0
  193. package/nitrogen/generated/ios/c++/views/HybridNitroMapComponent.mm +206 -0
  194. package/nitrogen/generated/ios/swift/Camera.swift +80 -0
  195. package/nitrogen/generated/ios/swift/ClusterAnimationStyle.swift +52 -0
  196. package/nitrogen/generated/ios/swift/ClusterConfig.swift +268 -0
  197. package/nitrogen/generated/ios/swift/ClusterPressEvent.swift +70 -0
  198. package/nitrogen/generated/ios/swift/ClusterStrategy.swift +40 -0
  199. package/nitrogen/generated/ios/swift/ColorValue.swift +18 -0
  200. package/nitrogen/generated/ios/swift/Coordinate.swift +47 -0
  201. package/nitrogen/generated/ios/swift/EdgePadding.swift +69 -0
  202. package/nitrogen/generated/ios/swift/Func_void.swift +47 -0
  203. package/nitrogen/generated/ios/swift/Func_void_Camera.swift +47 -0
  204. package/nitrogen/generated/ios/swift/Func_void_ClusterPressEvent.swift +47 -0
  205. package/nitrogen/generated/ios/swift/Func_void_MapBoundaries.swift +47 -0
  206. package/nitrogen/generated/ios/swift/Func_void_MapError.swift +47 -0
  207. package/nitrogen/generated/ios/swift/Func_void_MapPressEvent.swift +47 -0
  208. package/nitrogen/generated/ios/swift/Func_void_MarkerDragEvent.swift +47 -0
  209. package/nitrogen/generated/ios/swift/Func_void_MarkerPressEvent.swift +47 -0
  210. package/nitrogen/generated/ios/swift/Func_void_RegionChangeEvent.swift +47 -0
  211. package/nitrogen/generated/ios/swift/Func_void_std__exception_ptr.swift +47 -0
  212. package/nitrogen/generated/ios/swift/HybridNitroMapConfigSpec.swift +57 -0
  213. package/nitrogen/generated/ios/swift/HybridNitroMapConfigSpec_cxx.swift +142 -0
  214. package/nitrogen/generated/ios/swift/HybridNitroMapSpec.swift +93 -0
  215. package/nitrogen/generated/ios/swift/HybridNitroMapSpec_cxx.swift +953 -0
  216. package/nitrogen/generated/ios/swift/ImageMarkerConfig.swift +166 -0
  217. package/nitrogen/generated/ios/swift/MapBoundaries.swift +47 -0
  218. package/nitrogen/generated/ios/swift/MapError.swift +47 -0
  219. package/nitrogen/generated/ios/swift/MapPressEvent.swift +47 -0
  220. package/nitrogen/generated/ios/swift/MapProvider.swift +44 -0
  221. package/nitrogen/generated/ios/swift/MapStyleElement.swift +108 -0
  222. package/nitrogen/generated/ios/swift/MapStyler.swift +177 -0
  223. package/nitrogen/generated/ios/swift/MapType.swift +44 -0
  224. package/nitrogen/generated/ios/swift/MarkerAnimation.swift +44 -0
  225. package/nitrogen/generated/ios/swift/MarkerColor.swift +69 -0
  226. package/nitrogen/generated/ios/swift/MarkerConfig.swift +82 -0
  227. package/nitrogen/generated/ios/swift/MarkerData.swift +195 -0
  228. package/nitrogen/generated/ios/swift/MarkerDragEvent.swift +47 -0
  229. package/nitrogen/generated/ios/swift/MarkerPressEvent.swift +47 -0
  230. package/nitrogen/generated/ios/swift/MarkerStyle.swift +44 -0
  231. package/nitrogen/generated/ios/swift/Point.swift +47 -0
  232. package/nitrogen/generated/ios/swift/PriceMarkerStyle.swift +374 -0
  233. package/nitrogen/generated/ios/swift/Region.swift +69 -0
  234. package/nitrogen/generated/ios/swift/RegionChangeEvent.swift +47 -0
  235. package/nitrogen/generated/ios/swift/Variant_String_MarkerColor.swift +18 -0
  236. package/nitrogen/generated/shared/c++/Camera.hpp +92 -0
  237. package/nitrogen/generated/shared/c++/ClusterAnimationStyle.hpp +88 -0
  238. package/nitrogen/generated/shared/c++/ClusterConfig.hpp +140 -0
  239. package/nitrogen/generated/shared/c++/ClusterPressEvent.hpp +86 -0
  240. package/nitrogen/generated/shared/c++/ClusterStrategy.hpp +76 -0
  241. package/nitrogen/generated/shared/c++/Coordinate.hpp +79 -0
  242. package/nitrogen/generated/shared/c++/EdgePadding.hpp +87 -0
  243. package/nitrogen/generated/shared/c++/HybridNitroMapConfigSpec.cpp +22 -0
  244. package/nitrogen/generated/shared/c++/HybridNitroMapConfigSpec.hpp +65 -0
  245. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.cpp +82 -0
  246. package/nitrogen/generated/shared/c++/HybridNitroMapSpec.hpp +173 -0
  247. package/nitrogen/generated/shared/c++/ImageMarkerConfig.hpp +103 -0
  248. package/nitrogen/generated/shared/c++/MapBoundaries.hpp +80 -0
  249. package/nitrogen/generated/shared/c++/MapError.hpp +79 -0
  250. package/nitrogen/generated/shared/c++/MapPressEvent.hpp +83 -0
  251. package/nitrogen/generated/shared/c++/MapProvider.hpp +80 -0
  252. package/nitrogen/generated/shared/c++/MapStyleElement.hpp +87 -0
  253. package/nitrogen/generated/shared/c++/MapStyler.hpp +96 -0
  254. package/nitrogen/generated/shared/c++/MapType.hpp +80 -0
  255. package/nitrogen/generated/shared/c++/MarkerAnimation.hpp +80 -0
  256. package/nitrogen/generated/shared/c++/MarkerColor.hpp +87 -0
  257. package/nitrogen/generated/shared/c++/MarkerConfig.hpp +91 -0
  258. package/nitrogen/generated/shared/c++/MarkerData.hpp +131 -0
  259. package/nitrogen/generated/shared/c++/MarkerDragEvent.hpp +81 -0
  260. package/nitrogen/generated/shared/c++/MarkerPressEvent.hpp +81 -0
  261. package/nitrogen/generated/shared/c++/MarkerStyle.hpp +80 -0
  262. package/nitrogen/generated/shared/c++/Point.hpp +79 -0
  263. package/nitrogen/generated/shared/c++/PriceMarkerStyle.hpp +119 -0
  264. package/nitrogen/generated/shared/c++/Region.hpp +87 -0
  265. package/nitrogen/generated/shared/c++/RegionChangeEvent.hpp +80 -0
  266. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.cpp +351 -0
  267. package/nitrogen/generated/shared/c++/views/HybridNitroMapComponent.hpp +141 -0
  268. package/nitrogen/generated/shared/json/NitroMapConfig.json +32 -0
  269. package/package.json +176 -0
  270. package/react-native.config.js +16 -0
  271. package/src/components/ImageMarker.tsx +254 -0
  272. package/src/components/NitroMap.tsx +433 -0
  273. package/src/components/PriceMarker.tsx +311 -0
  274. package/src/context/NitroMapContext.tsx +33 -0
  275. package/src/hooks/useNitroMarker.ts +198 -0
  276. package/src/index.tsx +62 -0
  277. package/src/modules/index.ts +6 -0
  278. package/src/modules/module.ts +45 -0
  279. package/src/specs/NitroMap.nitro.ts +292 -0
  280. package/src/specs/NitroMapConfig.nitro.ts +8 -0
  281. package/src/types/map.ts +166 -0
  282. package/src/types/marker.ts +267 -0
  283. package/src/utils/colors.ts +159 -0
@@ -0,0 +1,80 @@
1
+ import UIKit
2
+
3
+ /// Shared cluster icon renderer — single source of truth for cluster icon rendering.
4
+ /// Eliminates duplication across NitroClusterIconGenerator, ClusterAnnotationView,
5
+ /// and NitroClusterAnnotationView (DRY).
6
+ enum ClusterIconRenderer {
7
+
8
+ /// Renders a cluster icon with the given count and optional config.
9
+ ///
10
+ /// - Parameters:
11
+ /// - count: Number of markers in the cluster
12
+ /// - config: Optional cluster configuration for colors/border
13
+ /// - size: Override diameter. If nil, auto-selects based on count.
14
+ /// - Returns: Rendered cluster icon image
15
+ static func renderIcon(
16
+ count: Int,
17
+ config: ClusterConfig?,
18
+ size: CGFloat? = nil
19
+ ) -> UIImage {
20
+ let diameter = size ?? clusterDiameter(for: count)
21
+
22
+ let bgColor = resolveColor(config?.backgroundColor.toMarkerColor(),
23
+ fallback: MarkerColor(r: 0, g: 122, b: 255, a: 255))
24
+ let textColor = resolveColor(config?.textColor.toMarkerColor(),
25
+ fallback: MarkerColor(r: 255, g: 255, b: 255, a: 255))
26
+ let borderWidth = CGFloat(config?.borderWidth ?? 2)
27
+ let borderUIColor = resolveColor(config?.borderColor.toMarkerColor(),
28
+ fallback: MarkerColor(r: 255, g: 255, b: 255, a: 255))
29
+
30
+ let renderer = UIGraphicsImageRenderer(
31
+ size: CGSize(width: diameter, height: diameter)
32
+ )
33
+ return renderer.image { context in
34
+ let fullRect = CGRect(x: 0, y: 0, width: diameter, height: diameter)
35
+ let insetRect = fullRect.insetBy(dx: borderWidth, dy: borderWidth)
36
+
37
+ // Border circle
38
+ borderUIColor.setFill()
39
+ context.cgContext.fillEllipse(in: fullRect)
40
+
41
+ // Background circle
42
+ bgColor.setFill()
43
+ context.cgContext.fillEllipse(in: insetRect)
44
+
45
+ // Text
46
+ let text = "\(count)"
47
+ let font = UIFont.boldSystemFont(ofSize: diameter * 0.35)
48
+ let attributes: [NSAttributedString.Key: Any] = [
49
+ .font: font,
50
+ .foregroundColor: textColor,
51
+ ]
52
+ let textSize = text.size(withAttributes: attributes)
53
+ let textRect = CGRect(
54
+ x: (diameter - textSize.width) / 2,
55
+ y: (diameter - textSize.height) / 2,
56
+ width: textSize.width,
57
+ height: textSize.height
58
+ )
59
+ text.draw(in: textRect, withAttributes: attributes)
60
+ }
61
+ }
62
+
63
+ /// Standard cluster diameter based on count (Apple HIG: minimum 44pt tap targets).
64
+ static func clusterDiameter(for count: Int) -> CGFloat {
65
+ return count < 10 ? 44 : (count < 100 ? 52 : 60)
66
+ }
67
+
68
+ // MARK: - Private
69
+
70
+ private static func resolveColor(_ markerColor: MarkerColor?,
71
+ fallback: MarkerColor) -> UIColor {
72
+ let c = markerColor ?? fallback
73
+ return UIColor(
74
+ red: CGFloat(c.r) / 255,
75
+ green: CGFloat(c.g) / 255,
76
+ blue: CGFloat(c.b) / 255,
77
+ alpha: CGFloat(c.a) / 255
78
+ )
79
+ }
80
+ }
@@ -0,0 +1,117 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ /// Swift wrapper for C++ ClusterEngine via Objective-C++ bridge.
5
+ /// Pure spatial grouping — no icon dimensions.
6
+ /// Caller (GoogleMapProvider) handles visual collision with real rendered sizes.
7
+ class NitroClusterEngine {
8
+
9
+ private let engine: ClusterEngineWrapper
10
+
11
+ init() {
12
+ engine = ClusterEngineWrapper()
13
+ }
14
+
15
+ // MARK: - Configuration
16
+
17
+ func setClusterRadius(_ radius: Double) {
18
+ engine.setClusterRadius(radius)
19
+ }
20
+
21
+ func setMinClusterSize(_ size: Int) {
22
+ engine.setMinClusterSize(size)
23
+ }
24
+
25
+ func setMaxZoom(_ zoom: Double) {
26
+ engine.setMaxZoom(zoom)
27
+ }
28
+
29
+ // MARK: - Marker Management
30
+
31
+ /// Add a marker for clustering. Only geographic data is stored;
32
+ /// icon dimensions are handled by the Swift collision resolver.
33
+ func addMarker(_ markerData: MarkerData) {
34
+ engine.addMarker(
35
+ withId: markerData.id,
36
+ latitude: markerData.coordinate.latitude,
37
+ longitude: markerData.coordinate.longitude
38
+ )
39
+ }
40
+
41
+ func removeMarker(_ id: String) {
42
+ engine.removeMarker(withId: id)
43
+ }
44
+
45
+ func clearMarkers() {
46
+ engine.clearMarkers()
47
+ }
48
+
49
+ // MARK: - Result Types
50
+
51
+ /// Single marker returned from clustering (not merged into a cluster).
52
+ struct SingleMarkerResult {
53
+ var markerId: String
54
+ var latitude: Double
55
+ var longitude: Double
56
+ }
57
+
58
+ struct ClusteringResult {
59
+ var clusters: [ClusterDataResult]
60
+ var singleMarkers: [SingleMarkerResult]
61
+ }
62
+
63
+ struct ClusterDataResult {
64
+ var coordinate: CLLocationCoordinate2D
65
+ var markerIds: [String]
66
+ var count: Int
67
+ var iconSize: Double // Hint from C++; collision uses real rendered size
68
+ }
69
+
70
+ // MARK: - Clustering
71
+
72
+ /// Provider-agnostic clustering method — returns spatial groups.
73
+ /// Caller resolves IDs to MarkerData and measures real icon sizes.
74
+ func clusterWithBounds(
75
+ minLat: Double,
76
+ maxLat: Double,
77
+ minLon: Double,
78
+ maxLon: Double,
79
+ zoom: Double,
80
+ mapSize: CGSize
81
+ ) -> ClusteringResult {
82
+ let objcResult = engine.cluster(
83
+ withMinLat: minLat,
84
+ maxLat: maxLat,
85
+ minLon: minLon,
86
+ maxLon: maxLon,
87
+ zoom: zoom,
88
+ mapWidth: Double(mapSize.width),
89
+ mapHeight: Double(mapSize.height)
90
+ )
91
+
92
+ var clusters: [ClusterDataResult] = []
93
+ var singleMarkers: [SingleMarkerResult] = []
94
+
95
+ for clusterData in objcResult.clusters {
96
+ clusters.append(ClusterDataResult(
97
+ coordinate: CLLocationCoordinate2D(
98
+ latitude: clusterData.latitude,
99
+ longitude: clusterData.longitude
100
+ ),
101
+ markerIds: clusterData.markerIds as [String],
102
+ count: clusterData.count,
103
+ iconSize: clusterData.iconSize
104
+ ))
105
+ }
106
+
107
+ for point in objcResult.singleMarkers {
108
+ singleMarkers.append(SingleMarkerResult(
109
+ markerId: point.markerId,
110
+ latitude: point.latitude,
111
+ longitude: point.longitude
112
+ ))
113
+ }
114
+
115
+ return ClusteringResult(clusters: clusters, singleMarkers: singleMarkers)
116
+ }
117
+ }
@@ -0,0 +1,35 @@
1
+ import Foundation
2
+ import UIKit
3
+
4
+ /// Generates and caches cluster icons.
5
+ /// Uses ClusterIconRenderer for the actual rendering.
6
+ ///
7
+ /// FIX #6: No longer extends GMUDefaultClusterIconGenerator — the GMU
8
+ /// clustering pipeline is gone. This is now a plain Swift class.
9
+ final class NitroClusterIconGenerator {
10
+
11
+ private var config: ClusterConfig?
12
+
13
+ /// Cache cluster icons to avoid re-rendering identical icons on every cluster() call
14
+ private let iconCache = NSCache<NSNumber, UIImage>()
15
+
16
+ func updateConfig(_ config: ClusterConfig?) {
17
+ self.config = config
18
+ iconCache.removeAllObjects()
19
+ }
20
+
21
+ func icon(forSize size: UInt) -> UIImage {
22
+ let cacheKey = NSNumber(value: size)
23
+ if let cached = iconCache.object(forKey: cacheKey) {
24
+ return cached
25
+ }
26
+
27
+ let image = ClusterIconRenderer.renderIcon(
28
+ count: Int(size),
29
+ config: config
30
+ )
31
+
32
+ iconCache.setObject(image, forKey: cacheKey)
33
+ return image
34
+ }
35
+ }
@@ -0,0 +1,322 @@
1
+ import UIKit
2
+
3
+ /// Global marker icon cache with aggressive texture reuse
4
+ final class MarkerIconCache {
5
+ static let shared = MarkerIconCache()
6
+
7
+ // Use NSCache for automatic memory management
8
+ private let cache = NSCache<NSString, UIImage>()
9
+
10
+ // Separate cache for URL image data to avoid repeated network calls
11
+ private let urlImageCache = NSCache<NSString, UIImage>()
12
+
13
+ // Track unique icon count for debugging
14
+ private var uniqueIconCount = 0
15
+ private var cacheHits = 0
16
+ private var cacheMisses = 0
17
+
18
+ private init() {
19
+ // Increased limits to handle more markers without texture exhaustion
20
+ cache.countLimit = 500 // Max 500 unique icons (was 200)
21
+ cache.totalCostLimit = 100 * 1024 * 1024 // 100MB max (was 50MB)
22
+
23
+ urlImageCache.countLimit = 200
24
+ urlImageCache.totalCostLimit = 50 * 1024 * 1024
25
+
26
+ // Listen for memory warnings to clear caches
27
+ NotificationCenter.default.addObserver(
28
+ self,
29
+ selector: #selector(handleMemoryWarning),
30
+ name: UIApplication.didReceiveMemoryWarningNotification,
31
+ object: nil
32
+ )
33
+ }
34
+
35
+ @objc private func handleMemoryWarning() {
36
+ // Clear half the cache on memory warning instead of all
37
+ // This keeps frequently used icons while freeing memory
38
+ #if DEBUG
39
+ print("[MarkerIconCache] Memory warning received, clearing caches")
40
+ #endif
41
+ cache.removeAllObjects()
42
+ urlImageCache.removeAllObjects()
43
+ uniqueIconCount = 0
44
+ }
45
+
46
+ func getIcon(forKey key: String) -> UIImage? {
47
+ if let cached = cache.object(forKey: key as NSString) {
48
+ cacheHits += 1
49
+ logStatsIfNeeded()
50
+ return cached
51
+ }
52
+ cacheMisses += 1
53
+ logStatsIfNeeded()
54
+ return nil
55
+ }
56
+
57
+ func setIcon(_ image: UIImage, forKey key: String) {
58
+ // Calculate cost based on image size for better memory management
59
+ let cost = Int(image.size.width * image.size.height * image.scale * image.scale * 4)
60
+ cache.setObject(image, forKey: key as NSString, cost: cost)
61
+ uniqueIconCount += 1
62
+
63
+ if uniqueIconCount % 100 == 0 {
64
+ #if DEBUG
65
+ print("[MarkerIconCache] Cached \(uniqueIconCount) unique icons")
66
+ #endif
67
+ }
68
+ }
69
+
70
+ private func logStatsIfNeeded() {
71
+ let total = cacheHits + cacheMisses
72
+ if total > 0 && total % 500 == 0 {
73
+ let hitRate = Double(cacheHits) / Double(total) * 100
74
+ #if DEBUG
75
+ print("[MarkerIconCache] Stats: hits=\(cacheHits), misses=\(cacheMisses), hitRate=\(String(format: "%.1f", hitRate))%, unique=\(uniqueIconCount)")
76
+ #endif
77
+ }
78
+ }
79
+
80
+ func getURLImage(forKey key: String) -> UIImage? {
81
+ return urlImageCache.object(forKey: key as NSString)
82
+ }
83
+
84
+ func setURLImage(_ image: UIImage, forKey key: String) {
85
+ let cost = Int(image.size.width * image.size.height * image.scale * image.scale * 4)
86
+ urlImageCache.setObject(image, forKey: key as NSString, cost: cost)
87
+ }
88
+
89
+ func clear() {
90
+ cache.removeAllObjects()
91
+ urlImageCache.removeAllObjects()
92
+ uniqueIconCount = 0
93
+ }
94
+
95
+ /// Generate a simplified cache key for price markers
96
+ /// Reduces texture diversity by bucketing similar configurations
97
+ static func priceMarkerKey(
98
+ price: String,
99
+ currency: String,
100
+ selected: Bool
101
+ ) -> String {
102
+ // Normalize price to reduce unique textures
103
+ return "price_\(price)_\(currency)_\(selected)"
104
+ }
105
+
106
+ /// Generate cache key for image markers
107
+ static func imageMarkerKey(
108
+ source: String,
109
+ width: Double,
110
+ height: Double,
111
+ cornerRadius: Double
112
+ ) -> String {
113
+ // Round dimensions to reduce unique keys
114
+ let w = Int(width / 5) * 5 // Round to nearest 5
115
+ let h = Int(height / 5) * 5
116
+ let r = Int(cornerRadius / 5) * 5
117
+ // Use stable hash for cache key
118
+ let sourceHash = stableHash(source)
119
+ return "image_\(sourceHash)_\(w)_\(h)_\(r)"
120
+ }
121
+
122
+ /// Stable hash that doesn't change between app launches
123
+ private static func stableHash(_ string: String) -> Int {
124
+ var hash = 5381
125
+ for char in string.utf8 {
126
+ hash = ((hash << 5) &+ hash) &+ Int(char)
127
+ }
128
+ return abs(hash)
129
+ }
130
+ }
131
+
132
+ class MarkerIconFactory {
133
+
134
+ static func createIcon(for markerData: MarkerData) -> UIImage? {
135
+ switch markerData.config.style {
136
+ case .image:
137
+ return createImageMarkerIcon(markerData.config.image)
138
+ case .pricemarker:
139
+ return createPriceMarkerIcon(markerData.config.priceMarker)
140
+ case .default:
141
+ return nil
142
+ }
143
+ }
144
+
145
+ // MARK: - Price Marker Icon
146
+ private static func createPriceMarkerIcon(_ config: PriceMarkerStyle?) -> UIImage? {
147
+ guard let config = config else { return nil }
148
+
149
+ // Generate normalized cache key
150
+ let cacheKey = MarkerIconCache.priceMarkerKey(
151
+ price: config.price,
152
+ currency: config.currency,
153
+ selected: config.selected
154
+ )
155
+
156
+ // Check cache first
157
+ if let cachedImage = MarkerIconCache.shared.getIcon(forKey: cacheKey) {
158
+ return cachedImage
159
+ }
160
+
161
+ // Generate new icon
162
+ let image = PriceMarkerRenderer.render(config: config)
163
+
164
+ // Cache the result
165
+ MarkerIconCache.shared.setIcon(image, forKey: cacheKey)
166
+
167
+ return image
168
+ }
169
+
170
+ // MARK: - Image Marker Icon
171
+ private static func createImageMarkerIcon(_ config: ImageMarkerConfig?) -> UIImage? {
172
+ guard let config = config else { return nil }
173
+
174
+ let imageSource = config.imageBase64 ?? config.imageUrl ?? ""
175
+
176
+ // Generate normalized cache key
177
+ let cacheKey = MarkerIconCache.imageMarkerKey(
178
+ source: imageSource,
179
+ width: config.width,
180
+ height: config.height,
181
+ cornerRadius: config.cornerRadius
182
+ )
183
+
184
+ // Check cache first
185
+ if let cachedImage = MarkerIconCache.shared.getIcon(forKey: cacheKey) {
186
+ return cachedImage
187
+ }
188
+
189
+ var sourceImage: UIImage?
190
+
191
+ if let base64 = config.imageBase64,
192
+ let data = Data(base64Encoded: base64)
193
+ {
194
+ sourceImage = UIImage(data: data)
195
+ } else if let urlString = config.imageUrl {
196
+ // Check URL image cache first
197
+ if let cachedURLImage = MarkerIconCache.shared.getURLImage(forKey: urlString) {
198
+ sourceImage = cachedURLImage
199
+ } else {
200
+ // Load URL image on background thread to avoid blocking
201
+ // Return placeholder and trigger async load
202
+ loadURLImageAsync(urlString: urlString, cacheKey: cacheKey, config: config)
203
+ // Return a placeholder image for now
204
+ sourceImage = createPlaceholderImage(
205
+ width: CGFloat(config.width),
206
+ height: CGFloat(config.height)
207
+ )
208
+ }
209
+ }
210
+
211
+ let image = renderImageMarker(sourceImage: sourceImage, config: config)
212
+
213
+ // Cache the result (will be replaced when async image loads)
214
+ MarkerIconCache.shared.setIcon(image, forKey: cacheKey)
215
+
216
+ return image
217
+ }
218
+
219
+ /// Load URL image asynchronously using URLSession (proper HTTP handling)
220
+ private static func loadURLImageAsync(urlString: String, cacheKey: String, config: ImageMarkerConfig) {
221
+ guard let url = URL(string: urlString) else { return }
222
+
223
+ var request = URLRequest(url: url)
224
+ request.timeoutInterval = 10 // 10 second timeout
225
+ request.cachePolicy = .returnCacheDataElseLoad
226
+
227
+ URLSession.shared.dataTask(with: request) { data, response, error in
228
+ if let error = error {
229
+ #if DEBUG
230
+ print("[MarkerIconFactory] Failed to load image from \(urlString): \(error.localizedDescription)")
231
+ #endif
232
+ return
233
+ }
234
+
235
+ // Validate HTTP response
236
+ if let httpResponse = response as? HTTPURLResponse,
237
+ !(200...299).contains(httpResponse.statusCode) {
238
+ #if DEBUG
239
+ print("[MarkerIconFactory] HTTP \(httpResponse.statusCode) for image \(urlString)")
240
+ #endif
241
+ return
242
+ }
243
+
244
+ guard let data = data, let image = UIImage(data: data) else {
245
+ return
246
+ }
247
+
248
+ // Cache and render on main thread
249
+ DispatchQueue.main.async {
250
+ MarkerIconCache.shared.setURLImage(image, forKey: urlString)
251
+
252
+ // Render the final marker icon with the loaded image
253
+ let finalIcon = renderImageMarker(sourceImage: image, config: config)
254
+ MarkerIconCache.shared.setIcon(finalIcon, forKey: cacheKey)
255
+
256
+ // Post notification for markers to refresh their icons
257
+ NotificationCenter.default.post(
258
+ name: Notification.Name("MarkerIconLoaded"),
259
+ object: nil,
260
+ userInfo: ["cacheKey": cacheKey, "icon": finalIcon]
261
+ )
262
+ }
263
+ }.resume()
264
+ }
265
+
266
+ /// Create a placeholder image while URL image loads
267
+ private static func createPlaceholderImage(width: CGFloat, height: CGFloat) -> UIImage {
268
+ let renderer = UIGraphicsImageRenderer(size: CGSize(width: width, height: height))
269
+ return renderer.image { context in
270
+ let rect = CGRect(x: 0, y: 0, width: width, height: height)
271
+ UIColor.lightGray.withAlphaComponent(0.3).setFill()
272
+ UIBezierPath(roundedRect: rect, cornerRadius: width * 0.1).fill()
273
+ }
274
+ }
275
+
276
+ /// Render image marker with given source image
277
+ private static func renderImageMarker(sourceImage: UIImage?, config: ImageMarkerConfig) -> UIImage {
278
+ let width = CGFloat(config.width)
279
+ let height = CGFloat(config.height)
280
+ let cornerRadius = CGFloat(config.cornerRadius)
281
+ let borderWidth = CGFloat(config.borderWidth)
282
+
283
+ let borderColorValue = config.borderColor.toMarkerColor()
284
+ let borderColor = UIColor(
285
+ red: CGFloat(borderColorValue.r) / 255,
286
+ green: CGFloat(borderColorValue.g) / 255,
287
+ blue: CGFloat(borderColorValue.b) / 255,
288
+ alpha: CGFloat(borderColorValue.a) / 255
289
+ )
290
+
291
+ let renderer = UIGraphicsImageRenderer(
292
+ size: CGSize(width: width, height: height)
293
+ )
294
+ return renderer.image { context in
295
+ let rect = CGRect(x: 0, y: 0, width: width, height: height)
296
+ let path = UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius)
297
+
298
+ // Clip to rounded rect
299
+ path.addClip()
300
+
301
+ // Draw image
302
+ if let image = sourceImage {
303
+ image.draw(in: rect)
304
+ } else {
305
+ UIColor.lightGray.setFill()
306
+ UIRectFill(rect)
307
+ }
308
+
309
+ // Draw border
310
+ if borderWidth > 0 {
311
+ borderColor.setStroke()
312
+ path.lineWidth = borderWidth
313
+ path.stroke()
314
+ }
315
+ }
316
+ }
317
+
318
+ // MARK: - Cache Management
319
+ static func clearCache() {
320
+ MarkerIconCache.shared.clear()
321
+ }
322
+ }
@@ -0,0 +1,140 @@
1
+ // ios/MarkerRenderer/PriceMarkerRenderer.swift
2
+ import UIKit
3
+
4
+ class PriceMarkerRenderer {
5
+
6
+ struct Style {
7
+ var price: String
8
+ var currency: String
9
+ var selected: Bool
10
+ var backgroundColor: UIColor
11
+ var selectedBackgroundColor: UIColor
12
+ var textColor: UIColor
13
+ var selectedTextColor: UIColor
14
+ var fontSize: CGFloat
15
+ var paddingHorizontal: CGFloat
16
+ var paddingVertical: CGFloat
17
+ var shadowOpacity: Float
18
+
19
+ static func fromConfig(_ config: PriceMarkerStyle?) -> Style {
20
+ guard let config = config else {
21
+ return Style(
22
+ price: "0",
23
+ currency: "UZS",
24
+ selected: false,
25
+ backgroundColor: .white,
26
+ selectedBackgroundColor: UIColor(red: 255/255, green: 59/255, blue: 48/255, alpha: 1),
27
+ textColor: UIColor(red: 51/255, green: 51/255, blue: 51/255, alpha: 1),
28
+ selectedTextColor: .white,
29
+ fontSize: 10,
30
+ paddingHorizontal: 8,
31
+ paddingVertical: 6,
32
+ shadowOpacity: 0.12
33
+ )
34
+ }
35
+
36
+ return Style(
37
+ price: config.price,
38
+ currency: config.currency,
39
+ selected: config.selected,
40
+ backgroundColor: PriceMarkerRenderer.variantToUIColor(config.backgroundColor) ?? .white,
41
+ selectedBackgroundColor: PriceMarkerRenderer.variantToUIColor(config.selectedBackgroundColor)
42
+ ?? UIColor(red: 255/255, green: 59/255, blue: 48/255, alpha: 1),
43
+ textColor: PriceMarkerRenderer.variantToUIColor(config.textColor)
44
+ ?? UIColor(red: 51/255, green: 51/255, blue: 51/255, alpha: 1),
45
+ selectedTextColor: PriceMarkerRenderer.variantToUIColor(config.selectedTextColor) ?? .white,
46
+ fontSize: CGFloat(config.fontSize ?? 10),
47
+ paddingHorizontal: CGFloat(config.paddingHorizontal ?? 8),
48
+ paddingVertical: CGFloat(config.paddingVertical ?? 6),
49
+ shadowOpacity: Float(config.shadowOpacity ?? 0.12)
50
+ )
51
+ }
52
+ }
53
+
54
+ /// Converts a Variant_String_MarkerColor (Nitro generated type) to UIColor.
55
+ private static func variantToUIColor(_ value: Variant_String_MarkerColor?) -> UIColor? {
56
+ guard let value = value else { return nil }
57
+ let mc: MarkerColor
58
+ switch value {
59
+ case .first(let hex):
60
+ mc = ColorValue.first(hex).toMarkerColor()
61
+ case .second(let markerColor):
62
+ mc = markerColor
63
+ }
64
+ return UIColor(
65
+ red: CGFloat(mc.r) / 255,
66
+ green: CGFloat(mc.g) / 255,
67
+ blue: CGFloat(mc.b) / 255,
68
+ alpha: CGFloat(mc.a) / 255
69
+ )
70
+ }
71
+
72
+ static func render(config: PriceMarkerStyle?) -> UIImage {
73
+ let style = Style.fromConfig(config)
74
+
75
+ // Build text: "9M UZS"
76
+ let text = "\(style.price) \(style.currency)"
77
+
78
+ // Font
79
+ let font = UIFont.systemFont(ofSize: style.fontSize, weight: .light)
80
+ let attributes: [NSAttributedString.Key: Any] = [.font: font]
81
+ let textSize = text.size(withAttributes: attributes)
82
+
83
+ // Calculate size with padding
84
+ let width = textSize.width + style.paddingHorizontal * 2
85
+ let height = textSize.height + style.paddingVertical * 2
86
+
87
+ // Reduced shadow size for smaller textures (was 20, now 8)
88
+ let shadowBlur: CGFloat = 8
89
+ let shadowOffset: CGFloat = 2
90
+ let canvasWidth = width + shadowBlur * 2
91
+ let canvasHeight = height + shadowBlur + shadowOffset + shadowBlur / 2
92
+
93
+ let renderer = UIGraphicsImageRenderer(size: CGSize(width: canvasWidth, height: canvasHeight))
94
+
95
+ return renderer.image { context in
96
+ let ctx = context.cgContext
97
+
98
+ // Pill rect (centered with shadow space)
99
+ let pillRect = CGRect(
100
+ x: shadowBlur,
101
+ y: shadowBlur / 2,
102
+ width: width,
103
+ height: height
104
+ )
105
+
106
+ // Corner radius = half height for pill shape
107
+ let cornerRadius = height / 2
108
+ let path = UIBezierPath(roundedRect: pillRect, cornerRadius: cornerRadius)
109
+
110
+ // Shadow
111
+ ctx.saveGState()
112
+ ctx.setShadow(
113
+ offset: CGSize(width: 0, height: shadowOffset),
114
+ blur: shadowBlur,
115
+ color: UIColor.black.withAlphaComponent(CGFloat(style.shadowOpacity)).cgColor
116
+ )
117
+
118
+ // Background
119
+ let bgColor = style.selected ? style.selectedBackgroundColor : style.backgroundColor
120
+ bgColor.setFill()
121
+ path.fill()
122
+
123
+ ctx.restoreGState()
124
+
125
+ // Text
126
+ let textColor = style.selected ? style.selectedTextColor : style.textColor
127
+ let textRect = CGRect(
128
+ x: pillRect.origin.x + style.paddingHorizontal,
129
+ y: pillRect.origin.y + style.paddingVertical,
130
+ width: textSize.width,
131
+ height: textSize.height
132
+ )
133
+
134
+ text.draw(in: textRect, withAttributes: [
135
+ .font: font,
136
+ .foregroundColor: textColor
137
+ ])
138
+ }
139
+ }
140
+ }