@gemx-dev/heatmap-react 3.5.50 → 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 (526) hide show
  1. package/dist/esm/components/Layout/HeatmapLayout.d.ts +2 -2
  2. package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
  3. package/dist/esm/components/Layout/HeatmapPreview.d.ts +2 -0
  4. package/dist/esm/components/Layout/HeatmapPreview.d.ts.map +1 -0
  5. package/dist/esm/components/Layout/MetricBar/ContentMetricBar.d.ts.map +1 -0
  6. package/dist/esm/components/Layout/MetricBar/index.d.ts +2 -0
  7. package/dist/esm/components/Layout/MetricBar/index.d.ts.map +1 -0
  8. package/dist/esm/components/Layout/Toolbar/ContentToolbar.d.ts.map +1 -0
  9. package/dist/esm/components/Layout/Toolbar/index.d.ts +2 -0
  10. package/dist/esm/components/Layout/Toolbar/index.d.ts.map +1 -0
  11. package/dist/esm/components/Layout/TopBar/ContentTopBar.d.ts.map +1 -0
  12. package/dist/esm/components/Layout/TopBar/index.d.ts +2 -0
  13. package/dist/esm/components/Layout/TopBar/index.d.ts.map +1 -0
  14. package/dist/esm/components/Layout/VizByMode/ContentVizByMode.d.ts.map +1 -0
  15. package/dist/esm/components/Layout/VizByMode/index.d.ts +2 -0
  16. package/dist/esm/components/Layout/VizByMode/index.d.ts.map +1 -0
  17. package/dist/esm/components/VizAreaClick/AreaControls.d.ts +10 -0
  18. package/dist/esm/components/VizAreaClick/AreaControls.d.ts.map +1 -0
  19. package/dist/esm/components/VizAreaClick/AreaEditHighlight.d.ts +8 -0
  20. package/dist/esm/components/VizAreaClick/AreaEditHighlight.d.ts.map +1 -0
  21. package/dist/esm/components/VizAreaClick/AreaLabel.d.ts +8 -0
  22. package/dist/esm/components/VizAreaClick/AreaLabel.d.ts.map +1 -0
  23. package/dist/esm/components/VizAreaClick/AreaOverlay.d.ts +12 -0
  24. package/dist/esm/components/VizAreaClick/AreaOverlay.d.ts.map +1 -0
  25. package/dist/esm/components/VizAreaClick/PortalAreaRenderer.d.ts +16 -0
  26. package/dist/esm/components/VizAreaClick/PortalAreaRenderer.d.ts.map +1 -0
  27. package/dist/esm/components/VizAreaClick/VizAreaClick.d.ts +17 -0
  28. package/dist/esm/components/VizAreaClick/VizAreaClick.d.ts.map +1 -0
  29. package/dist/esm/components/VizAreaClick/index.d.ts +5 -0
  30. package/dist/esm/components/VizAreaClick/index.d.ts.map +1 -0
  31. package/dist/esm/components/VizDom/VizContainer.d.ts.map +1 -1
  32. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  33. package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
  34. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
  35. package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
  36. package/dist/esm/components/VizScrollmap/HoverZones.d.ts +1 -1
  37. package/dist/esm/components/VizScrollmap/HoverZones.d.ts.map +1 -1
  38. package/dist/esm/components/VizScrollmap/ScrollMapMinimap.d.ts +1 -1
  39. package/dist/esm/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
  40. package/dist/esm/components/VizScrollmap/ScrollMapOverlay.d.ts.map +1 -1
  41. package/dist/esm/components/VizScrollmap/ScrollZoneHoverArea.d.ts +1 -2
  42. package/dist/esm/components/VizScrollmap/ScrollZoneHoverArea.d.ts.map +1 -1
  43. package/dist/esm/components/VizScrollmap/ScrollZoneTooltip.d.ts +1 -1
  44. package/dist/esm/components/VizScrollmap/ScrollZoneTooltip.d.ts.map +1 -1
  45. package/dist/esm/components/VizScrollmap/index.d.ts +1 -0
  46. package/dist/esm/components/VizScrollmap/index.d.ts.map +1 -1
  47. package/dist/esm/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts +1 -2
  48. package/dist/esm/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts.map +1 -1
  49. package/dist/esm/components/VizScrollmapV2/useScrollmapOverlay.d.ts +1 -1
  50. package/dist/esm/components/VizScrollmapV2/useScrollmapOverlay.d.ts.map +1 -1
  51. package/dist/esm/configs/style.d.ts +1 -1
  52. package/dist/esm/configs/style.d.ts.map +1 -1
  53. package/dist/esm/configs/viewId.d.ts +1 -19
  54. package/dist/esm/configs/viewId.d.ts.map +1 -1
  55. package/dist/esm/constants/index.d.ts +2 -13
  56. package/dist/esm/constants/index.d.ts.map +1 -1
  57. package/dist/esm/constants/viz-area-click.d.ts +6 -0
  58. package/dist/esm/constants/viz-area-click.d.ts.map +1 -0
  59. package/dist/esm/constants/viz-elm-callout.d.ts +8 -0
  60. package/dist/esm/constants/viz-elm-callout.d.ts.map +1 -0
  61. package/dist/esm/contexts/ViewIdContext.d.ts +3 -0
  62. package/dist/esm/contexts/ViewIdContext.d.ts.map +1 -0
  63. package/dist/esm/contexts/index.d.ts +1 -1
  64. package/dist/esm/contexts/index.d.ts.map +1 -1
  65. package/dist/esm/helpers/iframe-helper/fixer.d.ts +1 -1
  66. package/dist/esm/helpers/iframe-helper/fixer.d.ts.map +1 -1
  67. package/dist/esm/helpers/iframe-helper/init.d.ts +1 -1
  68. package/dist/esm/helpers/iframe-helper/init.d.ts.map +1 -1
  69. package/dist/esm/helpers/index.d.ts +5 -4
  70. package/dist/esm/helpers/index.d.ts.map +1 -1
  71. package/dist/esm/helpers/observable.d.ts +11 -0
  72. package/dist/esm/helpers/observable.d.ts.map +1 -0
  73. package/dist/esm/helpers/viewport/element.d.ts +3 -0
  74. package/dist/esm/helpers/viewport/element.d.ts.map +1 -0
  75. package/dist/esm/helpers/viewport/index.d.ts +2 -0
  76. package/dist/esm/helpers/viewport/index.d.ts.map +1 -0
  77. package/dist/esm/helpers/viz-area-click/area-builder.d.ts +8 -0
  78. package/dist/esm/helpers/viz-area-click/area-builder.d.ts.map +1 -0
  79. package/dist/esm/helpers/viz-area-click/area-color.d.ts +13 -0
  80. package/dist/esm/helpers/viz-area-click/area-color.d.ts.map +1 -0
  81. package/dist/esm/helpers/viz-area-click/area-overlap.d.ts +44 -0
  82. package/dist/esm/helpers/viz-area-click/area-overlap.d.ts.map +1 -0
  83. package/dist/esm/helpers/viz-area-click/area-utils.d.ts +54 -0
  84. package/dist/esm/helpers/viz-area-click/area-utils.d.ts.map +1 -0
  85. package/dist/esm/helpers/viz-area-click/index.d.ts +5 -0
  86. package/dist/esm/helpers/viz-area-click/index.d.ts.map +1 -0
  87. package/dist/esm/helpers/viz-canvas/area-clustering.d.ts +1 -1
  88. package/dist/esm/helpers/viz-canvas/area-clustering.d.ts.map +1 -1
  89. package/dist/esm/helpers/viz-canvas/area-overlay-manager-v2.d.ts +2 -2
  90. package/dist/esm/helpers/viz-canvas/area-overlay-manager-v2.d.ts.map +1 -1
  91. package/dist/esm/helpers/viz-canvas/area-overlay-manager.d.ts +1 -1
  92. package/dist/esm/helpers/viz-canvas/area-overlay-manager.d.ts.map +1 -1
  93. package/dist/esm/helpers/viz-canvas/hierarchical-area-clustering.d.ts +1 -1
  94. package/dist/esm/helpers/viz-canvas/hierarchical-area-clustering.d.ts.map +1 -1
  95. package/dist/esm/helpers/{iframe.d.ts → viz-dom/decode.d.ts} +2 -2
  96. package/dist/esm/helpers/viz-dom/decode.d.ts.map +1 -0
  97. package/dist/esm/helpers/viz-dom/index.d.ts +2 -0
  98. package/dist/esm/helpers/viz-dom/index.d.ts.map +1 -0
  99. package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts +4 -0
  100. package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts.map +1 -0
  101. package/dist/esm/helpers/viz-elm-callout/element.d.ts +10 -0
  102. package/dist/esm/helpers/viz-elm-callout/element.d.ts.map +1 -0
  103. package/dist/esm/helpers/{elm-getter.d.ts → viz-elm-callout/getter.d.ts} +2 -2
  104. package/dist/esm/helpers/viz-elm-callout/getter.d.ts.map +1 -0
  105. package/dist/esm/helpers/viz-elm-callout/index.d.ts +5 -0
  106. package/dist/esm/helpers/viz-elm-callout/index.d.ts.map +1 -0
  107. package/dist/esm/helpers/viz-elm-callout/position-calculator.d.ts +14 -0
  108. package/dist/esm/helpers/viz-elm-callout/position-calculator.d.ts.map +1 -0
  109. package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts +5 -0
  110. package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -0
  111. package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts +10 -0
  112. package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts.map +1 -0
  113. package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts +4 -0
  114. package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts.map +1 -0
  115. package/dist/esm/helpers/viz-elm-callout/rank-calculator.d.ts +7 -0
  116. package/dist/esm/helpers/viz-elm-callout/rank-calculator.d.ts.map +1 -0
  117. package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts +12 -0
  118. package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -0
  119. package/dist/esm/hooks/common/index.d.ts +2 -0
  120. package/dist/esm/hooks/common/index.d.ts.map +1 -0
  121. package/dist/esm/hooks/common/useDebounceCallback.d.ts +17 -0
  122. package/dist/esm/hooks/common/useDebounceCallback.d.ts.map +1 -0
  123. package/dist/esm/hooks/index.d.ts +5 -2
  124. package/dist/esm/hooks/index.d.ts.map +1 -1
  125. package/dist/esm/hooks/register/useRegisterControl.d.ts +1 -1
  126. package/dist/esm/hooks/register/useRegisterControl.d.ts.map +1 -1
  127. package/dist/esm/hooks/register/useRegisterData.d.ts +1 -1
  128. package/dist/esm/hooks/register/useRegisterData.d.ts.map +1 -1
  129. package/dist/esm/hooks/register/useRegisterHeatmap.d.ts +1 -1
  130. package/dist/esm/hooks/register/useRegisterHeatmap.d.ts.map +1 -1
  131. package/dist/esm/hooks/view-context/index.d.ts +3 -3
  132. package/dist/esm/hooks/view-context/index.d.ts.map +1 -1
  133. package/dist/esm/hooks/view-context/useHeatmapAreaClick.d.ts +19 -0
  134. package/dist/esm/hooks/view-context/useHeatmapAreaClick.d.ts.map +1 -0
  135. package/dist/{umd/hooks/view-context/useHeatmapInteraction.d.ts → esm/hooks/view-context/useHeatmapClick.d.ts} +7 -6
  136. package/dist/esm/hooks/view-context/useHeatmapClick.d.ts.map +1 -0
  137. package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
  138. package/dist/esm/hooks/view-context/useHeatmapData.d.ts +1 -1
  139. package/dist/esm/hooks/view-context/useHeatmapData.d.ts.map +1 -1
  140. package/dist/{umd/hooks/view-context/useHeatmapVizScrollmap.d.ts → esm/hooks/view-context/useHeatmapScroll.d.ts} +6 -5
  141. package/dist/esm/hooks/view-context/useHeatmapScroll.d.ts.map +1 -0
  142. package/dist/esm/hooks/view-context/useHeatmapViz.d.ts +1 -1
  143. package/dist/esm/hooks/view-context/useHeatmapViz.d.ts.map +1 -1
  144. package/dist/esm/hooks/viz-area/useAreaHeatmap.d.ts +1 -1
  145. package/dist/esm/hooks/viz-area/useAreaHeatmap.d.ts.map +1 -1
  146. package/dist/esm/hooks/viz-area/useAreaHeatmapManager.d.ts.map +1 -1
  147. package/dist/esm/hooks/viz-area-click/index.d.ts +3 -0
  148. package/dist/esm/hooks/viz-area-click/index.d.ts.map +1 -0
  149. package/dist/esm/hooks/viz-area-click/useAreaEditMode.d.ts +12 -0
  150. package/dist/esm/hooks/viz-area-click/useAreaEditMode.d.ts.map +1 -0
  151. package/dist/esm/hooks/viz-area-click/useAreaScrollSync.d.ts +8 -0
  152. package/dist/esm/hooks/viz-area-click/useAreaScrollSync.d.ts.map +1 -0
  153. package/dist/esm/hooks/viz-canvas/useAreamap.d.ts +1 -1
  154. package/dist/esm/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
  155. package/dist/esm/hooks/viz-elm/index.d.ts.map +1 -0
  156. package/dist/esm/hooks/viz-elm/useClickedElement.d.ts.map +1 -0
  157. package/dist/esm/hooks/viz-elm/useElementCalloutVisible.d.ts.map +1 -0
  158. package/dist/esm/hooks/viz-elm/useHeatmapEffects.d.ts.map +1 -0
  159. package/dist/esm/hooks/viz-elm/useHeatmapElementPosition.d.ts.map +1 -0
  160. package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts +1 -1
  161. package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts.map +1 -1
  162. package/dist/esm/hooks/viz-elm/useHoveredElement.d.ts.map +1 -0
  163. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  164. package/dist/esm/hooks/viz-render/useReplayRender.d.ts.map +1 -1
  165. package/dist/esm/hooks/viz-scroll/index.d.ts.map +1 -0
  166. package/dist/{umd/hooks/viz-scrollmap → esm/hooks/viz-scroll}/useScrollmapZones.d.ts +1 -1
  167. package/dist/esm/hooks/viz-scroll/useScrollmapZones.d.ts.map +1 -0
  168. package/dist/{umd/hooks/viz-scrollmap → esm/hooks/viz-scroll}/useZonePositions.d.ts +2 -2
  169. package/dist/esm/hooks/viz-scroll/useZonePositions.d.ts.map +1 -0
  170. package/dist/esm/index.d.ts +2 -1
  171. package/dist/esm/index.d.ts.map +1 -1
  172. package/dist/esm/index.js +1763 -746
  173. package/dist/esm/index.mjs +1763 -746
  174. package/dist/esm/performance/index.d.ts +3 -3
  175. package/dist/esm/performance/index.d.ts.map +1 -1
  176. package/dist/esm/performance/{PerformanceLogger.d.ts → performance-logger.d.ts} +1 -1
  177. package/dist/esm/performance/performance-logger.d.ts.map +1 -0
  178. package/dist/esm/performance/storeTracker.d.ts.map +1 -1
  179. package/dist/esm/performance/utils.d.ts +0 -18
  180. package/dist/esm/performance/utils.d.ts.map +1 -1
  181. package/dist/esm/performance/withPerformanceTracking.d.ts +1 -1
  182. package/dist/esm/performance/withPerformanceTracking.d.ts.map +1 -1
  183. package/dist/esm/stores/comp.d.ts +1 -1
  184. package/dist/esm/stores/comp.d.ts.map +1 -1
  185. package/dist/esm/stores/data.d.ts +1 -1
  186. package/dist/esm/stores/data.d.ts.map +1 -1
  187. package/dist/esm/stores/index.d.ts +3 -2
  188. package/dist/esm/stores/index.d.ts.map +1 -1
  189. package/dist/esm/stores/mode-compare.d.ts +1 -15
  190. package/dist/esm/stores/mode-compare.d.ts.map +1 -1
  191. package/dist/esm/stores/mode-live.d.ts +1 -1
  192. package/dist/esm/stores/mode-live.d.ts.map +1 -1
  193. package/dist/esm/stores/mode-single.d.ts +1 -1
  194. package/dist/esm/stores/mode-single.d.ts.map +1 -1
  195. package/dist/esm/stores/viz-area-click.d.ts +28 -0
  196. package/dist/esm/stores/viz-area-click.d.ts.map +1 -0
  197. package/dist/{umd/stores/interaction.d.ts → esm/stores/viz-click.d.ts} +6 -6
  198. package/dist/esm/stores/viz-click.d.ts.map +1 -0
  199. package/dist/esm/stores/viz-scrollmap.d.ts +1 -1
  200. package/dist/esm/stores/viz-scrollmap.d.ts.map +1 -1
  201. package/dist/esm/types/compare.d.ts +0 -50
  202. package/dist/esm/types/compare.d.ts.map +1 -1
  203. package/dist/esm/types/heatmap-info.d.ts +2 -2
  204. package/dist/esm/types/heatmap-info.d.ts.map +1 -1
  205. package/dist/esm/types/heatmap.d.ts +1 -0
  206. package/dist/esm/types/heatmap.d.ts.map +1 -1
  207. package/dist/esm/types/index.d.ts +5 -4
  208. package/dist/esm/types/index.d.ts.map +1 -1
  209. package/dist/esm/types/viz-area-click.d.ts +84 -0
  210. package/dist/esm/types/viz-area-click.d.ts.map +1 -0
  211. package/dist/esm/types/viz-elm-callout.d.ts +24 -0
  212. package/dist/esm/types/viz-elm-callout.d.ts.map +1 -0
  213. package/dist/esm/types/{viz-element.d.ts → viz-elm.d.ts} +1 -1
  214. package/dist/esm/types/viz-elm.d.ts.map +1 -0
  215. package/dist/esm/utils/sort.d.ts +1 -1
  216. package/dist/esm/utils/sort.d.ts.map +1 -1
  217. package/dist/umd/components/Layout/HeatmapLayout.d.ts +2 -2
  218. package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
  219. package/dist/umd/components/Layout/HeatmapPreview.d.ts +2 -0
  220. package/dist/umd/components/Layout/HeatmapPreview.d.ts.map +1 -0
  221. package/dist/umd/components/Layout/MetricBar/ContentMetricBar.d.ts.map +1 -0
  222. package/dist/umd/components/Layout/MetricBar/index.d.ts +2 -0
  223. package/dist/umd/components/Layout/MetricBar/index.d.ts.map +1 -0
  224. package/dist/umd/components/Layout/Toolbar/ContentToolbar.d.ts.map +1 -0
  225. package/dist/umd/components/Layout/Toolbar/index.d.ts +2 -0
  226. package/dist/umd/components/Layout/Toolbar/index.d.ts.map +1 -0
  227. package/dist/umd/components/Layout/TopBar/ContentTopBar.d.ts.map +1 -0
  228. package/dist/umd/components/Layout/TopBar/index.d.ts +2 -0
  229. package/dist/umd/components/Layout/TopBar/index.d.ts.map +1 -0
  230. package/dist/umd/components/Layout/VizByMode/ContentVizByMode.d.ts.map +1 -0
  231. package/dist/umd/components/Layout/VizByMode/index.d.ts +2 -0
  232. package/dist/umd/components/Layout/VizByMode/index.d.ts.map +1 -0
  233. package/dist/umd/components/VizAreaClick/AreaControls.d.ts +10 -0
  234. package/dist/umd/components/VizAreaClick/AreaControls.d.ts.map +1 -0
  235. package/dist/umd/components/VizAreaClick/AreaEditHighlight.d.ts +8 -0
  236. package/dist/umd/components/VizAreaClick/AreaEditHighlight.d.ts.map +1 -0
  237. package/dist/umd/components/VizAreaClick/AreaLabel.d.ts +8 -0
  238. package/dist/umd/components/VizAreaClick/AreaLabel.d.ts.map +1 -0
  239. package/dist/umd/components/VizAreaClick/AreaOverlay.d.ts +12 -0
  240. package/dist/umd/components/VizAreaClick/AreaOverlay.d.ts.map +1 -0
  241. package/dist/umd/components/VizAreaClick/PortalAreaRenderer.d.ts +16 -0
  242. package/dist/umd/components/VizAreaClick/PortalAreaRenderer.d.ts.map +1 -0
  243. package/dist/umd/components/VizAreaClick/VizAreaClick.d.ts +17 -0
  244. package/dist/umd/components/VizAreaClick/VizAreaClick.d.ts.map +1 -0
  245. package/dist/umd/components/VizAreaClick/index.d.ts +5 -0
  246. package/dist/umd/components/VizAreaClick/index.d.ts.map +1 -0
  247. package/dist/umd/components/VizDom/VizContainer.d.ts.map +1 -1
  248. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  249. package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
  250. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
  251. package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
  252. package/dist/umd/components/VizScrollmap/HoverZones.d.ts +1 -1
  253. package/dist/umd/components/VizScrollmap/HoverZones.d.ts.map +1 -1
  254. package/dist/umd/components/VizScrollmap/ScrollMapMinimap.d.ts +1 -1
  255. package/dist/umd/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
  256. package/dist/umd/components/VizScrollmap/ScrollMapOverlay.d.ts.map +1 -1
  257. package/dist/umd/components/VizScrollmap/ScrollZoneHoverArea.d.ts +1 -2
  258. package/dist/umd/components/VizScrollmap/ScrollZoneHoverArea.d.ts.map +1 -1
  259. package/dist/umd/components/VizScrollmap/ScrollZoneTooltip.d.ts +1 -1
  260. package/dist/umd/components/VizScrollmap/ScrollZoneTooltip.d.ts.map +1 -1
  261. package/dist/umd/components/VizScrollmap/index.d.ts +1 -0
  262. package/dist/umd/components/VizScrollmap/index.d.ts.map +1 -1
  263. package/dist/umd/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts +1 -2
  264. package/dist/umd/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts.map +1 -1
  265. package/dist/umd/components/VizScrollmapV2/useScrollmapOverlay.d.ts +1 -1
  266. package/dist/umd/components/VizScrollmapV2/useScrollmapOverlay.d.ts.map +1 -1
  267. package/dist/umd/configs/style.d.ts +1 -1
  268. package/dist/umd/configs/style.d.ts.map +1 -1
  269. package/dist/umd/configs/viewId.d.ts +1 -19
  270. package/dist/umd/configs/viewId.d.ts.map +1 -1
  271. package/dist/umd/constants/index.d.ts +2 -13
  272. package/dist/umd/constants/index.d.ts.map +1 -1
  273. package/dist/umd/constants/viz-area-click.d.ts +6 -0
  274. package/dist/umd/constants/viz-area-click.d.ts.map +1 -0
  275. package/dist/umd/constants/viz-elm-callout.d.ts +8 -0
  276. package/dist/umd/constants/viz-elm-callout.d.ts.map +1 -0
  277. package/dist/umd/contexts/ViewIdContext.d.ts +3 -0
  278. package/dist/umd/contexts/ViewIdContext.d.ts.map +1 -0
  279. package/dist/umd/contexts/index.d.ts +1 -1
  280. package/dist/umd/contexts/index.d.ts.map +1 -1
  281. package/dist/umd/helpers/iframe-helper/fixer.d.ts +1 -1
  282. package/dist/umd/helpers/iframe-helper/fixer.d.ts.map +1 -1
  283. package/dist/umd/helpers/iframe-helper/init.d.ts +1 -1
  284. package/dist/umd/helpers/iframe-helper/init.d.ts.map +1 -1
  285. package/dist/umd/helpers/index.d.ts +5 -4
  286. package/dist/umd/helpers/index.d.ts.map +1 -1
  287. package/dist/umd/helpers/observable.d.ts +11 -0
  288. package/dist/umd/helpers/observable.d.ts.map +1 -0
  289. package/dist/umd/helpers/viewport/element.d.ts +3 -0
  290. package/dist/umd/helpers/viewport/element.d.ts.map +1 -0
  291. package/dist/umd/helpers/viewport/index.d.ts +2 -0
  292. package/dist/umd/helpers/viewport/index.d.ts.map +1 -0
  293. package/dist/umd/helpers/viz-area-click/area-builder.d.ts +8 -0
  294. package/dist/umd/helpers/viz-area-click/area-builder.d.ts.map +1 -0
  295. package/dist/umd/helpers/viz-area-click/area-color.d.ts +13 -0
  296. package/dist/umd/helpers/viz-area-click/area-color.d.ts.map +1 -0
  297. package/dist/umd/helpers/viz-area-click/area-overlap.d.ts +44 -0
  298. package/dist/umd/helpers/viz-area-click/area-overlap.d.ts.map +1 -0
  299. package/dist/umd/helpers/viz-area-click/area-utils.d.ts +54 -0
  300. package/dist/umd/helpers/viz-area-click/area-utils.d.ts.map +1 -0
  301. package/dist/umd/helpers/viz-area-click/index.d.ts +5 -0
  302. package/dist/umd/helpers/viz-area-click/index.d.ts.map +1 -0
  303. package/dist/umd/helpers/viz-canvas/area-clustering.d.ts +1 -1
  304. package/dist/umd/helpers/viz-canvas/area-clustering.d.ts.map +1 -1
  305. package/dist/umd/helpers/viz-canvas/area-overlay-manager-v2.d.ts +2 -2
  306. package/dist/umd/helpers/viz-canvas/area-overlay-manager-v2.d.ts.map +1 -1
  307. package/dist/umd/helpers/viz-canvas/area-overlay-manager.d.ts +1 -1
  308. package/dist/umd/helpers/viz-canvas/area-overlay-manager.d.ts.map +1 -1
  309. package/dist/umd/helpers/viz-canvas/hierarchical-area-clustering.d.ts +1 -1
  310. package/dist/umd/helpers/viz-canvas/hierarchical-area-clustering.d.ts.map +1 -1
  311. package/dist/umd/helpers/{iframe.d.ts → viz-dom/decode.d.ts} +2 -2
  312. package/dist/umd/helpers/viz-dom/decode.d.ts.map +1 -0
  313. package/dist/umd/helpers/viz-dom/index.d.ts +2 -0
  314. package/dist/umd/helpers/viz-dom/index.d.ts.map +1 -0
  315. package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts +4 -0
  316. package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts.map +1 -0
  317. package/dist/umd/helpers/viz-elm-callout/element.d.ts +10 -0
  318. package/dist/umd/helpers/viz-elm-callout/element.d.ts.map +1 -0
  319. package/dist/umd/helpers/{elm-getter.d.ts → viz-elm-callout/getter.d.ts} +2 -2
  320. package/dist/umd/helpers/viz-elm-callout/getter.d.ts.map +1 -0
  321. package/dist/umd/helpers/viz-elm-callout/index.d.ts +5 -0
  322. package/dist/umd/helpers/viz-elm-callout/index.d.ts.map +1 -0
  323. package/dist/umd/helpers/viz-elm-callout/position-calculator.d.ts +14 -0
  324. package/dist/umd/helpers/viz-elm-callout/position-calculator.d.ts.map +1 -0
  325. package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts +5 -0
  326. package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -0
  327. package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts +10 -0
  328. package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts.map +1 -0
  329. package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts +4 -0
  330. package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts.map +1 -0
  331. package/dist/umd/helpers/viz-elm-callout/rank-calculator.d.ts +7 -0
  332. package/dist/umd/helpers/viz-elm-callout/rank-calculator.d.ts.map +1 -0
  333. package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts +12 -0
  334. package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -0
  335. package/dist/umd/hooks/common/index.d.ts +2 -0
  336. package/dist/umd/hooks/common/index.d.ts.map +1 -0
  337. package/dist/umd/hooks/common/useDebounceCallback.d.ts +17 -0
  338. package/dist/umd/hooks/common/useDebounceCallback.d.ts.map +1 -0
  339. package/dist/umd/hooks/index.d.ts +5 -2
  340. package/dist/umd/hooks/index.d.ts.map +1 -1
  341. package/dist/umd/hooks/register/useRegisterControl.d.ts +1 -1
  342. package/dist/umd/hooks/register/useRegisterControl.d.ts.map +1 -1
  343. package/dist/umd/hooks/register/useRegisterData.d.ts +1 -1
  344. package/dist/umd/hooks/register/useRegisterData.d.ts.map +1 -1
  345. package/dist/umd/hooks/register/useRegisterHeatmap.d.ts +1 -1
  346. package/dist/umd/hooks/register/useRegisterHeatmap.d.ts.map +1 -1
  347. package/dist/umd/hooks/view-context/index.d.ts +3 -3
  348. package/dist/umd/hooks/view-context/index.d.ts.map +1 -1
  349. package/dist/umd/hooks/view-context/useHeatmapAreaClick.d.ts +19 -0
  350. package/dist/umd/hooks/view-context/useHeatmapAreaClick.d.ts.map +1 -0
  351. package/dist/{esm/hooks/view-context/useHeatmapInteraction.d.ts → umd/hooks/view-context/useHeatmapClick.d.ts} +7 -6
  352. package/dist/umd/hooks/view-context/useHeatmapClick.d.ts.map +1 -0
  353. package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
  354. package/dist/umd/hooks/view-context/useHeatmapData.d.ts +1 -1
  355. package/dist/umd/hooks/view-context/useHeatmapData.d.ts.map +1 -1
  356. package/dist/{esm/hooks/view-context/useHeatmapVizScrollmap.d.ts → umd/hooks/view-context/useHeatmapScroll.d.ts} +6 -5
  357. package/dist/umd/hooks/view-context/useHeatmapScroll.d.ts.map +1 -0
  358. package/dist/umd/hooks/view-context/useHeatmapViz.d.ts +1 -1
  359. package/dist/umd/hooks/view-context/useHeatmapViz.d.ts.map +1 -1
  360. package/dist/umd/hooks/viz-area/useAreaHeatmap.d.ts +1 -1
  361. package/dist/umd/hooks/viz-area/useAreaHeatmap.d.ts.map +1 -1
  362. package/dist/umd/hooks/viz-area/useAreaHeatmapManager.d.ts.map +1 -1
  363. package/dist/umd/hooks/viz-area-click/index.d.ts +3 -0
  364. package/dist/umd/hooks/viz-area-click/index.d.ts.map +1 -0
  365. package/dist/umd/hooks/viz-area-click/useAreaEditMode.d.ts +12 -0
  366. package/dist/umd/hooks/viz-area-click/useAreaEditMode.d.ts.map +1 -0
  367. package/dist/umd/hooks/viz-area-click/useAreaScrollSync.d.ts +8 -0
  368. package/dist/umd/hooks/viz-area-click/useAreaScrollSync.d.ts.map +1 -0
  369. package/dist/umd/hooks/viz-canvas/useAreamap.d.ts +1 -1
  370. package/dist/umd/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
  371. package/dist/umd/hooks/viz-elm/index.d.ts.map +1 -0
  372. package/dist/umd/hooks/viz-elm/useClickedElement.d.ts.map +1 -0
  373. package/dist/umd/hooks/viz-elm/useElementCalloutVisible.d.ts.map +1 -0
  374. package/dist/umd/hooks/viz-elm/useHeatmapEffects.d.ts.map +1 -0
  375. package/dist/umd/hooks/viz-elm/useHeatmapElementPosition.d.ts.map +1 -0
  376. package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts +1 -1
  377. package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts.map +1 -1
  378. package/dist/umd/hooks/viz-elm/useHoveredElement.d.ts.map +1 -0
  379. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  380. package/dist/umd/hooks/viz-render/useReplayRender.d.ts.map +1 -1
  381. package/dist/umd/hooks/viz-scroll/index.d.ts.map +1 -0
  382. package/dist/{esm/hooks/viz-scrollmap → umd/hooks/viz-scroll}/useScrollmapZones.d.ts +1 -1
  383. package/dist/umd/hooks/viz-scroll/useScrollmapZones.d.ts.map +1 -0
  384. package/dist/{esm/hooks/viz-scrollmap → umd/hooks/viz-scroll}/useZonePositions.d.ts +2 -2
  385. package/dist/umd/hooks/viz-scroll/useZonePositions.d.ts.map +1 -0
  386. package/dist/umd/index.d.ts +2 -1
  387. package/dist/umd/index.d.ts.map +1 -1
  388. package/dist/umd/index.js +2 -2
  389. package/dist/umd/performance/index.d.ts +3 -3
  390. package/dist/umd/performance/index.d.ts.map +1 -1
  391. package/dist/umd/performance/{PerformanceLogger.d.ts → performance-logger.d.ts} +1 -1
  392. package/dist/umd/performance/performance-logger.d.ts.map +1 -0
  393. package/dist/umd/performance/storeTracker.d.ts.map +1 -1
  394. package/dist/umd/performance/utils.d.ts +0 -18
  395. package/dist/umd/performance/utils.d.ts.map +1 -1
  396. package/dist/umd/performance/withPerformanceTracking.d.ts +1 -1
  397. package/dist/umd/performance/withPerformanceTracking.d.ts.map +1 -1
  398. package/dist/umd/stores/comp.d.ts +1 -1
  399. package/dist/umd/stores/comp.d.ts.map +1 -1
  400. package/dist/umd/stores/data.d.ts +1 -1
  401. package/dist/umd/stores/data.d.ts.map +1 -1
  402. package/dist/umd/stores/index.d.ts +3 -2
  403. package/dist/umd/stores/index.d.ts.map +1 -1
  404. package/dist/umd/stores/mode-compare.d.ts +1 -15
  405. package/dist/umd/stores/mode-compare.d.ts.map +1 -1
  406. package/dist/umd/stores/mode-live.d.ts +1 -1
  407. package/dist/umd/stores/mode-live.d.ts.map +1 -1
  408. package/dist/umd/stores/mode-single.d.ts +1 -1
  409. package/dist/umd/stores/mode-single.d.ts.map +1 -1
  410. package/dist/umd/stores/viz-area-click.d.ts +28 -0
  411. package/dist/umd/stores/viz-area-click.d.ts.map +1 -0
  412. package/dist/{esm/stores/interaction.d.ts → umd/stores/viz-click.d.ts} +6 -6
  413. package/dist/umd/stores/viz-click.d.ts.map +1 -0
  414. package/dist/umd/stores/viz-scrollmap.d.ts +1 -1
  415. package/dist/umd/stores/viz-scrollmap.d.ts.map +1 -1
  416. package/dist/umd/types/compare.d.ts +0 -50
  417. package/dist/umd/types/compare.d.ts.map +1 -1
  418. package/dist/umd/types/heatmap-info.d.ts +2 -2
  419. package/dist/umd/types/heatmap-info.d.ts.map +1 -1
  420. package/dist/umd/types/heatmap.d.ts +1 -0
  421. package/dist/umd/types/heatmap.d.ts.map +1 -1
  422. package/dist/umd/types/index.d.ts +5 -4
  423. package/dist/umd/types/index.d.ts.map +1 -1
  424. package/dist/umd/types/viz-area-click.d.ts +84 -0
  425. package/dist/umd/types/viz-area-click.d.ts.map +1 -0
  426. package/dist/umd/types/viz-elm-callout.d.ts +24 -0
  427. package/dist/umd/types/viz-elm-callout.d.ts.map +1 -0
  428. package/dist/umd/types/{viz-element.d.ts → viz-elm.d.ts} +1 -1
  429. package/dist/umd/types/viz-elm.d.ts.map +1 -0
  430. package/dist/umd/utils/sort.d.ts +1 -1
  431. package/dist/umd/utils/sort.d.ts.map +1 -1
  432. package/package.json +4 -1
  433. package/dist/esm/components/Layout/ContentMetricBar.d.ts.map +0 -1
  434. package/dist/esm/components/Layout/ContentToolbar.d.ts.map +0 -1
  435. package/dist/esm/components/Layout/ContentTopBar.d.ts.map +0 -1
  436. package/dist/esm/components/Layout/ContentVizByMode.d.ts.map +0 -1
  437. package/dist/esm/components/Layout/WrapperLayout.d.ts +0 -2
  438. package/dist/esm/components/Layout/WrapperLayout.d.ts.map +0 -1
  439. package/dist/esm/components/Layout/WrapperPreview.d.ts +0 -2
  440. package/dist/esm/components/Layout/WrapperPreview.d.ts.map +0 -1
  441. package/dist/esm/components/VizElement/HeatmapExample.d.ts +0 -2
  442. package/dist/esm/components/VizElement/HeatmapExample.d.ts.map +0 -1
  443. package/dist/esm/contexts/CompareViewContext.d.ts +0 -28
  444. package/dist/esm/contexts/CompareViewContext.d.ts.map +0 -1
  445. package/dist/esm/helpers/elm-callout.d.ts +0 -9
  446. package/dist/esm/helpers/elm-callout.d.ts.map +0 -1
  447. package/dist/esm/helpers/elm-getter.d.ts.map +0 -1
  448. package/dist/esm/helpers/iframe.d.ts.map +0 -1
  449. package/dist/esm/helpers/viz-elements.d.ts +0 -9
  450. package/dist/esm/helpers/viz-elements.d.ts.map +0 -1
  451. package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts.map +0 -1
  452. package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +0 -1
  453. package/dist/esm/hooks/view-context/useViewId.d.ts +0 -15
  454. package/dist/esm/hooks/view-context/useViewId.d.ts.map +0 -1
  455. package/dist/esm/hooks/viz-elements/index.d.ts.map +0 -1
  456. package/dist/esm/hooks/viz-elements/useClickedElement.d.ts.map +0 -1
  457. package/dist/esm/hooks/viz-elements/useElementCalloutVisible.d.ts.map +0 -1
  458. package/dist/esm/hooks/viz-elements/useHeatmapEffects.d.ts.map +0 -1
  459. package/dist/esm/hooks/viz-elements/useHeatmapElementPosition.d.ts.map +0 -1
  460. package/dist/esm/hooks/viz-elements/useHoveredElement.d.ts.map +0 -1
  461. package/dist/esm/hooks/viz-scrollmap/index.d.ts.map +0 -1
  462. package/dist/esm/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +0 -1
  463. package/dist/esm/hooks/viz-scrollmap/useZonePositions.d.ts.map +0 -1
  464. package/dist/esm/performance/PerformanceLogger.d.ts.map +0 -1
  465. package/dist/esm/stores/interaction.d.ts.map +0 -1
  466. package/dist/esm/types/elm-callout.d.ts +0 -9
  467. package/dist/esm/types/elm-callout.d.ts.map +0 -1
  468. package/dist/esm/types/viz-element.d.ts.map +0 -1
  469. package/dist/umd/components/Layout/ContentMetricBar.d.ts.map +0 -1
  470. package/dist/umd/components/Layout/ContentToolbar.d.ts.map +0 -1
  471. package/dist/umd/components/Layout/ContentTopBar.d.ts.map +0 -1
  472. package/dist/umd/components/Layout/ContentVizByMode.d.ts.map +0 -1
  473. package/dist/umd/components/Layout/WrapperLayout.d.ts +0 -2
  474. package/dist/umd/components/Layout/WrapperLayout.d.ts.map +0 -1
  475. package/dist/umd/components/Layout/WrapperPreview.d.ts +0 -2
  476. package/dist/umd/components/Layout/WrapperPreview.d.ts.map +0 -1
  477. package/dist/umd/components/VizElement/HeatmapExample.d.ts +0 -2
  478. package/dist/umd/components/VizElement/HeatmapExample.d.ts.map +0 -1
  479. package/dist/umd/contexts/CompareViewContext.d.ts +0 -28
  480. package/dist/umd/contexts/CompareViewContext.d.ts.map +0 -1
  481. package/dist/umd/helpers/elm-callout.d.ts +0 -9
  482. package/dist/umd/helpers/elm-callout.d.ts.map +0 -1
  483. package/dist/umd/helpers/elm-getter.d.ts.map +0 -1
  484. package/dist/umd/helpers/iframe.d.ts.map +0 -1
  485. package/dist/umd/helpers/viz-elements.d.ts +0 -9
  486. package/dist/umd/helpers/viz-elements.d.ts.map +0 -1
  487. package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts.map +0 -1
  488. package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +0 -1
  489. package/dist/umd/hooks/view-context/useViewId.d.ts +0 -15
  490. package/dist/umd/hooks/view-context/useViewId.d.ts.map +0 -1
  491. package/dist/umd/hooks/viz-elements/index.d.ts.map +0 -1
  492. package/dist/umd/hooks/viz-elements/useClickedElement.d.ts.map +0 -1
  493. package/dist/umd/hooks/viz-elements/useElementCalloutVisible.d.ts.map +0 -1
  494. package/dist/umd/hooks/viz-elements/useHeatmapEffects.d.ts.map +0 -1
  495. package/dist/umd/hooks/viz-elements/useHeatmapElementPosition.d.ts.map +0 -1
  496. package/dist/umd/hooks/viz-elements/useHoveredElement.d.ts.map +0 -1
  497. package/dist/umd/hooks/viz-scrollmap/index.d.ts.map +0 -1
  498. package/dist/umd/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +0 -1
  499. package/dist/umd/hooks/viz-scrollmap/useZonePositions.d.ts.map +0 -1
  500. package/dist/umd/performance/PerformanceLogger.d.ts.map +0 -1
  501. package/dist/umd/stores/interaction.d.ts.map +0 -1
  502. package/dist/umd/types/elm-callout.d.ts +0 -9
  503. package/dist/umd/types/elm-callout.d.ts.map +0 -1
  504. package/dist/umd/types/viz-element.d.ts.map +0 -1
  505. /package/dist/esm/components/Layout/{ContentMetricBar.d.ts → MetricBar/ContentMetricBar.d.ts} +0 -0
  506. /package/dist/esm/components/Layout/{ContentToolbar.d.ts → Toolbar/ContentToolbar.d.ts} +0 -0
  507. /package/dist/esm/components/Layout/{ContentTopBar.d.ts → TopBar/ContentTopBar.d.ts} +0 -0
  508. /package/dist/esm/components/Layout/{ContentVizByMode.d.ts → VizByMode/ContentVizByMode.d.ts} +0 -0
  509. /package/dist/esm/hooks/{viz-elements → viz-elm}/index.d.ts +0 -0
  510. /package/dist/esm/hooks/{viz-elements → viz-elm}/useClickedElement.d.ts +0 -0
  511. /package/dist/esm/hooks/{viz-elements → viz-elm}/useElementCalloutVisible.d.ts +0 -0
  512. /package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapEffects.d.ts +0 -0
  513. /package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapElementPosition.d.ts +0 -0
  514. /package/dist/esm/hooks/{viz-elements → viz-elm}/useHoveredElement.d.ts +0 -0
  515. /package/dist/esm/hooks/{viz-scrollmap → viz-scroll}/index.d.ts +0 -0
  516. /package/dist/umd/components/Layout/{ContentMetricBar.d.ts → MetricBar/ContentMetricBar.d.ts} +0 -0
  517. /package/dist/umd/components/Layout/{ContentToolbar.d.ts → Toolbar/ContentToolbar.d.ts} +0 -0
  518. /package/dist/umd/components/Layout/{ContentTopBar.d.ts → TopBar/ContentTopBar.d.ts} +0 -0
  519. /package/dist/umd/components/Layout/{ContentVizByMode.d.ts → VizByMode/ContentVizByMode.d.ts} +0 -0
  520. /package/dist/umd/hooks/{viz-elements → viz-elm}/index.d.ts +0 -0
  521. /package/dist/umd/hooks/{viz-elements → viz-elm}/useClickedElement.d.ts +0 -0
  522. /package/dist/umd/hooks/{viz-elements → viz-elm}/useElementCalloutVisible.d.ts +0 -0
  523. /package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapEffects.d.ts +0 -0
  524. /package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapElementPosition.d.ts +0 -0
  525. /package/dist/umd/hooks/{viz-elements → viz-elm}/useHoveredElement.d.ts +0 -0
  526. /package/dist/umd/hooks/{viz-scrollmap → viz-scroll}/index.d.ts +0 -0
