@gemx-dev/heatmap-react 3.5.51 → 3.5.52

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 (318) hide show
  1. package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
  2. package/dist/esm/components/Layout/HeatmapPreview.d.ts +2 -0
  3. package/dist/esm/components/Layout/HeatmapPreview.d.ts.map +1 -0
  4. package/dist/esm/components/Layout/MetricBar/ContentMetricBar.d.ts.map +1 -0
  5. package/dist/esm/components/Layout/MetricBar/index.d.ts +2 -0
  6. package/dist/esm/components/Layout/MetricBar/index.d.ts.map +1 -0
  7. package/dist/esm/components/Layout/Toolbar/ContentToolbar.d.ts.map +1 -0
  8. package/dist/esm/components/Layout/Toolbar/index.d.ts +2 -0
  9. package/dist/esm/components/Layout/Toolbar/index.d.ts.map +1 -0
  10. package/dist/esm/components/Layout/TopBar/ContentTopBar.d.ts.map +1 -0
  11. package/dist/esm/components/Layout/TopBar/index.d.ts +2 -0
  12. package/dist/esm/components/Layout/TopBar/index.d.ts.map +1 -0
  13. package/dist/esm/components/Layout/VizByMode/ContentVizByMode.d.ts.map +1 -0
  14. package/dist/esm/components/Layout/VizByMode/index.d.ts +2 -0
  15. package/dist/esm/components/Layout/VizByMode/index.d.ts.map +1 -0
  16. package/dist/esm/components/VizAreaClick/AreaControls.d.ts +10 -0
  17. package/dist/esm/components/VizAreaClick/AreaControls.d.ts.map +1 -0
  18. package/dist/esm/components/VizAreaClick/AreaEditHighlight.d.ts +8 -0
  19. package/dist/esm/components/VizAreaClick/AreaEditHighlight.d.ts.map +1 -0
  20. package/dist/esm/components/VizAreaClick/AreaLabel.d.ts +8 -0
  21. package/dist/esm/components/VizAreaClick/AreaLabel.d.ts.map +1 -0
  22. package/dist/esm/components/VizAreaClick/AreaOverlay.d.ts +12 -0
  23. package/dist/esm/components/VizAreaClick/AreaOverlay.d.ts.map +1 -0
  24. package/dist/esm/components/VizAreaClick/PortalAreaRenderer.d.ts +16 -0
  25. package/dist/esm/components/VizAreaClick/PortalAreaRenderer.d.ts.map +1 -0
  26. package/dist/esm/components/VizAreaClick/VizAreaClick.d.ts +17 -0
  27. package/dist/esm/components/VizAreaClick/VizAreaClick.d.ts.map +1 -0
  28. package/dist/esm/components/VizAreaClick/index.d.ts +5 -0
  29. package/dist/esm/components/VizAreaClick/index.d.ts.map +1 -0
  30. package/dist/esm/components/VizDom/VizContainer.d.ts.map +1 -1
  31. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  32. package/dist/esm/components/VizScrollmap/index.d.ts +1 -0
  33. package/dist/esm/components/VizScrollmap/index.d.ts.map +1 -1
  34. package/dist/esm/configs/style.d.ts +1 -1
  35. package/dist/esm/configs/style.d.ts.map +1 -1
  36. package/dist/esm/configs/viewId.d.ts +1 -19
  37. package/dist/esm/configs/viewId.d.ts.map +1 -1
  38. package/dist/esm/constants/index.d.ts +2 -13
  39. package/dist/esm/constants/index.d.ts.map +1 -1
  40. package/dist/esm/constants/viz-area-click.d.ts +6 -0
  41. package/dist/esm/constants/viz-area-click.d.ts.map +1 -0
  42. package/dist/esm/constants/viz-elm-callout.d.ts +8 -0
  43. package/dist/esm/constants/viz-elm-callout.d.ts.map +1 -0
  44. package/dist/esm/contexts/ViewIdContext.d.ts +3 -0
  45. package/dist/esm/contexts/ViewIdContext.d.ts.map +1 -0
  46. package/dist/esm/contexts/index.d.ts +1 -1
  47. package/dist/esm/contexts/index.d.ts.map +1 -1
  48. package/dist/esm/helpers/index.d.ts +4 -2
  49. package/dist/esm/helpers/index.d.ts.map +1 -1
  50. package/dist/esm/helpers/observable.d.ts +11 -0
  51. package/dist/esm/helpers/observable.d.ts.map +1 -0
  52. package/dist/esm/helpers/viewport/element.d.ts +3 -0
  53. package/dist/esm/helpers/viewport/element.d.ts.map +1 -0
  54. package/dist/esm/helpers/viewport/index.d.ts +2 -0
  55. package/dist/esm/helpers/viewport/index.d.ts.map +1 -0
  56. package/dist/esm/helpers/viz-area-click/area-builder.d.ts +8 -0
  57. package/dist/esm/helpers/viz-area-click/area-builder.d.ts.map +1 -0
  58. package/dist/esm/helpers/viz-area-click/area-color.d.ts +13 -0
  59. package/dist/esm/helpers/viz-area-click/area-color.d.ts.map +1 -0
  60. package/dist/esm/helpers/viz-area-click/area-overlap.d.ts +44 -0
  61. package/dist/esm/helpers/viz-area-click/area-overlap.d.ts.map +1 -0
  62. package/dist/esm/helpers/viz-area-click/area-utils.d.ts +54 -0
  63. package/dist/esm/helpers/viz-area-click/area-utils.d.ts.map +1 -0
  64. package/dist/esm/helpers/viz-area-click/index.d.ts +5 -0
  65. package/dist/esm/helpers/viz-area-click/index.d.ts.map +1 -0
  66. package/dist/esm/helpers/{iframe.d.ts → viz-dom/decode.d.ts} +2 -2
  67. package/dist/esm/helpers/viz-dom/decode.d.ts.map +1 -0
  68. package/dist/esm/helpers/viz-dom/index.d.ts +2 -0
  69. package/dist/esm/helpers/viz-dom/index.d.ts.map +1 -0
  70. package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts +3 -3
  71. package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts.map +1 -1
  72. package/dist/esm/helpers/viz-elm-callout/element.d.ts +10 -0
  73. package/dist/esm/helpers/viz-elm-callout/element.d.ts.map +1 -0
  74. package/dist/esm/helpers/viz-elm-callout/index.d.ts +2 -0
  75. package/dist/esm/helpers/viz-elm-callout/index.d.ts.map +1 -1
  76. package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts +4 -5
  77. package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -1
  78. package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts +3 -3
  79. package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts.map +1 -1
  80. package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts.map +1 -1
  81. package/dist/esm/helpers/viz-elm-callout/rank-calculator.d.ts +7 -0
  82. package/dist/esm/helpers/viz-elm-callout/rank-calculator.d.ts.map +1 -0
  83. package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts +4 -2
  84. package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -1
  85. package/dist/esm/hooks/index.d.ts +3 -1
  86. package/dist/esm/hooks/index.d.ts.map +1 -1
  87. package/dist/esm/hooks/view-context/index.d.ts +3 -3
  88. package/dist/esm/hooks/view-context/index.d.ts.map +1 -1
  89. package/dist/esm/hooks/view-context/useHeatmapAreaClick.d.ts +19 -0
  90. package/dist/esm/hooks/view-context/useHeatmapAreaClick.d.ts.map +1 -0
  91. package/dist/{umd/hooks/view-context/useHeatmapInteraction.d.ts → esm/hooks/view-context/useHeatmapClick.d.ts} +5 -4
  92. package/dist/esm/hooks/view-context/useHeatmapClick.d.ts.map +1 -0
  93. package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
  94. package/dist/{umd/hooks/view-context/useHeatmapVizScrollmap.d.ts → esm/hooks/view-context/useHeatmapScroll.d.ts} +5 -4
  95. package/dist/esm/hooks/view-context/useHeatmapScroll.d.ts.map +1 -0
  96. package/dist/esm/hooks/viz-area-click/index.d.ts +3 -0
  97. package/dist/esm/hooks/viz-area-click/index.d.ts.map +1 -0
  98. package/dist/esm/hooks/viz-area-click/useAreaEditMode.d.ts +12 -0
  99. package/dist/esm/hooks/viz-area-click/useAreaEditMode.d.ts.map +1 -0
  100. package/dist/esm/hooks/viz-area-click/useAreaScrollSync.d.ts +8 -0
  101. package/dist/esm/hooks/viz-area-click/useAreaScrollSync.d.ts.map +1 -0
  102. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  103. package/dist/esm/hooks/viz-scroll/index.d.ts.map +1 -0
  104. package/dist/esm/hooks/viz-scroll/useScrollmapZones.d.ts.map +1 -0
  105. package/dist/esm/hooks/viz-scroll/useZonePositions.d.ts.map +1 -0
  106. package/dist/esm/index.d.ts +2 -1
  107. package/dist/esm/index.d.ts.map +1 -1
  108. package/dist/esm/index.js +1616 -638
  109. package/dist/esm/index.mjs +1616 -638
  110. package/dist/esm/stores/index.d.ts +3 -2
  111. package/dist/esm/stores/index.d.ts.map +1 -1
  112. package/dist/esm/stores/mode-compare.d.ts +0 -14
  113. package/dist/esm/stores/mode-compare.d.ts.map +1 -1
  114. package/dist/esm/stores/viz-area-click.d.ts +28 -0
  115. package/dist/esm/stores/viz-area-click.d.ts.map +1 -0
  116. package/dist/{umd/stores/interaction.d.ts → esm/stores/viz-click.d.ts} +5 -5
  117. package/dist/esm/stores/viz-click.d.ts.map +1 -0
  118. package/dist/esm/types/compare.d.ts +0 -50
  119. package/dist/esm/types/compare.d.ts.map +1 -1
  120. package/dist/esm/types/heatmap.d.ts +1 -0
  121. package/dist/esm/types/heatmap.d.ts.map +1 -1
  122. package/dist/esm/types/index.d.ts +5 -4
  123. package/dist/esm/types/index.d.ts.map +1 -1
  124. package/dist/esm/types/viz-area-click.d.ts +84 -0
  125. package/dist/esm/types/viz-area-click.d.ts.map +1 -0
  126. package/dist/esm/types/viz-elm-callout.d.ts +15 -0
  127. package/dist/esm/types/viz-elm-callout.d.ts.map +1 -1
  128. package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
  129. package/dist/umd/components/Layout/HeatmapPreview.d.ts +2 -0
  130. package/dist/umd/components/Layout/HeatmapPreview.d.ts.map +1 -0
  131. package/dist/umd/components/Layout/MetricBar/ContentMetricBar.d.ts.map +1 -0
  132. package/dist/umd/components/Layout/MetricBar/index.d.ts +2 -0
  133. package/dist/umd/components/Layout/MetricBar/index.d.ts.map +1 -0
  134. package/dist/umd/components/Layout/Toolbar/ContentToolbar.d.ts.map +1 -0
  135. package/dist/umd/components/Layout/Toolbar/index.d.ts +2 -0
  136. package/dist/umd/components/Layout/Toolbar/index.d.ts.map +1 -0
  137. package/dist/umd/components/Layout/TopBar/ContentTopBar.d.ts.map +1 -0
  138. package/dist/umd/components/Layout/TopBar/index.d.ts +2 -0
  139. package/dist/umd/components/Layout/TopBar/index.d.ts.map +1 -0
  140. package/dist/umd/components/Layout/VizByMode/ContentVizByMode.d.ts.map +1 -0
  141. package/dist/umd/components/Layout/VizByMode/index.d.ts +2 -0
  142. package/dist/umd/components/Layout/VizByMode/index.d.ts.map +1 -0
  143. package/dist/umd/components/VizAreaClick/AreaControls.d.ts +10 -0
  144. package/dist/umd/components/VizAreaClick/AreaControls.d.ts.map +1 -0
  145. package/dist/umd/components/VizAreaClick/AreaEditHighlight.d.ts +8 -0
  146. package/dist/umd/components/VizAreaClick/AreaEditHighlight.d.ts.map +1 -0
  147. package/dist/umd/components/VizAreaClick/AreaLabel.d.ts +8 -0
  148. package/dist/umd/components/VizAreaClick/AreaLabel.d.ts.map +1 -0
  149. package/dist/umd/components/VizAreaClick/AreaOverlay.d.ts +12 -0
  150. package/dist/umd/components/VizAreaClick/AreaOverlay.d.ts.map +1 -0
  151. package/dist/umd/components/VizAreaClick/PortalAreaRenderer.d.ts +16 -0
  152. package/dist/umd/components/VizAreaClick/PortalAreaRenderer.d.ts.map +1 -0
  153. package/dist/umd/components/VizAreaClick/VizAreaClick.d.ts +17 -0
  154. package/dist/umd/components/VizAreaClick/VizAreaClick.d.ts.map +1 -0
  155. package/dist/umd/components/VizAreaClick/index.d.ts +5 -0
  156. package/dist/umd/components/VizAreaClick/index.d.ts.map +1 -0
  157. package/dist/umd/components/VizDom/VizContainer.d.ts.map +1 -1
  158. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  159. package/dist/umd/components/VizScrollmap/index.d.ts +1 -0
  160. package/dist/umd/components/VizScrollmap/index.d.ts.map +1 -1
  161. package/dist/umd/configs/style.d.ts +1 -1
  162. package/dist/umd/configs/style.d.ts.map +1 -1
  163. package/dist/umd/configs/viewId.d.ts +1 -19
  164. package/dist/umd/configs/viewId.d.ts.map +1 -1
  165. package/dist/umd/constants/index.d.ts +2 -13
  166. package/dist/umd/constants/index.d.ts.map +1 -1
  167. package/dist/umd/constants/viz-area-click.d.ts +6 -0
  168. package/dist/umd/constants/viz-area-click.d.ts.map +1 -0
  169. package/dist/umd/constants/viz-elm-callout.d.ts +8 -0
  170. package/dist/umd/constants/viz-elm-callout.d.ts.map +1 -0
  171. package/dist/umd/contexts/ViewIdContext.d.ts +3 -0
  172. package/dist/umd/contexts/ViewIdContext.d.ts.map +1 -0
  173. package/dist/umd/contexts/index.d.ts +1 -1
  174. package/dist/umd/contexts/index.d.ts.map +1 -1
  175. package/dist/umd/helpers/index.d.ts +4 -2
  176. package/dist/umd/helpers/index.d.ts.map +1 -1
  177. package/dist/umd/helpers/observable.d.ts +11 -0
  178. package/dist/umd/helpers/observable.d.ts.map +1 -0
  179. package/dist/umd/helpers/viewport/element.d.ts +3 -0
  180. package/dist/umd/helpers/viewport/element.d.ts.map +1 -0
  181. package/dist/umd/helpers/viewport/index.d.ts +2 -0
  182. package/dist/umd/helpers/viewport/index.d.ts.map +1 -0
  183. package/dist/umd/helpers/viz-area-click/area-builder.d.ts +8 -0
  184. package/dist/umd/helpers/viz-area-click/area-builder.d.ts.map +1 -0
  185. package/dist/umd/helpers/viz-area-click/area-color.d.ts +13 -0
  186. package/dist/umd/helpers/viz-area-click/area-color.d.ts.map +1 -0
  187. package/dist/umd/helpers/viz-area-click/area-overlap.d.ts +44 -0
  188. package/dist/umd/helpers/viz-area-click/area-overlap.d.ts.map +1 -0
  189. package/dist/umd/helpers/viz-area-click/area-utils.d.ts +54 -0
  190. package/dist/umd/helpers/viz-area-click/area-utils.d.ts.map +1 -0
  191. package/dist/umd/helpers/viz-area-click/index.d.ts +5 -0
  192. package/dist/umd/helpers/viz-area-click/index.d.ts.map +1 -0
  193. package/dist/umd/helpers/{iframe.d.ts → viz-dom/decode.d.ts} +2 -2
  194. package/dist/umd/helpers/viz-dom/decode.d.ts.map +1 -0
  195. package/dist/umd/helpers/viz-dom/index.d.ts +2 -0
  196. package/dist/umd/helpers/viz-dom/index.d.ts.map +1 -0
  197. package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts +3 -3
  198. package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts.map +1 -1
  199. package/dist/umd/helpers/viz-elm-callout/element.d.ts +10 -0
  200. package/dist/umd/helpers/viz-elm-callout/element.d.ts.map +1 -0
  201. package/dist/umd/helpers/viz-elm-callout/index.d.ts +2 -0
  202. package/dist/umd/helpers/viz-elm-callout/index.d.ts.map +1 -1
  203. package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts +4 -5
  204. package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -1
  205. package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts +3 -3
  206. package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts.map +1 -1
  207. package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts.map +1 -1
  208. package/dist/umd/helpers/viz-elm-callout/rank-calculator.d.ts +7 -0
  209. package/dist/umd/helpers/viz-elm-callout/rank-calculator.d.ts.map +1 -0
  210. package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts +4 -2
  211. package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -1
  212. package/dist/umd/hooks/index.d.ts +3 -1
  213. package/dist/umd/hooks/index.d.ts.map +1 -1
  214. package/dist/umd/hooks/view-context/index.d.ts +3 -3
  215. package/dist/umd/hooks/view-context/index.d.ts.map +1 -1
  216. package/dist/umd/hooks/view-context/useHeatmapAreaClick.d.ts +19 -0
  217. package/dist/umd/hooks/view-context/useHeatmapAreaClick.d.ts.map +1 -0
  218. package/dist/{esm/hooks/view-context/useHeatmapInteraction.d.ts → umd/hooks/view-context/useHeatmapClick.d.ts} +5 -4
  219. package/dist/umd/hooks/view-context/useHeatmapClick.d.ts.map +1 -0
  220. package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
  221. package/dist/{esm/hooks/view-context/useHeatmapVizScrollmap.d.ts → umd/hooks/view-context/useHeatmapScroll.d.ts} +5 -4
  222. package/dist/umd/hooks/view-context/useHeatmapScroll.d.ts.map +1 -0
  223. package/dist/umd/hooks/viz-area-click/index.d.ts +3 -0
  224. package/dist/umd/hooks/viz-area-click/index.d.ts.map +1 -0
  225. package/dist/umd/hooks/viz-area-click/useAreaEditMode.d.ts +12 -0
  226. package/dist/umd/hooks/viz-area-click/useAreaEditMode.d.ts.map +1 -0
  227. package/dist/umd/hooks/viz-area-click/useAreaScrollSync.d.ts +8 -0
  228. package/dist/umd/hooks/viz-area-click/useAreaScrollSync.d.ts.map +1 -0
  229. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  230. package/dist/umd/hooks/viz-scroll/index.d.ts.map +1 -0
  231. package/dist/umd/hooks/viz-scroll/useScrollmapZones.d.ts.map +1 -0
  232. package/dist/umd/hooks/viz-scroll/useZonePositions.d.ts.map +1 -0
  233. package/dist/umd/index.d.ts +2 -1
  234. package/dist/umd/index.d.ts.map +1 -1
  235. package/dist/umd/index.js +2 -2
  236. package/dist/umd/stores/index.d.ts +3 -2
  237. package/dist/umd/stores/index.d.ts.map +1 -1
  238. package/dist/umd/stores/mode-compare.d.ts +0 -14
  239. package/dist/umd/stores/mode-compare.d.ts.map +1 -1
  240. package/dist/umd/stores/viz-area-click.d.ts +28 -0
  241. package/dist/umd/stores/viz-area-click.d.ts.map +1 -0
  242. package/dist/{esm/stores/interaction.d.ts → umd/stores/viz-click.d.ts} +5 -5
  243. package/dist/umd/stores/viz-click.d.ts.map +1 -0
  244. package/dist/umd/types/compare.d.ts +0 -50
  245. package/dist/umd/types/compare.d.ts.map +1 -1
  246. package/dist/umd/types/heatmap.d.ts +1 -0
  247. package/dist/umd/types/heatmap.d.ts.map +1 -1
  248. package/dist/umd/types/index.d.ts +5 -4
  249. package/dist/umd/types/index.d.ts.map +1 -1
  250. package/dist/umd/types/viz-area-click.d.ts +84 -0
  251. package/dist/umd/types/viz-area-click.d.ts.map +1 -0
  252. package/dist/umd/types/viz-elm-callout.d.ts +15 -0
  253. package/dist/umd/types/viz-elm-callout.d.ts.map +1 -1
  254. package/package.json +1 -1
  255. package/dist/esm/components/Layout/ContentMetricBar.d.ts.map +0 -1
  256. package/dist/esm/components/Layout/ContentToolbar.d.ts.map +0 -1
  257. package/dist/esm/components/Layout/ContentTopBar.d.ts.map +0 -1
  258. package/dist/esm/components/Layout/ContentVizByMode.d.ts.map +0 -1
  259. package/dist/esm/components/Layout/WrapperLayout.d.ts +0 -2
  260. package/dist/esm/components/Layout/WrapperLayout.d.ts.map +0 -1
  261. package/dist/esm/components/Layout/WrapperPreview.d.ts +0 -2
  262. package/dist/esm/components/Layout/WrapperPreview.d.ts.map +0 -1
  263. package/dist/esm/contexts/CompareViewContext.d.ts +0 -28
  264. package/dist/esm/contexts/CompareViewContext.d.ts.map +0 -1
  265. package/dist/esm/helpers/iframe.d.ts.map +0 -1
  266. package/dist/esm/helpers/viz-elm-callout/constants.d.ts +0 -4
  267. package/dist/esm/helpers/viz-elm-callout/constants.d.ts.map +0 -1
  268. package/dist/esm/helpers/viz-elm-callout/types.d.ts +0 -17
  269. package/dist/esm/helpers/viz-elm-callout/types.d.ts.map +0 -1
  270. package/dist/esm/helpers/viz-elm.d.ts +0 -9
  271. package/dist/esm/helpers/viz-elm.d.ts.map +0 -1
  272. package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts.map +0 -1
  273. package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +0 -1
  274. package/dist/esm/hooks/view-context/useViewId.d.ts +0 -15
  275. package/dist/esm/hooks/view-context/useViewId.d.ts.map +0 -1
  276. package/dist/esm/hooks/viz-scrollmap/index.d.ts.map +0 -1
  277. package/dist/esm/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +0 -1
  278. package/dist/esm/hooks/viz-scrollmap/useZonePositions.d.ts.map +0 -1
  279. package/dist/esm/stores/interaction.d.ts.map +0 -1
  280. package/dist/umd/components/Layout/ContentMetricBar.d.ts.map +0 -1
  281. package/dist/umd/components/Layout/ContentToolbar.d.ts.map +0 -1
  282. package/dist/umd/components/Layout/ContentTopBar.d.ts.map +0 -1
  283. package/dist/umd/components/Layout/ContentVizByMode.d.ts.map +0 -1
  284. package/dist/umd/components/Layout/WrapperLayout.d.ts +0 -2
  285. package/dist/umd/components/Layout/WrapperLayout.d.ts.map +0 -1
  286. package/dist/umd/components/Layout/WrapperPreview.d.ts +0 -2
  287. package/dist/umd/components/Layout/WrapperPreview.d.ts.map +0 -1
  288. package/dist/umd/contexts/CompareViewContext.d.ts +0 -28
  289. package/dist/umd/contexts/CompareViewContext.d.ts.map +0 -1
  290. package/dist/umd/helpers/iframe.d.ts.map +0 -1
  291. package/dist/umd/helpers/viz-elm-callout/constants.d.ts +0 -4
  292. package/dist/umd/helpers/viz-elm-callout/constants.d.ts.map +0 -1
  293. package/dist/umd/helpers/viz-elm-callout/types.d.ts +0 -17
  294. package/dist/umd/helpers/viz-elm-callout/types.d.ts.map +0 -1
  295. package/dist/umd/helpers/viz-elm.d.ts +0 -9
  296. package/dist/umd/helpers/viz-elm.d.ts.map +0 -1
  297. package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts.map +0 -1
  298. package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +0 -1
  299. package/dist/umd/hooks/view-context/useViewId.d.ts +0 -15
  300. package/dist/umd/hooks/view-context/useViewId.d.ts.map +0 -1
  301. package/dist/umd/hooks/viz-scrollmap/index.d.ts.map +0 -1
  302. package/dist/umd/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +0 -1
  303. package/dist/umd/hooks/viz-scrollmap/useZonePositions.d.ts.map +0 -1
  304. package/dist/umd/stores/interaction.d.ts.map +0 -1
  305. /package/dist/esm/components/Layout/{ContentMetricBar.d.ts → MetricBar/ContentMetricBar.d.ts} +0 -0
  306. /package/dist/esm/components/Layout/{ContentToolbar.d.ts → Toolbar/ContentToolbar.d.ts} +0 -0
  307. /package/dist/esm/components/Layout/{ContentTopBar.d.ts → TopBar/ContentTopBar.d.ts} +0 -0
  308. /package/dist/esm/components/Layout/{ContentVizByMode.d.ts → VizByMode/ContentVizByMode.d.ts} +0 -0
  309. /package/dist/esm/hooks/{viz-scrollmap → viz-scroll}/index.d.ts +0 -0
  310. /package/dist/esm/hooks/{viz-scrollmap → viz-scroll}/useScrollmapZones.d.ts +0 -0
  311. /package/dist/esm/hooks/{viz-scrollmap → viz-scroll}/useZonePositions.d.ts +0 -0
  312. /package/dist/umd/components/Layout/{ContentMetricBar.d.ts → MetricBar/ContentMetricBar.d.ts} +0 -0
  313. /package/dist/umd/components/Layout/{ContentToolbar.d.ts → Toolbar/ContentToolbar.d.ts} +0 -0
  314. /package/dist/umd/components/Layout/{ContentTopBar.d.ts → TopBar/ContentTopBar.d.ts} +0 -0
  315. /package/dist/umd/components/Layout/{ContentVizByMode.d.ts → VizByMode/ContentVizByMode.d.ts} +0 -0
  316. /package/dist/umd/hooks/{viz-scrollmap → viz-scroll}/index.d.ts +0 -0
  317. /package/dist/umd/hooks/{viz-scrollmap → viz-scroll}/useScrollmapZones.d.ts +0 -0
  318. /package/dist/umd/hooks/{viz-scrollmap → viz-scroll}/useZonePositions.d.ts +0 -0
