@gemx-dev/heatmap-react 3.5.44 → 3.5.46

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 (303) hide show
  1. package/dist/esm/components/Layout/ContentHeader.d.ts +4 -0
  2. package/dist/esm/components/Layout/ContentHeader.d.ts.map +1 -0
  3. package/dist/esm/components/Layout/VizMode.d.ts +2 -0
  4. package/dist/esm/components/Layout/VizMode.d.ts.map +1 -0
  5. package/dist/esm/components/Test.d.ts +121 -0
  6. package/dist/esm/components/Test.d.ts.map +1 -0
  7. package/dist/esm/components/VizDom/VizDomContainer.d.ts +6 -0
  8. package/dist/esm/components/VizDom/VizDomContainer.d.ts.map +1 -0
  9. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  10. package/dist/esm/components/VizElement/ClickedElementOverlay.d.ts +17 -0
  11. package/dist/esm/components/VizElement/ClickedElementOverlay.d.ts.map +1 -0
  12. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
  13. package/dist/esm/components/VizElement/HoveredElementOverlay.d.ts +12 -0
  14. package/dist/esm/components/VizElement/HoveredElementOverlay.d.ts.map +1 -0
  15. package/dist/esm/components/VizElement/MissingElementMessage.d.ts +7 -0
  16. package/dist/esm/components/VizElement/MissingElementMessage.d.ts.map +1 -0
  17. package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
  18. package/dist/esm/components/VizElement/temp/ClarityVisualizer.d.ts +150 -0
  19. package/dist/esm/components/VizElement/temp/ClarityVisualizer.d.ts.map +1 -0
  20. package/dist/esm/components/VizElement/temp/VizElementRank.d.ts +74 -0
  21. package/dist/esm/components/VizElement/temp/VizElementRank.d.ts.map +1 -0
  22. package/dist/esm/components/VizLive/VizLive.d.ts +2 -0
  23. package/dist/esm/components/VizLive/VizLive.d.ts.map +1 -0
  24. package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  25. package/dist/esm/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  26. package/dist/esm/configs/style.d.ts +0 -2
  27. package/dist/esm/configs/style.d.ts.map +1 -1
  28. package/dist/esm/helpers/index.d.ts +2 -1
  29. package/dist/esm/helpers/index.d.ts.map +1 -1
  30. package/dist/esm/helpers/viewport-fixer.d.ts +13 -0
  31. package/dist/esm/helpers/viewport-fixer.d.ts.map +1 -0
  32. package/dist/esm/helpers/viewport-replacer.d.ts +26 -0
  33. package/dist/esm/helpers/viewport-replacer.d.ts.map +1 -0
  34. package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
  35. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts +1 -3
  36. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  37. package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +1 -1
  38. package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -1
  39. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts +1 -3
  40. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
  41. package/dist/esm/hooks/viz-live/index.d.ts +1 -1
  42. package/dist/esm/hooks/viz-live/{useVizLiveIframeMsg.d.ts → useIframeMessage.d.ts} +10 -2
  43. package/dist/esm/hooks/viz-live/useIframeMessage.d.ts.map +1 -0
  44. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  45. package/dist/esm/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -1
  46. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
  47. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  48. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts +10 -0
  49. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts.map +1 -0
  50. package/dist/esm/index.d.ts +1 -1
  51. package/dist/esm/index.d.ts.map +1 -1
  52. package/dist/esm/index.js +203 -910
  53. package/dist/esm/index.mjs +203 -910
  54. package/dist/esm/stores/index.d.ts +0 -1
  55. package/dist/esm/stores/index.d.ts.map +1 -1
  56. package/dist/esm/stores/interaction.d.ts.map +1 -1
  57. package/dist/esm/stores/mode-live.d.ts +0 -4
  58. package/dist/esm/stores/mode-live.d.ts.map +1 -1
  59. package/dist/esm/stores/viz.d.ts +4 -0
  60. package/dist/esm/stores/viz.d.ts.map +1 -1
  61. package/dist/esm/types/index.d.ts +1 -1
  62. package/dist/esm/types/index.d.ts.map +1 -1
  63. package/dist/esm/types/viewport-fixer.d.ts +31 -0
  64. package/dist/esm/types/viewport-fixer.d.ts.map +1 -0
  65. package/dist/umd/components/Layout/ContentHeader.d.ts +4 -0
  66. package/dist/umd/components/Layout/ContentHeader.d.ts.map +1 -0
  67. package/dist/umd/components/Test.d.ts +121 -0
  68. package/dist/umd/components/Test.d.ts.map +1 -0
  69. package/dist/umd/components/VizDom/VizDomContainer.d.ts +2 -0
  70. package/dist/umd/components/VizDom/VizDomContainer.d.ts.map +1 -0
  71. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  72. package/dist/umd/components/VizElement/ClickedElementOverlay.d.ts +17 -0
  73. package/dist/umd/components/VizElement/ClickedElementOverlay.d.ts.map +1 -0
  74. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
  75. package/dist/umd/components/VizElement/HoveredElementOverlay.d.ts +12 -0
  76. package/dist/umd/components/VizElement/HoveredElementOverlay.d.ts.map +1 -0
  77. package/dist/umd/components/VizElement/MissingElementMessage.d.ts +7 -0
  78. package/dist/umd/components/VizElement/MissingElementMessage.d.ts.map +1 -0
  79. package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
  80. package/dist/umd/components/VizElement/temp/ClarityVisualizer.d.ts +150 -0
  81. package/dist/umd/components/VizElement/temp/ClarityVisualizer.d.ts.map +1 -0
  82. package/dist/umd/components/VizElement/temp/VizElementRank.d.ts +74 -0
  83. package/dist/umd/components/VizElement/temp/VizElementRank.d.ts.map +1 -0
  84. package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  85. package/dist/umd/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  86. package/dist/umd/configs/style.d.ts +0 -2
  87. package/dist/umd/configs/style.d.ts.map +1 -1
  88. package/dist/umd/helpers/index.d.ts +2 -1
  89. package/dist/umd/helpers/index.d.ts.map +1 -1
  90. package/dist/umd/helpers/viewport-fixer.d.ts +13 -0
  91. package/dist/umd/helpers/viewport-fixer.d.ts.map +1 -0
  92. package/dist/umd/helpers/viewport-replacer.d.ts +26 -0
  93. package/dist/umd/helpers/viewport-replacer.d.ts.map +1 -0
  94. package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
  95. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts +1 -3
  96. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  97. package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +1 -1
  98. package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -1
  99. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts +1 -3
  100. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
  101. package/dist/umd/hooks/viz-live/index.d.ts +1 -1
  102. package/dist/umd/hooks/viz-live/{useVizLiveIframeMsg.d.ts → useIframeMessage.d.ts} +10 -2
  103. package/dist/umd/hooks/viz-live/useIframeMessage.d.ts.map +1 -0
  104. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  105. package/dist/umd/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -1
  106. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
  107. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  108. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts +10 -0
  109. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts.map +1 -0
  110. package/dist/umd/index.d.ts +1 -1
  111. package/dist/umd/index.d.ts.map +1 -1
  112. package/dist/umd/index.js +2 -2
  113. package/dist/umd/stores/index.d.ts +0 -1
  114. package/dist/umd/stores/index.d.ts.map +1 -1
  115. package/dist/umd/stores/interaction.d.ts.map +1 -1
  116. package/dist/umd/stores/mode-live.d.ts +0 -4
  117. package/dist/umd/stores/mode-live.d.ts.map +1 -1
  118. package/dist/umd/stores/viz.d.ts +4 -0
  119. package/dist/umd/stores/viz.d.ts.map +1 -1
  120. package/dist/umd/types/index.d.ts +1 -1
  121. package/dist/umd/types/index.d.ts.map +1 -1
  122. package/dist/umd/types/viewport-fixer.d.ts +31 -0
  123. package/dist/umd/types/viewport-fixer.d.ts.map +1 -0
  124. package/package.json +13 -15
  125. package/src/components/GraphView.tsx +58 -0
  126. package/src/components/Layout/ContentMetricBar.tsx +23 -0
  127. package/src/components/Layout/ContentToolbar.tsx +22 -0
  128. package/src/components/Layout/ContentTopBar.tsx +24 -0
  129. package/src/components/Layout/ContentVizByMode.tsx +14 -0
  130. package/src/components/Layout/HeatmapLayout.tsx +60 -0
  131. package/src/components/Layout/LeftSidebar.tsx +44 -0
  132. package/src/components/Layout/WrapperLayout.tsx +12 -0
  133. package/src/components/Layout/WrapperPreview.tsx +24 -0
  134. package/src/components/Layout/index.ts +1 -0
  135. package/src/components/VizDom/ReplayControls.tsx +48 -0
  136. package/src/components/VizDom/VizContainer.tsx +40 -0
  137. package/src/components/VizDom/VizDomHeatmap.tsx +28 -0
  138. package/src/components/VizDom/VizDomRenderer.tsx +82 -0
  139. package/src/components/VizDom/VizLoading.tsx +8 -0
  140. package/src/components/VizDom/WrapperVisual.tsx +73 -0
  141. package/src/components/VizDom/index.ts +5 -0
  142. package/src/components/VizElement/DefaultRankBadges.tsx +36 -0
  143. package/src/components/VizElement/ElementCallout.tsx +82 -0
  144. package/src/components/VizElement/ElementMissing.tsx +35 -0
  145. package/src/components/VizElement/ElementOverlay.tsx +66 -0
  146. package/src/components/VizElement/HeatmapElements.tsx +127 -0
  147. package/src/components/VizElement/HeatmapExample.tsx +70 -0
  148. package/src/components/VizElement/RankBadge.tsx +25 -0
  149. package/src/components/VizElement/VizElements.tsx +57 -0
  150. package/src/components/VizElement/index.ts +1 -0
  151. package/src/components/VizLive/VizLiveHeatmap.tsx +27 -0
  152. package/src/components/VizLive/VizLiveRenderer.tsx +47 -0
  153. package/src/components/VizLive/index.ts +1 -0
  154. package/src/components/VizScrollmap/AverageFoldLine.tsx +57 -0
  155. package/src/components/VizScrollmap/HoverZones.tsx +58 -0
  156. package/src/components/VizScrollmap/MetricRow.tsx +0 -0
  157. package/src/components/VizScrollmap/ScrollMapMinimap.tsx +64 -0
  158. package/src/components/VizScrollmap/ScrollMapOverlay.tsx +79 -0
  159. package/src/components/VizScrollmap/ScrollZoneHoverArea.tsx +35 -0
  160. package/src/components/VizScrollmap/ScrollZoneTooltip.tsx +146 -0
  161. package/src/components/VizScrollmap/ScrollmapMarker.tsx +106 -0
  162. package/src/components/VizScrollmap/VizScrollMap.tsx +36 -0
  163. package/src/components/VizScrollmap/index.ts +1 -0
  164. package/src/components/VizScrollmapV2/ScrollmapOverlay.css +94 -0
  165. package/src/components/VizScrollmapV2/ScrollmapOverlayV2.tsx +130 -0
  166. package/src/components/VizScrollmapV2/index.ts +1 -0
  167. package/src/components/VizScrollmapV2/scrollmap.types.ts +21 -0
  168. package/src/components/VizScrollmapV2/useScrollmapOverlay.ts +187 -0
  169. package/src/components/index.tsx +2 -0
  170. package/src/configs/iframe.ts +15 -0
  171. package/src/configs/index.ts +2 -0
  172. package/src/configs/style.ts +21 -0
  173. package/src/constants/index.ts +4 -0
  174. package/src/global.d.ts +5 -0
  175. package/src/helpers/elm-callout.ts +347 -0
  176. package/src/helpers/elm-getter.ts +70 -0
  177. package/src/helpers/iframe-helper/fixer.ts +100 -0
  178. package/src/helpers/iframe-helper/index.ts +1 -0
  179. package/src/helpers/iframe-helper/init.ts +56 -0
  180. package/src/helpers/iframe-helper/navigation-blocker-v2.ts +371 -0
  181. package/src/helpers/iframe-helper/navigation-blocker.ts +367 -0
  182. package/src/helpers/iframe-helper/style-replacer.ts +231 -0
  183. package/src/helpers/iframe.ts +42 -0
  184. package/src/helpers/index.ts +8 -0
  185. package/src/helpers/viz-canvas/area-clustering.ts +234 -0
  186. package/src/helpers/viz-canvas/area-overlay-manager-v2.ts +176 -0
  187. package/src/helpers/viz-canvas/area-overlay-manager.ts +273 -0
  188. package/src/helpers/viz-canvas/hierarchical-area-clustering.ts +420 -0
  189. package/src/helpers/viz-canvas/index.ts +2 -0
  190. package/src/helpers/viz-elements.ts +43 -0
  191. package/src/hooks/index.ts +8 -0
  192. package/src/hooks/register/index.ts +4 -0
  193. package/src/hooks/register/useRegisterConfig.ts +17 -0
  194. package/src/hooks/register/useRegisterControl.ts +13 -0
  195. package/src/hooks/register/useRegisterData.ts +36 -0
  196. package/src/hooks/register/useRegisterHeatmap.ts +38 -0
  197. package/src/hooks/viz-area/useAreaHeatmap.ts +336 -0
  198. package/src/hooks/viz-area/useAreaHeatmapManager.ts +692 -0
  199. package/src/hooks/viz-canvas/index.ts +1 -0
  200. package/src/hooks/viz-canvas/useAreamap.ts +162 -0
  201. package/src/hooks/viz-canvas/useClickmap.ts +24 -0
  202. package/src/hooks/viz-canvas/useHeatmapCanvas.ts +27 -0
  203. package/src/hooks/viz-canvas/useScrollmap.ts +22 -0
  204. package/src/hooks/viz-elements/index.ts +5 -0
  205. package/src/hooks/viz-elements/useClickedElement.ts +86 -0
  206. package/src/hooks/viz-elements/useElementCalloutVisible.ts +45 -0
  207. package/src/hooks/viz-elements/useHeatmapEffects.ts +30 -0
  208. package/src/hooks/viz-elements/useHeatmapElementPosition.ts +60 -0
  209. package/src/hooks/viz-elements/useHeatmapMouseHandler.ts +255 -0
  210. package/src/hooks/viz-elements/useHoveredElement.ts +170 -0
  211. package/src/hooks/viz-live/index.ts +1 -0
  212. package/src/hooks/viz-live/useVizLiveIframeMsg.ts +88 -0
  213. package/src/hooks/viz-live/useVizLiveRender.ts +67 -0
  214. package/src/hooks/viz-render/index.ts +1 -0
  215. package/src/hooks/viz-render/useHeatmapRender.ts +71 -0
  216. package/src/hooks/viz-render/useHeatmapVizRender.ts +20 -0
  217. package/src/hooks/viz-render/useReplayRender.ts +160 -0
  218. package/src/hooks/viz-scale/index.ts +2 -0
  219. package/src/hooks/viz-scale/useContainerDimensions.ts +48 -0
  220. package/src/hooks/viz-scale/useContentDimensions.ts +25 -0
  221. package/src/hooks/viz-scale/useHeatmapScale.ts +52 -0
  222. package/src/hooks/viz-scale/useObserveIframeHeight.ts +162 -0
  223. package/src/hooks/viz-scale/useScaleCalculation.ts +31 -0
  224. package/src/hooks/viz-scale/useScrollSync.ts +36 -0
  225. package/src/hooks/viz-scale/useWrapperRefHeight.ts +91 -0
  226. package/src/hooks/viz-scrollmap/index.ts +2 -0
  227. package/src/hooks/viz-scrollmap/useScrollmapZones.ts +165 -0
  228. package/src/hooks/viz-scrollmap/useZonePositions.ts +38 -0
  229. package/src/index.ts +10 -0
  230. package/src/stores/comp.ts +31 -0
  231. package/src/stores/config.ts +37 -0
  232. package/src/stores/data.ts +30 -0
  233. package/src/stores/index.ts +10 -0
  234. package/src/stores/interaction.ts +32 -0
  235. package/src/stores/mode-live.ts +38 -0
  236. package/src/stores/mode-single.ts +18 -0
  237. package/src/stores/viz-scrollmap.ts +22 -0
  238. package/src/stores/viz.ts +17 -0
  239. package/src/styles/base.css +1 -0
  240. package/src/styles/style.css +137 -0
  241. package/src/types/clarity.ts +45 -0
  242. package/src/types/control.ts +10 -0
  243. package/src/types/elm-callout.ts +9 -0
  244. package/src/types/heatmap-info.ts +11 -0
  245. package/src/types/heatmap.ts +25 -0
  246. package/src/types/iframe-helper.ts +18 -0
  247. package/src/types/index.ts +12 -0
  248. package/src/types/viz-canvas.ts +20 -0
  249. package/src/types/viz-element.ts +34 -0
  250. package/src/types/viz-scrollmap.ts +28 -0
  251. package/src/ui/BoxStack/BoxStack.tsx +136 -0
  252. package/src/ui/BoxStack/index.ts +1 -0
  253. package/src/ui/index.ts +1 -0
  254. package/src/utils/debounce.ts +10 -0
  255. package/src/utils/device.ts +7 -0
  256. package/src/utils/retry.ts +20 -0
  257. package/src/utils/sort.ts +5 -0
  258. package/dist/esm/helpers/iframe-helper/fixer.d.ts +0 -18
  259. package/dist/esm/helpers/iframe-helper/fixer.d.ts.map +0 -1
  260. package/dist/esm/helpers/iframe-helper/index.d.ts +0 -2
  261. package/dist/esm/helpers/iframe-helper/index.d.ts.map +0 -1
  262. package/dist/esm/helpers/iframe-helper/init.d.ts +0 -5
  263. package/dist/esm/helpers/iframe-helper/init.d.ts.map +0 -1
  264. package/dist/esm/helpers/iframe-helper/navigation-blocker-v2.d.ts +0 -28
  265. package/dist/esm/helpers/iframe-helper/navigation-blocker-v2.d.ts.map +0 -1
  266. package/dist/esm/helpers/iframe-helper/navigation-blocker.d.ts +0 -20
  267. package/dist/esm/helpers/iframe-helper/navigation-blocker.d.ts.map +0 -1
  268. package/dist/esm/helpers/iframe-helper/style-replacer.d.ts +0 -25
  269. package/dist/esm/helpers/iframe-helper/style-replacer.d.ts.map +0 -1
  270. package/dist/esm/hooks/vix-elements/useHeatmapMouseHandler.d.ts +0 -34
  271. package/dist/esm/hooks/vix-elements/useHeatmapMouseHandler.d.ts.map +0 -1
  272. package/dist/esm/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +0 -1
  273. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts +0 -4
  274. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +0 -1
  275. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts +0 -10
  276. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts.map +0 -1
  277. package/dist/esm/stores/mode-single.d.ts +0 -9
  278. package/dist/esm/stores/mode-single.d.ts.map +0 -1
  279. package/dist/esm/types/iframe-helper.d.ts +0 -20
  280. package/dist/esm/types/iframe-helper.d.ts.map +0 -1
  281. package/dist/umd/helpers/iframe-helper/fixer.d.ts +0 -18
  282. package/dist/umd/helpers/iframe-helper/fixer.d.ts.map +0 -1
  283. package/dist/umd/helpers/iframe-helper/index.d.ts +0 -2
  284. package/dist/umd/helpers/iframe-helper/index.d.ts.map +0 -1
  285. package/dist/umd/helpers/iframe-helper/init.d.ts +0 -5
  286. package/dist/umd/helpers/iframe-helper/init.d.ts.map +0 -1
  287. package/dist/umd/helpers/iframe-helper/navigation-blocker-v2.d.ts +0 -28
  288. package/dist/umd/helpers/iframe-helper/navigation-blocker-v2.d.ts.map +0 -1
  289. package/dist/umd/helpers/iframe-helper/navigation-blocker.d.ts +0 -20
  290. package/dist/umd/helpers/iframe-helper/navigation-blocker.d.ts.map +0 -1
  291. package/dist/umd/helpers/iframe-helper/style-replacer.d.ts +0 -25
  292. package/dist/umd/helpers/iframe-helper/style-replacer.d.ts.map +0 -1
  293. package/dist/umd/hooks/vix-elements/useHeatmapMouseHandler.d.ts +0 -34
  294. package/dist/umd/hooks/vix-elements/useHeatmapMouseHandler.d.ts.map +0 -1
  295. package/dist/umd/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +0 -1
  296. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts +0 -4
  297. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +0 -1
  298. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts +0 -10
  299. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts.map +0 -1
  300. package/dist/umd/stores/mode-single.d.ts +0 -9
  301. package/dist/umd/stores/mode-single.d.ts.map +0 -1
  302. package/dist/umd/types/iframe-helper.d.ts +0 -20
  303. package/dist/umd/types/iframe-helper.d.ts.map +0 -1