package/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { useNodesState, ReactFlow, Controls, Background } from '@xyflow/react';
4
- import { useEffect, createContext, useContext, useMemo, useCallback, useState, useRef, forwardRef, Fragment as Fragment$1 } from 'react';
4
+ import { useEffect, useRef, useCallback, createContext, useContext, useMemo, useState, forwardRef, Fragment as Fragment$1 } from 'react';
5
5
  import { create } from 'zustand';
6
6
  import { subscribeWithSelector } from 'zustand/middleware';
7
7
  import { decode } from '@gemx-dev/clarity-decode';
@@ -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 = {
@@ -102,6 +81,36 @@ const Z_INDEX = {
102
81
  SIDEBAR_POPOVER: 4001,
103
82
  };
104
83
 
84
+ /**
85
+ * Creates a debounced version of a callback that delays invoking until after
86
+ * wait milliseconds have elapsed since the last time it was invoked.
87
+ *
88
+ * @param callback - The function to debounce
89
+ * @param delay - The number of milliseconds to delay
90
+ * @returns A debounced version of the callback
91
+ *
92
+ * @example
93
+ * ```tsx
94
+ * const handleSearch = useDebounceCallback((query: string) => {
95
+ * searchAPI(query);
96
+ * }, 300);
97
+ * ```
98
+ */
99
+ function useDebounceCallback(callback, delay) {
100
+ const timeoutRef = useRef();
101
+ const callbackRef = useRef(callback);
102
+ // Update callback ref when callback changes
103
+ callbackRef.current = callback;
104
+ return useCallback((...args) => {
105
+ if (timeoutRef.current) {
106
+ clearTimeout(timeoutRef.current);
107
+ }
108
+ timeoutRef.current = setTimeout(() => {
109
+ callbackRef.current(...args);
110
+ }, delay);
111
+ }, [delay]);
112
+ }
113
+
105
114
  const useHeatmapControlStore = create()((set, get) => {
106
115
  return {
107
116
  controls: {
@@ -127,6 +136,7 @@ const useHeatmapControlStore = create()((set, get) => {
127
136
  var IHeatmapType;
128
137
  (function (IHeatmapType) {
129
138
  IHeatmapType["Click"] = "click";
139
+ IHeatmapType["ClickArea"] = "click-area";
130
140
  IHeatmapType["Scroll"] = "scroll";
131
141
  })(IHeatmapType || (IHeatmapType = {}));
132
142
  var IClickType;
@@ -145,7 +155,7 @@ var IScrollType;
145
155
  IScrollType["Revenue"] = "revenue-scroll";
146
156
  })(IScrollType || (IScrollType = {}));
147
157
 
148
- const useHeatmapConfigStore = create()((set, get) => {
158
+ const useHeatmapConfigStore = create()((set) => {
149
159
  return {
150
160
  mode: 'single',
151
161
  width: 1440,
@@ -165,7 +175,7 @@ const useHeatmapConfigStore = create()((set, get) => {
165
175
  };
166
176
  });
167
177
 
168
- const useHeatmapDataStore = create()(subscribeWithSelector((set, get) => {
178
+ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
169
179
  return {
170
180
  data: { [DEFAULT_VIEW_ID]: undefined },
171
181
  clickmap: { [DEFAULT_VIEW_ID]: undefined },
@@ -214,10 +224,173 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set, get) => {
214
224
  };
215
225
  }));
216
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
+
217
390
  const DEFAULT_STATE = {
218
391
  hideSidebar: false,
219
392
  };
220
- const useHeatmapInteractionStore = create()(subscribeWithSelector((set, get) => {
393
+ const useHeatmapClickStore = create()(subscribeWithSelector((set) => {
221
394
  return {
222
395
  state: { [DEFAULT_VIEW_ID]: DEFAULT_STATE },
223
396
  selectedElement: { [DEFAULT_VIEW_ID]: null },
@@ -278,68 +451,7 @@ const useHeatmapInteractionStore = create()(subscribeWithSelector((set, get) =>
278
451
  };
279
452
  }));
280
453
 
281
- const useHeatmapVizStore = create()(subscribeWithSelector((set, get) => {
282
- return {
283
- isRenderViz: { [DEFAULT_VIEW_ID]: false },
284
- zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
285
- minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
286
- scale: { [DEFAULT_VIEW_ID]: 1 },
287
- isScaledToFit: { [DEFAULT_VIEW_ID]: false },
288
- setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID) => set((state) => ({
289
- isRenderViz: { ...state.isRenderViz, [viewId]: isRenderViz },
290
- })),
291
- setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
292
- zoomRatio: { ...state.zoomRatio, [viewId]: zoomRatio },
293
- })),
294
- setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
295
- minZoomRatio: { ...state.minZoomRatio, [viewId]: minZoomRatio },
296
- })),
297
- setScale: (scale, viewId = DEFAULT_VIEW_ID) => set((state) => ({
298
- scale: { ...state.scale, [viewId]: scale },
299
- })),
300
- setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID) => set((state) => ({
301
- isScaledToFit: { ...state.isScaledToFit, [viewId]: isScaledToFit },
302
- })),
303
- copyView: (fromViewId, toViewId) => set((state) => ({
304
- isRenderViz: { ...state.isRenderViz, [toViewId]: state.isRenderViz[fromViewId] ?? false },
305
- zoomRatio: { ...state.zoomRatio, [toViewId]: state.zoomRatio[fromViewId] ?? 100 },
306
- minZoomRatio: { ...state.minZoomRatio, [toViewId]: state.minZoomRatio[fromViewId] ?? 10 },
307
- scale: { ...state.scale, [toViewId]: state.scale[fromViewId] ?? 1 },
308
- isScaledToFit: {
309
- ...state.isScaledToFit,
310
- [toViewId]: state.isScaledToFit[fromViewId] ?? false,
311
- },
312
- })),
313
- clearView: (viewId) => set((state) => {
314
- const newIsRenderViz = { ...state.isRenderViz };
315
- const newZoomRatio = { ...state.zoomRatio };
316
- const newMinZoomRatio = { ...state.minZoomRatio };
317
- const newScale = { ...state.scale };
318
- const newIsScaledToFit = { ...state.isScaledToFit };
319
- delete newIsRenderViz[viewId];
320
- delete newZoomRatio[viewId];
321
- delete newMinZoomRatio[viewId];
322
- delete newScale[viewId];
323
- delete newIsScaledToFit[viewId];
324
- return {
325
- isRenderViz: newIsRenderViz,
326
- zoomRatio: newZoomRatio,
327
- minZoomRatio: newMinZoomRatio,
328
- scale: newScale,
329
- isScaledToFit: newIsScaledToFit,
330
- };
331
- }),
332
- resetAll: () => set({
333
- isRenderViz: { [DEFAULT_VIEW_ID]: false },
334
- zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
335
- minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
336
- scale: { [DEFAULT_VIEW_ID]: 1 },
337
- isScaledToFit: { [DEFAULT_VIEW_ID]: false },
338
- }),
339
- };
340
- }));
341
-
342
- const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set, get) => {
454
+ const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set) => {
343
455
  return {
344
456
  zones: { [DEFAULT_VIEW_ID]: [] },
345
457
  hoveredZone: { [DEFAULT_VIEW_ID]: null },
@@ -388,94 +500,9 @@ const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set, get) =>
388
500
  };
389
501
  }));
390
502
 
391
- const initialState = {
392
- payloads: [],
393
- htmlContent: '',
394
- };
395
- const useHeatmapLiveStore = create()((set, get) => {
396
- return {
397
- ...initialState,
398
- reset: () => set(initialState),
399
- setPayloads: (payloads) => set({ payloads }),
400
- addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
401
- setHtmlContent: (htmlContent) => set({ htmlContent }),
402
- };
403
- });
404
-
405
- const useHeatmapSingleStore = create()(subscribeWithSelector((set, get) => {
406
- return {
407
- vizRef: { [DEFAULT_VIEW_ID]: null },
408
- iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
409
- wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
410
- wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
411
- setVizRef: (vizRef, viewId = DEFAULT_VIEW_ID) => set((state) => ({
412
- vizRef: { ...state.vizRef, [viewId]: vizRef },
413
- })),
414
- setIframeHeight: (iframeHeight, viewId = DEFAULT_VIEW_ID) => {
415
- set((state) => ({
416
- iframeHeight: { ...state.iframeHeight, [viewId]: iframeHeight },
417
- }));
418
- },
419
- setWrapperHeight: (wrapperHeight, viewId = DEFAULT_VIEW_ID) => {
420
- set((state) => ({
421
- wrapperHeight: { ...state.wrapperHeight, [viewId]: wrapperHeight },
422
- }));
423
- },
424
- setWrapperWidth: (wrapperWidth, viewId = DEFAULT_VIEW_ID) => {
425
- set((state) => ({
426
- wrapperWidth: { ...state.wrapperWidth, [viewId]: wrapperWidth },
427
- }));
428
- },
429
- copyView: (fromViewId, toViewId) => set((state) => ({
430
- // Don't copy vizRef - each view needs its own visualizer instance
431
- iframeHeight: { ...state.iframeHeight, [toViewId]: state.iframeHeight[fromViewId] ?? 0 },
432
- wrapperHeight: {
433
- ...state.wrapperHeight,
434
- [toViewId]: state.wrapperHeight[fromViewId] ?? 0,
435
- },
436
- })),
437
- clearView: (viewId) => set((state) => {
438
- const newVizRef = { ...state.vizRef };
439
- const newIframeHeight = { ...state.iframeHeight };
440
- const newWrapperHeight = { ...state.wrapperHeight };
441
- const newWrapperWidth = { ...state.wrapperWidth };
442
- delete newVizRef[viewId];
443
- delete newIframeHeight[viewId];
444
- delete newWrapperHeight[viewId];
445
- delete newWrapperWidth[viewId];
446
- return {
447
- vizRef: newVizRef,
448
- iframeHeight: newIframeHeight,
449
- wrapperHeight: newWrapperHeight,
450
- wrapperWidth: newWrapperWidth,
451
- };
452
- }),
453
- resetAll: () => set({
454
- vizRef: { [DEFAULT_VIEW_ID]: null },
455
- iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
456
- wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
457
- wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
458
- }),
459
- };
460
- }));
461
-
462
- const createDefaultView = (id, label, options) => ({
503
+ const createDefaultView = (id, label) => ({
463
504
  id,
464
505
  label,
465
- heatmapType: options?.heatmapType ?? IHeatmapType.Scroll,
466
- clickType: options?.clickType ?? IClickType.Total,
467
- scrollType: options?.scrollType ?? IScrollType.Depth,
468
- data: options?.data,
469
- clickmap: undefined,
470
- scrollmap: undefined,
471
- dataInfo: undefined,
472
- vizRef: null,
473
- iframeHeight: 0,
474
- zoomRatio: 100,
475
- scale: 1,
476
- isScaledToFit: false,
477
- isRendering: true,
478
- isRenderViz: false,
479
506
  });
480
507
  const useHeatmapCompareStore = create()((set, get) => {
481
508
  return {
@@ -490,9 +517,9 @@ const useHeatmapCompareStore = create()((set, get) => {
490
517
  viewIdCounter: 0,
491
518
  addView: (options) => {
492
519
  const state = get();
493
- const viewId = `view-${state.viewIdCounter}`;
520
+ const viewId = getCompareViewId(state.viewIdCounter);
494
521
  const label = options?.label ?? `Version ${state.viewOrder.length + 1}`;
495
- const newView = createDefaultView(viewId, label, options);
522
+ const newView = createDefaultView(viewId, label);
496
523
  const newViews = new Map(state.views);
497
524
  newViews.set(viewId, newView);
498
525
  set({
@@ -519,16 +546,6 @@ const useHeatmapCompareStore = create()((set, get) => {
519
546
  const newViews = new Map(state.views);
520
547
  newViews.set(viewId, { ...view, ...updates });
521
548
  set({ views: newViews });
522
- // Handle syncing
523
- const { syncSettings } = state;
524
- if (syncSettings.zoomRatio && 'zoomRatio' in updates && updates.zoomRatio !== undefined) {
525
- get().syncProperty('zoomRatio', updates.zoomRatio);
526
- }
527
- if (syncSettings.heatmapType &&
528
- 'heatmapType' in updates &&
529
- updates.heatmapType !== undefined) {
530
- get().syncProperty('heatmapType', updates.heatmapType);
531
- }
532
549
  },
533
550
  getView: (viewId) => {
534
551
  return get().views.get(viewId);
@@ -543,9 +560,9 @@ const useHeatmapCompareStore = create()((set, get) => {
543
560
  const viewIds = [];
544
561
  const newViews = new Map();
545
562
  for (let i = 0; i < count; i++) {
546
- const viewId = `view-${i}`;
563
+ const viewId = getCompareViewId(i);
547
564
  const label = options?.label ?? String.fromCharCode(65 + i); // A, B, C, D
548
- newViews.set(viewId, createDefaultView(viewId, `Version ${label}`, options));
565
+ newViews.set(viewId, createDefaultView(viewId, `Version ${label}`));
549
566
  viewIds.push(viewId);
550
567
  }
551
568
  const layoutMap = {
@@ -587,54 +604,188 @@ const useHeatmapCompareStore = create()((set, get) => {
587
604
  };
588
605
  });
589
606
 
590
- const useRegisterConfig = () => {
591
- const mode = useHeatmapConfigStore((state) => state.mode);
592
- const width = useHeatmapConfigStore((state) => state.width);
593
- const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
594
- const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
595
- const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
596
- useEffect(() => {
597
- setIsRendering(true);
598
- setTimeout(() => {
599
- setIsRendering(false);
600
- }, 1000);
601
- }, [mode, width, sidebarWidth, heatmapType]);
602
- };
603
-
604
- const useRegisterControl = (control) => {
605
- const registerControl = useHeatmapControlStore((state) => state.registerControl);
606
- registerControl('Sidebar', control.Sidebar);
607
- registerControl('SidebarActivator', control.SidebarActivator);
608
- registerControl('TopBar', control.TopBar);
609
- registerControl('Toolbar', control.Toolbar);
610
- registerControl('MetricBar', control.MetricBar);
611
- registerControl('VizLoading', control.VizLoading);
612
- 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);
613
701
  };
614
702
 
615
- /**
616
- * Context to provide viewId to components
617
- * Used in compare mode to isolate data between views
618
- */
619
703
  const ViewIdContext = createContext(undefined);
620
- /**
621
- * Hook to get current viewId
622
- * Returns DEFAULT_VIEW_ID if not in a ViewIdContext (single mode)
623
- */
624
- const useViewId = () => {
704
+ const useViewIdContext = () => {
625
705
  const viewId = useContext(ViewIdContext);
626
706
  return viewId || DEFAULT_VIEW_ID;
627
707
  };
628
- /**
629
- * Hook to check if currently in compare mode
630
- */
631
- const useIsCompareMode = () => {
632
- const viewId = useContext(ViewIdContext);
633
- 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
+ };
634
785
  };
635
786
 
636
787
  const useHeatmapData = (props) => {
637
- const viewId = props?.viewId || useViewId();
788
+ const viewId = props?.viewId || useViewIdContext();
638
789
  const data = useHeatmapDataStore((state) => state.data[viewId]);
639
790
  const clickmap = useHeatmapDataStore((state) => state.clickmap[viewId]);
640
791
  const scrollmap = useHeatmapDataStore((state) => state.scrollmap[viewId]);
@@ -659,40 +810,30 @@ const useHeatmapData = (props) => {
659
810
  };
660
811
  };
661
812
 
662
- const useHeatmapInteraction = (props) => {
663
- const viewId = props?.viewId || useViewId();
664
- const state = useHeatmapInteractionStore((store) => store.state[viewId] ?? { hideSidebar: false });
665
- const selectedElement = useHeatmapInteractionStore((store) => store.selectedElement[viewId] ?? null);
666
- const hoveredElement = useHeatmapInteractionStore((store) => store.hoveredElement[viewId] ?? null);
667
- const shouldShowCallout = useHeatmapInteractionStore((store) => store.shouldShowCallout[viewId] ?? false);
668
- const setStateStore = useHeatmapInteractionStore((store) => store.setState);
669
- const setSelectedElementStore = useHeatmapInteractionStore((store) => store.setSelectedElement);
670
- const setHoveredElementStore = useHeatmapInteractionStore((store) => store.setHoveredElement);
671
- 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);
672
821
  const memoizedSetters = useMemo(() => ({
673
- setState: (newState) => setStateStore(newState, viewId),
674
- setSelectedElement: (element) => setSelectedElementStore(element, viewId),
675
- setHoveredElement: (element) => setHoveredElementStore(element, viewId),
676
- setShouldShowCallout: (value) => setShouldShowCalloutStore(value, viewId),
677
- }), [
678
- setStateStore,
679
- setSelectedElementStore,
680
- setHoveredElementStore,
681
- setShouldShowCalloutStore,
682
- viewId,
683
- ]);
822
+ setZones: (newZones) => setZonesStore(newZones, viewId),
823
+ setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
824
+ setShowMinimap: (value) => setShowMinimapStore(value, viewId),
825
+ }), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
684
826
  return {
685
- state,
686
- selectedElement,
687
- hoveredElement,
688
- shouldShowCallout,
827
+ zones,
828
+ hoveredZone,
829
+ showMinimap,
689
830
  // Setters (auto-inject viewId)
690
831
  ...memoizedSetters,
691
832
  };
692
833
  };
693
834
 
694
835
  const useHeatmapViz = (props) => {
695
- const viewId = props?.viewId || useViewId();
836
+ const viewId = props?.viewId || useViewIdContext();
696
837
  // Viz store
697
838
  const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
698
839
  const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
@@ -750,28 +891,6 @@ const useHeatmapViz = (props) => {
750
891
  };
751
892
  };
752
893
 
753
- const useHeatmapVizScrollmap = (props) => {
754
- const viewId = props?.viewId || useViewId();
755
- const zones = useHeatmapVizScrollmapStore((store) => store.zones[viewId] ?? []);
756
- const hoveredZone = useHeatmapVizScrollmapStore((store) => store.hoveredZone[viewId] ?? null);
757
- const showMinimap = useHeatmapVizScrollmapStore((store) => store.showMinimap[viewId] ?? true);
758
- const setZonesStore = useHeatmapVizScrollmapStore((store) => store.setZones);
759
- const setHoveredZoneStore = useHeatmapVizScrollmapStore((store) => store.setHoveredZone);
760
- const setShowMinimapStore = useHeatmapVizScrollmapStore((store) => store.setShowMinimap);
761
- const memoizedSetters = useMemo(() => ({
762
- setZones: (newZones) => setZonesStore(newZones, viewId),
763
- setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
764
- setShowMinimap: (value) => setShowMinimapStore(value, viewId),
765
- }), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
766
- return {
767
- zones,
768
- hoveredZone,
769
- showMinimap,
770
- // Setters (auto-inject viewId)
771
- ...memoizedSetters,
772
- };
773
- };
774
-
775
894
  /**
776
895
  * Hook to handle copying and clearing view data across all stores
777
896
  */
@@ -779,24 +898,28 @@ const useHeatmapCopyView = () => {
779
898
  const copyDataView = useHeatmapDataStore((state) => state.copyView);
780
899
  const copyVizView = useHeatmapVizStore((state) => state.copyView);
781
900
  const copySingleView = useHeatmapSingleStore((state) => state.copyView);
782
- const copyInteractionView = useHeatmapInteractionStore((state) => state.copyView);
901
+ const copyInteractionView = useHeatmapClickStore((state) => state.copyView);
783
902
  const copyVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.copyView);
903
+ const copyAreaClickView = useHeatmapAreaClickStore((state) => state.copyView);
784
904
  const clearDataView = useHeatmapDataStore((state) => state.clearView);
785
905
  const clearVizView = useHeatmapVizStore((state) => state.clearView);
786
906
  const clearSingleView = useHeatmapSingleStore((state) => state.clearView);
787
- const clearInteractionView = useHeatmapInteractionStore((state) => state.clearView);
907
+ const clearInteractionView = useHeatmapClickStore((state) => state.clearView);
788
908
  const clearVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.clearView);
909
+ const clearAreaClickView = useHeatmapAreaClickStore((state) => state.clearView);
789
910
  const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
790
911
  const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
791
912
  const resetSingleAll = useHeatmapSingleStore((state) => state.resetAll);
792
- const resetInteractionAll = useHeatmapInteractionStore((state) => state.resetAll);
913
+ const resetInteractionAll = useHeatmapClickStore((state) => state.resetAll);
793
914
  const resetVizScrollmapAll = useHeatmapVizScrollmapStore((state) => state.resetAll);
915
+ const resetAreaClickAll = useHeatmapAreaClickStore((state) => state.resetAll);
794
916
  const copyView = (fromViewId, toViewId) => {
795
917
  copyDataView(fromViewId, toViewId);
796
918
  copyVizView(fromViewId, toViewId);
797
919
  copySingleView(fromViewId, toViewId);
798
920
  copyInteractionView(fromViewId, toViewId);
799
921
  copyVizScrollmapView(fromViewId, toViewId);
922
+ copyAreaClickView(fromViewId, toViewId);
800
923
  };
801
924
  const copyViewToMultiple = (fromViewId, toViewIds) => {
802
925
  toViewIds.forEach((toViewId) => {
@@ -809,6 +932,7 @@ const useHeatmapCopyView = () => {
809
932
  clearSingleView(viewId);
810
933
  clearInteractionView(viewId);
811
934
  clearVizScrollmapView(viewId);
935
+ clearAreaClickView(viewId);
812
936
  };
813
937
  const clearMultipleViews = (viewIds) => {
814
938
  viewIds.forEach((viewId) => {
@@ -821,6 +945,7 @@ const useHeatmapCopyView = () => {
821
945
  resetSingleAll();
822
946
  resetInteractionAll();
823
947
  resetVizScrollmapAll();
948
+ resetAreaClickAll();
824
949
  };
825
950
  return {
826
951
  copyView,
@@ -873,23 +998,589 @@ const useRegisterHeatmap = ({ clickmap, scrollmap }) => {
873
998
  }, [scrollmap]);
874
999
  };
875
1000
 
876
- const PADDING = 0;
877
- const ARROW_SIZE = 8;
878
- const HORIZONTAL_OFFSET = 0;
879
- // ============================================================================
880
- // Viewport & Dimensions
881
- // ============================================================================
882
- const getViewportDimensions = () => ({
883
- width: window.innerWidth,
884
- height: window.innerHeight,
885
- });
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
+ }
1306
+
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))
1345
+ return;
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
1350
+ return;
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
+ }
1448
+
1449
+ function findLastSizeOfDom(data) {
1450
+ const listDocs = data
1451
+ .filter((item) => item.doc?.find((doc) => doc.data.width && doc.data.height))
1452
+ .flatMap((item) => item.doc?.flatMap((doc) => doc.data));
1453
+ const lastDoc = listDocs?.[listDocs.length - 1];
1454
+ const docSize = {
1455
+ width: lastDoc?.width,
1456
+ height: lastDoc?.height,
1457
+ };
1458
+ const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
1459
+ const lastResizeEvent = listResizes?.[listResizes.length - 1];
1460
+ const resize = {
1461
+ width: lastResizeEvent?.data.width,
1462
+ height: lastResizeEvent?.data.height,
1463
+ };
1464
+ return {
1465
+ doc: docSize,
1466
+ resize: resize,
1467
+ size: {
1468
+ width: resize.width || docSize.width,
1469
+ height: resize.height || docSize.height,
1470
+ },
1471
+ };
1472
+ }
1473
+ function decodePayloads(payload) {
1474
+ try {
1475
+ return decode(payload);
1476
+ }
1477
+ catch (_error) {
1478
+ return null;
1479
+ }
1480
+ }
1481
+
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
+ };
1499
+
1500
+ function getElementLayout(element) {
1501
+ if (!element?.getBoundingClientRect)
1502
+ return null;
1503
+ const rect = element.getBoundingClientRect();
1504
+ if (rect.width === 0 && rect.height === 0)
1505
+ return null;
1506
+ return {
1507
+ top: rect.top,
1508
+ left: rect.left,
1509
+ width: rect.width,
1510
+ height: rect.height,
1511
+ };
1512
+ }
1513
+ const getElementAtPoint = (doc, x, y) => {
1514
+ let el = null;
1515
+ if ('caretPositionFromPoint' in doc) {
1516
+ el = doc.caretPositionFromPoint(x, y)?.offsetNode ?? null;
1517
+ }
1518
+ el = el ?? doc.elementFromPoint(x, y);
1519
+ let element = el;
1520
+ while (element && element.nodeType === Node.TEXT_NODE) {
1521
+ element = element.parentElement;
1522
+ }
1523
+ return element;
1524
+ };
1525
+ function getElementHash(element) {
1526
+ return (element.getAttribute('data-clarity-hash') ||
1527
+ element.getAttribute('data-clarity-hashalpha') ||
1528
+ element.getAttribute('data-clarity-hashbeta'));
1529
+ }
1530
+ const getElementRank = (hash, elements) => {
1531
+ if (!elements)
1532
+ return 0;
1533
+ return elements.findIndex((e) => e.hash === hash) + 1;
1534
+ };
1535
+ const buildElementInfo = (hash, rect, heatmapInfo) => {
1536
+ if (!rect || !heatmapInfo)
1537
+ return null;
1538
+ const info = heatmapInfo.elementMapInfo?.[hash];
1539
+ if (!info)
1540
+ return null;
1541
+ const rank = getElementRank(hash, heatmapInfo.sortedElements);
1542
+ const clicks = info.totalclicks ?? 0;
1543
+ const selector = info.selector ?? '';
1544
+ const baseInfo = {
1545
+ hash,
1546
+ clicks,
1547
+ rank,
1548
+ selector,
1549
+ };
1550
+ return {
1551
+ ...baseInfo,
1552
+ ...rect,
1553
+ };
1554
+ };
1555
+
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
+ }
1565
+
1566
+ const getViewportDimensions = (containerElm) => {
1567
+ if (containerElm) {
1568
+ const containerRect = containerElm.getBoundingClientRect();
1569
+ return {
1570
+ width: containerRect.width,
1571
+ height: containerRect.height,
1572
+ };
1573
+ }
1574
+ return {
1575
+ width: window.innerWidth,
1576
+ height: window.innerHeight,
1577
+ };
1578
+ };
886
1579
  const getElementDimensions = (targetElm, calloutElm) => ({
887
1580
  targetRect: targetElm.getBoundingClientRect(),
888
1581
  calloutRect: calloutElm.getBoundingClientRect(),
889
1582
  });
890
- // ============================================================================
891
- // Alignment Order
892
- // ============================================================================
1583
+
893
1584
  const getAlignmentOrder = (alignment) => {
894
1585
  switch (alignment) {
895
1586
  case 'center':
@@ -900,9 +1591,6 @@ const getAlignmentOrder = (alignment) => {
900
1591
  return ['right', 'center', 'left'];
901
1592
  }
902
1593
  };
903
- // ============================================================================
904
- // Position Calculation
905
- // ============================================================================
906
1594
  const calculateLeftPosition = ({ targetRect, calloutRect, hozOffset, align, }) => {
907
1595
  switch (align) {
908
1596
  case 'left':
@@ -926,31 +1614,41 @@ const calculateHorizontalPlacementPosition = (targetRect, calloutRect, placement
926
1614
  : targetRect.left - calloutRect.width - padding - arrowSize;
927
1615
  return { top, left };
928
1616
  };
929
- // ============================================================================
930
- // Validation
931
- // ============================================================================
932
- const isLeftPositionValid = (leftPos, calloutWidth, viewportWidth, padding) => {
1617
+
1618
+ const isLeftPositionValid = (leftPos, calloutWidth, viewportWidth, padding, containerRect) => {
1619
+ if (containerRect) {
1620
+ return (leftPos >= containerRect.left + padding &&
1621
+ leftPos + calloutWidth <= containerRect.right - padding);
1622
+ }
933
1623
  return leftPos >= padding && leftPos + calloutWidth <= viewportWidth - padding;
934
1624
  };
935
- const isVerticalPositionValid = (targetRect, calloutRect, placement, viewportHeight, padding, arrowSize) => {
1625
+ const isVerticalPositionValid = (targetRect, calloutRect, placement, viewportHeight, padding, arrowSize, containerRect) => {
1626
+ if (containerRect) {
1627
+ return placement === 'top'
1628
+ ? targetRect.top - calloutRect.height - padding - arrowSize >= containerRect.top
1629
+ : targetRect.bottom + calloutRect.height + padding + arrowSize <= containerRect.bottom;
1630
+ }
936
1631
  return placement === 'top'
937
1632
  ? targetRect.top - calloutRect.height - padding - arrowSize > 0
938
1633
  : targetRect.bottom + calloutRect.height + padding + arrowSize < viewportHeight;
939
1634
  };
940
- const isHorizontalPlacementValid = (targetRect, calloutRect, placement, viewportWidth, padding, arrowSize) => {
1635
+ const isHorizontalPlacementValid = (targetRect, calloutRect, placement, viewportWidth, padding, arrowSize, containerRect) => {
1636
+ if (containerRect) {
1637
+ return placement === 'right'
1638
+ ? targetRect.right + calloutRect.width + padding + arrowSize <= containerRect.right
1639
+ : targetRect.left - calloutRect.width - padding - arrowSize >= containerRect.left;
1640
+ }
941
1641
  return placement === 'right'
942
1642
  ? targetRect.right + calloutRect.width + padding + arrowSize < viewportWidth
943
1643
  : targetRect.left - calloutRect.width - padding - arrowSize > 0;
944
1644
  };
945
- // ============================================================================
946
- // Position Candidates Generation
947
- // ============================================================================
948
- const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHeight, viewportWidth, alignment, hozOffset, padding, arrowSize) => {
1645
+
1646
+ const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHeight, viewportWidth, alignment, hozOffset, padding, arrowSize, containerRect) => {
949
1647
  const candidates = [];
950
1648
  const placements = ['top', 'bottom'];
951
1649
  placements.forEach((placement) => {
952
1650
  const verticalPos = calculateVerticalPosition(targetRect, calloutRect, placement, padding, arrowSize);
953
- const verticalValid = isVerticalPositionValid(targetRect, calloutRect, placement, viewportHeight, padding, arrowSize);
1651
+ const verticalValid = isVerticalPositionValid(targetRect, calloutRect, placement, viewportHeight, padding, arrowSize, containerRect);
954
1652
  const alignmentOrder = getAlignmentOrder(alignment);
955
1653
  alignmentOrder.forEach((align) => {
956
1654
  const horizontalPos = calculateLeftPosition({
@@ -965,13 +1663,13 @@ const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHei
965
1663
  left: horizontalPos,
966
1664
  horizontalAlign: align,
967
1665
  valid: verticalValid &&
968
- isLeftPositionValid(horizontalPos, calloutRect.width, viewportWidth, padding),
1666
+ isLeftPositionValid(horizontalPos, calloutRect.width, viewportWidth, padding, containerRect),
969
1667
  });
970
1668
  });
971
1669
  });
972
1670
  return candidates;
973
1671
  };
974
- const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportWidth, padding, arrowSize) => {
1672
+ const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportWidth, padding, arrowSize, containerRect) => {
975
1673
  const placements = ['left', 'right'];
976
1674
  return placements.map((placement) => {
977
1675
  const { top, left } = calculateHorizontalPlacementPosition(targetRect, calloutRect, placement, padding, arrowSize);
@@ -980,46 +1678,46 @@ const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportW
980
1678
  top,
981
1679
  left,
982
1680
  horizontalAlign: 'center',
983
- valid: isHorizontalPlacementValid(targetRect, calloutRect, placement, viewportWidth, padding, arrowSize),
1681
+ valid: isHorizontalPlacementValid(targetRect, calloutRect, placement, viewportWidth, padding, arrowSize, containerRect),
984
1682
  };
985
1683
  });
986
1684
  };
987
- const generateAllPositionCandidates = (rectDimensions, viewport, alignment, hozOffset, padding, arrowSize) => {
1685
+ const generateAllPositionCandidates = (rectDimensions, viewport, alignment, hozOffset, padding, arrowSize, containerRect) => {
988
1686
  const { targetRect, calloutRect } = rectDimensions;
989
- const verticalCandidates = generateVerticalPositionCandidates(targetRect, calloutRect, viewport.height, viewport.width, alignment, hozOffset, padding, arrowSize);
990
- const horizontalCandidates = generateHorizontalPositionCandidates(targetRect, calloutRect, viewport.width, padding, arrowSize);
1687
+ const verticalCandidates = generateVerticalPositionCandidates(targetRect, calloutRect, viewport.height, viewport.width, alignment, hozOffset, padding, arrowSize, containerRect);
1688
+ const horizontalCandidates = generateHorizontalPositionCandidates(targetRect, calloutRect, viewport.width, padding, arrowSize, containerRect);
991
1689
  return [...verticalCandidates, ...horizontalCandidates];
992
1690
  };
993
- // ============================================================================
994
- // Position Selection
995
- // ============================================================================
1691
+
996
1692
  const selectBestPosition = (candidates) => {
997
1693
  return candidates.find((p) => p.valid) || candidates[0];
998
1694
  };
999
- // ============================================================================
1000
- // Viewport Boundary Adjustment
1001
- // ============================================================================
1002
- const constrainToViewport = (position, calloutRect, viewport, padding) => {
1695
+ const constrainToViewport = (position, calloutRect, viewport, padding, containerRect) => {
1696
+ if (containerRect) {
1697
+ const left = Math.max(containerRect.left + padding, Math.min(position.left, containerRect.right - calloutRect.width - padding));
1698
+ const top = Math.max(containerRect.top + padding, Math.min(position.top, containerRect.bottom - calloutRect.height - padding));
1699
+ return { top, left };
1700
+ }
1003
1701
  const left = Math.max(padding, Math.min(position.left, viewport.width - calloutRect.width - padding));
1004
1702
  const top = Math.max(padding, Math.min(position.top, viewport.height - calloutRect.height - padding));
1005
1703
  return { top, left };
1006
1704
  };
1007
- // ============================================================================
1008
- // Main Function
1009
- // ============================================================================
1010
- const calcCalloutPosition = ({ targetElm, calloutElm, setPosition, hozOffset = HORIZONTAL_OFFSET, alignment = 'center', }) => {
1705
+
1706
+ const calcCalloutPosition = (options) => {
1707
+ const { targetElm, calloutElm, setPosition, hozOffset = CALLOUT_HORIZONTAL_OFFSET, alignment = 'center', containerElm, } = options;
1011
1708
  return () => {
1012
1709
  // 1. Get dimensions
1013
1710
  const rectDimensions = getElementDimensions(targetElm, calloutElm);
1014
- const viewport = getViewportDimensions();
1015
- const padding = PADDING;
1016
- const arrowSize = ARROW_SIZE;
1711
+ const viewport = getViewportDimensions(containerElm);
1712
+ const containerRect = containerElm?.getBoundingClientRect();
1713
+ const padding = CALLOUT_PADDING;
1714
+ const arrowSize = CALLOUT_ARROW_SIZE;
1017
1715
  // 2. Generate all position candidates
1018
- const candidates = generateAllPositionCandidates(rectDimensions, viewport, alignment, hozOffset, padding, arrowSize);
1716
+ const candidates = generateAllPositionCandidates(rectDimensions, viewport, alignment, hozOffset, padding, arrowSize, containerRect);
1019
1717
  // 3. Select best position
1020
1718
  const bestPosition = selectBestPosition(candidates);
1021
1719
  // 4. Constrain to viewport
1022
- const constrainedPosition = constrainToViewport({ top: bestPosition.top, left: bestPosition.left }, rectDimensions.calloutRect, viewport, padding);
1720
+ const constrainedPosition = constrainToViewport({ top: bestPosition.top, left: bestPosition.left }, rectDimensions.calloutRect, viewport, padding, containerRect);
1023
1721
  // 5. Create final position object
1024
1722
  const finalPosition = {
1025
1723
  top: constrainedPosition.top,
@@ -1031,124 +1729,6 @@ const calcCalloutPosition = ({ targetElm, calloutElm, setPosition, hozOffset = H
1031
1729
  };
1032
1730
  };
1033
1731
 
1034
- function getElementLayout(element) {
1035
- if (!element?.getBoundingClientRect)
1036
- return null;
1037
- const rect = element.getBoundingClientRect();
1038
- if (rect.width === 0 && rect.height === 0)
1039
- return null;
1040
- return {
1041
- top: rect.top,
1042
- left: rect.left,
1043
- width: rect.width,
1044
- height: rect.height,
1045
- };
1046
- }
1047
- const getElementAtPoint = (doc, x, y) => {
1048
- let el = null;
1049
- if ('caretPositionFromPoint' in doc) {
1050
- el = doc.caretPositionFromPoint(x, y)?.offsetNode ?? null;
1051
- }
1052
- el = el ?? doc.elementFromPoint(x, y);
1053
- let element = el;
1054
- while (element && element.nodeType === Node.TEXT_NODE) {
1055
- element = element.parentElement;
1056
- }
1057
- return element;
1058
- };
1059
- function getElementHash(element) {
1060
- return (element.getAttribute('data-clarity-hash') ||
1061
- element.getAttribute('data-clarity-hashalpha') ||
1062
- element.getAttribute('data-clarity-hashbeta'));
1063
- }
1064
- const getElementRank = (hash, elements) => {
1065
- if (!elements)
1066
- return 0;
1067
- return elements.findIndex((e) => e.hash === hash) + 1;
1068
- };
1069
- const buildElementInfo = (hash, rect, heatmapInfo) => {
1070
- if (!rect || !heatmapInfo)
1071
- return null;
1072
- const info = heatmapInfo.elementMapInfo?.[hash];
1073
- if (!info)
1074
- return null;
1075
- const rank = getElementRank(hash, heatmapInfo.sortedElements);
1076
- const clicks = info.totalclicks ?? 0;
1077
- const selector = info.selector ?? '';
1078
- const baseInfo = {
1079
- hash,
1080
- clicks,
1081
- rank,
1082
- selector,
1083
- };
1084
- return {
1085
- ...baseInfo,
1086
- ...rect,
1087
- };
1088
- };
1089
-
1090
- function findLastSizeOfDom(data) {
1091
- const listDocs = data
1092
- .filter((item) => item.doc?.find((doc) => doc.data.width && doc.data.height))
1093
- .flatMap((item) => item.doc?.flatMap((doc) => doc.data));
1094
- const lastDoc = listDocs?.[listDocs.length - 1];
1095
- const docSize = {
1096
- width: lastDoc?.width,
1097
- height: lastDoc?.height,
1098
- };
1099
- const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
1100
- const lastResizeEvent = listResizes?.[listResizes.length - 1];
1101
- const resize = {
1102
- width: lastResizeEvent?.data.width,
1103
- height: lastResizeEvent?.data.height,
1104
- };
1105
- return {
1106
- doc: docSize,
1107
- resize: resize,
1108
- size: {
1109
- width: resize.width || docSize.width,
1110
- height: resize.height || docSize.height,
1111
- },
1112
- };
1113
- }
1114
- function decodePayloads(payload) {
1115
- try {
1116
- return decode(payload);
1117
- }
1118
- catch (error) {
1119
- return null;
1120
- }
1121
- }
1122
-
1123
- function calculateRankPosition(rect, widthScale) {
1124
- const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
1125
- const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
1126
- return {
1127
- transform: `scale(${1.2 * widthScale})`,
1128
- top: Number.isNaN(top) ? undefined : top,
1129
- left: Number.isNaN(left) ? undefined : left,
1130
- };
1131
- }
1132
- function isElementInViewport(elementRect, visualRef, scale) {
1133
- if (!elementRect)
1134
- return false;
1135
- const visualRect = visualRef.current?.getBoundingClientRect();
1136
- if (!visualRect)
1137
- return false;
1138
- // Element position relative to the document (or container's content)
1139
- const elementTop = elementRect.top * scale;
1140
- const elementBottom = (elementRect.top + elementRect.height) * scale;
1141
- // Current scroll position
1142
- const scrollTop = visualRef.current?.scrollTop || 0;
1143
- const viewportHeight = visualRect.height;
1144
- // Visible viewport range in the scrollable content
1145
- const viewportTop = scrollTop;
1146
- const viewportBottom = scrollTop + viewportHeight;
1147
- // Check if element is within the visible viewport
1148
- // Element is visible if it overlaps with the viewport
1149
- return elementBottom > viewportTop && elementTop < viewportBottom;
1150
- }
1151
-
1152
1732
  class IframeNavigationBlockerV2 {
1153
1733
  doc;
1154
1734
  win;
@@ -1673,56 +2253,224 @@ class IframeHelperFixer {
1673
2253
  }));
1674
2254
  }
1675
2255
  catch (error) {
1676
- console.error('[IframeHelper] Failed to process:', error);
1677
- this.config.onError?.(error);
1678
- }
1679
- }
1680
- async recalculate() {
1681
- console.log('[IframeHelper] Recalculating...');
1682
- await this.process();
1683
- }
1684
- updateConfig(config) {
1685
- this.config = { ...this.config, ...config };
1686
- if (this.replacer) {
1687
- this.replacer.updateConfig(config);
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;
2447
+ try {
2448
+ vizRef?.clearmap?.();
2449
+ vizRef?.scrollmap?.(scrollmap);
2450
+ // setIsInitialized(true);
2451
+ }
2452
+ catch (error) {
2453
+ console.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
1688
2454
  }
1689
- }
1690
- enableNavigationBlocking() {
1691
- this.navigationBlocker?.enable();
1692
- }
1693
- enableNavigationBlockingMessage() {
1694
- this.navigationBlocker?.enableMessage();
1695
- }
1696
- disableNavigationBlocking() {
1697
- this.navigationBlocker?.disable();
1698
- }
1699
- disableNavigationBlockingMessage() {
1700
- this.navigationBlocker?.disableMessage();
1701
- }
1702
- destroy() {
1703
- this.replacer = null;
1704
- this.navigationBlocker?.destroy();
1705
- this.navigationBlocker = null;
1706
- console.log('[IframeHelper] Destroyed');
1707
- }
1708
- }
2455
+ }, [vizRef, scrollmap]);
2456
+ return { start };
2457
+ };
1709
2458
 
1710
- function initIframeHelperFixer(config) {
1711
- const fixer = new IframeHelperFixer(config);
1712
- window.addEventListener('iframe-dimensions-applied', ((e) => {
1713
- const ev = e;
1714
- console.log('[IframeHelper] Iframe dimensions finalized:', ev.detail);
1715
- }));
1716
- window.addEventListener('iframe-navigation-blocked', ((e) => {
1717
- const ev = e;
1718
- console.warn('[IframeHelper] Iframe tried to navigate to:', ev.detail.url);
1719
- }));
1720
- window.addEventListener('iframe-form-submit', ((e) => {
1721
- const ev = e;
1722
- console.log('[IframeHelper] Iframe form submitted:', ev.detail.data);
1723
- }));
1724
- return fixer;
1725
- }
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
+ };
1726
2474
 
1727
2475
  const scrollToElementIfNeeded = (visualRef, rect, scale) => {
1728
2476
  if (!visualRef.current)
@@ -1742,7 +2490,7 @@ const scrollToElementIfNeeded = (visualRef, rect, scale) => {
1742
2490
  });
1743
2491
  };
1744
2492
  const useClickedElement = ({ visualRef, getRect }) => {
1745
- const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapInteraction();
2493
+ const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapClick();
1746
2494
  const { widthScale } = useHeatmapViz();
1747
2495
  const { dataInfo } = useHeatmapData();
1748
2496
  const [clickedElement, setClickedElement] = useState(null);
@@ -1785,7 +2533,7 @@ const useClickedElement = ({ visualRef, getRect }) => {
1785
2533
  };
1786
2534
 
1787
2535
  const useElementCalloutVisible = ({ visualRef, getRect }) => {
1788
- const { selectedElement, setShouldShowCallout } = useHeatmapInteraction();
2536
+ const { selectedElement, setShouldShowCallout } = useHeatmapClick();
1789
2537
  const { widthScale } = useHeatmapViz();
1790
2538
  const { dataInfo } = useHeatmapData();
1791
2539
  useEffect(() => {
@@ -1816,7 +2564,7 @@ const useElementCalloutVisible = ({ visualRef, getRect }) => {
1816
2564
  };
1817
2565
 
1818
2566
  const useHeatmapEffects = ({ isVisible }) => {
1819
- useHeatmapInteraction();
2567
+ useHeatmapClick();
1820
2568
  const resetAll = () => {
1821
2569
  // setShouldShowCallout(false);
1822
2570
  };
@@ -1986,7 +2734,7 @@ function HeatmapComponent() {
1986
2734
  */
1987
2735
 
1988
2736
  const useHoveredElement = ({ iframeRef, getRect }) => {
1989
- const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapInteraction();
2737
+ const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapClick();
1990
2738
  const { widthScale } = useHeatmapViz();
1991
2739
  const { dataInfo } = useHeatmapData();
1992
2740
  const reset = useCallback(() => {
@@ -2018,11 +2766,12 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
2018
2766
  return;
2019
2767
  }
2020
2768
  const hash = getElementHash(targetElement);
2021
- if (!!hash)
2769
+ if (hash)
2022
2770
  return hash;
2023
2771
  reset();
2024
2772
  return;
2025
- }, [dataInfo, iframeRef, getRect, widthScale, reset]);
2773
+ }, [dataInfo, iframeRef, widthScale, reset]);
2774
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2026
2775
  const handleMouseMove = useCallback(debounce((event) => {
2027
2776
  if (!dataInfo) {
2028
2777
  reset();
@@ -2046,6 +2795,7 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
2046
2795
  if (!hoveredElement?.hash)
2047
2796
  return;
2048
2797
  setSelectedElement(hoveredElement.hash);
2798
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2049
2799
  }, [hoveredElement?.hash]);
2050
2800
  return {
2051
2801
  hoveredElement,
@@ -2071,7 +2821,6 @@ const findTargetElement = (doc, x, y, heatmapInfo) => {
2071
2821
  const element = elementsAtPoint[i];
2072
2822
  const elementHash = element.getAttribute(HEATMAP_ELEMENT_ATTRIBUTE);
2073
2823
  if (elementHash && heatmapInfo.elementMapInfo?.[elementHash]) {
2074
- heatmapInfo.elementMapInfo[elementHash];
2075
2824
  const boundingBox = getBoundingBox(element);
2076
2825
  if (boundingBox) {
2077
2826
  dataElement = element;
@@ -2079,7 +2828,7 @@ const findTargetElement = (doc, x, y, heatmapInfo) => {
2079
2828
  }
2080
2829
  }
2081
2830
  }
2082
- if (!!dataElement) {
2831
+ if (dataElement) {
2083
2832
  return dataElement;
2084
2833
  }
2085
2834
  let targetElement = getElementAtPoint(doc, x, y);
@@ -2209,8 +2958,7 @@ function reset(iframe, rect, onSuccess) {
2209
2958
 
2210
2959
  const useHeatmapRender = () => {
2211
2960
  const { data } = useHeatmapData();
2212
- const { vizRef, setVizRef, setIsRenderViz, setIframeHeight, wrapperHeight, wrapperWidth } = useHeatmapViz();
2213
- console.log(`🚀 🐥 ~ useHeatmapRender ~ wrapperHeight:`, wrapperHeight);
2961
+ const { vizRef, setVizRef, setIsRenderViz, setIframeHeight } = useHeatmapViz();
2214
2962
  const iframeRef = useRef(null);
2215
2963
  const renderHeatmap = useCallback(async (payloads) => {
2216
2964
  if (!payloads || payloads.length === 0)
@@ -2733,7 +3481,7 @@ const useWrapperRefHeight = (props) => {
2733
3481
  return {};
2734
3482
  };
2735
3483
 
2736
- const useZonePositions = (options) => {
3484
+ const useZonePositions = (_options) => {
2737
3485
  const { iframeHeight } = useHeatmapViz();
2738
3486
  const getZonePosition = useCallback((zone) => {
2739
3487
  if (!iframeHeight) {
@@ -3188,7 +3936,7 @@ function withPerformanceTracking(Component, options = {}) {
3188
3936
  const name = componentName || Component.displayName || Component.name || 'Unknown';
3189
3937
  const viewId = viewIdProp in props ? props[viewIdProp] : undefined;
3190
3938
  if (trackProps) {
3191
- useWhyDidYouUpdate(name, props, viewId);
3939
+ useWhyDidYouUpdate(name, props, viewId); // eslint-disable-line react-hooks/rules-of-hooks
3192
3940
  }
3193
3941
  return jsx(Component, { ...props });
3194
3942
  };
@@ -3261,16 +4009,10 @@ function trackStoreAction(storeName, action, viewId, metadata) {
3261
4009
  performanceLogger.log(metric);
3262
4010
  }
3263
4011
 
3264
- /**
3265
- * Get performance report as JSON string
3266
- */
3267
4012
  function getPerformanceReportJSON() {
3268
4013
  const report = performanceLogger.generateReport();
3269
4014
  return JSON.stringify(report, null, 2);
3270
4015
  }
3271
- /**
3272
- * Download performance report as JSON file
3273
- */
3274
4016
  function downloadPerformanceReport(filename = 'heatmap-performance-report.json') {
3275
4017
  const report = performanceLogger.generateReport();
3276
4018
  const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
@@ -3281,9 +4023,6 @@ function downloadPerformanceReport(filename = 'heatmap-performance-report.json')
3281
4023
  link.click();
3282
4024
  URL.revokeObjectURL(url);
3283
4025
  }
3284
- /**
3285
- * Send performance report to external endpoint
3286
- */
3287
4026
  async function sendPerformanceReport(endpoint) {
3288
4027
  const report = performanceLogger.generateReport();
3289
4028
  try {
@@ -3303,9 +4042,6 @@ async function sendPerformanceReport(endpoint) {
3303
4042
  throw error;
3304
4043
  }
3305
4044
  }
3306
- /**
3307
- * Print performance summary to console
3308
- */
3309
4045
  function printPerformanceSummary() {
3310
4046
  const report = performanceLogger.generateReport();
3311
4047
  console.group('📊 Performance Summary');
@@ -3331,9 +4067,6 @@ function printPerformanceSummary() {
3331
4067
  }
3332
4068
  console.groupEnd();
3333
4069
  }
3334
- /**
3335
- * Get performance metrics filtered by viewId
3336
- */
3337
4070
  function getMetricsByViewId(viewId) {
3338
4071
  const allMetrics = performanceLogger.getMetrics();
3339
4072
  const filteredMetrics = allMetrics.filter((m) => {
@@ -3349,17 +4082,28 @@ function getMetricsByViewId(viewId) {
3349
4082
  totalHookCalls: filteredMetrics.filter((m) => m.type === 'hook').length,
3350
4083
  totalStoreUpdates: filteredMetrics.filter((m) => m.type === 'store').length,
3351
4084
  averageRenderTime: 0, // Recalculate if needed
3352
- viewMetrics: { [viewId]: report.summary.viewMetrics[viewId] || { renders: 0, hookCalls: 0, storeUpdates: 0 } },
4085
+ viewMetrics: {
4086
+ [viewId]: report.summary.viewMetrics[viewId] || {
4087
+ renders: 0,
4088
+ hookCalls: 0,
4089
+ storeUpdates: 0,
4090
+ },
4091
+ },
3353
4092
  },
3354
4093
  };
3355
4094
  }
3356
- /**
3357
- * Compare performance between two viewIds
3358
- */
3359
4095
  function compareViewPerformance(viewId1, viewId2) {
3360
4096
  const report = performanceLogger.generateReport();
3361
- const view1Metrics = report.summary.viewMetrics[viewId1] || { renders: 0, hookCalls: 0, storeUpdates: 0 };
3362
- const view2Metrics = report.summary.viewMetrics[viewId2] || { renders: 0, hookCalls: 0, storeUpdates: 0 };
4097
+ const view1Metrics = report.summary.viewMetrics[viewId1] || {
4098
+ renders: 0,
4099
+ hookCalls: 0,
4100
+ storeUpdates: 0,
4101
+ };
4102
+ const view2Metrics = report.summary.viewMetrics[viewId2] || {
4103
+ renders: 0,
4104
+ hookCalls: 0,
4105
+ storeUpdates: 0,
4106
+ };
3363
4107
  return {
3364
4108
  view1: view1Metrics,
3365
4109
  view2: view2Metrics,
@@ -3382,7 +4126,7 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
3382
4126
  const style = props.style || {};
3383
4127
  const gap = props.gap || 0;
3384
4128
  const height = props.height || 'auto';
3385
- const isZIndexDefined = typeof props.zIndex !== undefined;
4129
+ const isZIndexDefined = typeof props.zIndex !== 'undefined';
3386
4130
  const zIndex = props.zIndex;
3387
4131
  const backgroundColor = props.backgroundColor || 'transparent';
3388
4132
  const styleGap = useMemo(() => {
@@ -3413,19 +4157,7 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
3413
4157
  };
3414
4158
  return (jsx("div", { id: id, style: styleProps, ref: ref, children: children }));
3415
4159
  });
3416
-
3417
- const ContentTopBar = () => {
3418
- const controls = useHeatmapControlStore((state) => state.controls);
3419
- useHeatmapConfigStore((state) => state.mode);
3420
- const TopBar = controls.TopBar;
3421
- // In compare mode, hide individual top bars since we have a global header
3422
- // if (mode === 'compare') {
3423
- // return null;
3424
- // }
3425
- return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
3426
- borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3427
- }, children: TopBar && jsx(TopBar, {}) }));
3428
- };
4160
+ BoxStack.displayName = 'BoxStack';
3429
4161
 
3430
4162
  const ContentMetricBar = () => {
3431
4163
  const controls = useHeatmapControlStore((state) => state.controls);
@@ -3434,22 +4166,11 @@ const ContentMetricBar = () => {
3434
4166
  borderBottom,
3435
4167
  }, children: controls.MetricBar ?? null }));
3436
4168
  };
3437
-
3438
- const ContentToolbar = () => {
3439
- const controls = useHeatmapControlStore((state) => state.controls);
3440
- return (jsx("div", { id: "gx-hm-content-toolbar", style: {
3441
- position: 'absolute',
3442
- bottom: 0,
3443
- left: '8px',
3444
- right: '24px',
3445
- padding: '8px',
3446
- paddingBlock: '16px',
3447
- }, children: controls.Toolbar ?? null }));
3448
- };
4169
+ ContentMetricBar.displayName = 'ContentMetricBar';
3449
4170
 
3450
4171
  const ContentSidebar = () => {
3451
4172
  const controls = useHeatmapControlStore((state) => state.controls);
3452
- const { state } = useHeatmapInteraction();
4173
+ const { state } = useHeatmapClick();
3453
4174
  const isHideSidebar = state.hideSidebar;
3454
4175
  const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3455
4176
  const mode = useHeatmapConfigStore((state) => state.mode);
@@ -3483,7 +4204,7 @@ const PopoverSidebar = () => {
3483
4204
  const CompSidebarActivator = useHeatmapControlStore((state) => state.controls.SidebarActivator);
3484
4205
  const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3485
4206
  const [isPopoverOpen, setIsPopoverOpen] = useState(false);
3486
- const { state } = useHeatmapInteraction();
4207
+ const { state } = useHeatmapClick();
3487
4208
  const isCompareMode = mode === 'compare';
3488
4209
  const isHideSidebar = state.hideSidebar;
3489
4210
  const stylePopover = {
@@ -3508,9 +4229,21 @@ const PopoverSidebar = () => {
3508
4229
  }, children: jsx(CompSidebar, { closeAction: { onClick: () => setIsPopoverOpen(false) } }) }) }))] }));
3509
4230
  };
3510
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
+
3511
4244
  const VizContainer = ({ children, isActive = false }) => {
3512
4245
  const wrapperRef = useRef(null);
3513
- const viewId = useViewId();
4246
+ const viewId = useViewIdContext();
3514
4247
  useWrapperRefHeight({
3515
4248
  isActive,
3516
4249
  wrapperRef,
@@ -3520,80 +4253,350 @@ const VizContainer = ({ children, isActive = false }) => {
3520
4253
  }, children: children }), jsx(PopoverSidebar, {})] }));
3521
4254
  };
3522
4255
 
3523
- const useClickmap = () => {
3524
- const { vizRef } = useHeatmapViz();
3525
- const { clickmap } = useHeatmapData();
3526
- const start = useCallback(() => {
3527
- 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);
3528
4298
  return;
3529
- try {
3530
- vizRef?.clearmap?.();
3531
- vizRef?.clickmap?.(clickmap);
3532
4299
  }
3533
- catch (error) {
3534
- 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);
3535
4312
  }
3536
- }, [vizRef, clickmap]);
3537
- 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
+ } }));
3538
4328
  };
4329
+ AreaEditHighlight.displayName = 'AreaEditHighlight';
3539
4330
 
3540
- const useScrollmap = () => {
3541
- const { vizRef } = useHeatmapViz();
3542
- const { scrollmap } = useHeatmapData();
3543
- const start = useCallback(() => {
3544
- // if (isInitialized) return;
3545
- 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}`);
3546
4472
  return;
4473
+ }
3547
4474
  try {
3548
- vizRef?.clearmap?.();
3549
- vizRef?.scrollmap?.(scrollmap);
3550
- // 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
+ });
3551
4486
  }
3552
4487
  catch (error) {
3553
- console.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
4488
+ console.error('[useAreaRenderer] Failed to create area:', error);
3554
4489
  }
3555
- }, [vizRef, scrollmap]);
3556
- return { start };
3557
- };
3558
-
3559
- const useHeatmapCanvas = () => {
3560
- const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
3561
- const { start: startClickmap } = useClickmap();
3562
- const { start: startScrollmap } = useScrollmap();
3563
- useEffect(() => {
3564
- switch (heatmapType) {
3565
- case IHeatmapType.Click:
3566
- startClickmap();
3567
- break;
3568
- case IHeatmapType.Scroll:
3569
- startScrollmap();
3570
- 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);
3571
4507
  }
3572
- }, [heatmapType, startClickmap, startScrollmap]);
3573
- };
3574
-
3575
- // Base IDs for elements (without viewId suffix)
3576
- const CLICKED_ELEMENT_ID_BASE = 'gx-hm-clicked-element';
3577
- const SECONDARY_CLICKED_ELEMENT_ID_BASE = 'gx-hm-secondary-clicked-element';
3578
- const HOVERED_ELEMENT_ID_BASE = 'gx-hm-hovered-element';
3579
- const SECONDARY_HOVERED_ELEMENT_ID_BASE = 'gx-hm-secondary-hovered-element';
3580
- /**
3581
- * Generate unique element ID for a specific view
3582
- * @param baseId - Base element ID
3583
- * @param viewId - View ID
3584
- * @returns Unique element ID (e.g., 'gx-hm-clicked-element-view-0')
3585
- */
3586
- const getElementId = (baseId, viewId) => {
3587
- return `${baseId}-${viewId}`;
3588
- };
3589
- const getClickedElementId = (viewId, isSecondary = false) => {
3590
- const baseId = isSecondary ? SECONDARY_CLICKED_ELEMENT_ID_BASE : CLICKED_ELEMENT_ID_BASE;
3591
- return getElementId(baseId, viewId);
3592
- };
3593
- const getHoveredElementId = (viewId, isSecondary = false) => {
3594
- const baseId = isSecondary ? SECONDARY_HOVERED_ELEMENT_ID_BASE : HOVERED_ELEMENT_ID_BASE;
3595
- 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] }));
3596
4598
  };
4599
+ VizAreaClick.displayName = 'VizAreaClick';
3597
4600
 
3598
4601
  const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
3599
4602
  const style = calculateRankPosition(elementRect, widthScale);
@@ -3623,7 +4626,7 @@ const DEFAULT_POSITION = {
3623
4626
  };
3624
4627
  const ElementCallout = (props) => {
3625
4628
  const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
3626
- const viewId = useViewId();
4629
+ const viewId = useViewIdContext();
3627
4630
  const { element, target, visualRef, hozOffset, alignment = 'left' } = props;
3628
4631
  const calloutRef = useRef(null);
3629
4632
  const [position, setPosition] = useState(DEFAULT_POSITION);
@@ -3638,6 +4641,7 @@ const ElementCallout = (props) => {
3638
4641
  setPosition,
3639
4642
  hozOffset,
3640
4643
  alignment,
4644
+ containerElm: visualRef?.current,
3641
4645
  });
3642
4646
  positionFn();
3643
4647
  const handleUpdate = () => {
@@ -3706,7 +4710,7 @@ const ELEMENT_CALLOUT = {
3706
4710
  alignment: 'left',
3707
4711
  };
3708
4712
  const HeatmapElements = (props) => {
3709
- const viewId = useViewId();
4713
+ const viewId = useViewIdContext();
3710
4714
  const { iframeHeight } = useHeatmapViz();
3711
4715
  const clickedElementId = getClickedElementId(viewId, props.isSecondary);
3712
4716
  const hoveredElementId = getHoveredElementId(viewId, props.isSecondary);
@@ -3755,121 +4759,9 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
3755
4759
  } }));
3756
4760
  };
3757
4761
 
3758
- const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
3759
- const { dataInfo } = useHeatmapData();
3760
- const { getZonePosition } = useZonePositions();
3761
- const averageFold = dataInfo?.averageFold || 50;
3762
- const position = getZonePosition({
3763
- startY: averageFold,
3764
- endY: averageFold,
3765
- });
3766
- if (!position)
3767
- return null;
3768
- return (jsx("div", { style: {
3769
- position: 'absolute',
3770
- top: `${position.top}px`,
3771
- left: 0,
3772
- width: '100%',
3773
- height: '2px',
3774
- backgroundColor: '#0078D4',
3775
- pointerEvents: 'none',
3776
- zIndex: 2,
3777
- boxShadow: '0 0 4px rgba(0,120,212,0.5)',
3778
- display: 'flex',
3779
- alignItems: 'center',
3780
- }, children: jsxs("div", { style: {
3781
- position: 'absolute',
3782
- padding: '8px',
3783
- backgroundColor: 'rgba(0, 120, 212, 0.9)',
3784
- color: 'white',
3785
- fontSize: '16px',
3786
- fontWeight: 600,
3787
- borderRadius: '4px',
3788
- whiteSpace: 'nowrap',
3789
- left: '12px',
3790
- minWidth: '120px',
3791
- textAlign: 'center',
3792
- }, children: ["Average fold - ", averageFold.toFixed(0), "%"] }) }));
3793
- };
3794
-
3795
- const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
3796
- const scrollType = useHeatmapConfigStore((state) => state.scrollType);
3797
- const { scrollmap } = useHeatmapData();
3798
- const { getZonePosition } = useZonePositions();
3799
- if (!scrollmap || scrollmap.length === 0)
3800
- return null;
3801
- const findScrollPositionForUserPercent = (targetPercent) => {
3802
- for (let i = 0; i < scrollmap.length; i++) {
3803
- if (scrollmap[i].percUsers <= targetPercent) {
3804
- if (i > 0) {
3805
- return scrollmap[i - 1].scrollReachY;
3806
- }
3807
- return scrollmap[i].scrollReachY;
3808
- }
3809
- }
3810
- return scrollmap[scrollmap.length - 1]?.scrollReachY || null;
3811
- };
3812
- const boundaries = [
3813
- { percent: 75, label: '75%', color: '#10B981' },
3814
- { percent: 50, label: '50%', color: '#F59E0B' },
3815
- { percent: 25, label: '25%', color: '#EF4444' },
3816
- { percent: 5, label: '5%', color: '#8B5CF6' },
3817
- ];
3818
- const isScrollDepth = scrollType === IScrollType.Depth;
3819
- if (!isScrollDepth)
3820
- return null;
3821
- return (jsx(Fragment, { children: boundaries.map((boundary) => {
3822
- const scrollY = findScrollPositionForUserPercent(boundary.percent);
3823
- if (scrollY === null)
3824
- return null;
3825
- const position = getZonePosition({
3826
- startY: scrollY,
3827
- endY: scrollY,
3828
- });
3829
- if (!position)
3830
- return null;
3831
- return (jsx("div", { className: `marker-boundary-line-${boundary.percent}`, style: {
3832
- position: 'absolute',
3833
- top: `${position.top}px`,
3834
- left: 0,
3835
- transformOrigin: 'left center',
3836
- width: '100%',
3837
- height: '0px',
3838
- // borderBottom: `2px dashed #323130`,
3839
- borderBottom: `2px solid ${boundary.color}`,
3840
- // background: 'repeating-linear-gradient(90deg, #323130, transparent 2px 3px)',
3841
- zIndex: 1,
3842
- display: 'flex',
3843
- alignItems: 'center',
3844
- }, children: jsx("div", { style: {
3845
- position: 'absolute',
3846
- padding: '8px',
3847
- backgroundColor: boundary.color,
3848
- color: 'white',
3849
- fontSize: '16px',
3850
- fontWeight: 600,
3851
- borderRadius: '4px',
3852
- whiteSpace: 'nowrap',
3853
- left: '12px',
3854
- minWidth: '120px',
3855
- textAlign: 'center',
3856
- // textAlign: 'center',
3857
- // padding: '8px',
3858
- // paddingInline: '8px',
3859
- // fontSize: '16px',
3860
- // background: '#fff',
3861
- // width: 'auto',
3862
- // borderRadius: '4px',
3863
- // position: 'absolute',
3864
- // left: '12px',
3865
- // minWidth: '120px',
3866
- }, children: boundary.label }) }, boundary.label));
3867
- }) }));
3868
- };
3869
-
3870
4762
  const ScrollMapMinimap = ({ zones, maxUsers }) => {
3871
4763
  const scrollType = useHeatmapConfigStore((state) => state.scrollType);
3872
- const { showMinimap } = useHeatmapVizScrollmap();
4764
+ const { showMinimap } = useHeatmapScroll();
3873
4765
  const isScrollType = [IScrollType.Attention].includes(scrollType);
3874
4766
  if (!showMinimap || !isScrollType)
3875
4767
  return null;
@@ -4025,6 +4917,118 @@ const ScrollMapOverlay = ({ wrapperRef, iframeRef }) => {
4025
4917
  }, children: jsx(HoverZones, { position: position, currentScrollPercent: currentScrollPercent, iframeRef: iframeRef, wrapperRef: wrapperRef }) }));