@@ -48,6 +48,7 @@ const HEATMAP_IFRAME = {
48
48
  height: '100%',
49
49
  };
50
50
 
51
+ const DEFAULT_SIDEBAR_WIDTH = 260;
51
52
  const HEATMAP_CONFIG = {
52
53
  padding: 8,
53
54
  borderWidth: 1,
@@ -66,32 +67,10 @@ const HEATMAP_STYLE = {
66
67
  paddingInline: `${HEATMAP_CONFIG.padding}px`,
67
68
  },
68
69
  };
69
- const DEFAULT_SIDEBAR_WIDTH = 260;
70
70
 
71
- /**
72
- * Default view ID for single mode
73
- */
74
71
  const DEFAULT_VIEW_ID = 'default';
75
- /**
76
- * Generate compare view ID
77
- * @param index - Zero-based index (0, 1, 2, 3)
78
- * @returns View ID string like 'view-0', 'view-1', etc.
79
- */
80
- const getCompareViewId = (index) => `view-${index}`;
81
- /**
82
- * Check if a view ID is a compare view
83
- */
84
- const isCompareViewId = (viewId) => viewId.startsWith('view-');
85
- /**
86
- * Extract index from compare view ID
87
- * @param viewId - View ID like 'view-0'
88
- * @returns Index number or -1 if invalid
89
- */
90
- const getCompareViewIndex = (viewId) => {
91
- if (!isCompareViewId(viewId))
92
- return -1;
93
- const match = viewId.match(/^view-(\d+)$/);
94
- return match ? parseInt(match[1], 10) : -1;
72
+ const getCompareViewId = (viewId) => {
73
+ return `compare-${viewId}`;
95
74
  };
96
75
 
97
76
  const Z_INDEX = {
@@ -157,6 +136,7 @@ const useHeatmapControlStore = create()((set, get) => {
157
136
  var IHeatmapType;
158
137
  (function (IHeatmapType) {
159
138
  IHeatmapType["Click"] = "click";
139
+ IHeatmapType["ClickArea"] = "click-area";
160
140
  IHeatmapType["Scroll"] = "scroll";
161
141
  })(IHeatmapType || (IHeatmapType = {}));
162
142
  var IClickType;
@@ -244,10 +224,173 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
244
224
  };
245
225
  }));
246
226
 
227
+ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
228
+ return {
229
+ isRenderViz: { [DEFAULT_VIEW_ID]: false },
230
+ zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
231
+ minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
232
+ scale: { [DEFAULT_VIEW_ID]: 1 },
233
+ isScaledToFit: { [DEFAULT_VIEW_ID]: false },
234
+ setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID) => set((state) => ({
235
+ isRenderViz: { ...state.isRenderViz, [viewId]: isRenderViz },
236
+ })),
237
+ setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
238
+ zoomRatio: { ...state.zoomRatio, [viewId]: zoomRatio },
239
+ })),
240
+ setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
241
+ minZoomRatio: { ...state.minZoomRatio, [viewId]: minZoomRatio },
242
+ })),
243
+ setScale: (scale, viewId = DEFAULT_VIEW_ID) => set((state) => ({
244
+ scale: { ...state.scale, [viewId]: scale },
245
+ })),
246
+ setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID) => set((state) => ({
247
+ isScaledToFit: { ...state.isScaledToFit, [viewId]: isScaledToFit },
248
+ })),
249
+ copyView: (fromViewId, toViewId) => set((state) => ({
250
+ isRenderViz: { ...state.isRenderViz, [toViewId]: state.isRenderViz[fromViewId] ?? false },
251
+ zoomRatio: { ...state.zoomRatio, [toViewId]: state.zoomRatio[fromViewId] ?? 100 },
252
+ minZoomRatio: { ...state.minZoomRatio, [toViewId]: state.minZoomRatio[fromViewId] ?? 10 },
253
+ scale: { ...state.scale, [toViewId]: state.scale[fromViewId] ?? 1 },
254
+ isScaledToFit: {
255
+ ...state.isScaledToFit,
256
+ [toViewId]: state.isScaledToFit[fromViewId] ?? false,
257
+ },
258
+ })),
259
+ clearView: (viewId) => set((state) => {
260
+ const newIsRenderViz = { ...state.isRenderViz };
261
+ const newZoomRatio = { ...state.zoomRatio };
262
+ const newMinZoomRatio = { ...state.minZoomRatio };
263
+ const newScale = { ...state.scale };
264
+ const newIsScaledToFit = { ...state.isScaledToFit };
265
+ delete newIsRenderViz[viewId];
266
+ delete newZoomRatio[viewId];
267
+ delete newMinZoomRatio[viewId];
268
+ delete newScale[viewId];
269
+ delete newIsScaledToFit[viewId];
270
+ return {
271
+ isRenderViz: newIsRenderViz,
272
+ zoomRatio: newZoomRatio,
273
+ minZoomRatio: newMinZoomRatio,
274
+ scale: newScale,
275
+ isScaledToFit: newIsScaledToFit,
276
+ };
277
+ }),
278
+ resetAll: () => set({
279
+ isRenderViz: { [DEFAULT_VIEW_ID]: false },
280
+ zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
281
+ minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
282
+ scale: { [DEFAULT_VIEW_ID]: 1 },
283
+ isScaledToFit: { [DEFAULT_VIEW_ID]: false },
284
+ }),
285
+ };
286
+ }));
287
+
288
+ const useHeatmapAreaClickStore = create()(subscribeWithSelector((set) => ({
289
+ selectedArea: { [DEFAULT_VIEW_ID]: null },
290
+ hoveredArea: { [DEFAULT_VIEW_ID]: null },
291
+ areas: { [DEFAULT_VIEW_ID]: [] },
292
+ isEditingMode: { [DEFAULT_VIEW_ID]: false },
293
+ setSelectedArea: (area, viewId = DEFAULT_VIEW_ID) => set((state) => ({
294
+ selectedArea: { ...state.selectedArea, [viewId]: area },
295
+ })),
296
+ setHoveredArea: (area, viewId = DEFAULT_VIEW_ID) => set((state) => ({
297
+ hoveredArea: { ...state.hoveredArea, [viewId]: area },
298
+ })),
299
+ setAreas: (areas, viewId = DEFAULT_VIEW_ID) => set((state) => ({
300
+ areas: { ...state.areas, [viewId]: areas },
301
+ })),
302
+ setIsEditingMode: (isEditing, viewId = DEFAULT_VIEW_ID) => set((state) => ({
303
+ isEditingMode: { ...state.isEditingMode, [viewId]: isEditing },
304
+ })),
305
+ addArea: (area, viewId = DEFAULT_VIEW_ID) => set((state) => {
306
+ const currentAreas = state.areas[viewId] || [];
307
+ // Check if area already exists
308
+ const exists = currentAreas.some((a) => a.id === area.id);
309
+ if (exists)
310
+ return state;
311
+ return {
312
+ areas: {
313
+ ...state.areas,
314
+ [viewId]: [...currentAreas, area],
315
+ },
316
+ };
317
+ }),
318
+ removeArea: (areaId, viewId = DEFAULT_VIEW_ID) => set((state) => {
319
+ const currentAreas = state.areas[viewId] || [];
320
+ const filtered = currentAreas.filter((a) => a.id !== areaId);
321
+ // If removed area was selected/hovered, clear it
322
+ const selectedArea = state.selectedArea[viewId];
323
+ const hoveredArea = state.hoveredArea[viewId];
324
+ return {
325
+ areas: { ...state.areas, [viewId]: filtered },
326
+ selectedArea: {
327
+ ...state.selectedArea,
328
+ [viewId]: selectedArea?.id === areaId ? null : selectedArea,
329
+ },
330
+ hoveredArea: {
331
+ ...state.hoveredArea,
332
+ [viewId]: hoveredArea?.id === areaId ? null : hoveredArea,
333
+ },
334
+ };
335
+ }),
336
+ updateArea: (areaId, updates, viewId = DEFAULT_VIEW_ID) => set((state) => {
337
+ const currentAreas = state.areas[viewId] || [];
338
+ const updatedAreas = currentAreas.map((area) => area.id === areaId ? { ...area, ...updates } : area);
339
+ return {
340
+ areas: { ...state.areas, [viewId]: updatedAreas },
341
+ };
342
+ }),
343
+ clearAreas: (viewId = DEFAULT_VIEW_ID) => set((state) => ({
344
+ areas: { ...state.areas, [viewId]: [] },
345
+ selectedArea: { ...state.selectedArea, [viewId]: null },
346
+ hoveredArea: { ...state.hoveredArea, [viewId]: null },
347
+ })),
348
+ copyView: (fromViewId, toViewId) => set((state) => ({
349
+ selectedArea: {
350
+ ...state.selectedArea,
351
+ [toViewId]: state.selectedArea[fromViewId] ?? null,
352
+ },
353
+ hoveredArea: {
354
+ ...state.hoveredArea,
355
+ [toViewId]: state.hoveredArea[fromViewId] ?? null,
356
+ },
357
+ areas: {
358
+ ...state.areas,
359
+ [toViewId]: state.areas[fromViewId] ?? [],
360
+ },
361
+ isEditingMode: {
362
+ ...state.isEditingMode,
363
+ [toViewId]: state.isEditingMode[fromViewId] ?? false,
364
+ },
365
+ })),
366
+ clearView: (viewId) => set((state) => {
367
+ const newSelectedArea = { ...state.selectedArea };
368
+ const newHoveredArea = { ...state.hoveredArea };
369
+ const newAreas = { ...state.areas };
370
+ const newIsEditingMode = { ...state.isEditingMode };
371
+ delete newSelectedArea[viewId];
372
+ delete newHoveredArea[viewId];
373
+ delete newAreas[viewId];
374
+ delete newIsEditingMode[viewId];
375
+ return {
376
+ selectedArea: newSelectedArea,
377
+ hoveredArea: newHoveredArea,
378
+ areas: newAreas,
379
+ isEditingMode: newIsEditingMode,
380
+ };
381
+ }),
382
+ resetAll: () => set({
383
+ selectedArea: { [DEFAULT_VIEW_ID]: null },
384
+ hoveredArea: { [DEFAULT_VIEW_ID]: null },
385
+ areas: { [DEFAULT_VIEW_ID]: [] },
386
+ isEditingMode: { [DEFAULT_VIEW_ID]: false },
387
+ }),
388
+ })));
389
+
247
390
  const DEFAULT_STATE = {
248
391
  hideSidebar: false,
249
392
  };