@@ -0,0 +1,234 @@
1
+ import { ClickMapPoint, IClusterArea, IClusterAreaRect } from '../../types';
2
+
3
+ interface ClickElement {
4
+ hash: string;
5
+ element: HTMLElement;
6
+ rect: IClusterAreaRect;
7
+ clicks: number;
8
+ }
9
+
10
+ const HEATMAP_GRADIENT_COLORS: [number, number, number][] = [
11
+ [0, 0, 255], // Blue (cold)
12
+ [0, 255, 255], // Cyan
13
+ [0, 255, 0], // Green
14
+ [255, 255, 0], // Yellow
15
+ [255, 0, 0], // Red (hot)
16
+ ];
17
+
18
+ const GRADIENT_COLOR_COUNT = HEATMAP_GRADIENT_COLORS.length - 1;
19
+
20
+ // Configuration
21
+ const CLUSTERING_CONFIG = {
22
+ maxDistance: 1, // Khoảng cách tối đa để gom nhóm (px)
23
+ minElements: 1, // Số elements tối thiểu trong 1 cluster
24
+ mergePadding: 0, // Padding khi merge các rects
25
+ };
26
+
27
+ export class AreaClusteringEngine {
28
+ /**
29
+ * Cluster click elements thành các vùng
30
+ */
31
+ public clusterAreas(
32
+ clickData: ClickMapPoint[],
33
+ iframeDoc: Document,
34
+ totalClicks: number,
35
+ ): IClusterArea[] {
36
+ // 1. Get all click elements với rect
37
+ const clickElements = this.getClickElements(clickData, iframeDoc);
38
+ if (clickElements.length === 0) return [];
39
+
40
+ // 2. Clustering based on proximity
41
+ const clusters = this.performClustering(clickElements);
42
+
43
+ // 3. Create area objects từ clusters
44
+ const areas = this.createAreasFromClusters(clusters, totalClicks);
45
+
46
+ // 4. Sort by click percentage (descending)
47
+ areas.sort((a, b) => b.clickPercentage - a.clickPercentage);
48
+
49
+ console.log(
50
+ `[AreaClustering] Created ${areas.length} areas from ${clickElements.length} elements`,
51
+ );
52
+
53
+ return areas;
54
+ }
55
+
56
+ /**
57
+ * Get click elements with their rects
58
+ */
59
+ private getClickElements(clickData: ClickMapPoint[], iframeDoc: Document): ClickElement[] {
60
+ const elements: ClickElement[] = [];
61
+
62
+ clickData.forEach((item) => {
63
+ const element = iframeDoc.querySelector(
64
+ `[data-clarity-hashalpha="${item.hash}"]`,
65
+ ) as HTMLElement;
66
+
67
+ if (!element || item.clicks.length === 0) return;
68
+ if (element.tagName === 'BODY') return;
69
+
70
+ const rect = this.getElementRect(element);
71
+
72
+ // Lọc elements quá nhỏ hoặc không visible
73
+ if (rect.width < 10 || rect.height < 10) return;
74
+
75
+ elements.push({
76
+ hash: item.hash,
77
+ element,
78
+ rect,
79
+ clicks: item.clicks.reduce((sum, click) => sum + click, 0),
80
+ });
81
+ });
82
+
83
+ return elements;
84
+ }
85
+
86
+ /**
87
+ * Get element rect
88
+ */
89
+ private getElementRect(element: HTMLElement): IClusterAreaRect {
90
+ const domRect = element.getBoundingClientRect();
91
+ return {
92
+ top: domRect.top,
93
+ left: domRect.left,
94
+ width: domRect.width,
95
+ height: domRect.height,
96
+ right: domRect.right,
97
+ bottom: domRect.bottom,
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Perform clustering using distance-based algorithm
103
+ */
104
+ private performClustering(clickElements: ClickElement[]): ClickElement[][] {
105
+ const clusters: ClickElement[][] = [];
106
+ const used = new Set<number>();
107
+
108
+ // Sort by clicks descending để ưu tiên elements có nhiều clicks
109
+ const sorted = [...clickElements].sort((a, b) => b.clicks - a.clicks);
110
+
111
+ sorted.forEach((element, index) => {
112
+ if (used.has(index)) return;
113
+
114
+ // Tạo cluster mới với element này làm seed
115
+ const cluster: ClickElement[] = [element];
116
+ used.add(index);
117
+
118
+ // Tìm các elements gần với cluster này
119
+ sorted.forEach((other, otherIndex) => {
120
+ if (used.has(otherIndex)) return;
121
+
122
+ // Check khoảng cách với bất kỳ element nào trong cluster
123
+ const isNear = cluster.some((clusterEl) => {
124
+ return this.getDistance(clusterEl.rect, other.rect) <= CLUSTERING_CONFIG.maxDistance;
125
+ });
126
+
127
+ if (isNear) {
128
+ cluster.push(other);
129
+ used.add(otherIndex);
130
+ }
131
+ });
132
+
133
+ // Chỉ thêm cluster nếu đủ elements
134
+ if (cluster.length >= CLUSTERING_CONFIG.minElements) {
135
+ clusters.push(cluster);
136
+ }
137
+ });
138
+
139
+ return clusters;
140
+ }
141
+
142
+ /**
143
+ * Calculate distance between two rects (edge-to-edge)
144
+ */
145
+ private getDistance(rect1: IClusterAreaRect, rect2: IClusterAreaRect): number {
146
+ // Tính khoảng cách edge-to-edge thay vì center-to-center
147
+ const horizontalDistance = Math.max(0, rect1.left - rect2.right, rect2.left - rect1.right);
148
+
149
+ const verticalDistance = Math.max(0, rect1.top - rect2.bottom, rect2.top - rect1.bottom);
150
+
151
+ // Euclidean distance
152
+ return Math.sqrt(horizontalDistance ** 2 + verticalDistance ** 2);
153
+ }
154
+
155
+ /**
156
+ * Create area objects from clusters
157
+ */
158
+ private createAreasFromClusters(clusters: ClickElement[][], totalClicks: number): IClusterArea[] {
159
+ return clusters.map((cluster, index) => {
160
+ const totalClusterClicks = cluster.reduce((sum, el) => sum + el.clicks, 0);
161
+ const clickPercentage = (totalClusterClicks / totalClicks) * 100;
162
+
163
+ // Calculate bounding rect cho cluster
164
+ const boundingRect = this.calculateBoundingRect(cluster.map((c) => c.rect));
165
+
166
+ // Calculate centroid
167
+ const centroid = this.calculateCentroid(cluster.map((c) => c.rect));
168
+
169
+ return {
170
+ id: `area_${index}`,
171
+ elements: cluster.map((c) => c.element),
172
+ hashes: cluster.map((c) => c.hash),
173
+ boundingRect,
174
+ totalClicks: totalClusterClicks,
175
+ clickPercentage,
176
+ color: this.getHeatmapColor(clickPercentage / 100),
177
+ hoverColor: this.getHoverColor(clickPercentage / 100),
178
+ centroid,
179
+ };
180
+ });
181
+ }
182
+
183
+ /**
184
+ * Calculate bounding rect for multiple rects
185
+ */
186
+ private calculateBoundingRect(rects: IClusterAreaRect[]): IClusterAreaRect {
187
+ const padding = CLUSTERING_CONFIG.mergePadding;
188
+
189
+ const minLeft = Math.min(...rects.map((r) => r.left)) - padding;
190
+ const minTop = Math.min(...rects.map((r) => r.top)) - padding;
191
+ const maxRight = Math.max(...rects.map((r) => r.right)) + padding;
192
+ const maxBottom = Math.max(...rects.map((r) => r.bottom)) + padding;
193
+
194
+ return {
195
+ left: minLeft,
196
+ top: minTop,
197
+ width: maxRight - minLeft,
198
+ height: maxBottom - minTop,
199
+ right: maxRight,
200
+ bottom: maxBottom,
201
+ };
202
+ }
203
+
204
+ /**
205
+ * Calculate centroid (center point) of cluster
206
+ */
207
+ private calculateCentroid(rects: IClusterAreaRect[]): { x: number; y: number } {
208
+ const sumX = rects.reduce((sum, r) => sum + (r.left + r.width / 2), 0);
209
+ const sumY = rects.reduce((sum, r) => sum + (r.top + r.height / 2), 0);
210
+
211
+ return {
212
+ x: sumX / rects.length,
213
+ y: sumY / rects.length,
214
+ };
215
+ }
216
+
217
+ /**
218
+ * Get heatmap color based on percentage (0-1)
219
+ */
220
+ private getHeatmapColor(percentage: number): string {
221
+ const index = Math.min(Math.floor(percentage * GRADIENT_COLOR_COUNT), GRADIENT_COLOR_COUNT - 1);
222
+ const [r, g, b] = HEATMAP_GRADIENT_COLORS[index];
223
+ return `rgba(${r}, ${g}, ${b}, 0.5)`;
224
+ }
225
+
226
+ /**
227
+ * Get hover color
228
+ */
229
+ private getHoverColor(percentage: number): string {
230
+ const index = Math.min(Math.floor(percentage * GRADIENT_COLOR_COUNT), GRADIENT_COLOR_COUNT - 1);
231
+ const [r, g, b] = HEATMAP_GRADIENT_COLORS[index];
232
+ return `rgba(${r}, ${g}, ${b}, 0.8)`;
233
+ }
234
+ }
@@ -0,0 +1,176 @@
1
+ import { AreaMapData } from './area-overlay-manager';
2
+ import {
3
+ HierarchicalAreaClusteringEngine,
4
+ HierarchicalCluster,
5
+ } from './hierarchical-area-clustering';
6
+
7
+ const MIN_LABEL_WIDTH = 67;
8
+ const MIN_LABEL_HEIGHT = 30;
9
+
10
+ export class AreaOverlayManagerV2 {
11
+ private iframe: HTMLIFrameElement;
12
+ private overlayContainer: HTMLDivElement | null = null;
13
+ private areas = new Map<string, HierarchicalCluster>();
14
+ private clusteringEngine = new HierarchicalAreaClusteringEngine();
15
+ private overlayElements = new Map<string, HTMLDivElement>();
16
+
17
+ constructor(iframe: HTMLIFrameElement) {
18
+ this.iframe = iframe;
19
+ }
20
+
21
+ public init(): void {
22
+ if (this.overlayContainer) {
23
+ this.clear();
24
+ }
25
+
26
+ const container = document.createElement('div');
27
+ container.id = 'area-overlay-container';
28
+
29
+ Object.assign(container.style, {
30
+ position: 'absolute',
31
+ top: '0',
32
+ left: '0',
33
+ width: '100%',
34
+ height: '100%',
35
+ pointerEvents: 'none',
36
+ zIndex: '9998',
37
+ overflow: 'hidden',
38
+ });
39
+
40
+ const iframeStyle = window.getComputedStyle(this.iframe);
41
+ // if (iframeStyle.position === 'static') {
42
+ // this.iframe.style.position = 'relative';
43
+ // }
44
+
45
+ this.iframe.parentElement?.insertBefore(container, this.iframe.nextSibling);
46
+ this.overlayContainer = container;
47
+
48
+ console.log('[AreaOverlayManager] Initialized');
49
+ }
50
+
51
+ public render(data: AreaMapData): void {
52
+ if (!this.overlayContainer || !this.iframe.contentDocument) {
53
+ console.warn('[AreaOverlayManager] Not ready');
54
+ return;
55
+ }
56
+
57
+ this.clear();
58
+
59
+ // Use hierarchical clustering
60
+ const clusters = this.clusteringEngine.clusterAreas(
61
+ data.clickData,
62
+ this.iframe.contentDocument,
63
+ data.totalClicks,
64
+ );
65
+ console.log(`🚀 🐥 ~ AreaOverlayManagerV2 ~ render ~ clusters:`, clusters);
66
+
67
+ // Render each cluster
68
+ clusters.forEach((cluster) => {
69
+ this.createOverlay(cluster);
70
+ this.areas.set(cluster.id, cluster);
71
+ });
72
+
73
+ console.log(`[AreaOverlayManager] Rendered ${clusters.length} hierarchical clusters`);
74
+ }
75
+
76
+ private createOverlay(cluster: HierarchicalCluster): void {
77
+ if (!this.overlayContainer) return;
78
+
79
+ const overlay = document.createElement('div');
80
+ const rect = cluster.boundingRect;
81
+
82
+ Object.assign(overlay.style, {
83
+ position: 'absolute',
84
+ top: `${rect.top}px`,
85
+ left: `${rect.left}px`,
86
+ width: `${rect.width}px`,
87
+ height: `${rect.height}px`,
88
+ backgroundColor: cluster.color,
89
+ boxShadow: '0 0 0 2px white inset',
90
+ display: 'flex',
91
+ alignItems: 'center',
92
+ justifyContent: 'center',
93
+ pointerEvents: 'auto',
94
+ cursor: 'pointer',
95
+ transition: 'background-color 0.2s ease, box-shadow 0.2s ease',
96
+ zIndex: '9999',
97
+ boxSizing: 'border-box',
98
+ borderRadius: '4px',
99
+ });
100
+
101
+ overlay.setAttribute('data-area-id', cluster.id);
102
+ overlay.setAttribute('data-area-level', cluster.level.toString());
103
+ overlay.setAttribute('data-area-clicks', cluster.totalClicks.toString());
104
+
105
+ if (rect.width >= MIN_LABEL_WIDTH && rect.height >= MIN_LABEL_HEIGHT) {
106
+ const label = document.createElement('div');
107
+ label.textContent = `${cluster.clickPercentage.toFixed(2)}%`;
108
+
109
+ Object.assign(label.style, {
110
+ color: '#161514',
111
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
112
+ padding: '8px 12px',
113
+ borderRadius: '4px',
114
+ fontSize: '14px',
115
+ lineHeight: '20px',
116
+ fontWeight: '600',
117
+ fontFamily: '"Segoe UI", system-ui, -apple-system, sans-serif',
118
+ pointerEvents: 'none',
119
+ userSelect: 'none',
120
+ boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
121
+ });
122
+
123
+ overlay.appendChild(label);
124
+ }
125
+
126
+ // Events
127
+ overlay.addEventListener('mouseenter', () => {
128
+ overlay.style.backgroundColor = cluster.hoverColor;
129
+ overlay.style.boxShadow = '0 0 0 1px #0078D4, 0 0 0 1px #0078D4 inset, 0 0 0 2px white inset';
130
+ });
131
+
132
+ overlay.addEventListener('mouseleave', () => {
133
+ overlay.style.backgroundColor = cluster.color;
134
+ overlay.style.boxShadow = '0 0 0 2px white inset';
135
+ });
136
+
137
+ overlay.addEventListener('click', () => {
138
+ console.log('[AreaOverlayManager] Cluster clicked:', {
139
+ id: cluster.id,
140
+ level: cluster.level,
141
+ parentHash: cluster.parentHash,
142
+ clicks: cluster.totalClicks,
143
+ elements: cluster.elements.length,
144
+ });
145
+
146
+ window.dispatchEvent(new CustomEvent('area-clicked', { detail: cluster }));
147
+
148
+ if (cluster.elements[0]) {
149
+ cluster.elements[0].scrollIntoView({ behavior: 'smooth', block: 'center' });
150
+ }
151
+ });
152
+
153
+ this.overlayContainer.appendChild(overlay);
154
+ this.overlayElements.set(cluster.id, overlay);
155
+ }
156
+
157
+ public clear(): void {
158
+ if (this.overlayContainer) {
159
+ this.overlayContainer.innerHTML = '';
160
+ }
161
+ this.areas.clear();
162
+ this.overlayElements.clear();
163
+ }
164
+
165
+ public destroy(): void {
166
+ this.clear();
167
+ if (this.overlayContainer?.parentElement) {
168
+ this.overlayContainer.parentElement.removeChild(this.overlayContainer);
169
+ }
170
+ this.overlayContainer = null;
171
+ }
172
+
173
+ public getAreas(): HierarchicalCluster[] {
174
+ return Array.from(this.areas.values());
175
+ }
176
+ }
@@ -0,0 +1,273 @@
1
+ import { ClickMapPoint, IClusterArea } from '../../types';
2
+ import { AreaClusteringEngine } from './area-clustering';
3
+
4
+ export interface AreaMapData {
5
+ clickData: ClickMapPoint[];
6
+ totalClicks: number;
7
+ }
8
+
9
+ const MIN_LABEL_WIDTH = 67;
10
+ const MIN_LABEL_HEIGHT = 30;
11
+
12
+ export class AreaOverlayManager {
13
+ private iframe: HTMLIFrameElement;
14
+ private overlayContainer: HTMLDivElement | null = null;
15
+ private areas = new Map<string, IClusterArea>();
16
+ private clusteringEngine = new AreaClusteringEngine();
17
+ private overlayElements = new Map<string, HTMLDivElement>();
18
+
19
+ constructor(iframe: HTMLIFrameElement) {
20
+ this.iframe = iframe;
21
+ }
22
+
23
+ /**
24
+ * Initialize overlay container
25
+ */
26
+ public init(): void {
27
+ if (this.overlayContainer) {
28
+ this.clear();
29
+ }
30
+
31
+ const container = document.createElement('div');
32
+ container.id = 'area-overlay-container';
33
+
34
+ Object.assign(container.style, {
35
+ position: 'absolute',
36
+ top: '0',
37
+ left: '0',
38
+ width: '100%',
39
+ height: '100%',
40
+ pointerEvents: 'none',
41
+ zIndex: '9998',
42
+ overflow: 'hidden',
43
+ });
44
+
45
+ const iframeStyle = window.getComputedStyle(this.iframe);
46
+ // if (iframeStyle.position === 'static') {
47
+ // this.iframe.style.position = 'relative';
48
+ // }
49
+
50
+ this.iframe.parentElement?.insertBefore(container, this.iframe.nextSibling);
51
+ this.overlayContainer = container;
52
+
53
+ console.log('[AreaOverlayManager] Initialized');
54
+ }
55
+
56
+ /**
57
+ * Render clustered areas
58
+ */
59
+ public render(data: AreaMapData): void {
60
+ if (!this.overlayContainer || !this.iframe.contentDocument) {
61
+ console.warn('[AreaOverlayManager] Not ready');
62
+ return;
63
+ }
64
+
65
+ this.clear();
66
+
67
+ // Cluster areas
68
+ const clusterAreas = this.clusteringEngine.clusterAreas(
69
+ data.clickData,
70
+ this.iframe.contentDocument,
71
+ data.totalClicks,
72
+ );
73
+
74
+ // Render each area
75
+ clusterAreas.forEach((area) => {
76
+ this.createOverlay(area);
77
+ this.areas.set(area.id, area);
78
+ });
79
+
80
+ console.log(`[AreaOverlayManager] Rendered ${clusterAreas.length} cluster areas`);
81
+ }
82
+
83
+ /**
84
+ * Create overlay for a cluster area
85
+ */
86
+ private createOverlay(area: IClusterArea): void {
87
+ if (!this.overlayContainer) return;
88
+
89
+ const overlay = document.createElement('div');
90
+ const rect = area.boundingRect;
91
+
92
+ Object.assign(overlay.style, {
93
+ position: 'absolute',
94
+ top: `${rect.top}px`,
95
+ left: `${rect.left}px`,
96
+ width: `${rect.width}px`,
97
+ height: `${rect.height}px`,
98
+ backgroundColor: area.color,
99
+ boxShadow: '0 0 0 2px white inset',
100
+ display: 'flex',
101
+ alignItems: 'center',
102
+ justifyContent: 'center',
103
+ pointerEvents: 'auto',
104
+ cursor: 'pointer',
105
+ transition: 'background-color 0.2s ease, box-shadow 0.2s ease',
106
+ zIndex: '9999',
107
+ boxSizing: 'border-box',
108
+ borderRadius: '4px',
109
+ });
110
+
111
+ overlay.setAttribute('data-area-id', area.id);
112
+ overlay.setAttribute('data-area-clicks', area.totalClicks.toString());
113
+
114
+ // Add label
115
+ if (rect.width >= MIN_LABEL_WIDTH && rect.height >= MIN_LABEL_HEIGHT) {
116
+ const label = this.createLabel(area);
117
+ overlay.appendChild(label);
118
+ }
119
+
120
+ // Event handlers
121
+ overlay.addEventListener('mouseenter', () => {
122
+ overlay.style.backgroundColor = area.hoverColor;
123
+ overlay.style.boxShadow = '0 0 0 1px #0078D4, 0 0 0 1px #0078D4 inset, 0 0 0 2px white inset';
124
+ });
125
+
126
+ overlay.addEventListener('mouseleave', () => {
127
+ overlay.style.backgroundColor = area.color;
128
+ overlay.style.boxShadow = '0 0 0 2px white inset';
129
+ });
130
+
131
+ overlay.addEventListener('click', () => {
132
+ this.handleAreaClick(area);
133
+ });
134
+
135
+ this.overlayContainer.appendChild(overlay);
136
+ this.overlayElements.set(area.id, overlay);
137
+ }
138
+
139
+ /**
140
+ * Create label
141
+ */
142
+ private createLabel(area: IClusterArea): HTMLDivElement {
143
+ const label = document.createElement('div');
144
+
145
+ // Hiển thị % clicks
146
+ label.textContent = `${area.clickPercentage.toFixed(2)}%`;
147
+
148
+ Object.assign(label.style, {
149
+ color: '#161514',
150
+ backgroundColor: 'rgba(255, 255, 255, 0.9)',
151
+ padding: '8px 12px',
152
+ borderRadius: '4px',
153
+ fontSize: '14px',
154
+ lineHeight: '20px',
155
+ fontWeight: '600',
156
+ fontFamily: '"Segoe UI", system-ui, -apple-system, sans-serif',
157
+ pointerEvents: 'none',
158
+ userSelect: 'none',
159
+ boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
160
+ });
161
+
162
+ return label;
163
+ }
164
+
165
+ /**
166
+ * Handle area click
167
+ */
168
+ private handleAreaClick(area: IClusterArea): void {
169
+ console.log('[AreaOverlayManager] Area clicked:', {
170
+ id: area.id,
171
+ clicks: area.totalClicks,
172
+ percentage: area.clickPercentage,
173
+ elements: area.elements.length,
174
+ });
175
+
176
+ // Dispatch event
177
+ window.dispatchEvent(
178
+ new CustomEvent('area-clicked', {
179
+ detail: area,
180
+ }),
181
+ );
182
+
183
+ // Scroll to centroid
184
+ if (area.elements[0]) {
185
+ area.elements[0].scrollIntoView({
186
+ behavior: 'smooth',
187
+ block: 'center',
188
+ });
189
+ }
190
+ }
191
+
192
+ /**
193
+ * Update positions when iframe resizes
194
+ */
195
+ public updatePositions(): void {
196
+ if (!this.iframe.contentDocument) return;
197
+
198
+ this.areas.forEach((area) => {
199
+ const overlay = this.overlayElements.get(area.id);
200
+ if (!overlay) return;
201
+
202
+ // Recalculate bounding rect
203
+ const rects = area.elements.map((el) => {
204
+ const domRect = el.getBoundingClientRect();
205
+ return {
206
+ top: domRect.top,
207
+ left: domRect.left,
208
+ width: domRect.width,
209
+ height: domRect.height,
210
+ right: domRect.right,
211
+ bottom: domRect.bottom,
212
+ };
213
+ });
214
+
215
+ const newBoundingRect = this.calculateBoundingRect(rects);
216
+
217
+ Object.assign(overlay.style, {
218
+ top: `${newBoundingRect.top}px`,
219
+ left: `${newBoundingRect.left}px`,
220
+ width: `${newBoundingRect.width}px`,
221
+ height: `${newBoundingRect.height}px`,
222
+ });
223
+ });
224
+ }
225
+
226
+ private calculateBoundingRect(rects: any[]): any {
227
+ const padding = 0;
228
+ const minLeft = Math.min(...rects.map((r) => r.left)) - padding;
229
+ const minTop = Math.min(...rects.map((r) => r.top)) - padding;
230
+ const maxRight = Math.max(...rects.map((r) => r.right)) + padding;
231
+ const maxBottom = Math.max(...rects.map((r) => r.bottom)) + padding;
232
+
233
+ return {
234
+ left: minLeft,
235
+ top: minTop,
236
+ width: maxRight - minLeft,
237
+ height: maxBottom - minTop,
238
+ right: maxRight,
239
+ bottom: maxBottom,
240
+ };
241
+ }
242
+
243
+ /**
244
+ * Clear all overlays
245
+ */
246
+ public clear(): void {
247
+ if (this.overlayContainer) {
248
+ this.overlayContainer.innerHTML = '';
249
+ }
250
+ this.areas.clear();
251
+ this.overlayElements.clear();
252
+ }
253
+
254
+ /**
255
+ * Destroy
256
+ */
257
+ public destroy(): void {
258
+ this.clear();
259
+
260
+ if (this.overlayContainer?.parentElement) {
261
+ this.overlayContainer.parentElement.removeChild(this.overlayContainer);
262
+ }
263
+
264
+ this.overlayContainer = null;
265
+ }
266
+
267
+ /**
268
+ * Get all areas
269
+ */
270
+ public getAreas(): IClusterArea[] {
271
+ return Array.from(this.areas.values());
272
+ }
273
+ }