4026
4918
  };
4027
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
+
4028
5032
  const SCROLL_TYPES = [IHeatmapType.Scroll];
4029
5033
  const VizScrollMap = ({ iframeRef, wrapperRef }) => {
4030
5034
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
@@ -4083,7 +5087,7 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
4083
5087
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
4084
5088
  const wrapperRef = useRef(null);
4085
5089
  const visualRef = useRef(null);
4086
- const { setSelectedElement } = useHeatmapInteraction();
5090
+ const { setSelectedElement } = useHeatmapClick();
4087
5091
  const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
4088
5092
  const { iframeRef } = useHeatmapVizRender(mode);
4089
5093
  const { scaledHeight, handleScroll } = useHeatmapScale({
@@ -4106,7 +5110,9 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
4106
5110
  useEffect(() => {
4107
5111
  return cleanUp;
4108
5112
  }, []);
4109
- 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 })] }));
4110
5116
  };
4111
5117
 
4112
5118
  const VizLoading = () => {
@@ -4124,6 +5130,7 @@ const VizDomHeatmap = () => {
4124
5130
  }, []);
4125
5131
  return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
4126
5132
  };
5133
+ VizDomHeatmap.displayName = 'VizDomHeatmap';
4127
5134
 
4128
5135
  const VizLiveRenderer = () => {
4129
5136
  const contentWidth = useHeatmapConfigStore((state) => state.width);
@@ -4157,6 +5164,7 @@ const VizLiveHeatmap = () => {
4157
5164
  }, []);