250
- const useHeatmapInteractionStore = create()(subscribeWithSelector((set) => {
393
+ const useHeatmapClickStore = create()(subscribeWithSelector((set) => {
251
394
  return {
252
395
  state: { [DEFAULT_VIEW_ID]: DEFAULT_STATE },
253
396
  selectedElement: { [DEFAULT_VIEW_ID]: null },
@@ -308,67 +451,6 @@ const useHeatmapInteractionStore = create()(subscribeWithSelector((set) => {
308
451
  };
309
452
  }));
310
453
 
311
- const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
312
- return {
313
- isRenderViz: { [DEFAULT_VIEW_ID]: false },
314
- zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
315
- minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
316
- scale: { [DEFAULT_VIEW_ID]: 1 },
317
- isScaledToFit: { [DEFAULT_VIEW_ID]: false },
318
- setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID) => set((state) => ({
319
- isRenderViz: { ...state.isRenderViz, [viewId]: isRenderViz },
320
- })),
321
- setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
322
- zoomRatio: { ...state.zoomRatio, [viewId]: zoomRatio },
323
- })),
324
- setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
325
- minZoomRatio: { ...state.minZoomRatio, [viewId]: minZoomRatio },
326
- })),
327
- setScale: (scale, viewId = DEFAULT_VIEW_ID) => set((state) => ({
328
- scale: { ...state.scale, [viewId]: scale },
329
- })),
330
- setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID) => set((state) => ({
331
- isScaledToFit: { ...state.isScaledToFit, [viewId]: isScaledToFit },
332
- })),
333
- copyView: (fromViewId, toViewId) => set((state) => ({
334
- isRenderViz: { ...state.isRenderViz, [toViewId]: state.isRenderViz[fromViewId] ?? false },
335
- zoomRatio: { ...state.zoomRatio, [toViewId]: state.zoomRatio[fromViewId] ?? 100 },
336
- minZoomRatio: { ...state.minZoomRatio, [toViewId]: state.minZoomRatio[fromViewId] ?? 10 },
337
- scale: { ...state.scale, [toViewId]: state.scale[fromViewId] ?? 1 },
338
- isScaledToFit: {
339
- ...state.isScaledToFit,
340
- [toViewId]: state.isScaledToFit[fromViewId] ?? false,
341
- },
342
- })),
343
- clearView: (viewId) => set((state) => {
344
- const newIsRenderViz = { ...state.isRenderViz };
345
- const newZoomRatio = { ...state.zoomRatio };
346
- const newMinZoomRatio = { ...state.minZoomRatio };
347
- const newScale = { ...state.scale };
348
- const newIsScaledToFit = { ...state.isScaledToFit };
349
- delete newIsRenderViz[viewId];
350
- delete newZoomRatio[viewId];
351
- delete newMinZoomRatio[viewId];
352
- delete newScale[viewId];
353
- delete newIsScaledToFit[viewId];
354
- return {
355
- isRenderViz: newIsRenderViz,
356
- zoomRatio: newZoomRatio,
357
- minZoomRatio: newMinZoomRatio,
358
- scale: newScale,
359
- isScaledToFit: newIsScaledToFit,
360
- };
361
- }),
362
- resetAll: () => set({
363
- isRenderViz: { [DEFAULT_VIEW_ID]: false },
364
- zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
365
- minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
366
- scale: { [DEFAULT_VIEW_ID]: 1 },
367
- isScaledToFit: { [DEFAULT_VIEW_ID]: false },
368
- }),
369
- };
370
- }));
371
-
372
454
  const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set) => {
373
455
  return {
374
456
  zones: { [DEFAULT_VIEW_ID]: [] },
@@ -418,94 +500,9 @@ const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set) => {
418
500
  };
419
501
  }));
420
502
 
421
- const initialState = {
422
- payloads: [],
423
- htmlContent: '',
424
- };
425
- const useHeatmapLiveStore = create()((set) => {
426
- return {
427
- ...initialState,
428
- reset: () => set(initialState),
429
- setPayloads: (payloads) => set({ payloads }),
430
- addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
431
- setHtmlContent: (htmlContent) => set({ htmlContent }),
432
- };
433
- });
434
-
435
- const useHeatmapSingleStore = create()(subscribeWithSelector((set) => {
436
- return {
437
- vizRef: { [DEFAULT_VIEW_ID]: null },
438
- iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
439
- wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
440
- wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
441
- setVizRef: (vizRef, viewId = DEFAULT_VIEW_ID) => set((state) => ({
442
- vizRef: { ...state.vizRef, [viewId]: vizRef },
443
- })),
444
- setIframeHeight: (iframeHeight, viewId = DEFAULT_VIEW_ID) => {
445
- set((state) => ({
446
- iframeHeight: { ...state.iframeHeight, [viewId]: iframeHeight },
447
- }));
448
- },
449
- setWrapperHeight: (wrapperHeight, viewId = DEFAULT_VIEW_ID) => {
450
- set((state) => ({
451
- wrapperHeight: { ...state.wrapperHeight, [viewId]: wrapperHeight },
452
- }));
453
- },
454
- setWrapperWidth: (wrapperWidth, viewId = DEFAULT_VIEW_ID) => {
455
- set((state) => ({
456
- wrapperWidth: { ...state.wrapperWidth, [viewId]: wrapperWidth },
457
- }));
458
- },
459
- copyView: (fromViewId, toViewId) => set((state) => ({
460
- // Don't copy vizRef - each view needs its own visualizer instance
461
- iframeHeight: { ...state.iframeHeight, [toViewId]: state.iframeHeight[fromViewId] ?? 0 },
462
- wrapperHeight: {
463
- ...state.wrapperHeight,
464
- [toViewId]: state.wrapperHeight[fromViewId] ?? 0,
465
- },
466
- })),
467
- clearView: (viewId) => set((state) => {
468
- const newVizRef = { ...state.vizRef };
469
- const newIframeHeight = { ...state.iframeHeight };
470
- const newWrapperHeight = { ...state.wrapperHeight };
471
- const newWrapperWidth = { ...state.wrapperWidth };
472
- delete newVizRef[viewId];
473
- delete newIframeHeight[viewId];
474
- delete newWrapperHeight[viewId];
475
- delete newWrapperWidth[viewId];
476
- return {
477
- vizRef: newVizRef,
478
- iframeHeight: newIframeHeight,
479
- wrapperHeight: newWrapperHeight,
480
- wrapperWidth: newWrapperWidth,
481
- };
482
- }),
483
- resetAll: () => set({
484
- vizRef: { [DEFAULT_VIEW_ID]: null },
485
- iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
486
- wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
487
- wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
488
- }),
489
- };
490
- }));
491
-
492
- const createDefaultView = (id, label, options) => ({
503
+ const createDefaultView = (id, label) => ({
493
504
  id,
494
505
  label,
495
- heatmapType: options?.heatmapType ?? IHeatmapType.Scroll,
496
- clickType: options?.clickType ?? IClickType.Total,
497
- scrollType: options?.scrollType ?? IScrollType.Depth,
498
- data: options?.data,
499
- clickmap: undefined,
500
- scrollmap: undefined,
501
- dataInfo: undefined,
502
- vizRef: null,
503
- iframeHeight: 0,
504
- zoomRatio: 100,
505
- scale: 1,
506
- isScaledToFit: false,
507
- isRendering: true,
508
- isRenderViz: false,
509
506
  });
510
507
  const useHeatmapCompareStore = create()((set, get) => {
511
508
  return {
@@ -520,9 +517,9 @@ const useHeatmapCompareStore = create()((set, get) => {
520
517
  viewIdCounter: 0,
521
518
  addView: (options) => {
522
519
  const state = get();
523
- const viewId = `view-${state.viewIdCounter}`;
520
+ const viewId = getCompareViewId(state.viewIdCounter);
524
521
  const label = options?.label ?? `Version ${state.viewOrder.length + 1}`;
525
- const newView = createDefaultView(viewId, label, options);
522
+ const newView = createDefaultView(viewId, label);
526
523
  const newViews = new Map(state.views);
527
524
  newViews.set(viewId, newView);
528
525
  set({
@@ -549,16 +546,6 @@ const useHeatmapCompareStore = create()((set, get) => {
549
546
  const newViews = new Map(state.views);
550
547
  newViews.set(viewId, { ...view, ...updates });
551
548
  set({ views: newViews });
552
- // Handle syncing
553
- const { syncSettings } = state;
554
- if (syncSettings.zoomRatio && 'zoomRatio' in updates && updates.zoomRatio !== undefined) {
555
- get().syncProperty('zoomRatio', updates.zoomRatio);
556
- }
557
- if (syncSettings.heatmapType &&
558
- 'heatmapType' in updates &&
559
- updates.heatmapType !== undefined) {
560
- get().syncProperty('heatmapType', updates.heatmapType);
561
- }
562
549
  },
563
550
  getView: (viewId) => {
564
551
  return get().views.get(viewId);
@@ -573,9 +560,9 @@ const useHeatmapCompareStore = create()((set, get) => {
573
560
  const viewIds = [];
574
561
  const newViews = new Map();
575
562
  for (let i = 0; i < count; i++) {
576
- const viewId = `view-${i}`;
563
+ const viewId = getCompareViewId(i);
577
564
  const label = options?.label ?? String.fromCharCode(65 + i); // A, B, C, D
578
- newViews.set(viewId, createDefaultView(viewId, `Version ${label}`, options));
565
+ newViews.set(viewId, createDefaultView(viewId, `Version ${label}`));
579
566
  viewIds.push(viewId);
580
567
  }
581
568
  const layoutMap = {
@@ -617,54 +604,188 @@ const useHeatmapCompareStore = create()((set, get) => {
617
604
  };
618
605
  });
619
606
 
620
- const useRegisterConfig = () => {
621
- const mode = useHeatmapConfigStore((state) => state.mode);
622
- const width = useHeatmapConfigStore((state) => state.width);
623
- const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
624
- const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
625
- const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
626
- useEffect(() => {
627
- setIsRendering(true);
628
- setTimeout(() => {
629
- setIsRendering(false);
630
- }, 1000);
631
- }, [mode, width, sidebarWidth, heatmapType]);
632
- };
633
-
634
- const useRegisterControl = (control) => {
635
- const registerControl = useHeatmapControlStore((state) => state.registerControl);
636
- registerControl('Sidebar', control.Sidebar);
637
- registerControl('SidebarActivator', control.SidebarActivator);
638
- registerControl('TopBar', control.TopBar);
639
- registerControl('Toolbar', control.Toolbar);
640
- registerControl('MetricBar', control.MetricBar);
641
- registerControl('VizLoading', control.VizLoading);
642
- registerControl('ElementCallout', control.ElementCallout);
607
+ const initialState = {
608
+ payloads: [],
609
+ htmlContent: '',
610
+ };
611
+ const useHeatmapLiveStore = create()((set) => {
612
+ return {
613
+ ...initialState,
614
+ reset: () => set(initialState),
615
+ setPayloads: (payloads) => set({ payloads }),
616
+ addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
617
+ setHtmlContent: (htmlContent) => set({ htmlContent }),
618
+ };
619
+ });
620
+
621
+ const useHeatmapSingleStore = create()(subscribeWithSelector((set) => {
622
+ return {
623
+ vizRef: { [DEFAULT_VIEW_ID]: null },
624
+ iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
625
+ wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
626
+ wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
627
+ setVizRef: (vizRef, viewId = DEFAULT_VIEW_ID) => set((state) => ({
628
+ vizRef: { ...state.vizRef, [viewId]: vizRef },
629
+ })),
630
+ setIframeHeight: (iframeHeight, viewId = DEFAULT_VIEW_ID) => {
631
+ set((state) => ({
632
+ iframeHeight: { ...state.iframeHeight, [viewId]: iframeHeight },
633
+ }));
634
+ },
635
+ setWrapperHeight: (wrapperHeight, viewId = DEFAULT_VIEW_ID) => {
636
+ set((state) => ({
637
+ wrapperHeight: { ...state.wrapperHeight, [viewId]: wrapperHeight },
638
+ }));
639
+ },
640
+ setWrapperWidth: (wrapperWidth, viewId = DEFAULT_VIEW_ID) => {
641
+ set((state) => ({
642
+ wrapperWidth: { ...state.wrapperWidth, [viewId]: wrapperWidth },
643
+ }));
644
+ },
645
+ copyView: (fromViewId, toViewId) => set((state) => ({
646
+ // Don't copy vizRef - each view needs its own visualizer instance
647
+ iframeHeight: { ...state.iframeHeight, [toViewId]: state.iframeHeight[fromViewId] ?? 0 },
648
+ wrapperHeight: {
649
+ ...state.wrapperHeight,
650
+ [toViewId]: state.wrapperHeight[fromViewId] ?? 0,
651
+ },
652
+ })),
653
+ clearView: (viewId) => set((state) => {
654
+ const newVizRef = { ...state.vizRef };
655
+ const newIframeHeight = { ...state.iframeHeight };
656
+ const newWrapperHeight = { ...state.wrapperHeight };
657
+ const newWrapperWidth = { ...state.wrapperWidth };
658
+ delete newVizRef[viewId];
659
+ delete newIframeHeight[viewId];
660
+ delete newWrapperHeight[viewId];
661
+ delete newWrapperWidth[viewId];
662
+ return {
663
+ vizRef: newVizRef,
664
+ iframeHeight: newIframeHeight,
665
+ wrapperHeight: newWrapperHeight,
666
+ wrapperWidth: newWrapperWidth,
667
+ };
668
+ }),
669
+ resetAll: () => set({
670
+ vizRef: { [DEFAULT_VIEW_ID]: null },
671
+ iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
672
+ wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
673
+ wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
674
+ }),
675
+ };
676
+ }));
677
+
678
+ const useRegisterConfig = () => {
679
+ const mode = useHeatmapConfigStore((state) => state.mode);
680
+ const width = useHeatmapConfigStore((state) => state.width);
681
+ const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
682
+ const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
683
+ const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
684
+ useEffect(() => {
685
+ setIsRendering(true);
686
+ setTimeout(() => {
687
+ setIsRendering(false);
688
+ }, 1000);
689
+ }, [mode, width, sidebarWidth, heatmapType]);
690
+ };
691
+
692
+ const useRegisterControl = (control) => {
693
+ const registerControl = useHeatmapControlStore((state) => state.registerControl);
694
+ registerControl('Sidebar', control.Sidebar);
695
+ registerControl('SidebarActivator', control.SidebarActivator);
696
+ registerControl('TopBar', control.TopBar);
697
+ registerControl('Toolbar', control.Toolbar);
698
+ registerControl('MetricBar', control.MetricBar);
699
+ registerControl('VizLoading', control.VizLoading);
700
+ registerControl('ElementCallout', control.ElementCallout);
643
701
  };
644
702
 
645
- /**
646
- * Context to provide viewId to components
647
- * Used in compare mode to isolate data between views
648
- */
649
703
  const ViewIdContext = createContext(undefined);
650
- /**
651
- * Hook to get current viewId
652
- * Returns DEFAULT_VIEW_ID if not in a ViewIdContext (single mode)
653
- */
654
- const useViewId = () => {
704
+ const useViewIdContext = () => {
655
705
  const viewId = useContext(ViewIdContext);
656
706
  return viewId || DEFAULT_VIEW_ID;
657
707
  };
658
- /**
659
- * Hook to check if currently in compare mode
660
- */
661
- const useIsCompareMode = () => {
662
- const viewId = useContext(ViewIdContext);
663
- return viewId !== undefined && viewId !== DEFAULT_VIEW_ID;
708
+
709
+ const useHeatmapAreaClick = (props) => {
710
+ const viewId = props?.viewId || useViewIdContext();
711
+ // Optimized: Only subscribe to specific viewId slice
712
+ const selectedArea = useHeatmapAreaClickStore((state) => state.selectedArea[viewId] ?? null);
713
+ const hoveredArea = useHeatmapAreaClickStore((state) => state.hoveredArea[viewId] ?? null);
714
+ const areas = useHeatmapAreaClickStore((state) => state.areas[viewId] ?? []);
715
+ const isEditingMode = useHeatmapAreaClickStore((state) => state.isEditingMode[viewId] ?? false);
716
+ // Get setters from store
717
+ const setSelectedAreaStore = useHeatmapAreaClickStore((state) => state.setSelectedArea);
718
+ const setHoveredAreaStore = useHeatmapAreaClickStore((state) => state.setHoveredArea);
719
+ const setAreasStore = useHeatmapAreaClickStore((state) => state.setAreas);
720
+ const setIsEditingModeStore = useHeatmapAreaClickStore((state) => state.setIsEditingMode);
721
+ const addAreaStore = useHeatmapAreaClickStore((state) => state.addArea);
722
+ const removeAreaStore = useHeatmapAreaClickStore((state) => state.removeArea);
723
+ const updateAreaStore = useHeatmapAreaClickStore((state) => state.updateArea);
724
+ const clearAreasStore = useHeatmapAreaClickStore((state) => state.clearAreas);
725
+ // Memoize operations to prevent unnecessary re-renders
726
+ const memoizedOperations = useMemo(() => ({
727
+ setSelectedArea: (area) => setSelectedAreaStore(area, viewId),
728
+ setHoveredArea: (area) => setHoveredAreaStore(area, viewId),
729
+ setAreas: (areas) => setAreasStore(areas, viewId),
730
+ setIsEditingMode: (isEditing) => setIsEditingModeStore(isEditing, viewId),
731
+ addArea: (area) => addAreaStore(area, viewId),
732
+ removeArea: (areaId) => removeAreaStore(areaId, viewId),
733
+ updateArea: (areaId, updates) => updateAreaStore(areaId, updates, viewId),
734
+ clearAreas: () => clearAreasStore(viewId),
735
+ }), [
736
+ setSelectedAreaStore,
737
+ setHoveredAreaStore,
738
+ setAreasStore,
739
+ setIsEditingModeStore,
740
+ addAreaStore,
741
+ removeAreaStore,
742
+ updateAreaStore,
743
+ clearAreasStore,
744
+ viewId,
745
+ ]);
746
+ return {
747
+ selectedArea,
748
+ hoveredArea,
749
+ areas,
750
+ isEditingMode,
751
+ ...memoizedOperations,
752
+ };
753
+ };
754
+
755
+ const useHeatmapClick = (props) => {
756
+ const viewId = props?.viewId || useViewIdContext();
757
+ const state = useHeatmapClickStore((store) => store.state[viewId] ?? { hideSidebar: false });
758
+ const selectedElement = useHeatmapClickStore((store) => store.selectedElement[viewId] ?? null);
759
+ const hoveredElement = useHeatmapClickStore((store) => store.hoveredElement[viewId] ?? null);
760
+ const shouldShowCallout = useHeatmapClickStore((store) => store.shouldShowCallout[viewId] ?? false);
761
+ const setStateStore = useHeatmapClickStore((store) => store.setState);
762
+ const setSelectedElementStore = useHeatmapClickStore((store) => store.setSelectedElement);
763
+ const setHoveredElementStore = useHeatmapClickStore((store) => store.setHoveredElement);
764
+ const setShouldShowCalloutStore = useHeatmapClickStore((store) => store.setShouldShowCallout);
765
+ const memoizedSetters = useMemo(() => ({
766
+ setState: (newState) => setStateStore(newState, viewId),
767
+ setSelectedElement: (element) => setSelectedElementStore(element, viewId),
768
+ setHoveredElement: (element) => setHoveredElementStore(element, viewId),
769
+ setShouldShowCallout: (value) => setShouldShowCalloutStore(value, viewId),
770
+ }), [
771
+ setStateStore,
772
+ setSelectedElementStore,
773
+ setHoveredElementStore,
774
+ setShouldShowCalloutStore,
775
+ viewId,
776
+ ]);
777
+ return {
778
+ state,
779
+ selectedElement,
780
+ hoveredElement,
781
+ shouldShowCallout,
782
+ // Setters (auto-inject viewId)
783
+ ...memoizedSetters,
784
+ };
664
785
  };
665
786
 
666
787
  const useHeatmapData = (props) => {
667
- const viewId = props?.viewId || useViewId();
788
+ const viewId = props?.viewId || useViewIdContext();
668
789
  const data = useHeatmapDataStore((state) => state.data[viewId]);
669
790
  const clickmap = useHeatmapDataStore((state) => state.clickmap[viewId]);
670
791
  const scrollmap = useHeatmapDataStore((state) => state.scrollmap[viewId]);
@@ -689,40 +810,30 @@ const useHeatmapData = (props) => {
689
810
  };
690
811
  };
691
812
 
692
- const useHeatmapInteraction = (props) => {
693
- const viewId = props?.viewId || useViewId();
694
- const state = useHeatmapInteractionStore((store) => store.state[viewId] ?? { hideSidebar: false });
695
- const selectedElement = useHeatmapInteractionStore((store) => store.selectedElement[viewId] ?? null);
696
- const hoveredElement = useHeatmapInteractionStore((store) => store.hoveredElement[viewId] ?? null);
697
- const shouldShowCallout = useHeatmapInteractionStore((store) => store.shouldShowCallout[viewId] ?? false);
698
- const setStateStore = useHeatmapInteractionStore((store) => store.setState);
699
- const setSelectedElementStore = useHeatmapInteractionStore((store) => store.setSelectedElement);
700
- const setHoveredElementStore = useHeatmapInteractionStore((store) => store.setHoveredElement);
701
- const setShouldShowCalloutStore = useHeatmapInteractionStore((store) => store.setShouldShowCallout);
813
+ const useHeatmapScroll = (props) => {
814
+ const viewId = props?.viewId || useViewIdContext();
815
+ const zones = useHeatmapVizScrollmapStore((store) => store.zones[viewId] ?? []);
816
+ const hoveredZone = useHeatmapVizScrollmapStore((store) => store.hoveredZone[viewId] ?? null);
817
+ const showMinimap = useHeatmapVizScrollmapStore((store) => store.showMinimap[viewId] ?? true);
818
+ const setZonesStore = useHeatmapVizScrollmapStore((store) => store.setZones);
819
+ const setHoveredZoneStore = useHeatmapVizScrollmapStore((store) => store.setHoveredZone);
820
+ const setShowMinimapStore = useHeatmapVizScrollmapStore((store) => store.setShowMinimap);
702
821
  const memoizedSetters = useMemo(() => ({
703
- setState: (newState) => setStateStore(newState, viewId),
704
- setSelectedElement: (element) => setSelectedElementStore(element, viewId),
705
- setHoveredElement: (element) => setHoveredElementStore(element, viewId),
706
- setShouldShowCallout: (value) => setShouldShowCalloutStore(value, viewId),
707
- }), [
708
- setStateStore,
709
- setSelectedElementStore,
710
- setHoveredElementStore,
711
- setShouldShowCalloutStore,
712
- viewId,
713
- ]);
822
+ setZones: (newZones) => setZonesStore(newZones, viewId),
823
+ setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
824
+ setShowMinimap: (value) => setShowMinimapStore(value, viewId),
825
+ }), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
714
826
  return {
715
- state,
716
- selectedElement,
717
- hoveredElement,
718
- shouldShowCallout,
827
+ zones,
828
+ hoveredZone,
829
+ showMinimap,
719
830
  // Setters (auto-inject viewId)
720
831
  ...memoizedSetters,
721
832
  };
722
833
  };
723
834
 
724
835
  const useHeatmapViz = (props) => {
725
- const viewId = props?.viewId || useViewId();
836
+ const viewId = props?.viewId || useViewIdContext();
726
837
  // Viz store
727
838
  const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
728
839
  const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
@@ -780,28 +891,6 @@ const useHeatmapViz = (props) => {
780
891
  };
781
892
  };
782
893
 
783
- const useHeatmapVizScrollmap = (props) => {
784
- const viewId = props?.viewId || useViewId();
785
- const zones = useHeatmapVizScrollmapStore((store) => store.zones[viewId] ?? []);
786
- const hoveredZone = useHeatmapVizScrollmapStore((store) => store.hoveredZone[viewId] ?? null);
787
- const showMinimap = useHeatmapVizScrollmapStore((store) => store.showMinimap[viewId] ?? true);
788
- const setZonesStore = useHeatmapVizScrollmapStore((store) => store.setZones);
789
- const setHoveredZoneStore = useHeatmapVizScrollmapStore((store) => store.setHoveredZone);
790
- const setShowMinimapStore = useHeatmapVizScrollmapStore((store) => store.setShowMinimap);
791
- const memoizedSetters = useMemo(() => ({
792
- setZones: (newZones) => setZonesStore(newZones, viewId),
793
- setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
794
- setShowMinimap: (value) => setShowMinimapStore(value, viewId),
795
- }), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
796
- return {
797
- zones,
798
- hoveredZone,
799
- showMinimap,
800
- // Setters (auto-inject viewId)
801
- ...memoizedSetters,
802
- };
803
- };
804
-
805
894
  /**
806
895
  * Hook to handle copying and clearing view data across all stores
807
896
  */
@@ -809,24 +898,28 @@ const useHeatmapCopyView = () => {
809
898
  const copyDataView = useHeatmapDataStore((state) => state.copyView);
810
899
  const copyVizView = useHeatmapVizStore((state) => state.copyView);
811
900
  const copySingleView = useHeatmapSingleStore((state) => state.copyView);
812
- const copyInteractionView = useHeatmapInteractionStore((state) => state.copyView);
901
+ const copyInteractionView = useHeatmapClickStore((state) => state.copyView);
813
902
  const copyVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.copyView);
903
+ const copyAreaClickView = useHeatmapAreaClickStore((state) => state.copyView);
814
904
  const clearDataView = useHeatmapDataStore((state) => state.clearView);
815
905
  const clearVizView = useHeatmapVizStore((state) => state.clearView);
816
906
  const clearSingleView = useHeatmapSingleStore((state) => state.clearView);
817
- const clearInteractionView = useHeatmapInteractionStore((state) => state.clearView);
907
+ const clearInteractionView = useHeatmapClickStore((state) => state.clearView);
818
908
  const clearVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.clearView);
909
+ const clearAreaClickView = useHeatmapAreaClickStore((state) => state.clearView);
819
910
  const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
820
911
  const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
821
912
  const resetSingleAll = useHeatmapSingleStore((state) => state.resetAll);
822
- const resetInteractionAll = useHeatmapInteractionStore((state) => state.resetAll);
913
+ const resetInteractionAll = useHeatmapClickStore((state) => state.resetAll);
823
914
  const resetVizScrollmapAll = useHeatmapVizScrollmapStore((state) => state.resetAll);
915
+ const resetAreaClickAll = useHeatmapAreaClickStore((state) => state.resetAll);
824
916
  const copyView = (fromViewId, toViewId) => {
825
917
  copyDataView(fromViewId, toViewId);
826
918
  copyVizView(fromViewId, toViewId);
827
919
  copySingleView(fromViewId, toViewId);
828
920
  copyInteractionView(fromViewId, toViewId);
829
921
  copyVizScrollmapView(fromViewId, toViewId);
922
+ copyAreaClickView(fromViewId, toViewId);
830
923
  };
831
924
  const copyViewToMultiple = (fromViewId, toViewIds) => {
832
925
  toViewIds.forEach((toViewId) => {
@@ -839,6 +932,7 @@ const useHeatmapCopyView = () => {
839
932
  clearSingleView(viewId);
840
933
  clearInteractionView(viewId);
841
934
  clearVizScrollmapView(viewId);
935
+ clearAreaClickView(viewId);
842
936
  };
843
937
  const clearMultipleViews = (viewIds) => {
844
938
  viewIds.forEach((viewId) => {
@@ -851,6 +945,7 @@ const useHeatmapCopyView = () => {
851
945
  resetSingleAll();
852
946
  resetInteractionAll();
853
947
  resetVizScrollmapAll();
948
+ resetAreaClickAll();
854
949
  };
855
950
  return {
856
951
  copyView,
@@ -859,49 +954,497 @@ const useHeatmapCopyView = () => {
859
954
  clearMultipleViews,
860
955
  resetAll,
861
956
  };
862
- };
863
-
864
- const useRegisterData = (data, dataInfo) => {
865
- const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
866
- const { setData, setDataInfo } = useHeatmapData();
867
- const handleSetData = useCallback((data) => {
868
- if (!data)
869
- return;
870
- setData(data);
871
- setIsRendering(false);
872
- }, [data]);
873
- const handleSetDataInfo = useCallback((dataInfo) => {
874
- if (!dataInfo)
875
- return;
876
- setDataInfo(dataInfo);
877
- }, [setDataInfo]);
878
- useEffect(() => {
879
- handleSetData(data);
880
- }, [data]);
881
- useEffect(() => {
882
- handleSetDataInfo(dataInfo);
883
- }, [dataInfo]);
884
- };
957
+ };
958
+
959
+ const useRegisterData = (data, dataInfo) => {
960
+ const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
961
+ const { setData, setDataInfo } = useHeatmapData();
962
+ const handleSetData = useCallback((data) => {
963
+ if (!data)
964
+ return;
965
+ setData(data);
966
+ setIsRendering(false);
967
+ }, [data]);
968
+ const handleSetDataInfo = useCallback((dataInfo) => {
969
+ if (!dataInfo)
970
+ return;
971
+ setDataInfo(dataInfo);
972
+ }, [setDataInfo]);
973
+ useEffect(() => {
974
+ handleSetData(data);
975
+ }, [data]);
976
+ useEffect(() => {
977
+ handleSetDataInfo(dataInfo);
978
+ }, [dataInfo]);
979
+ };
980
+
981
+ const useRegisterHeatmap = ({ clickmap, scrollmap }) => {
982
+ const { setClickmap, setScrollmap } = useHeatmapData();
983
+ const handleSetClickmap = useCallback((clickmap) => {
984
+ if (!clickmap)
985
+ return;
986
+ setClickmap(clickmap);
987
+ }, [clickmap]);
988
+ const handleSetScrollmap = useCallback((scrollmap) => {
989
+ if (!scrollmap)
990
+ return;
991
+ setScrollmap(scrollmap);
992
+ }, [scrollmap]);
993
+ useEffect(() => {
994
+ handleSetClickmap(clickmap);
995
+ }, [clickmap]);
996
+ useEffect(() => {
997
+ handleSetScrollmap(scrollmap);
998
+ }, [scrollmap]);
999
+ };
1000
+
1001
+ /**
1002
+ * Create an observable value with subscribe/unsubscribe pattern
1003
+ */
1004
+ function createObservable(initialValue) {
1005
+ const subscribers = new Set();
1006
+ const observable = {
1007
+ value: initialValue,
1008
+ observe: (callback) => {
1009
+ subscribers.add(callback);
1010
+ // Immediately call with current value
1011
+ if (observable.value !== undefined) {
1012
+ callback(observable.value);
1013
+ }
1014
+ },
1015
+ unobserve: (callback) => {
1016
+ subscribers.delete(callback);
1017
+ },
1018
+ update: (newValue) => {
1019
+ observable.value = newValue;
1020
+ // Notify all subscribers
1021
+ subscribers.forEach((callback) => {
1022
+ callback(newValue);
1023
+ });
1024
+ },
1025
+ };
1026
+ return observable;
1027
+ }
1028
+
1029
+ function isElementInViewport(elementRect, visualRef, scale) {
1030
+ if (!elementRect)
1031
+ return false;
1032
+ const visualRect = visualRef.current?.getBoundingClientRect();
1033
+ if (!visualRect)
1034
+ return false;
1035
+ // Element position relative to the document (or container's content)
1036
+ const elementTop = elementRect.top * scale;
1037
+ const elementBottom = (elementRect.top + elementRect.height) * scale;
1038
+ // Current scroll position
1039
+ const scrollTop = visualRef.current?.scrollTop || 0;
1040
+ const viewportHeight = visualRect.height;
1041
+ // Visible viewport range in the scrollable content
1042
+ const viewportTop = scrollTop;
1043
+ const viewportBottom = scrollTop + viewportHeight;
1044
+ // Check if element is within the visible viewport
1045
+ // Element is visible if it overlaps with the viewport
1046
+ return elementBottom > viewportTop && elementTop < viewportBottom;
1047
+ }
1048
+
1049
+ const AREA_HOVER_BOX_SHADOW = '0 0 0 1px #0078D4, 0 0 0 1px #0078D4 inset, 0 0 0 2px white inset';
1050
+ const AREA_HOVER_ELEMENT_ID = 'clarity-edit-hover';
1051
+ const AREA_MAP_DIV_ATTRIBUTE = 'data-clarity-area-map-div';
1052
+ const AREA_COLOR_GRADIENT = [
1053
+ [0, 0, 255], // Blue
1054
+ [0, 255, 255], // Cyan
1055
+ [0, 255, 0], // Green
1056
+ [255, 255, 0], // Yellow
1057
+ [255, 0, 0], // Red
1058
+ ];
1059
+
1060
+ const CALLOUT_PADDING = 0;
1061
+ const CALLOUT_ARROW_SIZE = 8;
1062
+ const CALLOUT_HORIZONTAL_OFFSET = 0;
1063
+ const CLICKED_ELEMENT_ID_BASE = 'gx-hm-clicked-element';
1064
+ const SECONDARY_CLICKED_ELEMENT_ID_BASE = 'gx-hm-secondary-clicked-element';
1065
+ const HOVERED_ELEMENT_ID_BASE = 'gx-hm-hovered-element';
1066
+ const SECONDARY_HOVERED_ELEMENT_ID_BASE = 'gx-hm-secondary-hovered-element';
1067
+
1068
+ /**
1069
+ * Get color from click distribution percentage (0-100)
1070
+ */
1071
+ function getColorFromClickDist(clickDist) {
1072
+ // Ensure clickDist is in range [0, 100]
1073
+ const normalizedDist = Math.max(0, Math.min(100, clickDist));
1074
+ // Calculate gradient index
1075
+ const maxIndex = AREA_COLOR_GRADIENT.length - 1;
1076
+ const index = Math.floor((normalizedDist / 100) * maxIndex);
1077
+ const clampedIndex = Math.min(index, maxIndex);
1078
+ const [r, g, b] = AREA_COLOR_GRADIENT[clampedIndex];
1079
+ // Return rgba with 60% opacity
1080
+ return `rgba(${r}, ${g}, ${b}, 0.6)`;
1081
+ }
1082
+ /**
1083
+ * Get hover color (slightly lighter) from click distribution
1084
+ */
1085
+ function getHoverColorFromClickDist(clickDist) {
1086
+ const normalizedDist = Math.max(0, Math.min(100, clickDist));
1087
+ const maxIndex = AREA_COLOR_GRADIENT.length - 1;
1088
+ const index = Math.floor((normalizedDist / 100) * maxIndex);
1089
+ const clampedIndex = Math.min(index, maxIndex);
1090
+ const [r, g, b] = AREA_COLOR_GRADIENT[clampedIndex];
1091
+ // Return rgba with 80% opacity for hover
1092
+ return `rgba(${r}, ${g}, ${b}, 0.8)`;
1093
+ }
1094
+ /**
1095
+ * Calculate click distribution percentage from total clicks
1096
+ */
1097
+ function calculateClickDistribution(elementClicks, totalClicks) {
1098
+ if (totalClicks === 0)
1099
+ return 0;
1100
+ return (elementClicks / totalClicks) * 100;
1101
+ }
1102
+
1103
+ /**
1104
+ * Get element position and dimensions for rendering inside iframe
1105
+ *
1106
+ * This function calculates position relative to the iframe's document,
1107
+ * accounting for scroll position. Suitable for elements rendered in
1108
+ * shadow DOM inside the iframe with absolute positioning.
1109
+ */
1110
+ function getElementRect(element, _shadowRoot) {
1111
+ const rect = element.getBoundingClientRect();
1112
+ const width = rect.width;
1113
+ const height = rect.height;
1114
+ // Get the document to access scroll position
1115
+ const doc = element.ownerDocument || document;
1116
+ // Get scroll offset from documentElement or body
1117
+ const scrollTop = doc.documentElement?.scrollTop || doc.body?.scrollTop || 0;
1118
+ const scrollLeft = doc.documentElement?.scrollLeft || doc.body?.scrollLeft || 0;
1119
+ // Calculate position relative to document (not viewport)
1120
+ // getBoundingClientRect() is relative to viewport, so add scroll offset
1121
+ const top = rect.top + scrollTop;
1122
+ const left = rect.left + scrollLeft;
1123
+ // For absolute positioning calculations (overlap detection)
1124
+ const absoluteLeft = left;
1125
+ const absoluteTop = top;
1126
+ const absoluteRight = absoluteLeft + width;
1127
+ const absoluteBottom = absoluteTop + height;
1128
+ return {
1129
+ width,
1130
+ height,
1131
+ top,
1132
+ left,
1133
+ absoluteLeft,
1134
+ absoluteTop,
1135
+ absoluteRight,
1136
+ absoluteBottom,
1137
+ outOfBounds: false,
1138
+ };
1139
+ }
1140
+ /**
1141
+ * Check if element has CSS position: fixed
1142
+ */
1143
+ function isElementFixed(element) {
1144
+ if (getComputedStyle(element).position === 'fixed') {
1145
+ return true;
1146
+ }
1147
+ if (element.nodeName === 'HTML') {
1148
+ return false;
1149
+ }
1150
+ const parent = element.parentElement;
1151
+ return parent ? isElementFixed(parent) : false;
1152
+ }
1153
+ /**
1154
+ * Check if two areas overlap
1155
+ */
1156
+ function doAreasOverlap(area1, area2) {
1157
+ const r1 = area1.rect.value;
1158
+ const r2 = area2.rect.value;
1159
+ if (!r1 || !r2)
1160
+ return false;
1161
+ return ((r1.absoluteBottom > r2.absoluteTop &&
1162
+ r1.absoluteTop < r2.absoluteBottom &&
1163
+ r1.absoluteRight > r2.absoluteLeft &&
1164
+ r1.absoluteLeft < r2.absoluteRight) ||
1165
+ (r2.absoluteBottom > r1.absoluteTop &&
1166
+ r2.absoluteTop < r1.absoluteBottom &&
1167
+ r2.absoluteRight > r1.absoluteLeft &&
1168
+ r2.absoluteLeft < r1.absoluteRight));
1169
+ }
1170
+ /**
1171
+ * Check if area1 is completely inside area2
1172
+ */
1173
+ function isAreaContainedIn(area1, area2) {
1174
+ const r1 = area1.rect.value;
1175
+ const r2 = area2.rect.value;
1176
+ if (!r1 || !r2)
1177
+ return false;
1178
+ return (r1.absoluteTop >= r2.absoluteTop &&
1179
+ r1.absoluteBottom <= r2.absoluteBottom &&
1180
+ r1.absoluteLeft >= r2.absoluteLeft &&
1181
+ r1.absoluteRight <= r2.absoluteRight);
1182
+ }
1183
+ /**
1184
+ * Check if element contains another element in DOM tree
1185
+ */
1186
+ function isElementAncestorOf(ancestor, descendant, doc) {
1187
+ return ancestor.contains(descendant);
1188
+ }
1189
+ /**
1190
+ * Get all elements at a specific point, including shadow DOM
1191
+ */
1192
+ function getElementsAtPoint$1(doc, x, y, filter, visited = new Set()) {
1193
+ const elements = doc.elementsFromPoint(x, y);
1194
+ const filtered = elements.find(filter);
1195
+ // Check shadow DOM
1196
+ if (filtered?.shadowRoot && !visited.has(filtered.shadowRoot)) {
1197
+ visited.add(filtered.shadowRoot);
1198
+ return getElementsAtPoint$1(filtered.shadowRoot, x, y, filter, visited);
1199
+ }
1200
+ return elements;
1201
+ }
1202
+ /**
1203
+ * Check if element should be selectable for area creation
1204
+ */
1205
+ function isElementSelectable(element, index, elements) {
1206
+ // Skip if it's an area map div
1207
+ if (element.hasAttribute(AREA_MAP_DIV_ATTRIBUTE)) {
1208
+ return false;
1209
+ }
1210
+ // Skip first element if it's BODY and there are other elements
1211
+ if (index === 0 && elements.length > 1 && element.nodeName === 'BODY') {
1212
+ return false;
1213
+ }
1214
+ return true;
1215
+ }
1216
+ /**
1217
+ * Sort areas by click distribution (descending)
1218
+ */
1219
+ function sortAreasByClickDist(areas) {
1220
+ return [...areas].sort((a, b) => {
1221
+ // Higher clickDist first
1222
+ if (a.clickDist !== b.clickDist) {
1223
+ return b.clickDist - a.clickDist;
1224
+ }
1225
+ // Then by total clicks
1226
+ return b.totalclicks - a.totalclicks;
1227
+ });
1228
+ }
1229
+ /**
1230
+ * Create shadow root or return existing one
1231
+ */
1232
+ function getOrCreateShadowRoot(element) {
1233
+ if (element.shadowRoot) {
1234
+ return element.shadowRoot;
1235
+ }
1236
+ return element.attachShadow({ mode: 'open' });
1237
+ }
1238
+ /**
1239
+ * Check if rect is too small to show label
1240
+ */
1241
+ function isRectTooSmallForLabel(rect) {
1242
+ return rect.width < 67 || rect.height < 30;
1243
+ }
1244
+
1245
+ function getElementSelector(element) {
1246
+ if (element.id) {
1247
+ return `#${element.id}`;
1248
+ }
1249
+ if (element.className) {
1250
+ const classes = Array.from(element.classList).join('.');
1251
+ if (classes) {
1252
+ return `${element.tagName.toLowerCase()}.${classes}`;
1253
+ }
1254
+ }
1255
+ return element.tagName.toLowerCase();
1256
+ }
1257
+ function buildAreaNode(element, hash, elementMapInfo, totalClicks, shadowRoot) {
1258
+ const elementInfo = elementMapInfo[hash];
1259
+ const elementClicks = elementInfo?.totalclicks || 0;
1260
+ const clickDist = calculateClickDistribution(elementClicks, totalClicks);
1261
+ const rect = getElementRect(element);
1262
+ const color = getColorFromClickDist(clickDist);
1263
+ const hoverColor = getHoverColorFromClickDist(clickDist);
1264
+ const areaNode = {
1265
+ kind: 'area',
1266
+ id: `${hash}_${Date.now()}`,
1267
+ hash,
1268
+ selector: elementInfo?.selector || getElementSelector(element),
1269
+ // DOM references
1270
+ element,
1271
+ areaElement: null,
1272
+ shadowElement: null,
1273
+ shadowStyleElement: null,
1274
+ // Graph structure
1275
+ parentNode: null,
1276
+ childNodes: new Set(),
1277
+ // Position
1278
+ rect: createObservable(rect),
1279
+ isFixed: isElementFixed(element),
1280
+ priority: false,
1281
+ // Click tracking
1282
+ totalclicks: elementClicks,
1283
+ cumulativeClicks: elementClicks,
1284
+ cumulativeMaxClicks: totalClicks,
1285
+ clickDist,
1286
+ hasClickInfo: true,
1287
+ // Visual
1288
+ color,
1289
+ hoverColor,
1290
+ // Lifecycle
1291
+ changeObserver: null,
1292
+ };
1293
+ return areaNode;
1294
+ }
1295
+ function getTopElementsByClicks(elementMapInfo, topN = 10) {
1296
+ const elements = Object.entries(elementMapInfo)
1297
+ .map(([hash, info]) => ({
1298
+ hash,
1299
+ totalclicks: info.totalclicks,
1300
+ selector: info.selector || '',
1301
+ }))
1302
+ .sort((a, b) => b.totalclicks - a.totalclicks)
1303
+ .slice(0, topN);
1304
+ return elements;
1305
+ }
885
1306
 
886
- const useRegisterHeatmap = ({ clickmap, scrollmap }) => {
887
- const { setClickmap, setScrollmap } = useHeatmapData();
888
- const handleSetClickmap = useCallback((clickmap) => {
889
- if (!clickmap)
1307
+ /**
1308
+ * Resolve overlapping areas by priority rules
1309
+ *
1310
+ * Priority Rules (in order):
1311
+ * 1. Priority flag (manually set areas win)
1312
+ * 2. Click distribution (higher % wins)
1313
+ * 3. Total clicks (more clicks wins)
1314
+ * 4. DOM containment (parent contains child, parent wins)
1315
+ * 5. Size (smaller areas win - more specific)
1316
+ */
1317
+ function resolveOverlaps(areas, iframeDocument) {
1318
+ if (areas.length === 0)
1319
+ return [];
1320
+ // Group overlapping areas
1321
+ const overlapGroups = findOverlapGroups(areas);
1322
+ // Resolve each group
1323
+ const visibleAreas = new Set();
1324
+ overlapGroups.forEach((group) => {
1325
+ const winner = resolveOverlapGroup(group, iframeDocument);
1326
+ visibleAreas.add(winner);
1327
+ });
1328
+ // Add non-overlapping areas
1329
+ areas.forEach((area) => {
1330
+ const hasOverlap = overlapGroups.some((group) => group.areas.includes(area));
1331
+ if (!hasOverlap) {
1332
+ visibleAreas.add(area);
1333
+ }
1334
+ });
1335
+ return Array.from(visibleAreas);
1336
+ }
1337
+ /**
1338
+ * Find groups of overlapping areas
1339
+ */
1340
+ function findOverlapGroups(areas) {
1341
+ const groups = [];
1342
+ const processed = new Set();
1343
+ areas.forEach((area) => {
1344
+ if (processed.has(area.id))
890
1345
  return;
891
- setClickmap(clickmap);
892
- }, [clickmap]);
893
- const handleSetScrollmap = useCallback((scrollmap) => {
894
- if (!scrollmap)
1346
+ // Find all areas that overlap with this one
1347
+ const overlapping = areas.filter((other) => other.id !== area.id && doAreasOverlap(area, other));
1348
+ if (overlapping.length === 0) {
1349
+ // No overlap, skip grouping
895
1350
  return;
896
- setScrollmap(scrollmap);
897
- }, [scrollmap]);
898
- useEffect(() => {
899
- handleSetClickmap(clickmap);
900
- }, [clickmap]);
901
- useEffect(() => {
902
- handleSetScrollmap(scrollmap);
903
- }, [scrollmap]);
904
- };
1351
+ }
1352
+ // Create group with this area and all overlapping
1353
+ const groupAreas = [area, ...overlapping];
1354
+ groupAreas.forEach((a) => processed.add(a.id));
1355
+ // Placeholder - will be resolved later
1356
+ groups.push({
1357
+ areas: groupAreas,
1358
+ winner: area,
1359
+ hidden: [],
1360
+ });
1361
+ });
1362
+ return groups;
1363
+ }
1364
+ /**
1365
+ * Resolve a single overlap group to find the winner
1366
+ */
1367
+ function resolveOverlapGroup(group, iframeDocument) {
1368
+ const { areas } = group;
1369
+ if (areas.length === 1)
1370
+ return areas[0];
1371
+ // Sort by priority rules
1372
+ const sorted = [...areas].sort((a, b) => {
1373
+ // Rule 1: Priority flag
1374
+ if (a.priority !== b.priority) {
1375
+ return a.priority ? -1 : 1;
1376
+ }
1377
+ // Rule 2: Click distribution
1378
+ if (a.clickDist !== b.clickDist) {
1379
+ return b.clickDist - a.clickDist;
1380
+ }
1381
+ // Rule 3: Total clicks
1382
+ if (a.totalclicks !== b.totalclicks) {
1383
+ return b.totalclicks - a.totalclicks;
1384
+ }
1385
+ // Rule 4: DOM containment - parent beats child
1386
+ if (iframeDocument) {
1387
+ const aContainsB = isElementAncestorOf(a.element, b.element);
1388
+ const bContainsA = isElementAncestorOf(b.element, a.element);
1389
+ if (aContainsB)
1390
+ return -1; // a is parent, a wins
1391
+ if (bContainsA)
1392
+ return 1; // b is parent, b wins
1393
+ }
1394
+ // Rule 5: Size - smaller (more specific) wins
1395
+ const aSize = (a.rect.value?.width || 0) * (a.rect.value?.height || 0);
1396
+ const bSize = (b.rect.value?.width || 0) * (b.rect.value?.height || 0);
1397
+ return aSize - bSize;
1398
+ });
1399
+ const winner = sorted[0];
1400
+ group.winner = winner;
1401
+ group.hidden = sorted.slice(1);
1402
+ return winner;
1403
+ }
1404
+ /**
1405
+ * Filter out areas that are completely contained within others
1406
+ * and have lower priority
1407
+ */
1408
+ function filterContainedAreas(areas) {
1409
+ const visible = [];
1410
+ areas.forEach((area) => {
1411
+ // Check if this area is contained by a higher priority area
1412
+ const isContained = areas.some((other) => {
1413
+ if (other.id === area.id)
1414
+ return false;
1415
+ // Check containment
1416
+ if (!isAreaContainedIn(area, other))
1417
+ return false;
1418
+ // Check priority
1419
+ if (other.priority && !area.priority)
1420
+ return true;
1421
+ if (!other.priority && area.priority)
1422
+ return false;
1423
+ // Compare by click dist
1424
+ if (other.clickDist > area.clickDist)
1425
+ return true;
1426
+ if (other.clickDist < area.clickDist)
1427
+ return false;
1428
+ // Compare by total clicks
1429
+ return other.totalclicks > area.totalclicks;
1430
+ });
1431
+ if (!isContained) {
1432
+ visible.push(area);
1433
+ }
1434
+ });
1435
+ return visible;
1436
+ }
1437
+ /**
1438
+ * Get visible areas after resolving overlaps
1439
+ */
1440
+ function getVisibleAreas(areas, iframeDocument) {
1441
+ // First pass: filter contained areas
1442
+ let visible = filterContainedAreas(areas);
1443
+ // Second pass: resolve overlaps
1444
+ visible = resolveOverlaps(visible, iframeDocument);
1445
+ // Sort by click dist for rendering order
1446
+ return sortAreasByClickDist(visible);
1447
+ }
905
1448
 
906
1449
  function findLastSizeOfDom(data) {
907
1450
  const listDocs = data
@@ -931,39 +1474,28 @@ function decodePayloads(payload) {
931
1474
  try {
932
1475
  return decode(payload);
933
1476
  }
934
- catch (error) {
1477
+ catch (_error) {
935
1478
  return null;
936
1479
  }
937
1480
  }
938
1481
 
939
- function calculateRankPosition(rect, widthScale) {
940
- const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
941
- const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
942
- return {
943
- transform: `scale(${1.2 * widthScale})`,
944
- top: Number.isNaN(top) ? undefined : top,
945
- left: Number.isNaN(left) ? undefined : left,
946
- };
947
- }
948
- function isElementInViewport(elementRect, visualRef, scale) {
949
- if (!elementRect)
950
- return false;
951
- const visualRect = visualRef.current?.getBoundingClientRect();
952
- if (!visualRect)
953
- return false;
954
- // Element position relative to the document (or container's content)
955
- const elementTop = elementRect.top * scale;
956
- const elementBottom = (elementRect.top + elementRect.height) * scale;
957
- // Current scroll position
958
- const scrollTop = visualRef.current?.scrollTop || 0;
959
- const viewportHeight = visualRect.height;
960
- // Visible viewport range in the scrollable content
961
- const viewportTop = scrollTop;
962
- const viewportBottom = scrollTop + viewportHeight;
963
- // Check if element is within the visible viewport
964
- // Element is visible if it overlaps with the viewport
965
- return elementBottom > viewportTop && elementTop < viewportBottom;
966
- }
1482
+ /**
1483
+ * Generate unique element ID for a specific view
1484
+ * @param baseId - Base element ID
1485
+ * @param viewId - View ID
1486
+ * @returns Unique element ID (e.g., 'gx-hm-clicked-element-view-0')
1487
+ */
1488
+ const getElementId = (baseId, viewId) => {
1489
+ return `${baseId}-${viewId}`;
1490
+ };
1491
+ const getClickedElementId = (viewId, isSecondary = false) => {
1492
+ const baseId = isSecondary ? SECONDARY_CLICKED_ELEMENT_ID_BASE : CLICKED_ELEMENT_ID_BASE;
1493
+ return getElementId(baseId, viewId);
1494
+ };
1495
+ const getHoveredElementId = (viewId, isSecondary = false) => {
1496
+ const baseId = isSecondary ? SECONDARY_HOVERED_ELEMENT_ID_BASE : HOVERED_ELEMENT_ID_BASE;
1497
+ return getElementId(baseId, viewId);
1498
+ };
967
1499
 
968
1500
  function getElementLayout(element) {
969
1501
  if (!element?.getBoundingClientRect)
@@ -1021,9 +1553,15 @@ const buildElementInfo = (hash, rect, heatmapInfo) => {
1021
1553
  };
1022
1554
  };
1023
1555
 
1024
- const PADDING = 0;
1025
- const ARROW_SIZE = 8;
1026
- const HORIZONTAL_OFFSET = 0;
1556
+ function calculateRankPosition(rect, widthScale) {
1557
+ const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
1558
+ const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
1559
+ return {
1560
+ transform: `scale(${1.2 * widthScale})`,
1561
+ top: Number.isNaN(top) ? undefined : top,
1562
+ left: Number.isNaN(left) ? undefined : left,
1563
+ };
1564
+ }
1027
1565
 
1028
1566
  const getViewportDimensions = (containerElm) => {
1029
1567
  if (containerElm) {
@@ -1079,7 +1617,8 @@ const calculateHorizontalPlacementPosition = (targetRect, calloutRect, placement
1079
1617
 
1080
1618
  const isLeftPositionValid = (leftPos, calloutWidth, viewportWidth, padding, containerRect) => {
1081
1619
  if (containerRect) {
1082
- return leftPos >= containerRect.left + padding && leftPos + calloutWidth <= containerRect.right - padding;
1620
+ return (leftPos >= containerRect.left + padding &&
1621
+ leftPos + calloutWidth <= containerRect.right - padding);
1083
1622
  }
1084
1623
  return leftPos >= padding && leftPos + calloutWidth <= viewportWidth - padding;
1085
1624
  };
@@ -1164,14 +1703,15 @@ const constrainToViewport = (position, calloutRect, viewport, padding, container
1164
1703
  return { top, left };
1165
1704
  };
1166
1705
 
1167
- const calcCalloutPosition = ({ targetElm, calloutElm, setPosition, hozOffset = HORIZONTAL_OFFSET, alignment = 'center', containerElm, }) => {
1706
+ const calcCalloutPosition = (options) => {
1707
+ const { targetElm, calloutElm, setPosition, hozOffset = CALLOUT_HORIZONTAL_OFFSET, alignment = 'center', containerElm, } = options;
1168
1708
  return () => {
1169
1709
  // 1. Get dimensions
1170
1710
  const rectDimensions = getElementDimensions(targetElm, calloutElm);
1171
1711
  const viewport = getViewportDimensions(containerElm);
1172
1712
  const containerRect = containerElm?.getBoundingClientRect();
1173
- const padding = PADDING;
1174
- const arrowSize = ARROW_SIZE;
1713
+ const padding = CALLOUT_PADDING;
1714
+ const arrowSize = CALLOUT_ARROW_SIZE;
1175
1715
  // 2. Generate all position candidates
1176
1716
  const candidates = generateAllPositionCandidates(rectDimensions, viewport, alignment, hozOffset, padding, arrowSize, containerRect);
1177
1717
  // 3. Select best position
@@ -1695,74 +2235,242 @@ class IframeHelperFixer {
1695
2235
  console.error('[IframeHelper] Cannot access iframe document');
1696
2236
  this.config.onError?.(new Error('Cannot access iframe document'));
1697
2237
  return;
1698
- }
2238
+ }
2239
+ try {
2240
+ console.log('[IframeHelper] Processing viewport units...');
2241
+ // Create replacer instance
2242
+ this.replacer = new IframeStyleReplacer(this.iframe, this.config);
2243
+ // Create navigation blocker
2244
+ this.navigationBlocker = new IframeNavigationBlockerV2(this.iframe);
2245
+ // Run replacement
2246
+ const result = await this.replacer.run();
2247
+ console.log('[IframeHelper] Process completed:', result);
2248
+ // Trigger success callback
2249
+ this.config.onSuccess?.(result);
2250
+ // Dispatch custom event
2251
+ window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', {
2252
+ detail: result,
2253
+ }));
2254
+ }
2255
+ catch (error) {
2256
+ console.error('[IframeHelper] Failed to process:', error);
2257
+ this.config.onError?.(error);
2258
+ }
2259
+ }
2260
+ async recalculate() {
2261
+ console.log('[IframeHelper] Recalculating...');
2262
+ await this.process();
2263
+ }
2264
+ updateConfig(config) {
2265
+ this.config = { ...this.config, ...config };
2266
+ if (this.replacer) {
2267
+ this.replacer.updateConfig(config);
2268
+ }
2269
+ }
2270
+ enableNavigationBlocking() {
2271
+ this.navigationBlocker?.enable();
2272
+ }
2273
+ enableNavigationBlockingMessage() {
2274
+ this.navigationBlocker?.enableMessage();
2275
+ }
2276
+ disableNavigationBlocking() {
2277
+ this.navigationBlocker?.disable();
2278
+ }
2279
+ disableNavigationBlockingMessage() {
2280
+ this.navigationBlocker?.disableMessage();
2281
+ }
2282
+ destroy() {
2283
+ this.replacer = null;
2284
+ this.navigationBlocker?.destroy();
2285
+ this.navigationBlocker = null;
2286
+ console.log('[IframeHelper] Destroyed');
2287
+ }
2288
+ }
2289
+
2290
+ function initIframeHelperFixer(config) {
2291
+ const fixer = new IframeHelperFixer(config);
2292
+ window.addEventListener('iframe-dimensions-applied', ((e) => {
2293
+ const ev = e;
2294
+ console.log('[IframeHelper] Iframe dimensions finalized:', ev.detail);
2295
+ }));
2296
+ window.addEventListener('iframe-navigation-blocked', ((e) => {
2297
+ const ev = e;
2298
+ console.warn('[IframeHelper] Iframe tried to navigate to:', ev.detail.url);
2299
+ }));
2300
+ window.addEventListener('iframe-form-submit', ((e) => {
2301
+ const ev = e;
2302
+ console.log('[IframeHelper] Iframe form submitted:', ev.detail.data);
2303
+ }));
2304
+ return fixer;
2305
+ }
2306
+
2307
+ function useAreaEditMode({ iframeRef, onCreateArea, enabled = false, }) {
2308
+ const [hoveredElement, setHoveredElement] = useState(null);
2309
+ const [isHovering, setIsHovering] = useState(false);
2310
+ const { isEditingMode } = useHeatmapAreaClick();
2311
+ const iframeDocument = iframeRef.current?.contentDocument;
2312
+ const isActive = enabled && isEditingMode;
2313
+ const handleMouseMove = useCallback((e) => {
2314
+ if (!isActive || !iframeDocument)
2315
+ return;
2316
+ const elements = getElementsAtPoint$1(iframeDocument, e.clientX, e.clientY, isElementSelectable);
2317
+ // Find first selectable element
2318
+ const selectableElement = elements.find((el, index, arr) => isElementSelectable(el, index, arr));
2319
+ if (selectableElement && selectableElement !== hoveredElement) {
2320
+ setHoveredElement(selectableElement);
2321
+ setIsHovering(true);
2322
+ }
2323
+ else if (!selectableElement && hoveredElement) {
2324
+ setHoveredElement(null);
2325
+ setIsHovering(false);
2326
+ }
2327
+ }, [isActive, iframeDocument, hoveredElement]);
2328
+ const handleClick = useCallback((e) => {
2329
+ if (!isActive || !hoveredElement)
2330
+ return;
2331
+ e.stopPropagation();
2332
+ e.preventDefault();
2333
+ if (onCreateArea) {
2334
+ onCreateArea(hoveredElement);
2335
+ }
2336
+ }, [isActive, hoveredElement]);
2337
+ const handleMouseLeave = useCallback(() => {
2338
+ setHoveredElement(null);
2339
+ setIsHovering(false);
2340
+ }, []);
2341
+ useEffect(() => {
2342
+ if (!isActive || !iframeDocument) {
2343
+ setHoveredElement(null);
2344
+ setIsHovering(false);
2345
+ return;
2346
+ }
2347
+ // Throttle mouse move
2348
+ let rafId = null;
2349
+ const throttledMouseMove = (e) => {
2350
+ if (rafId)
2351
+ return;
2352
+ rafId = requestAnimationFrame(() => {
2353
+ handleMouseMove(e);
2354
+ rafId = null;
2355
+ });
2356
+ };
2357
+ iframeDocument.addEventListener('mousemove', throttledMouseMove);
2358
+ iframeDocument.addEventListener('click', handleClick);
2359
+ iframeDocument.addEventListener('mouseleave', handleMouseLeave);
2360
+ iframeDocument.addEventListener('scroll', handleMouseLeave);
2361
+ return () => {
2362
+ if (rafId) {
2363
+ cancelAnimationFrame(rafId);
2364
+ }
2365
+ iframeDocument.removeEventListener('mousemove', throttledMouseMove);
2366
+ iframeDocument.removeEventListener('click', handleClick);
2367
+ iframeDocument.removeEventListener('mouseleave', handleMouseLeave);
2368
+ iframeDocument.removeEventListener('scroll', handleMouseLeave);
2369
+ };
2370
+ }, [isActive, iframeDocument]);
2371
+ return {
2372
+ hoveredElement,
2373
+ isHovering,
2374
+ };
2375
+ }
2376
+
2377
+ function useAreaScrollSync(options) {
2378
+ const { areas, iframeRef, enabled = true } = options;
2379
+ const iframeDocument = iframeRef.current?.contentDocument;
2380
+ useEffect(() => {
2381
+ if (!enabled || !iframeDocument || areas.length === 0) {
2382
+ return;
2383
+ }
2384
+ let rafId = null;
2385
+ let isUpdating = false;
2386
+ const updateAreaPositions = () => {
2387
+ if (isUpdating)
2388
+ return;
2389
+ isUpdating = true;
2390
+ rafId = requestAnimationFrame(() => {
2391
+ areas.forEach((area) => {
2392
+ if (!area.element || !area.rect)
2393
+ return;
2394
+ try {
2395
+ const newRect = getElementRect(area.element);
2396
+ area.rect.update(newRect);
2397
+ }
2398
+ catch (error) {
2399
+ console.warn('[useAreaScrollSync] Failed to update area rect:', error);
2400
+ }
2401
+ });
2402
+ isUpdating = false;
2403
+ rafId = null;
2404
+ });
2405
+ };
2406
+ iframeDocument.addEventListener('scroll', updateAreaPositions, { passive: true });
2407
+ const iframeWindow = iframeDocument.defaultView;
2408
+ if (iframeWindow) {
2409
+ iframeWindow.addEventListener('resize', updateAreaPositions, { passive: true });
2410
+ }
2411
+ return () => {
2412
+ if (rafId !== null) {
2413
+ cancelAnimationFrame(rafId);
2414
+ }
2415
+ iframeDocument.removeEventListener('scroll', updateAreaPositions);
2416
+ if (iframeWindow) {
2417
+ iframeWindow.removeEventListener('resize', updateAreaPositions);
2418
+ }
2419
+ };
2420
+ }, [areas, iframeDocument, enabled]);
2421
+ }
2422
+
2423
+ const useClickmap = () => {
2424
+ const { vizRef } = useHeatmapViz();
2425
+ const { clickmap } = useHeatmapData();
2426
+ const start = useCallback(() => {
2427
+ if (!vizRef || !clickmap || clickmap.length === 0)
2428
+ return;
2429
+ try {
2430
+ vizRef?.clearmap?.();
2431
+ vizRef?.clickmap?.(clickmap);
2432
+ }
2433
+ catch (error) {
2434
+ console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
2435
+ }
2436
+ }, [vizRef, clickmap]);
2437
+ return { start };
2438
+ };
2439
+
2440
+ const useScrollmap = () => {
2441
+ const { vizRef } = useHeatmapViz();
2442
+ const { scrollmap } = useHeatmapData();
2443
+ const start = useCallback(() => {
2444
+ // if (isInitialized) return;
2445
+ if (!vizRef || !scrollmap || scrollmap.length === 0)
2446
+ return;
1699
2447
  try {
1700
- console.log('[IframeHelper] Processing viewport units...');
1701
- // Create replacer instance
1702
- this.replacer = new IframeStyleReplacer(this.iframe, this.config);
1703
- // Create navigation blocker
1704
- this.navigationBlocker = new IframeNavigationBlockerV2(this.iframe);
1705
- // Run replacement
1706
- const result = await this.replacer.run();
1707
- console.log('[IframeHelper] Process completed:', result);
1708
- // Trigger success callback
1709
- this.config.onSuccess?.(result);
1710
- // Dispatch custom event
1711
- window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', {
1712
- detail: result,
1713
- }));
2448
+ vizRef?.clearmap?.();
2449
+ vizRef?.scrollmap?.(scrollmap);
2450
+ // setIsInitialized(true);
1714
2451
  }
1715
2452
  catch (error) {
1716
- console.error('[IframeHelper] Failed to process:', error);
1717
- this.config.onError?.(error);
1718
- }
1719
- }
1720
- async recalculate() {
1721
- console.log('[IframeHelper] Recalculating...');
1722
- await this.process();
1723
- }
1724
- updateConfig(config) {
1725
- this.config = { ...this.config, ...config };
1726
- if (this.replacer) {
1727
- this.replacer.updateConfig(config);
2453
+ console.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
1728
2454
  }
1729
- }
1730
- enableNavigationBlocking() {
1731
- this.navigationBlocker?.enable();
1732
- }
1733
- enableNavigationBlockingMessage() {
1734
- this.navigationBlocker?.enableMessage();
1735
- }
1736
- disableNavigationBlocking() {
1737
- this.navigationBlocker?.disable();
1738
- }
1739
- disableNavigationBlockingMessage() {
1740
- this.navigationBlocker?.disableMessage();
1741
- }
1742
- destroy() {
1743
- this.replacer = null;
1744
- this.navigationBlocker?.destroy();
1745
- this.navigationBlocker = null;
1746
- console.log('[IframeHelper] Destroyed');
1747
- }
1748
- }
2455
+ }, [vizRef, scrollmap]);
2456
+ return { start };
2457
+ };
1749
2458
 
1750
- function initIframeHelperFixer(config) {
1751
- const fixer = new IframeHelperFixer(config);
1752
- window.addEventListener('iframe-dimensions-applied', ((e) => {
1753
- const ev = e;
1754
- console.log('[IframeHelper] Iframe dimensions finalized:', ev.detail);
1755
- }));
1756
- window.addEventListener('iframe-navigation-blocked', ((e) => {
1757
- const ev = e;
1758
- console.warn('[IframeHelper] Iframe tried to navigate to:', ev.detail.url);
1759
- }));
1760
- window.addEventListener('iframe-form-submit', ((e) => {
1761
- const ev = e;
1762
- console.log('[IframeHelper] Iframe form submitted:', ev.detail.data);
1763
- }));
1764
- return fixer;
1765
- }
2459
+ const useHeatmapCanvas = () => {
2460
+ const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
2461
+ const { start: startClickmap } = useClickmap();
2462
+ const { start: startScrollmap } = useScrollmap();
2463
+ useEffect(() => {
2464
+ switch (heatmapType) {
2465
+ case IHeatmapType.Click:
2466
+ startClickmap();
2467
+ break;
2468
+ case IHeatmapType.Scroll:
2469
+ startScrollmap();
2470
+ break;
2471
+ }
2472
+ }, [heatmapType, startClickmap, startScrollmap]);
2473
+ };
1766
2474
 
1767
2475
  const scrollToElementIfNeeded = (visualRef, rect, scale) => {
1768
2476
  if (!visualRef.current)
@@ -1782,7 +2490,7 @@ const scrollToElementIfNeeded = (visualRef, rect, scale) => {
1782
2490
  });
1783
2491
  };
1784
2492
  const useClickedElement = ({ visualRef, getRect }) => {
1785
- const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapInteraction();
2493
+ const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapClick();
1786
2494
  const { widthScale } = useHeatmapViz();
1787
2495
  const { dataInfo } = useHeatmapData();
1788
2496
  const [clickedElement, setClickedElement] = useState(null);
@@ -1825,7 +2533,7 @@ const useClickedElement = ({ visualRef, getRect }) => {
1825
2533
  };
1826
2534
 
1827
2535
  const useElementCalloutVisible = ({ visualRef, getRect }) => {
1828
- const { selectedElement, setShouldShowCallout } = useHeatmapInteraction();
2536
+ const { selectedElement, setShouldShowCallout } = useHeatmapClick();
1829
2537
  const { widthScale } = useHeatmapViz();
1830
2538
  const { dataInfo } = useHeatmapData();
1831
2539
  useEffect(() => {
@@ -1856,7 +2564,7 @@ const useElementCalloutVisible = ({ visualRef, getRect }) => {
1856
2564
  };
1857
2565
 
1858
2566
  const useHeatmapEffects = ({ isVisible }) => {
1859
- useHeatmapInteraction();
2567
+ useHeatmapClick();
1860
2568
  const resetAll = () => {
1861
2569
  // setShouldShowCallout(false);
1862
2570
  };
@@ -2026,7 +2734,7 @@ function HeatmapComponent() {
2026
2734
  */
2027
2735
 
2028
2736
  const useHoveredElement = ({ iframeRef, getRect }) => {
2029
- const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapInteraction();
2737
+ const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapClick();
2030
2738
  const { widthScale } = useHeatmapViz();
2031
2739
  const { dataInfo } = useHeatmapData();
2032
2740
  const reset = useCallback(() => {
@@ -2250,8 +2958,7 @@ function reset(iframe, rect, onSuccess) {
2250
2958
 
2251
2959
  const useHeatmapRender = () => {
2252
2960
  const { data } = useHeatmapData();
2253
- const { vizRef, setVizRef, setIsRenderViz, setIframeHeight, wrapperHeight, wrapperWidth } = useHeatmapViz();
2254
- console.log(`🚀 🐥 ~ useHeatmapRender ~ wrapperHeight:`, wrapperHeight);
2961
+ const { vizRef, setVizRef, setIsRenderViz, setIframeHeight } = useHeatmapViz();
2255
2962
  const iframeRef = useRef(null);
2256
2963
  const renderHeatmap = useCallback(async (payloads) => {
2257
2964
  if (!payloads || payloads.length === 0)
@@ -3452,19 +4159,6 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
3452
4159
  });
3453
4160
  BoxStack.displayName = 'BoxStack';
3454
4161
 
3455
- const ContentTopBar = () => {
3456
- const controls = useHeatmapControlStore((state) => state.controls);
3457
- useHeatmapConfigStore((state) => state.mode);
3458
- const TopBar = controls.TopBar;
3459
- // In compare mode, hide individual top bars since we have a global header
3460
- // if (mode === 'compare') {
3461
- // return null;
3462
- // }
3463
- return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
3464
- borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3465
- }, children: TopBar && jsx(TopBar, {}) }));
3466
- };
3467
-
3468
4162
  const ContentMetricBar = () => {
3469
4163
  const controls = useHeatmapControlStore((state) => state.controls);
3470
4164
  const borderBottom = `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`;
@@ -3472,22 +4166,11 @@ const ContentMetricBar = () => {
3472
4166
  borderBottom,
3473
4167
  }, children: controls.MetricBar ?? null }));
3474
4168
  };
3475
-
3476
- const ContentToolbar = () => {
3477
- const controls = useHeatmapControlStore((state) => state.controls);
3478
- return (jsx("div", { id: "gx-hm-content-toolbar", style: {
3479
- position: 'absolute',
3480
- bottom: 0,
3481
- left: '8px',
3482
- right: '24px',
3483
- padding: '8px',
3484
- paddingBlock: '16px',
3485
- }, children: controls.Toolbar ?? null }));
3486
- };
4169
+ ContentMetricBar.displayName = 'ContentMetricBar';
3487
4170
 
3488
4171
  const ContentSidebar = () => {
3489
4172
  const controls = useHeatmapControlStore((state) => state.controls);
3490
- const { state } = useHeatmapInteraction();
4173
+ const { state } = useHeatmapClick();
3491
4174
  const isHideSidebar = state.hideSidebar;
3492
4175
  const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3493
4176
  const mode = useHeatmapConfigStore((state) => state.mode);
@@ -3521,7 +4204,7 @@ const PopoverSidebar = () => {
3521
4204
  const CompSidebarActivator = useHeatmapControlStore((state) => state.controls.SidebarActivator);
3522
4205
  const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3523
4206
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);
3524
- const { state } = useHeatmapInteraction();
4207
+ const { state } = useHeatmapClick();
3525
4208
  const isCompareMode = mode === 'compare';
3526
4209
  const isHideSidebar = state.hideSidebar;
3527
4210
  const stylePopover = {
@@ -3546,9 +4229,21 @@ const PopoverSidebar = () => {
3546
4229
  }, children: jsx(CompSidebar, { closeAction: { onClick: () => setIsPopoverOpen(false) } }) }) }))] }));
3547
4230
  };
3548
4231
 
4232
+ const ContentToolbar = () => {
4233
+ const controls = useHeatmapControlStore((state) => state.controls);
4234
+ return (jsx("div", { id: "gx-hm-content-toolbar", style: {
4235
+ position: 'absolute',
4236
+ bottom: 0,
4237
+ left: '8px',
4238
+ right: '24px',
4239
+ padding: '8px',
4240
+ paddingBlock: '16px',
4241
+ }, children: controls.Toolbar ?? null }));
4242
+ };
4243
+
3549
4244
  const VizContainer = ({ children, isActive = false }) => {
3550
4245
  const wrapperRef = useRef(null);
3551
- const viewId = useViewId();
4246
+ const viewId = useViewIdContext();
3552
4247
  useWrapperRefHeight({
3553
4248
  isActive,
3554
4249
  wrapperRef,
@@ -3558,80 +4253,350 @@ const VizContainer = ({ children, isActive = false }) => {
3558
4253
  }, children: children }), jsx(PopoverSidebar, {})] }));
3559
4254
  };
3560
4255
 
3561
- const useClickmap = () => {
3562
- const { vizRef } = useHeatmapViz();
3563
- const { clickmap } = useHeatmapData();
3564
- const start = useCallback(() => {
3565
- if (!vizRef || !clickmap || clickmap.length === 0)
4256
+ /**
4257
+ * Controls for area click feature - toggle edit mode, clear areas, etc.
4258
+ */
4259
+ const AreaControls = ({ className, style }) => {
4260
+ const { isEditingMode, setIsEditingMode, areas, clearAreas } = useHeatmapAreaClick();
4261
+ return (jsxs("div", { className: className, style: {
4262
+ display: 'flex',
4263
+ gap: '8px',
4264
+ alignItems: 'center',
4265
+ ...style,
4266
+ }, children: [jsx("button", { onClick: () => setIsEditingMode(!isEditingMode), style: {
4267
+ padding: '8px 16px',
4268
+ borderRadius: '4px',
4269
+ border: '1px solid #ccc',
4270
+ backgroundColor: isEditingMode ? '#0078D4' : 'white',
4271
+ color: isEditingMode ? 'white' : '#161514',
4272
+ cursor: 'pointer',
4273
+ fontWeight: 500,
4274
+ transition: 'all 0.2s',
4275
+ }, children: isEditingMode ? 'Exit Edit Mode' : 'Edit Areas' }), areas.length > 0 && (jsxs(Fragment, { children: [jsxs("span", { style: { color: '#605E5C', fontSize: '14px' }, children: [areas.length, " area", areas.length !== 1 ? 's' : ''] }), jsx("button", { onClick: () => {
4276
+ if (confirm(`Clear all ${areas.length} areas?`)) {
4277
+ clearAreas();
4278
+ }
4279
+ }, style: {
4280
+ padding: '8px 16px',
4281
+ borderRadius: '4px',
4282
+ border: '1px solid #ccc',
4283
+ backgroundColor: 'white',
4284
+ color: '#A4262C',
4285
+ cursor: 'pointer',
4286
+ fontWeight: 500,
4287
+ transition: 'all 0.2s',
4288
+ }, children: "Clear All" })] }))] }));
4289
+ };
4290
+ AreaControls.displayName = 'AreaControls';
4291
+
4292
+ const AreaEditHighlight = ({ element, shadowRoot, onClick, }) => {
4293
+ const [rect, setRect] = useState(null);
4294
+ const highlightRef = useRef(null);
4295
+ useEffect(() => {
4296
+ if (!element) {
4297
+ setRect(null);
3566
4298
  return;
3567
- try {
3568
- vizRef?.clearmap?.();
3569
- vizRef?.clickmap?.(clickmap);
3570
4299
  }
3571
- catch (error) {
3572
- console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
4300
+ // Calculate element position
4301
+ const elementRect = getElementRect(element);
4302
+ setRect(elementRect);
4303
+ }, [element, shadowRoot]);
4304
+ if (!rect) {
4305
+ return null;
4306
+ }
4307
+ const handleClick = (e) => {
4308
+ if (element && onClick) {
4309
+ e.stopPropagation();
4310
+ e.preventDefault();
4311
+ onClick(element);
3573
4312
  }
3574
- }, [vizRef, clickmap]);
3575
- return { start };
4313
+ };
4314
+ return (jsx("div", { ref: highlightRef, id: AREA_HOVER_ELEMENT_ID, [AREA_MAP_DIV_ATTRIBUTE]: '1', onClick: handleClick, style: {
4315
+ position: 'absolute',
4316
+ top: `${rect.absoluteTop}px`,
4317
+ left: `${rect.absoluteLeft}px`,
4318
+ width: `${rect.width}px`,
4319
+ height: `${rect.height}px`,
4320
+ zIndex: Number.MAX_SAFE_INTEGER,
4321
+ boxShadow: AREA_HOVER_BOX_SHADOW,
4322
+ backgroundColor: 'rgba(128, 128, 128, 0.4)',
4323
+ backgroundImage: 'repeating-linear-gradient(135deg, transparent, transparent 35px, rgba(255,255,255,.5) 35px, rgba(255,255,255,.5) 70px)',
4324
+ pointerEvents: 'auto',
4325
+ cursor: 'pointer',
4326
+ boxSizing: 'border-box',
4327
+ } }));
3576
4328
  };
4329
+ AreaEditHighlight.displayName = 'AreaEditHighlight';
3577
4330
 
3578
- const useScrollmap = () => {
3579
- const { vizRef } = useHeatmapViz();
3580
- const { scrollmap } = useHeatmapData();
3581
- const start = useCallback(() => {
3582
- // if (isInitialized) return;
3583
- if (!vizRef || !scrollmap || scrollmap.length === 0)
4331
+ const AreaLabel = ({ clickDist, totalClicks, kind }) => {
4332
+ if (kind === 'money') {
4333
+ return null;
4334
+ }
4335
+ return (jsxs("div", { style: {
4336
+ color: '#161514',
4337
+ backgroundColor: 'rgba(255, 255, 255, 0.86)',
4338
+ display: 'flex',
4339
+ flexDirection: 'column',
4340
+ alignItems: 'center',
4341
+ padding: '8px',
4342
+ borderRadius: '4px',
4343
+ fontSize: '16px',
4344
+ lineHeight: '20px',
4345
+ minWidth: '56px',
4346
+ fontWeight: 600,
4347
+ fontFamily: '"Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif',
4348
+ pointerEvents: 'none',
4349
+ }, children: [jsxs("span", { children: [clickDist.toFixed(2), "%"] }), jsxs("span", { style: { fontSize: '12px', fontWeight: 400, opacity: 0.8 }, children: [totalClicks, " clicks"] })] }));
4350
+ };
4351
+ AreaLabel.displayName = 'AreaLabel';
4352
+
4353
+ const AreaOverlay = ({ area, onClick, onMouseEnter, onMouseLeave, isSelected, isHovered, }) => {
4354
+ const [rect, setRect] = useState(area.rect.value);
4355
+ useEffect(() => {
4356
+ const handleRectChange = (newRect) => {
4357
+ if (newRect) {
4358
+ setRect(newRect);
4359
+ }
4360
+ };
4361
+ area.rect.observe(handleRectChange);
4362
+ return () => {
4363
+ area.rect.unobserve(handleRectChange);
4364
+ };
4365
+ }, [area.rect]);
4366
+ if (!rect)
4367
+ return null;
4368
+ const position = area.isFixed ? 'fixed' : 'absolute';
4369
+ const showLabel = !isRectTooSmallForLabel(rect);
4370
+ const backgroundColor = isHovered ? area.hoverColor : area.color;
4371
+ const boxShadow = isSelected
4372
+ ? '0 0 0 3px #0078d4 inset'
4373
+ : isHovered
4374
+ ? AREA_HOVER_BOX_SHADOW
4375
+ : '0 0 0 2px white inset';
4376
+ return (jsx("div", { id: `area-${area.id}`, "data-area-id": area.id, [AREA_MAP_DIV_ATTRIBUTE]: '1', onClick: () => onClick?.(area), onMouseEnter: () => onMouseEnter?.(area), onMouseLeave: () => onMouseLeave?.(area), style: {
4377
+ position,
4378
+ top: `${rect.top}px`,
4379
+ left: `${rect.left}px`,
4380
+ width: `${rect.width}px`,
4381
+ height: `${rect.height}px`,
4382
+ backgroundColor,
4383
+ boxShadow,
4384
+ boxSizing: 'border-box',
4385
+ display: 'flex',
4386
+ alignItems: 'center',
4387
+ justifyContent: 'center',
4388
+ cursor: 'pointer',
4389
+ transition: 'background-color 0.2s, box-shadow 0.2s',
4390
+ pointerEvents: 'auto',
4391
+ }, children: showLabel && (jsx(AreaLabel, { clickDist: area.clickDist, totalClicks: area.totalclicks, kind: area.kind })) }));
4392
+ };
4393
+
4394
+ function useAreaRenderer(options) {
4395
+ const { iframeRef, shadowRoot: customShadowRoot, onAreaCreated, onAreaClick } = options;
4396
+ const iframeDocument = iframeRef.current?.contentDocument;
4397
+ // Get heatmap data for building areas
4398
+ const { dataInfo } = useHeatmapData();
4399
+ const { areas, selectedArea, hoveredArea, isEditingMode, setSelectedArea, setHoveredArea, addArea, } = useHeatmapAreaClick();
4400
+ const [shadowContainer, setShadowContainer] = useState(null);
4401
+ const [isReady, setIsReady] = useState(false);
4402
+ const containerRef = useRef(null);
4403
+ useEffect(() => {
4404
+ if (!iframeDocument) {
4405
+ setIsReady(false);
4406
+ return;
4407
+ }
4408
+ let container = iframeDocument.querySelector(`[${AREA_MAP_DIV_ATTRIBUTE}]`);
4409
+ if (!container) {
4410
+ container = iframeDocument.createElement('div');
4411
+ container.setAttribute(AREA_MAP_DIV_ATTRIBUTE, 'true');
4412
+ container.style.cssText = `
4413
+ position: absolute;
4414
+ top: 0;
4415
+ left: 0;
4416
+ width: 100%;
4417
+ height: 100%;
4418
+ pointer-events: none;
4419
+ z-index: 999999;
4420
+ `;
4421
+ // Append to custom shadow root or body
4422
+ const targetRoot = customShadowRoot || iframeDocument.body;
4423
+ if (targetRoot) {
4424
+ targetRoot.appendChild(container);
4425
+ }
4426
+ }
4427
+ // Create shadow root if needed
4428
+ let shadowRoot;
4429
+ if (!container.shadowRoot) {
4430
+ shadowRoot = getOrCreateShadowRoot(container);
4431
+ }
4432
+ else {
4433
+ shadowRoot = container.shadowRoot;
4434
+ }
4435
+ // Create inner container for React portal
4436
+ let innerContainer = shadowRoot.querySelector('.heatmap-area-container');
4437
+ if (!innerContainer) {
4438
+ innerContainer = iframeDocument.createElement('div');
4439
+ innerContainer.className = 'heatmap-area-container';
4440
+ innerContainer.style.cssText = `
4441
+ position: relative;
4442
+ width: 100%;
4443
+ height: 100%;
4444
+ `;
4445
+ shadowRoot.appendChild(innerContainer);
4446
+ }
4447
+ containerRef.current = innerContainer;
4448
+ setShadowContainer(innerContainer);
4449
+ setIsReady(true);
4450
+ return () => {
4451
+ if (container && container.parentNode) {
4452
+ container.parentNode.removeChild(container);
4453
+ }
4454
+ containerRef.current = null;
4455
+ setShadowContainer(null);
4456
+ setIsReady(false);
4457
+ };
4458
+ }, [iframeDocument, customShadowRoot]);
4459
+ const handleCreateAreaFromElement = useCallback((element) => {
4460
+ if (!dataInfo?.elementMapInfo || !dataInfo?.totalClicks) {
4461
+ console.warn('[useAreaRenderer] Cannot create area: missing heatmap data');
4462
+ return;
4463
+ }
4464
+ const hash = getElementHash(element);
4465
+ if (!hash) {
4466
+ console.warn('[useAreaRenderer] Cannot create area: missing hash');
4467
+ return;
4468
+ }
4469
+ const alreadyExists = areas.some((area) => area.hash === hash);
4470
+ if (alreadyExists) {
4471
+ console.warn(`[useAreaRenderer] Area already exists for element: ${hash}`);
3584
4472
  return;
4473
+ }
3585
4474
  try {
3586
- vizRef?.clearmap?.();
3587
- vizRef?.scrollmap?.(scrollmap);
3588
- // setIsInitialized(true);
4475
+ const area = buildAreaNode(element, hash, dataInfo.elementMapInfo, dataInfo.totalClicks, customShadowRoot);
4476
+ addArea(area);
4477
+ if (onAreaCreated) {
4478
+ onAreaCreated(area);
4479
+ }
4480
+ console.log('[useAreaRenderer] Area created:', {
4481
+ hash,
4482
+ selector: area.selector,
4483
+ clicks: area.totalclicks,
4484
+ clickDist: area.clickDist,
4485
+ });
3589
4486
  }
3590
4487
  catch (error) {
3591
- console.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
4488
+ console.error('[useAreaRenderer] Failed to create area:', error);
3592
4489
  }
3593
- }, [vizRef, scrollmap]);
3594
- return { start };
3595
- };
3596
-
3597
- const useHeatmapCanvas = () => {
3598
- const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
3599
- const { start: startClickmap } = useClickmap();
3600
- const { start: startScrollmap } = useScrollmap();
3601
- useEffect(() => {
3602
- switch (heatmapType) {
3603
- case IHeatmapType.Click:
3604
- startClickmap();
3605
- break;
3606
- case IHeatmapType.Scroll:
3607
- startScrollmap();
3608
- break;
4490
+ }, [dataInfo, areas, customShadowRoot, addArea]);
4491
+ const { hoveredElement } = useAreaEditMode({
4492
+ iframeRef,
4493
+ enabled: isEditingMode,
4494
+ onCreateArea: handleCreateAreaFromElement,
4495
+ });
4496
+ useAreaScrollSync({
4497
+ areas,
4498
+ iframeRef,
4499
+ enabled: isReady,
4500
+ });
4501
+ const handleAreaClick = (area) => {
4502
+ if (isEditingMode)
4503
+ return;
4504
+ setSelectedArea(selectedArea?.id === area.id ? null : area);
4505
+ if (onAreaClick) {
4506
+ onAreaClick(area);
3609
4507
  }
3610
- }, [heatmapType, startClickmap, startScrollmap]);
3611
- };
3612
-
3613
- // Base IDs for elements (without viewId suffix)
3614
- const CLICKED_ELEMENT_ID_BASE = 'gx-hm-clicked-element';
3615
- const SECONDARY_CLICKED_ELEMENT_ID_BASE = 'gx-hm-secondary-clicked-element';
3616
- const HOVERED_ELEMENT_ID_BASE = 'gx-hm-hovered-element';
3617
- const SECONDARY_HOVERED_ELEMENT_ID_BASE = 'gx-hm-secondary-hovered-element';
3618
- /**
3619
- * Generate unique element ID for a specific view
3620
- * @param baseId - Base element ID
3621
- * @param viewId - View ID
3622
- * @returns Unique element ID (e.g., 'gx-hm-clicked-element-view-0')
3623
- */
3624
- const getElementId = (baseId, viewId) => {
3625
- return `${baseId}-${viewId}`;
3626
- };
3627
- const getClickedElementId = (viewId, isSecondary = false) => {
3628
- const baseId = isSecondary ? SECONDARY_CLICKED_ELEMENT_ID_BASE : CLICKED_ELEMENT_ID_BASE;
3629
- return getElementId(baseId, viewId);
3630
- };
3631
- const getHoveredElementId = (viewId, isSecondary = false) => {
3632
- const baseId = isSecondary ? SECONDARY_HOVERED_ELEMENT_ID_BASE : HOVERED_ELEMENT_ID_BASE;
3633
- return getElementId(baseId, viewId);
4508
+ };
4509
+ const handleAreaMouseEnter = (area) => {
4510
+ if (isEditingMode)
4511
+ return;
4512
+ setHoveredArea(area);
4513
+ };
4514
+ const handleAreaMouseLeave = (area) => {
4515
+ if (isEditingMode)
4516
+ return;
4517
+ if (hoveredArea?.id === area.id) {
4518
+ setHoveredArea(null);
4519
+ }
4520
+ };
4521
+ const areasPortal = shadowContainer && isReady
4522
+ ? createPortal(jsx(Fragment, { children: areas.map((area) => (jsx(AreaOverlay, { area: area, onClick: handleAreaClick, onMouseEnter: handleAreaMouseEnter, onMouseLeave: handleAreaMouseLeave, isSelected: selectedArea?.id === area.id, isHovered: hoveredArea?.id === area.id }, area.id))) }), shadowContainer)
4523
+ : null;
4524
+ const editHighlightPortal = shadowContainer && isReady && isEditingMode && hoveredElement
4525
+ ? createPortal(jsx(AreaEditHighlight, { element: hoveredElement, shadowRoot: customShadowRoot, onClick: handleCreateAreaFromElement }), shadowContainer)
4526
+ : null;
4527
+ return {
4528
+ areasPortal: areasPortal,
4529
+ editHighlightPortal: editHighlightPortal,
4530
+ shadowContainer,
4531
+ isReady,
4532
+ };
4533
+ }
4534
+
4535
+ const VizAreaClick = ({ iframeRef, shadowRoot, autoCreateTopN = 10, enableOverlapResolution = true, renderControls, onAreaClick, }) => {
4536
+ const iframeDocument = iframeRef.current?.contentDocument;
4537
+ const { dataInfo } = useHeatmapData();
4538
+ const { areas, isEditingMode, setIsEditingMode, addArea, clearAreas, setAreas } = useHeatmapAreaClick();
4539
+ const { areasPortal, editHighlightPortal, isReady } = useAreaRenderer({
4540
+ iframeRef,
4541
+ shadowRoot,
4542
+ onAreaClick,
4543
+ });
4544
+ // Auto-create areas from top elements
4545
+ useEffect(() => {
4546
+ if (!dataInfo?.elementMapInfo || !dataInfo?.totalClicks)
4547
+ return;
4548
+ if (autoCreateTopN <= 0)
4549
+ return;
4550
+ if (areas.length > 0)
4551
+ return; // Already have areas
4552
+ // Get top elements by clicks
4553
+ const topElements = getTopElementsByClicks(dataInfo.elementMapInfo, autoCreateTopN);
4554
+ // Build area nodes
4555
+ const newAreas = [];
4556
+ topElements.forEach(({ hash, selector }) => {
4557
+ // Find element in DOM
4558
+ const element = iframeDocument?.querySelector(selector);
4559
+ if (!element || !(element instanceof HTMLElement))
4560
+ return;
4561
+ const area = buildAreaNode(element, hash, dataInfo.elementMapInfo, dataInfo.totalClicks);
4562
+ newAreas.push(area);
4563
+ });
4564
+ // Add all areas
4565
+ newAreas.forEach((area) => addArea(area));
4566
+ }, [dataInfo, autoCreateTopN, areas.length, iframeDocument, shadowRoot]);
4567
+ // Apply overlap resolution
4568
+ const visibleAreas = useMemo(() => {
4569
+ if (!enableOverlapResolution)
4570
+ return areas;
4571
+ if (!iframeDocument)
4572
+ return areas;
4573
+ return getVisibleAreas(areas, iframeDocument);
4574
+ }, [areas, iframeDocument]);
4575
+ // Update visible areas in store when resolution changes
4576
+ useEffect(() => {
4577
+ if (enableOverlapResolution && visibleAreas.length !== areas.length) {
4578
+ setAreas(visibleAreas);
4579
+ }
4580
+ }, [visibleAreas, areas.length]);
4581
+ const handleToggleEdit = useCallback(() => {
4582
+ setIsEditingMode(!isEditingMode);
4583
+ }, [isEditingMode]);
4584
+ const handleClearAll = useCallback(() => {
4585
+ if (window.confirm(`Clear all ${areas.length} areas?`)) {
4586
+ clearAreas();
4587
+ }
4588
+ }, [areas.length]);
4589
+ const controlsElement = renderControls ? (renderControls({
4590
+ isEditingMode,
4591
+ areasCount: areas.length,
4592
+ onToggleEdit: handleToggleEdit,
4593
+ onClearAll: handleClearAll,
4594
+ })) : (jsx(AreaControls, {}));
4595
+ if (!isReady)
4596
+ return null;
4597
+ return (jsxs(Fragment, { children: [areasPortal, editHighlightPortal, controlsElement] }));
3634
4598
  };
4599
+ VizAreaClick.displayName = 'VizAreaClick';
3635
4600
 
3636
4601
  const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
3637
4602
  const style = calculateRankPosition(elementRect, widthScale);
@@ -3661,7 +4626,7 @@ const DEFAULT_POSITION = {
3661
4626
  };
3662
4627
  const ElementCallout = (props) => {
3663
4628
  const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
3664
- const viewId = useViewId();
4629
+ const viewId = useViewIdContext();
3665
4630
  const { element, target, visualRef, hozOffset, alignment = 'left' } = props;
3666
4631
  const calloutRef = useRef(null);
3667
4632
  const [position, setPosition] = useState(DEFAULT_POSITION);
@@ -3745,7 +4710,7 @@ const ELEMENT_CALLOUT = {
3745
4710
  alignment: 'left',
3746
4711
  };
3747
4712
  const HeatmapElements = (props) => {
3748
- const viewId = useViewId();
4713
+ const viewId = useViewIdContext();
3749
4714
  const { iframeHeight } = useHeatmapViz();
3750
4715
  const clickedElementId = getClickedElementId(viewId, props.isSecondary);
3751
4716
  const hoveredElementId = getHoveredElementId(viewId, props.isSecondary);
@@ -3794,121 +4759,9 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
3794
4759
  } }));
3795
4760
  };
3796
4761
 
3797
- const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
3798
- const { dataInfo } = useHeatmapData();
3799
- const { getZonePosition } = useZonePositions();
3800
- const averageFold = dataInfo?.averageFold || 50;
3801
- const position = getZonePosition({
3802
- startY: averageFold,
3803
- endY: averageFold,
3804
- });
3805
- if (!position)
3806
- return null;
3807
- return (jsx("div", { style: {
3808
- position: 'absolute',
3809
- top: `${position.top}px`,
3810
- left: 0,
3811
- width: '100%',
3812
- height: '2px',
3813
- backgroundColor: '#0078D4',
3814
- pointerEvents: 'none',
3815
- zIndex: 2,
3816
- boxShadow: '0 0 4px rgba(0,120,212,0.5)',
3817
- display: 'flex',
3818
- alignItems: 'center',
3819
- }, children: jsxs("div", { style: {
3820
- position: 'absolute',
3821
- padding: '8px',
3822
- backgroundColor: 'rgba(0, 120, 212, 0.9)',
3823
- color: 'white',
3824
- fontSize: '16px',
3825
- fontWeight: 600,
3826
- borderRadius: '4px',
3827
- whiteSpace: 'nowrap',
3828
- left: '12px',
3829
- minWidth: '120px',
3830
- textAlign: 'center',
3831
- }, children: ["Average fold - ", averageFold.toFixed(0), "%"] }) }));
3832
- };
3833
-
3834
- const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
3835
- const scrollType = useHeatmapConfigStore((state) => state.scrollType);
3836
- const { scrollmap } = useHeatmapData();
3837
- const { getZonePosition } = useZonePositions();
3838
- if (!scrollmap || scrollmap.length === 0)
3839
- return null;
3840
- const findScrollPositionForUserPercent = (targetPercent) => {
3841
- for (let i = 0; i < scrollmap.length; i++) {
3842
- if (scrollmap[i].percUsers <= targetPercent) {
3843
- if (i > 0) {
3844
- return scrollmap[i - 1].scrollReachY;
3845
- }
3846
- return scrollmap[i].scrollReachY;
3847
- }
3848
- }
3849
- return scrollmap[scrollmap.length - 1]?.scrollReachY || null;
3850
- };
3851
- const boundaries = [
3852
- { percent: 75, label: '75%', color: '#10B981' },
3853
- { percent: 50, label: '50%', color: '#F59E0B' },
3854
- { percent: 25, label: '25%', color: '#EF4444' },
3855
- { percent: 5, label: '5%', color: '#8B5CF6' },
3856
- ];
3857
- const isScrollDepth = scrollType === IScrollType.Depth;
3858
- if (!isScrollDepth)
3859
- return null;
3860
- return (jsx(Fragment, { children: boundaries.map((boundary) => {
3861
- const scrollY = findScrollPositionForUserPercent(boundary.percent);
3862
- if (scrollY === null)
3863
- return null;
3864
- const position = getZonePosition({
3865
- startY: scrollY,
3866
- endY: scrollY,
3867
- });
3868
- if (!position)
3869
- return null;
3870
- return (jsx("div", { className: `marker-boundary-line-${boundary.percent}`, style: {
3871
- position: 'absolute',
3872
- top: `${position.top}px`,
3873
- left: 0,
3874
- transformOrigin: 'left center',
3875
- width: '100%',
3876
- height: '0px',
3877
- // borderBottom: `2px dashed #323130`,
3878
- borderBottom: `2px solid ${boundary.color}`,
3879
- // background: 'repeating-linear-gradient(90deg, #323130, transparent 2px 3px)',
3880
- zIndex: 1,
3881
- display: 'flex',
3882
- alignItems: 'center',
3883
- }, children: jsx("div", { style: {
3884
- position: 'absolute',
3885
- padding: '8px',
3886
- backgroundColor: boundary.color,
3887
- color: 'white',
3888
- fontSize: '16px',
3889
- fontWeight: 600,
3890
- borderRadius: '4px',
3891
- whiteSpace: 'nowrap',
3892
- left: '12px',
3893
- minWidth: '120px',
3894
- textAlign: 'center',
3895
- // textAlign: 'center',
3896
- // padding: '8px',
3897
- // paddingInline: '8px',
3898
- // fontSize: '16px',
3899
- // background: '#fff',
3900
- // width: 'auto',
3901
- // borderRadius: '4px',
3902
- // position: 'absolute',
3903
- // left: '12px',
3904
- // minWidth: '120px',
3905
- }, children: boundary.label }) }, boundary.label));
3906
- }) }));
3907
- };
3908
-
3909
4762
  const ScrollMapMinimap = ({ zones, maxUsers }) => {
3910
4763
  const scrollType = useHeatmapConfigStore((state) => state.scrollType);
3911
- const { showMinimap } = useHeatmapVizScrollmap();
4764
+ const { showMinimap } = useHeatmapScroll();
3912
4765
  const isScrollType = [IScrollType.Attention].includes(scrollType);
3913
4766
  if (!showMinimap || !isScrollType)
3914
4767
  return null;
@@ -4064,6 +4917,118 @@ const ScrollMapOverlay = ({ wrapperRef, iframeRef }) => {
4064
4917
  }, children: jsx(HoverZones, { position: position, currentScrollPercent: currentScrollPercent, iframeRef: iframeRef, wrapperRef: wrapperRef }) }));
4065
4918
  };
4066
4919
 
4920
+ const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
4921
+ const { dataInfo } = useHeatmapData();
4922
+ const { getZonePosition } = useZonePositions();
4923
+ const averageFold = dataInfo?.averageFold || 50;
4924
+ const position = getZonePosition({
4925
+ startY: averageFold,
4926
+ endY: averageFold,
4927
+ });
4928
+ if (!position)
4929
+ return null;
4930
+ return (jsx("div", { style: {
4931
+ position: 'absolute',
4932
+ top: `${position.top}px`,
4933
+ left: 0,
4934
+ width: '100%',
4935
+ height: '2px',
4936
+ backgroundColor: '#0078D4',
4937
+ pointerEvents: 'none',
4938
+ zIndex: 2,
4939
+ boxShadow: '0 0 4px rgba(0,120,212,0.5)',
4940
+ display: 'flex',
4941
+ alignItems: 'center',
4942
+ }, children: jsxs("div", { style: {
4943
+ position: 'absolute',
4944
+ padding: '8px',
4945
+ backgroundColor: 'rgba(0, 120, 212, 0.9)',
4946
+ color: 'white',
4947
+ fontSize: '16px',
4948
+ fontWeight: 600,
4949
+ borderRadius: '4px',
4950
+ whiteSpace: 'nowrap',
4951
+ left: '12px',
4952
+ minWidth: '120px',
4953
+ textAlign: 'center',
4954
+ }, children: ["Average fold - ", averageFold.toFixed(0), "%"] }) }));
4955
+ };
4956
+
4957
+ const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
4958
+ const scrollType = useHeatmapConfigStore((state) => state.scrollType);
4959
+ const { scrollmap } = useHeatmapData();
4960
+ const { getZonePosition } = useZonePositions();
4961
+ if (!scrollmap || scrollmap.length === 0)
4962
+ return null;
4963
+ const findScrollPositionForUserPercent = (targetPercent) => {
4964
+ for (let i = 0; i < scrollmap.length; i++) {
4965
+ if (scrollmap[i].percUsers <= targetPercent) {
4966
+ if (i > 0) {
4967
+ return scrollmap[i - 1].scrollReachY;
4968
+ }
4969
+ return scrollmap[i].scrollReachY;
4970
+ }
4971
+ }
4972
+ return scrollmap[scrollmap.length - 1]?.scrollReachY || null;
4973
+ };
4974
+ const boundaries = [
4975
+ { percent: 75, label: '75%', color: '#10B981' },
4976
+ { percent: 50, label: '50%', color: '#F59E0B' },
4977
+ { percent: 25, label: '25%', color: '#EF4444' },
4978
+ { percent: 5, label: '5%', color: '#8B5CF6' },
4979
+ ];
4980
+ const isScrollDepth = scrollType === IScrollType.Depth;
4981
+ if (!isScrollDepth)
4982
+ return null;
4983
+ return (jsx(Fragment, { children: boundaries.map((boundary) => {
4984
+ const scrollY = findScrollPositionForUserPercent(boundary.percent);
4985
+ if (scrollY === null)
4986
+ return null;
4987
+ const position = getZonePosition({
4988
+ startY: scrollY,
4989
+ endY: scrollY,
4990
+ });
4991
+ if (!position)
4992
+ return null;
4993
+ return (jsx("div", { className: `marker-boundary-line-${boundary.percent}`, style: {
4994
+ position: 'absolute',
4995
+ top: `${position.top}px`,
4996
+ left: 0,
4997
+ transformOrigin: 'left center',
4998
+ width: '100%',
4999
+ height: '0px',
5000
+ // borderBottom: `2px dashed #323130`,
5001
+ borderBottom: `2px solid ${boundary.color}`,
5002
+ // background: 'repeating-linear-gradient(90deg, #323130, transparent 2px 3px)',
5003
+ zIndex: 1,
5004
+ display: 'flex',
5005
+ alignItems: 'center',
5006
+ }, children: jsx("div", { style: {
5007
+ position: 'absolute',
5008
+ padding: '8px',
5009
+ backgroundColor: boundary.color,
5010
+ color: 'white',
5011
+ fontSize: '16px',
5012
+ fontWeight: 600,
5013
+ borderRadius: '4px',
5014
+ whiteSpace: 'nowrap',
5015
+ left: '12px',
5016
+ minWidth: '120px',
5017
+ textAlign: 'center',
5018
+ // textAlign: 'center',
5019
+ // padding: '8px',
5020
+ // paddingInline: '8px',
5021
+ // fontSize: '16px',
5022
+ // background: '#fff',
5023
+ // width: 'auto',
5024
+ // borderRadius: '4px',
5025
+ // position: 'absolute',
5026
+ // left: '12px',
5027
+ // minWidth: '120px',
5028
+ }, children: boundary.label }) }, boundary.label));
5029
+ }) }));
5030
+ };
5031
+
4067
5032
  const SCROLL_TYPES = [IHeatmapType.Scroll];
4068
5033
  const VizScrollMap = ({ iframeRef, wrapperRef }) => {
4069
5034
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
@@ -4122,7 +5087,7 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
4122
5087
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
4123
5088
  const wrapperRef = useRef(null);
4124
5089
  const visualRef = useRef(null);
4125
- const { setSelectedElement } = useHeatmapInteraction();
5090
+ const { setSelectedElement } = useHeatmapClick();
4126
5091
  const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
4127
5092
  const { iframeRef } = useHeatmapVizRender(mode);
4128
5093
  const { scaledHeight, handleScroll } = useHeatmapScale({
@@ -4145,7 +5110,9 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
4145
5110
  useEffect(() => {
4146
5111
  return cleanUp;
4147
5112
  }, []);
4148
- return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [heatmapType === IHeatmapType.Click && (jsx(VizElements, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef })), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, scrolling: "no" }), jsx(VizScrollMap, { iframeRef: iframeRef, wrapperRef: visualRef })] }));
5113
+ return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [heatmapType === IHeatmapType.Click && (jsx(VizElements, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef })), heatmapType === IHeatmapType.ClickArea && (jsx(VizAreaClick, { iframeRef: iframeRef, onAreaClick: (area) => {
5114
+ console.log('area clicked', area);
5115
+ } })), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, scrolling: "no" }), jsx(VizScrollMap, { iframeRef: iframeRef, wrapperRef: visualRef })] }));
4149
5116
  };