4158
5165
  return (jsxs(VizContainer, { isActive: true, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
4159
5166
  };
5167
+ VizLiveHeatmap.displayName = 'VizLiveHeatmap';
4160
5168
 
4161
5169
  const ContentVizByMode = () => {
4162
5170
  const mode = useHeatmapConfigStore((state) => state.mode);
@@ -4173,14 +5181,23 @@ const ContentVizByMode = () => {
4173
5181
  return jsx(VizDomHeatmap, {});
4174
5182
  }
4175
5183
  };
5184
+ ContentVizByMode.displayName = 'ContentVizByMode';
4176
5185
 
4177
- const WrapperPreview = () => {
5186
+ const HeatmapPreview = () => {
4178
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, {})] })] }));
4179
5188
  };
4180
5189
 
4181
- const WrapperLayout = () => {
4182
- useRenderCount('WrapperLayout');
4183
- 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, {}) }));
4184
5201
  };
4185
5202
 
4186
5203
  const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
@@ -4191,11 +5208,11 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
4191
5208
  performanceLogger.configure({
4192
5209
  enabled: true,
4193
5210
  logToConsole: false,
4194
- logLevel: 'normal', // 'verbose' | 'normal' | 'minimal'
5211
+ logLevel: 'normal',
4195
5212
  thresholds: {
4196
- slowRenderMs: 16, // Warn if render > 16ms (60fps)
4197
- slowHookMs: 5, // Warn if hook > 5ms
4198
- excessiveRenderCount: 10, // Warn if component renders > 10 times
5213
+ slowRenderMs: 16,
5214
+ slowHookMs: 5,
5215
+ excessiveRenderCount: 10,
4199
5216
  },
4200
5217
  externalLogger: (metric) => {
4201
5218
  if (metric.name === 'VizDomRenderer') ;
@@ -4204,7 +5221,7 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
4204
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: {
4205
5222
  minHeight: '100%',
4206
5223
  display: 'flex',
4207
- }, children: jsx(WrapperLayout, {}) }) }) }));
5224
+ }, children: jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(HeatmapPreview, {})] }) }) }) }));
4208
5225
  function getVariableStyle() {
4209
5226
  return {
4210
5227
  '--gx-hm-border-width': `${HEATMAP_CONFIG.borderWidth}px`,
@@ -4214,4 +5231,4 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
4214
5231
  }
4215
5232
  };
4216
5233
 
4217
- 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, 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 };