4150
5117
 
4151
5118
  const VizLoading = () => {
@@ -4163,6 +5130,7 @@ const VizDomHeatmap = () => {
4163
5130
  }, []);
4164
5131
  return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
4165
5132
  };
5133
+ VizDomHeatmap.displayName = 'VizDomHeatmap';
4166
5134
 
4167
5135
  const VizLiveRenderer = () => {
4168
5136
  const contentWidth = useHeatmapConfigStore((state) => state.width);
@@ -4196,6 +5164,7 @@ const VizLiveHeatmap = () => {
4196
5164
  }, []);
4197
5165
  return (jsxs(VizContainer, { isActive: true, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
4198
5166
  };
5167
+ VizLiveHeatmap.displayName = 'VizLiveHeatmap';
4199
5168
 
4200
5169
  const ContentVizByMode = () => {
4201
5170
  const mode = useHeatmapConfigStore((state) => state.mode);
@@ -4212,14 +5181,23 @@ const ContentVizByMode = () => {
4212
5181
  return jsx(VizDomHeatmap, {});
4213
5182
  }
4214
5183
  };
5184
+ ContentVizByMode.displayName = 'ContentVizByMode';
4215
5185
 
4216
- const WrapperPreview = () => {
5186
+ const HeatmapPreview = () => {
4217
5187
  return (jsxs(BoxStack, { id: "gx-hm-container", flexDirection: "row", overflow: "hidden", flex: "1", position: "relative", children: [jsx(ContentSidebar, {}), jsxs(BoxStack, { flexDirection: "column", flex: "1", children: [jsx(ContentMetricBar, {}), jsx(ContentVizByMode, {}), jsx(ContentToolbar, {})] })] }));
4218
5188
  };
4219
5189
 
4220
- const WrapperLayout = () => {
4221
- useRenderCount('WrapperLayout');
4222
- return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(WrapperPreview, {})] }));
5190
+ const ContentTopBar = () => {
5191
+ const controls = useHeatmapControlStore((state) => state.controls);
5192
+ useHeatmapConfigStore((state) => state.mode);
5193
+ const TopBar = controls.TopBar;
5194
+ // In compare mode, hide individual top bars since we have a global header
5195
+ // if (mode === 'compare') {
5196
+ // return null;
5197
+ // }
5198
+ return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
5199
+ borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
5200
+ }, children: TopBar && jsx(TopBar, {}) }));
4223
5201
  };
4224
5202
 
4225
5203
  const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
@@ -4230,11 +5208,11 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
4230
5208
  performanceLogger.configure({
4231
5209
  enabled: true,
4232
5210
  logToConsole: false,
4233
- logLevel: 'normal', // 'verbose' | 'normal' | 'minimal'
5211
+ logLevel: 'normal',
4234
5212
  thresholds: {
4235
- slowRenderMs: 16, // Warn if render > 16ms (60fps)
4236
- slowHookMs: 5, // Warn if hook > 5ms
4237
- excessiveRenderCount: 10, // Warn if component renders > 10 times
5213
+ slowRenderMs: 16,
5214
+ slowHookMs: 5,
5215
+ excessiveRenderCount: 10,
4238
5216
  },
4239
5217
  externalLogger: (metric) => {
4240
5218
  if (metric.name === 'VizDomRenderer') ;
@@ -4243,7 +5221,7 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
4243
5221
  return (jsx(BoxStack, { id: "gx-hm-project", flexDirection: "column", flex: "1", height: "100%", style: getVariableStyle(), children: jsx(BoxStack, { id: "gx-hm-project-content", flexDirection: "column", flex: "1", children: jsx("div", { style: {
4244
5222
  minHeight: '100%',
4245
5223
  display: 'flex',
4246
- }, children: jsx(WrapperLayout, {}) }) }) }));
5224
+ }, children: jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(HeatmapPreview, {})] }) }) }) }));
4247
5225
  function getVariableStyle() {
4248
5226
  return {
4249
5227
  '--gx-hm-border-width': `${HEATMAP_CONFIG.borderWidth}px`,
@@ -4253,4 +5231,4 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
4253
5231
  }
4254
5232
  };
4255
5233
 
4256
- export { DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, IClickType, IHeatmapType, IScrollType, ViewIdContext, Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, downloadPerformanceReport, getCompareViewId, getCompareViewIndex, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, isCompareViewId, performanceLogger, printPerformanceSummary, sendPerformanceReport, trackStoreAction, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapData, useHeatmapDataStore, useHeatmapEffects, useHeatmapElementPosition, useHeatmapInteraction, useHeatmapInteractionStore, useHeatmapLiveStore, useHeatmapScale, useHeatmapSingleStore, useHeatmapViz, useHeatmapVizRender, useHeatmapVizScrollmap, useHeatmapVizScrollmapStore, useHeatmapVizStore, useHoveredElement, useIsCompareMode, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewId, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };
5234
+ export { DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, IClickType, IHeatmapType, IScrollType, ViewIdContext, Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, downloadPerformanceReport, getCompareViewId, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, performanceLogger, printPerformanceSummary, sendPerformanceReport, trackStoreAction, useAreaEditMode, useAreaScrollSync, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapAreaClick, useHeatmapCanvas, useHeatmapClick, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapData, useHeatmapEffects, useHeatmapElementPosition, useHeatmapLiveStore, useHeatmapScale, useHeatmapScroll, useHeatmapViz, useHeatmapVizRender, useHoveredElement, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };