@gemx-dev/heatmap-react 3.5.48 → 3.5.50

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 (293) hide show
  1. package/dist/esm/components/Layout/ContentMetricBar.d.ts.map +1 -1
  2. package/dist/esm/components/Layout/ContentTopBar.d.ts.map +1 -1
  3. package/dist/esm/components/Layout/ContentVizByMode.d.ts.map +1 -1
  4. package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
  5. package/dist/esm/components/Layout/Sidebar/ContentSidebar.d.ts +2 -0
  6. package/dist/esm/components/Layout/Sidebar/ContentSidebar.d.ts.map +1 -0
  7. package/dist/esm/components/Layout/Sidebar/PopoverSidebar.d.ts +2 -0
  8. package/dist/esm/components/Layout/Sidebar/PopoverSidebar.d.ts.map +1 -0
  9. package/dist/esm/components/Layout/Sidebar/index.d.ts +3 -0
  10. package/dist/esm/components/Layout/Sidebar/index.d.ts.map +1 -0
  11. package/dist/esm/components/Layout/WrapperLayout.d.ts.map +1 -1
  12. package/dist/esm/components/Layout/WrapperPreview.d.ts.map +1 -1
  13. package/dist/esm/components/VizCompare/CompareGrid.d.ts +1 -0
  14. package/dist/esm/components/VizCompare/CompareGrid.d.ts.map +1 -0
  15. package/dist/esm/components/VizCompare/CompareView.d.ts +1 -0
  16. package/dist/esm/components/VizCompare/CompareView.d.ts.map +1 -0
  17. package/dist/esm/components/VizCompare/CompareViewHeader.d.ts +1 -0
  18. package/dist/esm/components/VizCompare/CompareViewHeader.d.ts.map +1 -0
  19. package/dist/esm/components/VizCompare/CompareVizRenderer.d.ts +1 -0
  20. package/dist/esm/components/VizCompare/CompareVizRenderer.d.ts.map +1 -0
  21. package/dist/esm/components/VizCompare/VizCompareHeatmap.d.ts +1 -0
  22. package/dist/esm/components/VizCompare/VizCompareHeatmap.d.ts.map +1 -0
  23. package/dist/esm/components/VizCompare/index.d.ts +1 -0
  24. package/dist/esm/components/VizCompare/index.d.ts.map +1 -0
  25. package/dist/esm/components/VizDom/VizContainer.d.ts +1 -1
  26. package/dist/esm/components/VizDom/VizContainer.d.ts.map +1 -1
  27. package/dist/esm/components/VizDom/VizDomHeatmap.d.ts.map +1 -1
  28. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  29. package/dist/esm/components/VizDom/WrapperVisual.d.ts.map +1 -1
  30. package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
  31. package/dist/esm/components/VizElement/ElementMissing.d.ts.map +1 -1
  32. package/dist/esm/components/VizElement/ElementOverlay.d.ts +1 -0
  33. package/dist/esm/components/VizElement/ElementOverlay.d.ts.map +1 -1
  34. package/dist/esm/components/VizElement/HeatmapElements.d.ts +0 -1
  35. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
  36. package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
  37. package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  38. package/dist/esm/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  39. package/dist/esm/components/VizScrollmap/AverageFoldLine.d.ts.map +1 -1
  40. package/dist/esm/components/VizScrollmap/HoverZones.d.ts.map +1 -1
  41. package/dist/esm/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
  42. package/dist/esm/components/VizScrollmap/ScrollMapOverlay.d.ts.map +1 -1
  43. package/dist/esm/components/VizScrollmap/VizScrollMap.d.ts.map +1 -1
  44. package/dist/esm/components/VizScrollmapV2/useScrollmapOverlay.d.ts.map +1 -1
  45. package/dist/esm/configs/index.d.ts +2 -0
  46. package/dist/esm/configs/index.d.ts.map +1 -1
  47. package/dist/esm/configs/viewId.d.ts +21 -0
  48. package/dist/esm/configs/viewId.d.ts.map +1 -0
  49. package/dist/esm/configs/z-index.d.ts +8 -0
  50. package/dist/esm/configs/z-index.d.ts.map +1 -0
  51. package/dist/esm/constants/index.d.ts +13 -4
  52. package/dist/esm/constants/index.d.ts.map +1 -1
  53. package/dist/esm/contexts/CompareViewContext.d.ts +28 -0
  54. package/dist/esm/contexts/CompareViewContext.d.ts.map +1 -0
  55. package/dist/esm/contexts/index.d.ts +2 -0
  56. package/dist/esm/contexts/index.d.ts.map +1 -0
  57. package/dist/esm/helpers/iframe-helper/style-replacer.d.ts.map +1 -1
  58. package/dist/esm/hooks/index.d.ts +1 -0
  59. package/dist/esm/hooks/index.d.ts.map +1 -1
  60. package/dist/esm/hooks/register/useRegisterControl.d.ts.map +1 -1
  61. package/dist/esm/hooks/register/useRegisterData.d.ts.map +1 -1
  62. package/dist/esm/hooks/register/useRegisterHeatmap.d.ts.map +1 -1
  63. package/dist/esm/hooks/view-context/index.d.ts +7 -0
  64. package/dist/esm/hooks/view-context/index.d.ts.map +1 -0
  65. package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts +33 -0
  66. package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -0
  67. package/dist/esm/hooks/view-context/useHeatmapData.d.ts +16 -0
  68. package/dist/esm/hooks/view-context/useHeatmapData.d.ts.map +1 -0
  69. package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts +16 -0
  70. package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts.map +1 -0
  71. package/dist/esm/hooks/view-context/useHeatmapViz.d.ts +26 -0
  72. package/dist/esm/hooks/view-context/useHeatmapViz.d.ts.map +1 -0
  73. package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts +13 -0
  74. package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +1 -0
  75. package/dist/esm/hooks/view-context/useViewId.d.ts +15 -0
  76. package/dist/esm/hooks/view-context/useViewId.d.ts.map +1 -0
  77. package/dist/esm/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
  78. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  79. package/dist/esm/hooks/viz-canvas/useHeatmapCanvas.d.ts +1 -3
  80. package/dist/esm/hooks/viz-canvas/useHeatmapCanvas.d.ts.map +1 -1
  81. package/dist/esm/hooks/viz-elements/useClickedElement.d.ts +0 -1
  82. package/dist/esm/hooks/viz-elements/useClickedElement.d.ts.map +1 -1
  83. package/dist/esm/hooks/viz-elements/useHeatmapEffects.d.ts +1 -4
  84. package/dist/esm/hooks/viz-elements/useHeatmapEffects.d.ts.map +1 -1
  85. package/dist/esm/hooks/viz-elements/useHeatmapElementPosition.d.ts.map +1 -1
  86. package/dist/esm/hooks/viz-elements/useHoveredElement.d.ts.map +1 -1
  87. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
  88. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  89. package/dist/esm/hooks/viz-render/useReplayRender.d.ts.map +1 -1
  90. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
  91. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  92. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts +1 -1
  93. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -1
  94. package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts +1 -1
  95. package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -1
  96. package/dist/esm/hooks/viz-scale/useScrollSync.d.ts +2 -1
  97. package/dist/esm/hooks/viz-scale/useScrollSync.d.ts.map +1 -1
  98. package/dist/esm/hooks/viz-scale/useWrapperRefHeight.d.ts +0 -1
  99. package/dist/esm/hooks/viz-scale/useWrapperRefHeight.d.ts.map +1 -1
  100. package/dist/esm/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +1 -1
  101. package/dist/esm/hooks/viz-scrollmap/useZonePositions.d.ts.map +1 -1
  102. package/dist/esm/index.d.ts +4 -1
  103. package/dist/esm/index.d.ts.map +1 -1
  104. package/dist/esm/index.js +1457 -313
  105. package/dist/esm/index.mjs +1457 -313
  106. package/dist/esm/performance/PerformanceLogger.d.ts +22 -0
  107. package/dist/esm/performance/PerformanceLogger.d.ts.map +1 -0
  108. package/dist/esm/performance/hooks.d.ts +28 -0
  109. package/dist/esm/performance/hooks.d.ts.map +1 -0
  110. package/dist/esm/performance/index.d.ts +7 -0
  111. package/dist/esm/performance/index.d.ts.map +1 -0
  112. package/dist/esm/performance/storeTracker.d.ts +10 -0
  113. package/dist/esm/performance/storeTracker.d.ts.map +1 -0
  114. package/dist/esm/performance/types.d.ts +79 -0
  115. package/dist/esm/performance/types.d.ts.map +1 -0
  116. package/dist/esm/performance/utils.d.ts +42 -0
  117. package/dist/esm/performance/utils.d.ts.map +1 -0
  118. package/dist/esm/performance/withPerformanceTracking.d.ts +14 -0
  119. package/dist/esm/performance/withPerformanceTracking.d.ts.map +1 -0
  120. package/dist/esm/stores/comp.d.ts.map +1 -1
  121. package/dist/esm/stores/config.d.ts +2 -0
  122. package/dist/esm/stores/config.d.ts.map +1 -1
  123. package/dist/esm/stores/data.d.ts +20 -11
  124. package/dist/esm/stores/data.d.ts.map +1 -1
  125. package/dist/esm/stores/index.d.ts +1 -0
  126. package/dist/esm/stores/index.d.ts.map +1 -1
  127. package/dist/esm/stores/interaction.d.ts +20 -9
  128. package/dist/esm/stores/interaction.d.ts.map +1 -1
  129. package/dist/esm/stores/mode-compare.d.ts +33 -0
  130. package/dist/esm/stores/mode-compare.d.ts.map +1 -0
  131. package/dist/esm/stores/mode-live.d.ts +0 -4
  132. package/dist/esm/stores/mode-live.d.ts.map +1 -1
  133. package/dist/esm/stores/mode-single.d.ts +20 -5
  134. package/dist/esm/stores/mode-single.d.ts.map +1 -1
  135. package/dist/esm/stores/viz-scrollmap.d.ts +18 -7
  136. package/dist/esm/stores/viz-scrollmap.d.ts.map +1 -1
  137. package/dist/esm/stores/viz.d.ts +22 -11
  138. package/dist/esm/stores/viz.d.ts.map +1 -1
  139. package/dist/esm/types/compare.d.ts +68 -0
  140. package/dist/esm/types/compare.d.ts.map +1 -0
  141. package/dist/esm/types/control.d.ts +10 -2
  142. package/dist/esm/types/control.d.ts.map +1 -1
  143. package/dist/esm/types/index.d.ts +2 -0
  144. package/dist/esm/types/index.d.ts.map +1 -1
  145. package/dist/style.css +7 -1
  146. package/dist/umd/components/Layout/ContentMetricBar.d.ts.map +1 -1
  147. package/dist/umd/components/Layout/ContentTopBar.d.ts.map +1 -1
  148. package/dist/umd/components/Layout/ContentVizByMode.d.ts.map +1 -1
  149. package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
  150. package/dist/umd/components/Layout/Sidebar/ContentSidebar.d.ts +2 -0
  151. package/dist/umd/components/Layout/Sidebar/ContentSidebar.d.ts.map +1 -0
  152. package/dist/umd/components/Layout/Sidebar/PopoverSidebar.d.ts +2 -0
  153. package/dist/umd/components/Layout/Sidebar/PopoverSidebar.d.ts.map +1 -0
  154. package/dist/umd/components/Layout/Sidebar/index.d.ts +3 -0
  155. package/dist/umd/components/Layout/Sidebar/index.d.ts.map +1 -0
  156. package/dist/umd/components/Layout/WrapperLayout.d.ts.map +1 -1
  157. package/dist/umd/components/Layout/WrapperPreview.d.ts.map +1 -1
  158. package/dist/umd/components/VizCompare/CompareGrid.d.ts +1 -0
  159. package/dist/umd/components/VizCompare/CompareGrid.d.ts.map +1 -0
  160. package/dist/umd/components/VizCompare/CompareView.d.ts +1 -0
  161. package/dist/umd/components/VizCompare/CompareView.d.ts.map +1 -0
  162. package/dist/umd/components/VizCompare/CompareViewHeader.d.ts +1 -0
  163. package/dist/umd/components/VizCompare/CompareViewHeader.d.ts.map +1 -0
  164. package/dist/umd/components/VizCompare/CompareVizRenderer.d.ts +1 -0
  165. package/dist/umd/components/VizCompare/CompareVizRenderer.d.ts.map +1 -0
  166. package/dist/umd/components/VizCompare/VizCompareHeatmap.d.ts +1 -0
  167. package/dist/umd/components/VizCompare/VizCompareHeatmap.d.ts.map +1 -0
  168. package/dist/umd/components/VizCompare/index.d.ts +1 -0
  169. package/dist/umd/components/VizCompare/index.d.ts.map +1 -0
  170. package/dist/umd/components/VizDom/VizContainer.d.ts +1 -1
  171. package/dist/umd/components/VizDom/VizContainer.d.ts.map +1 -1
  172. package/dist/umd/components/VizDom/VizDomHeatmap.d.ts.map +1 -1
  173. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  174. package/dist/umd/components/VizDom/WrapperVisual.d.ts.map +1 -1
  175. package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
  176. package/dist/umd/components/VizElement/ElementMissing.d.ts.map +1 -1
  177. package/dist/umd/components/VizElement/ElementOverlay.d.ts +1 -0
  178. package/dist/umd/components/VizElement/ElementOverlay.d.ts.map +1 -1
  179. package/dist/umd/components/VizElement/HeatmapElements.d.ts +0 -1
  180. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
  181. package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
  182. package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  183. package/dist/umd/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  184. package/dist/umd/components/VizScrollmap/AverageFoldLine.d.ts.map +1 -1
  185. package/dist/umd/components/VizScrollmap/HoverZones.d.ts.map +1 -1
  186. package/dist/umd/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
  187. package/dist/umd/components/VizScrollmap/ScrollMapOverlay.d.ts.map +1 -1
  188. package/dist/umd/components/VizScrollmap/VizScrollMap.d.ts.map +1 -1
  189. package/dist/umd/components/VizScrollmapV2/useScrollmapOverlay.d.ts.map +1 -1
  190. package/dist/umd/configs/index.d.ts +2 -0
  191. package/dist/umd/configs/index.d.ts.map +1 -1
  192. package/dist/umd/configs/viewId.d.ts +21 -0
  193. package/dist/umd/configs/viewId.d.ts.map +1 -0
  194. package/dist/umd/configs/z-index.d.ts +8 -0
  195. package/dist/umd/configs/z-index.d.ts.map +1 -0
  196. package/dist/umd/constants/index.d.ts +13 -4
  197. package/dist/umd/constants/index.d.ts.map +1 -1
  198. package/dist/umd/contexts/CompareViewContext.d.ts +28 -0
  199. package/dist/umd/contexts/CompareViewContext.d.ts.map +1 -0
  200. package/dist/umd/contexts/index.d.ts +2 -0
  201. package/dist/umd/contexts/index.d.ts.map +1 -0
  202. package/dist/umd/helpers/iframe-helper/style-replacer.d.ts.map +1 -1
  203. package/dist/umd/hooks/index.d.ts +1 -0
  204. package/dist/umd/hooks/index.d.ts.map +1 -1
  205. package/dist/umd/hooks/register/useRegisterControl.d.ts.map +1 -1
  206. package/dist/umd/hooks/register/useRegisterData.d.ts.map +1 -1
  207. package/dist/umd/hooks/register/useRegisterHeatmap.d.ts.map +1 -1
  208. package/dist/umd/hooks/view-context/index.d.ts +7 -0
  209. package/dist/umd/hooks/view-context/index.d.ts.map +1 -0
  210. package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts +33 -0
  211. package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -0
  212. package/dist/umd/hooks/view-context/useHeatmapData.d.ts +16 -0
  213. package/dist/umd/hooks/view-context/useHeatmapData.d.ts.map +1 -0
  214. package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts +16 -0
  215. package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts.map +1 -0
  216. package/dist/umd/hooks/view-context/useHeatmapViz.d.ts +26 -0
  217. package/dist/umd/hooks/view-context/useHeatmapViz.d.ts.map +1 -0
  218. package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts +13 -0
  219. package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +1 -0
  220. package/dist/umd/hooks/view-context/useViewId.d.ts +15 -0
  221. package/dist/umd/hooks/view-context/useViewId.d.ts.map +1 -0
  222. package/dist/umd/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
  223. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  224. package/dist/umd/hooks/viz-canvas/useHeatmapCanvas.d.ts +1 -3
  225. package/dist/umd/hooks/viz-canvas/useHeatmapCanvas.d.ts.map +1 -1
  226. package/dist/umd/hooks/viz-elements/useClickedElement.d.ts +0 -1
  227. package/dist/umd/hooks/viz-elements/useClickedElement.d.ts.map +1 -1
  228. package/dist/umd/hooks/viz-elements/useHeatmapEffects.d.ts +1 -4
  229. package/dist/umd/hooks/viz-elements/useHeatmapEffects.d.ts.map +1 -1
  230. package/dist/umd/hooks/viz-elements/useHeatmapElementPosition.d.ts.map +1 -1
  231. package/dist/umd/hooks/viz-elements/useHoveredElement.d.ts.map +1 -1
  232. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
  233. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  234. package/dist/umd/hooks/viz-render/useReplayRender.d.ts.map +1 -1
  235. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
  236. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  237. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts +1 -1
  238. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -1
  239. package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts +1 -1
  240. package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -1
  241. package/dist/umd/hooks/viz-scale/useScrollSync.d.ts +2 -1
  242. package/dist/umd/hooks/viz-scale/useScrollSync.d.ts.map +1 -1
  243. package/dist/umd/hooks/viz-scale/useWrapperRefHeight.d.ts +0 -1
  244. package/dist/umd/hooks/viz-scale/useWrapperRefHeight.d.ts.map +1 -1
  245. package/dist/umd/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +1 -1
  246. package/dist/umd/hooks/viz-scrollmap/useZonePositions.d.ts.map +1 -1
  247. package/dist/umd/index.d.ts +4 -1
  248. package/dist/umd/index.d.ts.map +1 -1
  249. package/dist/umd/index.js +2 -2
  250. package/dist/umd/performance/PerformanceLogger.d.ts +22 -0
  251. package/dist/umd/performance/PerformanceLogger.d.ts.map +1 -0
  252. package/dist/umd/performance/hooks.d.ts +28 -0
  253. package/dist/umd/performance/hooks.d.ts.map +1 -0
  254. package/dist/umd/performance/index.d.ts +7 -0
  255. package/dist/umd/performance/index.d.ts.map +1 -0
  256. package/dist/umd/performance/storeTracker.d.ts +10 -0
  257. package/dist/umd/performance/storeTracker.d.ts.map +1 -0
  258. package/dist/umd/performance/types.d.ts +79 -0
  259. package/dist/umd/performance/types.d.ts.map +1 -0
  260. package/dist/umd/performance/utils.d.ts +42 -0
  261. package/dist/umd/performance/utils.d.ts.map +1 -0
  262. package/dist/umd/performance/withPerformanceTracking.d.ts +14 -0
  263. package/dist/umd/performance/withPerformanceTracking.d.ts.map +1 -0
  264. package/dist/umd/stores/comp.d.ts.map +1 -1
  265. package/dist/umd/stores/config.d.ts +2 -0
  266. package/dist/umd/stores/config.d.ts.map +1 -1
  267. package/dist/umd/stores/data.d.ts +20 -11
  268. package/dist/umd/stores/data.d.ts.map +1 -1
  269. package/dist/umd/stores/index.d.ts +1 -0
  270. package/dist/umd/stores/index.d.ts.map +1 -1
  271. package/dist/umd/stores/interaction.d.ts +20 -9
  272. package/dist/umd/stores/interaction.d.ts.map +1 -1
  273. package/dist/umd/stores/mode-compare.d.ts +33 -0
  274. package/dist/umd/stores/mode-compare.d.ts.map +1 -0
  275. package/dist/umd/stores/mode-live.d.ts +0 -4
  276. package/dist/umd/stores/mode-live.d.ts.map +1 -1
  277. package/dist/umd/stores/mode-single.d.ts +20 -5
  278. package/dist/umd/stores/mode-single.d.ts.map +1 -1
  279. package/dist/umd/stores/viz-scrollmap.d.ts +18 -7
  280. package/dist/umd/stores/viz-scrollmap.d.ts.map +1 -1
  281. package/dist/umd/stores/viz.d.ts +22 -11
  282. package/dist/umd/stores/viz.d.ts.map +1 -1
  283. package/dist/umd/types/compare.d.ts +68 -0
  284. package/dist/umd/types/compare.d.ts.map +1 -0
  285. package/dist/umd/types/control.d.ts +10 -2
  286. package/dist/umd/types/control.d.ts.map +1 -1
  287. package/dist/umd/types/index.d.ts +2 -0
  288. package/dist/umd/types/index.d.ts.map +1 -1
  289. package/package.json +1 -1
  290. package/dist/esm/components/Layout/LeftSidebar.d.ts +0 -2
  291. package/dist/esm/components/Layout/LeftSidebar.d.ts.map +0 -1
  292. package/dist/umd/components/Layout/LeftSidebar.d.ts +0 -2
  293. package/dist/umd/components/Layout/LeftSidebar.d.ts.map +0 -1
@@ -1,8 +1,9 @@
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, useCallback, useState, useRef, useMemo, forwardRef, Fragment as Fragment$1 } from 'react';
4
+ import { useEffect, createContext, useContext, useMemo, useCallback, useState, useRef, forwardRef, Fragment as Fragment$1 } from 'react';
5
5
  import { create } from 'zustand';
6
+ import { subscribeWithSelector } from 'zustand/middleware';
6
7
  import { decode } from '@gemx-dev/clarity-decode';
7
8
  import { Visualizer } from '@gemx-dev/clarity-visualize';
8
9
  import { createPortal } from 'react-dom';
@@ -67,15 +68,50 @@ const HEATMAP_STYLE = {
67
68
  };
68
69
  const DEFAULT_SIDEBAR_WIDTH = 260;
69
70
 
71
+ /**
72
+ * Default view ID for single mode
73
+ */
74
+ 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;
95
+ };
96
+
97
+ const Z_INDEX = {
98
+ ELEMENTS: 1000,
99
+ CALLOUT: 2000,
100
+ MINIMAP: 3000,
101
+ SIDEBAR: 4000,
102
+ SIDEBAR_POPOVER: 4001,
103
+ };
104
+
70
105
  const useHeatmapControlStore = create()((set, get) => {
71
106
  return {
72
107
  controls: {
73
- Sidebar: null,
74
- TopBar: null,
108
+ Sidebar: undefined,
109
+ SidebarActivator: undefined,
75
110
  Toolbar: null,
76
111
  MetricBar: null,
77
112
  VizLoading: null,
78
- ElementCallout: null,
113
+ TopBar: undefined,
114
+ ElementCallout: undefined,
79
115
  },
80
116
  registerControl: (key, control) => {
81
117
  set({
@@ -114,9 +150,10 @@ const useHeatmapConfigStore = create()((set, get) => {
114
150
  mode: 'single',
115
151
  width: 1440,
116
152
  sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
117
- heatmapType: IHeatmapType.Scroll,
153
+ heatmapType: IHeatmapType.Click,
118
154
  clickType: IClickType.Total,
119
155
  scrollType: IScrollType.Depth,
156
+ isRendering: true,
120
157
  setMode: (mode) => set({ mode }),
121
158
  resetMode: () => set({ mode: 'single' }),
122
159
  setWidth: (width) => set({ width }),
@@ -124,70 +161,236 @@ const useHeatmapConfigStore = create()((set, get) => {
124
161
  setHeatmapType: (heatmapType) => set({ heatmapType }),
125
162
  setClickType: (clickType) => set({ clickType }),
126
163
  setScrollType: (scrollType) => set({ scrollType }),
164
+ setIsRendering: (isRendering) => set({ isRendering }),
127
165
  };
128
166
  });
129
167
 
130
- const useHeatmapDataStore = create()((set, get) => {
168
+ const useHeatmapDataStore = create()(subscribeWithSelector((set, get) => {
131
169
  return {
132
- data: undefined,
133
- clickmap: undefined,
134
- dataInfo: undefined,
135
- scrollmap: undefined,
136
- isRendering: true,
137
- setIsRendering: (isRendering) => set({ isRendering }),
138
- setDataInfo: (dataInfo) => set({ dataInfo }),
139
- setData: (data) => set({ data }),
140
- setClickmap: (clickmap) => set({ clickmap }),
141
- setScrollmap: (scrollmap) => set({ scrollmap }),
170
+ data: { [DEFAULT_VIEW_ID]: undefined },
171
+ clickmap: { [DEFAULT_VIEW_ID]: undefined },
172
+ dataInfo: { [DEFAULT_VIEW_ID]: undefined },
173
+ scrollmap: { [DEFAULT_VIEW_ID]: undefined },
174
+ setDataInfo: (dataInfo, viewId = DEFAULT_VIEW_ID) => set((state) => ({
175
+ dataInfo: { ...state.dataInfo, [viewId]: dataInfo },
176
+ })),
177
+ setData: (data, viewId = DEFAULT_VIEW_ID) => set((state) => ({
178
+ data: { ...state.data, [viewId]: data },
179
+ })),
180
+ setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID) => set((state) => ({
181
+ clickmap: { ...state.clickmap, [viewId]: clickmap },
182
+ })),
183
+ setScrollmap: (scrollmap, viewId = DEFAULT_VIEW_ID) => set((state) => ({
184
+ scrollmap: { ...state.scrollmap, [viewId]: scrollmap },
185
+ })),
186
+ copyView: (fromViewId, toViewId) => set((state) => ({
187
+ data: { ...state.data, [toViewId]: state.data[fromViewId] },
188
+ clickmap: { ...state.clickmap, [toViewId]: state.clickmap[fromViewId] },
189
+ dataInfo: { ...state.dataInfo, [toViewId]: state.dataInfo[fromViewId] },
190
+ scrollmap: { ...state.scrollmap, [toViewId]: state.scrollmap[fromViewId] },
191
+ })),
192
+ clearView: (viewId) => set((state) => {
193
+ const newData = { ...state.data };
194
+ const newClickmap = { ...state.clickmap };
195
+ const newDataInfo = { ...state.dataInfo };
196
+ const newScrollmap = { ...state.scrollmap };
197
+ delete newData[viewId];
198
+ delete newClickmap[viewId];
199
+ delete newDataInfo[viewId];
200
+ delete newScrollmap[viewId];
201
+ return {
202
+ data: newData,
203
+ clickmap: newClickmap,
204
+ dataInfo: newDataInfo,
205
+ scrollmap: newScrollmap,
206
+ };
207
+ }),
208
+ resetAll: () => set({
209
+ data: { [DEFAULT_VIEW_ID]: undefined },
210
+ clickmap: { [DEFAULT_VIEW_ID]: undefined },
211
+ dataInfo: { [DEFAULT_VIEW_ID]: undefined },
212
+ scrollmap: { [DEFAULT_VIEW_ID]: undefined },
213
+ }),
142
214
  };
143
- });
215
+ }));
144
216
 
145
- const useHeatmapInteractionStore = create()((set, get) => {
217
+ const DEFAULT_STATE = {
218
+ hideSidebar: false,
219
+ };
220
+ const useHeatmapInteractionStore = create()(subscribeWithSelector((set, get) => {
146
221
  return {
147
- state: {
148
- hideSidebar: false,
149
- },
150
- setState: (state) => set({ state }),
151
- selectedElement: null,
152
- setSelectedElement: (selectedElement) => set({ selectedElement }),
153
- hoveredElement: null,
154
- setHoveredElement: (hoveredElement) => set({ hoveredElement }),
155
- shouldShowCallout: false,
156
- setShouldShowCallout: (shouldShowCallout) => set({ shouldShowCallout }),
222
+ state: { [DEFAULT_VIEW_ID]: DEFAULT_STATE },
223
+ selectedElement: { [DEFAULT_VIEW_ID]: null },
224
+ hoveredElement: { [DEFAULT_VIEW_ID]: null },
225
+ shouldShowCallout: { [DEFAULT_VIEW_ID]: false },
226
+ setState: (state, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
227
+ state: { ...prev.state, [viewId]: state },
228
+ })),
229
+ setSelectedElement: (selectedElement, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
230
+ selectedElement: { ...prev.selectedElement, [viewId]: selectedElement },
231
+ })),
232
+ setHoveredElement: (hoveredElement, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
233
+ hoveredElement: { ...prev.hoveredElement, [viewId]: hoveredElement },
234
+ })),
235
+ setShouldShowCallout: (shouldShowCallout, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
236
+ shouldShowCallout: { ...prev.shouldShowCallout, [viewId]: shouldShowCallout },
237
+ })),
238
+ copyView: (fromViewId, toViewId) => set((state) => ({
239
+ state: {
240
+ ...state.state,
241
+ [toViewId]: state.state[fromViewId] ?? DEFAULT_STATE,
242
+ },
243
+ selectedElement: {
244
+ ...state.selectedElement,
245
+ [toViewId]: state.selectedElement[fromViewId] ?? null,
246
+ },
247
+ hoveredElement: {
248
+ ...state.hoveredElement,
249
+ [toViewId]: state.hoveredElement[fromViewId] ?? null,
250
+ },
251
+ shouldShowCallout: {
252
+ ...state.shouldShowCallout,
253
+ [toViewId]: state.shouldShowCallout[fromViewId] ?? false,
254
+ },
255
+ })),
256
+ clearView: (viewId) => set((state) => {
257
+ const newState = { ...state.state };
258
+ const newSelectedElement = { ...state.selectedElement };
259
+ const newHoveredElement = { ...state.hoveredElement };
260
+ const newShouldShowCallout = { ...state.shouldShowCallout };
261
+ delete newState[viewId];
262
+ delete newSelectedElement[viewId];
263
+ delete newHoveredElement[viewId];
264
+ delete newShouldShowCallout[viewId];
265
+ return {
266
+ state: newState,
267
+ selectedElement: newSelectedElement,
268
+ hoveredElement: newHoveredElement,
269
+ shouldShowCallout: newShouldShowCallout,
270
+ };
271
+ }),
272
+ resetAll: () => set({
273
+ state: { default: DEFAULT_STATE },
274
+ selectedElement: { default: null },
275
+ hoveredElement: { default: null },
276
+ shouldShowCallout: { default: false },
277
+ }),
157
278
  };
158
- });
279
+ }));
159
280
 
160
- const useHeatmapVizStore = create()((set, get) => {
281
+ const useHeatmapVizStore = create()(subscribeWithSelector((set, get) => {
161
282
  return {
162
- isRenderViz: false,
163
- setIsRenderViz: (isRenderViz) => set({ isRenderViz }),
164
- zoomRatio: 100,
165
- minZoomRatio: 10,
166
- setMinZoomRatio: (minZoomRatio) => set({ minZoomRatio }),
167
- setZoomRatio: (zoomRatio) => set({ zoomRatio }),
168
- scale: 1,
169
- setScale: (scale) => set({ scale }),
170
- isScaledToFit: false,
171
- setIsScaledToFit: (isScaledToFit) => set({ isScaledToFit }),
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
+ }),
172
339
  };
173
- });
340
+ }));
174
341
 
175
- const useHeatmapVizScrollmapStore = create()((set, get) => {
342
+ const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set, get) => {
176
343
  return {
177
- zones: [],
178
- hoveredZone: null,
179
- showMinimap: true,
180
- setZones: (zones) => set({ zones }),
181
- setHoveredZone: (hoveredZone) => set({ hoveredZone }),
182
- setShowMinimap: (showMinimap) => set({ showMinimap }),
344
+ zones: { [DEFAULT_VIEW_ID]: [] },
345
+ hoveredZone: { [DEFAULT_VIEW_ID]: null },
346
+ showMinimap: { [DEFAULT_VIEW_ID]: true },
347
+ setZones: (zones, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
348
+ zones: { ...prev.zones, [viewId]: zones },
349
+ })),
350
+ setHoveredZone: (hoveredZone, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
351
+ hoveredZone: { ...prev.hoveredZone, [viewId]: hoveredZone },
352
+ })),
353
+ setShowMinimap: (showMinimap, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
354
+ showMinimap: { ...prev.showMinimap, [viewId]: showMinimap },
355
+ })),
356
+ copyView: (fromViewId, toViewId) => set((state) => ({
357
+ zones: {
358
+ ...state.zones,
359
+ [toViewId]: state.zones[fromViewId] ?? [],
360
+ },
361
+ hoveredZone: {
362
+ ...state.hoveredZone,
363
+ [toViewId]: state.hoveredZone[fromViewId] ?? null,
364
+ },
365
+ showMinimap: {
366
+ ...state.showMinimap,
367
+ [toViewId]: state.showMinimap[fromViewId] ?? true,
368
+ },
369
+ })),
370
+ clearView: (viewId) => set((state) => {
371
+ const newZones = { ...state.zones };
372
+ const newHoveredZone = { ...state.hoveredZone };
373
+ const newShowMinimap = { ...state.showMinimap };
374
+ delete newZones[viewId];
375
+ delete newHoveredZone[viewId];
376
+ delete newShowMinimap[viewId];
377
+ return {
378
+ zones: newZones,
379
+ hoveredZone: newHoveredZone,
380
+ showMinimap: newShowMinimap,
381
+ };
382
+ }),
383
+ resetAll: () => set({
384
+ zones: { default: [] },
385
+ hoveredZone: { default: null },
386
+ showMinimap: { default: true },
387
+ }),
183
388
  };
184
- });
389
+ }));
185
390
 
186
391
  const initialState = {
187
392
  payloads: [],
188
393
  htmlContent: '',
189
- wrapperHeight: 0,
190
- iframeHeight: 0,
191
394
  };
192
395
  const useHeatmapLiveStore = create()((set, get) => {
193
396
  return {
@@ -196,17 +399,191 @@ const useHeatmapLiveStore = create()((set, get) => {
196
399
  setPayloads: (payloads) => set({ payloads }),
197
400
  addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
198
401
  setHtmlContent: (htmlContent) => set({ htmlContent }),
199
- setWrapperHeight: (wrapperHeight) => set({ wrapperHeight }),
200
- setIframeHeight: (iframeHeight) => set({ iframeHeight }),
201
402
  };
202
403
  });
203
404
 
204
- const useHeatmapSingleStore = create()((set, get) => {
405
+ const useHeatmapSingleStore = create()(subscribeWithSelector((set, get) => {
205
406
  return {
206
- vizRef: null,
207
- iframeHeight: 0,
208
- setVizRef: (vizRef) => set({ vizRef }),
209
- setIframeHeight: (iframeHeight) => set({ iframeHeight }),
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) => ({
463
+ id,
464
+ 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
+ });
480
+ const useHeatmapCompareStore = create()((set, get) => {
481
+ return {
482
+ views: new Map(),
483
+ viewOrder: [],
484
+ layout: 'grid-2',
485
+ syncSettings: {
486
+ zoomRatio: false,
487
+ scrolling: false,
488
+ heatmapType: false,
489
+ },
490
+ viewIdCounter: 0,
491
+ addView: (options) => {
492
+ const state = get();
493
+ const viewId = `view-${state.viewIdCounter}`;
494
+ const label = options?.label ?? `Version ${state.viewOrder.length + 1}`;
495
+ const newView = createDefaultView(viewId, label, options);
496
+ const newViews = new Map(state.views);
497
+ newViews.set(viewId, newView);
498
+ set({
499
+ views: newViews,
500
+ viewOrder: [...state.viewOrder, viewId],
501
+ viewIdCounter: state.viewIdCounter + 1,
502
+ });
503
+ return viewId;
504
+ },
505
+ removeView: (viewId) => {
506
+ const state = get();
507
+ const newViews = new Map(state.views);
508
+ newViews.delete(viewId);
509
+ set({
510
+ views: newViews,
511
+ viewOrder: state.viewOrder.filter((id) => id !== viewId),
512
+ });
513
+ },
514
+ updateView: (viewId, updates) => {
515
+ const state = get();
516
+ const view = state.views.get(viewId);
517
+ if (!view)
518
+ return;
519
+ const newViews = new Map(state.views);
520
+ newViews.set(viewId, { ...view, ...updates });
521
+ 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
+ },
533
+ getView: (viewId) => {
534
+ return get().views.get(viewId);
535
+ },
536
+ setLayout: (layout) => {
537
+ set({ layout });
538
+ },
539
+ setSyncSettings: (settings) => {
540
+ set({ syncSettings: { ...get().syncSettings, ...settings } });
541
+ },
542
+ initializeCompare: (count, options) => {
543
+ const viewIds = [];
544
+ const newViews = new Map();
545
+ for (let i = 0; i < count; i++) {
546
+ const viewId = `view-${i}`;
547
+ const label = options?.label ?? String.fromCharCode(65 + i); // A, B, C, D
548
+ newViews.set(viewId, createDefaultView(viewId, `Version ${label}`, options));
549
+ viewIds.push(viewId);
550
+ }
551
+ const layoutMap = {
552
+ 2: 'grid-2',
553
+ 3: 'grid-3',
554
+ 4: 'grid-4',
555
+ };
556
+ set({
557
+ views: newViews,
558
+ viewOrder: viewIds,
559
+ layout: layoutMap[count],
560
+ viewIdCounter: count,
561
+ });
562
+ },
563
+ resetCompare: () => {
564
+ set({
565
+ views: new Map(),
566
+ viewOrder: [],
567
+ layout: 'grid-2',
568
+ viewIdCounter: 0,
569
+ syncSettings: {
570
+ zoomRatio: false,
571
+ scrolling: false,
572
+ heatmapType: false,
573
+ },
574
+ });
575
+ },
576
+ syncProperty: (property, value) => {
577
+ const state = get();
578
+ const newViews = new Map(state.views);
579
+ state.viewOrder.forEach((viewId) => {
580
+ const view = newViews.get(viewId);
581
+ if (view) {
582
+ newViews.set(viewId, { ...view, [property]: value });
583
+ }
584
+ });
585
+ set({ views: newViews });
586
+ },
210
587
  };
211
588
  });
212
589
 
@@ -215,7 +592,7 @@ const useRegisterConfig = () => {
215
592
  const width = useHeatmapConfigStore((state) => state.width);
216
593
  const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
217
594
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
218
- const setIsRendering = useHeatmapDataStore((state) => state.setIsRendering);
595
+ const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
219
596
  useEffect(() => {
220
597
  setIsRendering(true);
221
598
  setTimeout(() => {
@@ -227,6 +604,7 @@ const useRegisterConfig = () => {
227
604
  const useRegisterControl = (control) => {
228
605
  const registerControl = useHeatmapControlStore((state) => state.registerControl);
229
606
  registerControl('Sidebar', control.Sidebar);
607
+ registerControl('SidebarActivator', control.SidebarActivator);
230
608
  registerControl('TopBar', control.TopBar);
231
609
  registerControl('Toolbar', control.Toolbar);
232
610
  registerControl('MetricBar', control.MetricBar);
@@ -234,10 +612,228 @@ const useRegisterControl = (control) => {
234
612
  registerControl('ElementCallout', control.ElementCallout);
235
613
  };
236
614
 
237
- const useRegisterData = (data, dataInfo) => {
615
+ /**
616
+ * Context to provide viewId to components
617
+ * Used in compare mode to isolate data between views
618
+ */
619
+ 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 = () => {
625
+ const viewId = useContext(ViewIdContext);
626
+ return viewId || DEFAULT_VIEW_ID;
627
+ };
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;
634
+ };
635
+
636
+ const useHeatmapData = (props) => {
637
+ const viewId = props?.viewId || useViewId();
638
+ const data = useHeatmapDataStore((state) => state.data[viewId]);
639
+ const clickmap = useHeatmapDataStore((state) => state.clickmap[viewId]);
640
+ const scrollmap = useHeatmapDataStore((state) => state.scrollmap[viewId]);
641
+ const dataInfo = useHeatmapDataStore((state) => state.dataInfo[viewId]);
238
642
  const setData = useHeatmapDataStore((state) => state.setData);
239
- const setIsRendering = useHeatmapDataStore((state) => state.setIsRendering);
643
+ const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
644
+ const setScrollmap = useHeatmapDataStore((state) => state.setScrollmap);
240
645
  const setDataInfo = useHeatmapDataStore((state) => state.setDataInfo);
646
+ const memoizedSetters = useMemo(() => ({
647
+ setData: (newData) => setData(newData, viewId),
648
+ setClickmap: (newClickmap) => setClickmap(newClickmap, viewId),
649
+ setScrollmap: (newScrollmap) => setScrollmap(newScrollmap, viewId),
650
+ setDataInfo: (newDataInfo) => setDataInfo(newDataInfo, viewId),
651
+ }), [setData, setClickmap, setScrollmap, setDataInfo, viewId]);
652
+ return {
653
+ data,
654
+ clickmap,
655
+ scrollmap,
656
+ dataInfo,
657
+ // Setters (auto-inject viewId)
658
+ ...memoizedSetters,
659
+ };
660
+ };
661
+
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);
672
+ 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
+ ]);
684
+ return {
685
+ state,
686
+ selectedElement,
687
+ hoveredElement,
688
+ shouldShowCallout,
689
+ // Setters (auto-inject viewId)
690
+ ...memoizedSetters,
691
+ };
692
+ };
693
+
694
+ const useHeatmapViz = (props) => {
695
+ const viewId = props?.viewId || useViewId();
696
+ // Viz store
697
+ const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
698
+ const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
699
+ const minZoomRatio = useHeatmapVizStore((state) => state.minZoomRatio[viewId] ?? 10);
700
+ const widthScale = useHeatmapVizStore((state) => state.scale[viewId] ?? 1);
701
+ const isScaledToFit = useHeatmapVizStore((state) => state.isScaledToFit[viewId] ?? false);
702
+ const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
703
+ const setZoomRatio = useHeatmapVizStore((state) => state.setZoomRatio);
704
+ const setMinZoomRatio = useHeatmapVizStore((state) => state.setMinZoomRatio);
705
+ const setScale = useHeatmapVizStore((state) => state.setScale);
706
+ const setIsScaledToFit = useHeatmapVizStore((state) => state.setIsScaledToFit);
707
+ // Single store
708
+ const vizRef = useHeatmapSingleStore((state) => state.vizRef[viewId] ?? null);
709
+ const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight[viewId] ?? 0);
710
+ const wrapperHeight = useHeatmapSingleStore((state) => state.wrapperHeight[viewId] ?? 0);
711
+ const wrapperWidth = useHeatmapSingleStore((state) => state.wrapperWidth[viewId] ?? 0);
712
+ const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
713
+ const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
714
+ const setWrapperHeight = useHeatmapSingleStore((state) => state.setWrapperHeight);
715
+ const setWrapperWidth = useHeatmapSingleStore((state) => state.setWrapperWidth);
716
+ const memoizedSetters = useMemo(() => ({
717
+ setIsRenderViz: (value) => setIsRenderViz(value, viewId),
718
+ setZoomRatio: (value) => setZoomRatio(value, viewId),
719
+ setMinZoomRatio: (value) => setMinZoomRatio(value, viewId),
720
+ setScale: (value) => setScale(value, viewId),
721
+ setIsScaledToFit: (value) => setIsScaledToFit(value, viewId),
722
+ setVizRef: (value) => setVizRef(value, viewId),
723
+ setIframeHeight: (value) => setIframeHeight(value, viewId),
724
+ setWrapperHeight: (value) => setWrapperHeight(value, viewId),
725
+ setWrapperWidth: (value) => setWrapperWidth(value, viewId),
726
+ }), [
727
+ setIsRenderViz,
728
+ setZoomRatio,
729
+ setMinZoomRatio,
730
+ setScale,
731
+ setIsScaledToFit,
732
+ setVizRef,
733
+ setIframeHeight,
734
+ setWrapperHeight,
735
+ setWrapperWidth,
736
+ viewId,
737
+ ]);
738
+ return {
739
+ isRenderViz,
740
+ zoomRatio,
741
+ minZoomRatio,
742
+ widthScale,
743
+ isScaledToFit,
744
+ vizRef,
745
+ iframeHeight,
746
+ wrapperHeight,
747
+ wrapperWidth,
748
+ // Setters (auto-inject viewId)
749
+ ...memoizedSetters,
750
+ };
751
+ };
752
+
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
+ /**
776
+ * Hook to handle copying and clearing view data across all stores
777
+ */
778
+ const useHeatmapCopyView = () => {
779
+ const copyDataView = useHeatmapDataStore((state) => state.copyView);
780
+ const copyVizView = useHeatmapVizStore((state) => state.copyView);
781
+ const copySingleView = useHeatmapSingleStore((state) => state.copyView);
782
+ const copyInteractionView = useHeatmapInteractionStore((state) => state.copyView);
783
+ const copyVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.copyView);
784
+ const clearDataView = useHeatmapDataStore((state) => state.clearView);
785
+ const clearVizView = useHeatmapVizStore((state) => state.clearView);
786
+ const clearSingleView = useHeatmapSingleStore((state) => state.clearView);
787
+ const clearInteractionView = useHeatmapInteractionStore((state) => state.clearView);
788
+ const clearVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.clearView);
789
+ const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
790
+ const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
791
+ const resetSingleAll = useHeatmapSingleStore((state) => state.resetAll);
792
+ const resetInteractionAll = useHeatmapInteractionStore((state) => state.resetAll);
793
+ const resetVizScrollmapAll = useHeatmapVizScrollmapStore((state) => state.resetAll);
794
+ const copyView = (fromViewId, toViewId) => {
795
+ copyDataView(fromViewId, toViewId);
796
+ copyVizView(fromViewId, toViewId);
797
+ copySingleView(fromViewId, toViewId);
798
+ copyInteractionView(fromViewId, toViewId);
799
+ copyVizScrollmapView(fromViewId, toViewId);
800
+ };
801
+ const copyViewToMultiple = (fromViewId, toViewIds) => {
802
+ toViewIds.forEach((toViewId) => {
803
+ copyView(fromViewId, toViewId);
804
+ });
805
+ };
806
+ const clearView = (viewId) => {
807
+ clearDataView(viewId);
808
+ clearVizView(viewId);
809
+ clearSingleView(viewId);
810
+ clearInteractionView(viewId);
811
+ clearVizScrollmapView(viewId);
812
+ };
813
+ const clearMultipleViews = (viewIds) => {
814
+ viewIds.forEach((viewId) => {
815
+ clearView(viewId);
816
+ });
817
+ };
818
+ const resetAll = () => {
819
+ resetDataAll();
820
+ resetVizAll();
821
+ resetSingleAll();
822
+ resetInteractionAll();
823
+ resetVizScrollmapAll();
824
+ };
825
+ return {
826
+ copyView,
827
+ copyViewToMultiple,
828
+ clearView,
829
+ clearMultipleViews,
830
+ resetAll,
831
+ };
832
+ };
833
+
834
+ const useRegisterData = (data, dataInfo) => {
835
+ const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
836
+ const { setData, setDataInfo } = useHeatmapData();
241
837
  const handleSetData = useCallback((data) => {
242
838
  if (!data)
243
839
  return;
@@ -258,8 +854,7 @@ const useRegisterData = (data, dataInfo) => {
258
854
  };
259
855
 
260
856
  const useRegisterHeatmap = ({ clickmap, scrollmap }) => {
261
- const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
262
- const setScrollmap = useHeatmapDataStore((state) => state.setScrollmap);
857
+ const { setClickmap, setScrollmap } = useHeatmapData();
263
858
  const handleSetClickmap = useCallback((clickmap) => {
264
859
  if (!clickmap)
265
860
  return;
@@ -493,33 +1088,29 @@ const buildElementInfo = (hash, rect, heatmapInfo) => {
493
1088
  };
494
1089
 
495
1090
  function findLastSizeOfDom(data) {
496
- const firstDoc = data.find((item) => item.envelope.sequence === 1)?.doc;
497
- const docSorted = firstDoc?.sort(sort);
498
- const firstEvent = docSorted?.[0];
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];
499
1095
  const docSize = {
500
- width: firstEvent?.data.width,
501
- height: firstEvent?.data.height,
1096
+ width: lastDoc?.width,
1097
+ height: lastDoc?.height,
502
1098
  };
503
- const newData = JSON.parse(JSON.stringify(data));
504
- const reversedData = newData.reverse();
505
- const lastResizeEvent = reversedData.find((item) => !!item.resize);
506
- const firstEventResize = lastResizeEvent?.resize?.[0];
1099
+ const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
1100
+ const lastResizeEvent = listResizes?.[listResizes.length - 1];
507
1101
  const resize = {
508
- width: firstEventResize?.data.width,
509
- height: firstEventResize?.data.height,
1102
+ width: lastResizeEvent?.data.width,
1103
+ height: lastResizeEvent?.data.height,
510
1104
  };
511
1105
  return {
512
1106
  doc: docSize,
513
1107
  resize: resize,
514
1108
  size: {
515
- width: resize.width ?? docSize.width,
516
- height: resize.height ?? docSize.height,
1109
+ width: resize.width || docSize.width,
1110
+ height: resize.height || docSize.height,
517
1111
  },
518
1112
  };
519
1113
  }
520
- function sort(a, b) {
521
- return a.time - b.time;
522
- }
523
1114
  function decodePayloads(payload) {
524
1115
  try {
525
1116
  return decode(payload);
@@ -855,7 +1446,7 @@ class IframeStyleReplacer {
855
1446
  doc;
856
1447
  win;
857
1448
  config;
858
- regex = /([-.\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
1449
+ regex = /([-.\d]+)(vh|svh|lvh|dvh)/gi; //vw|svw|lvw|dvw
859
1450
  constructor(iframe, config) {
860
1451
  if (!iframe.contentDocument || !iframe.contentWindow) {
861
1452
  throw new Error('Iframe document or window not accessible');
@@ -1151,11 +1742,9 @@ const scrollToElementIfNeeded = (visualRef, rect, scale) => {
1151
1742
  });
1152
1743
  };
1153
1744
  const useClickedElement = ({ visualRef, getRect }) => {
1154
- const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
1155
- const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
1156
- const shouldShowCallout = useHeatmapInteractionStore((state) => state.shouldShowCallout);
1157
- const setShouldShowCallout = useHeatmapInteractionStore((state) => state.setShouldShowCallout);
1158
- const scale = useHeatmapVizStore((state) => state.scale);
1745
+ const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapInteraction();
1746
+ const { widthScale } = useHeatmapViz();
1747
+ const { dataInfo } = useHeatmapData();
1159
1748
  const [clickedElement, setClickedElement] = useState(null);
1160
1749
  const [showMissingElement, setShowMissingElement] = useState(false);
1161
1750
  const reset = () => {
@@ -1164,11 +1753,13 @@ const useClickedElement = ({ visualRef, getRect }) => {
1164
1753
  setShouldShowCallout(false);
1165
1754
  };
1166
1755
  useEffect(() => {
1167
- if (!selectedElement || !heatmapInfo?.elementMapInfo) {
1756
+ if (selectedElement === clickedElement?.hash)
1757
+ return;
1758
+ if (!selectedElement || !dataInfo?.elementMapInfo) {
1168
1759
  reset();
1169
1760
  return;
1170
1761
  }
1171
- const info = heatmapInfo.elementMapInfo[selectedElement];
1762
+ const info = dataInfo.elementMapInfo[selectedElement];
1172
1763
  if (!info) {
1173
1764
  setClickedElement(null);
1174
1765
  return;
@@ -1176,7 +1767,7 @@ const useClickedElement = ({ visualRef, getRect }) => {
1176
1767
  const hash = selectedElement;
1177
1768
  const selector = info.selector;
1178
1769
  const rect = getRect({ hash: selectedElement, selector });
1179
- const elementInfo = buildElementInfo(hash, rect, heatmapInfo);
1770
+ const elementInfo = buildElementInfo(hash, rect, dataInfo);
1180
1771
  if (!rect) {
1181
1772
  setClickedElement(elementInfo);
1182
1773
  setShowMissingElement(true);
@@ -1184,29 +1775,28 @@ const useClickedElement = ({ visualRef, getRect }) => {
1184
1775
  return;
1185
1776
  }
1186
1777
  setShowMissingElement(false);
1187
- scrollToElementIfNeeded(visualRef, rect, scale);
1778
+ scrollToElementIfNeeded(visualRef, rect, widthScale);
1188
1779
  setShouldShowCallout(true);
1189
1780
  requestAnimationFrame(() => {
1190
1781
  setClickedElement(elementInfo);
1191
1782
  });
1192
- }, [selectedElement, heatmapInfo, getRect, visualRef, scale]);
1193
- return { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout };
1783
+ }, [selectedElement, dataInfo, visualRef, widthScale]);
1784
+ return { clickedElement, showMissingElement, shouldShowCallout };
1194
1785
  };
1195
1786
 
1196
1787
  const useElementCalloutVisible = ({ visualRef, getRect }) => {
1197
- const scale = useHeatmapVizStore((state) => state.scale);
1198
- const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
1199
- const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
1200
- const setShouldShowCallout = useHeatmapInteractionStore((state) => state.setShouldShowCallout);
1788
+ const { selectedElement, setShouldShowCallout } = useHeatmapInteraction();
1789
+ const { widthScale } = useHeatmapViz();
1790
+ const { dataInfo } = useHeatmapData();
1201
1791
  useEffect(() => {
1202
1792
  if (!selectedElement)
1203
1793
  return;
1204
1794
  const elementIsInViewportFn = () => {
1205
- const elementInfo = heatmapInfo?.elementMapInfo?.[selectedElement];
1795
+ const elementInfo = dataInfo?.elementMapInfo?.[selectedElement];
1206
1796
  if (!elementInfo)
1207
1797
  return;
1208
1798
  const rect = getRect({ hash: selectedElement, selector: elementInfo.selector });
1209
- const isInViewport = isElementInViewport(rect, visualRef, scale);
1799
+ const isInViewport = isElementInViewport(rect, visualRef, widthScale);
1210
1800
  setShouldShowCallout(isInViewport);
1211
1801
  };
1212
1802
  elementIsInViewportFn();
@@ -1221,32 +1811,31 @@ const useElementCalloutVisible = ({ visualRef, getRect }) => {
1221
1811
  window.removeEventListener('resize', handleUpdate);
1222
1812
  visualRef?.current?.removeEventListener('scroll', handleUpdate);
1223
1813
  };
1224
- }, [selectedElement, visualRef, getRect, scale, heatmapInfo, setShouldShowCallout]);
1814
+ }, [selectedElement, visualRef, widthScale, dataInfo]);
1225
1815
  return {};
1226
1816
  };
1227
1817
 
1228
- const useHeatmapEffects = ({ isVisible, isElementSidebarOpen, setShouldShowCallout, resetAll, }) => {
1229
- const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
1818
+ const useHeatmapEffects = ({ isVisible }) => {
1819
+ useHeatmapInteraction();
1820
+ const resetAll = () => {
1821
+ // setShouldShowCallout(false);
1822
+ };
1230
1823
  // Reset khi ẩn
1231
1824
  useEffect(() => {
1232
- if (!isVisible)
1233
- resetAll();
1234
1825
  }, [isVisible, resetAll]);
1235
1826
  // Ẩn callout khi sidebar mở
1236
- useEffect(() => {
1237
- if (isElementSidebarOpen && selectedElement) {
1238
- setShouldShowCallout(false);
1239
- }
1240
- else if (!isElementSidebarOpen && selectedElement) {
1241
- setShouldShowCallout(true);
1242
- }
1243
- }, [isElementSidebarOpen, selectedElement, setShouldShowCallout]);
1827
+ // useEffect(() => {
1828
+ // if (isElementSidebarOpen && selectedElement) {
1829
+ // setShouldShowCallout(false);
1830
+ // } else if (!isElementSidebarOpen && selectedElement) {
1831
+ // setShouldShowCallout(true);
1832
+ // }
1833
+ // }, [isElementSidebarOpen, selectedElement]);
1244
1834
  };
1245
1835
 
1246
1836
  const useHeatmapElementPosition = ({ iframeRef, wrapperRef, visualizer }) => {
1247
- const widthScale = useHeatmapVizStore((state) => state.scale);
1248
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
1249
1837
  const heatmapWidth = useHeatmapConfigStore((state) => state.width);
1838
+ const { iframeHeight, widthScale } = useHeatmapViz();
1250
1839
  return useCallback((element) => {
1251
1840
  const hash = element?.hash;
1252
1841
  if (!iframeRef.current?.contentDocument || !hash || !visualizer)
@@ -1397,11 +1986,9 @@ function HeatmapComponent() {
1397
1986
  */
1398
1987
 
1399
1988
  const useHoveredElement = ({ iframeRef, getRect }) => {
1400
- const hoveredElement = useHeatmapInteractionStore((state) => state.hoveredElement);
1401
- const setHoveredElement = useHeatmapInteractionStore((state) => state.setHoveredElement);
1402
- const onSelect = useHeatmapInteractionStore((state) => state.setSelectedElement);
1403
- const widthScale = useHeatmapVizStore((state) => state.scale);
1404
- const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
1989
+ const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapInteraction();
1990
+ const { widthScale } = useHeatmapViz();
1991
+ const { dataInfo } = useHeatmapData();
1405
1992
  const reset = useCallback(() => {
1406
1993
  setHoveredElement(null);
1407
1994
  }, [setHoveredElement]);
@@ -1409,16 +1996,24 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
1409
1996
  reset();
1410
1997
  }, [reset]);
1411
1998
  const getHashFromEvent = useCallback((event) => {
1412
- if (!heatmapInfo || !isIframeReady(iframeRef, heatmapInfo)) {
1999
+ if (!dataInfo || !isIframeReady(iframeRef, dataInfo)) {
1413
2000
  reset();
1414
2001
  return;
1415
2002
  }
1416
2003
  const iframe = iframeRef.current;
2004
+ if (!iframe) {
2005
+ reset();
2006
+ return;
2007
+ }
1417
2008
  const doc = iframe.contentDocument;
2009
+ if (!doc) {
2010
+ reset();
2011
+ return;
2012
+ }
1418
2013
  const iframeRect = iframe.getBoundingClientRect();
1419
2014
  const { x, y } = convertViewportToIframeCoords(event.clientX, event.clientY, iframeRect, widthScale);
1420
- const targetElement = findTargetElement(doc, x, y, heatmapInfo);
1421
- if (!targetElement || !isValidElement(targetElement, heatmapInfo)) {
2015
+ const targetElement = findTargetElement(doc, x, y, dataInfo);
2016
+ if (!targetElement || !isValidElement(targetElement, dataInfo)) {
1422
2017
  reset();
1423
2018
  return;
1424
2019
  }
@@ -1427,9 +2022,9 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
1427
2022
  return hash;
1428
2023
  reset();
1429
2024
  return;
1430
- }, [heatmapInfo, iframeRef, getRect, widthScale, reset]);
2025
+ }, [dataInfo, iframeRef, getRect, widthScale, reset]);
1431
2026
  const handleMouseMove = useCallback(debounce((event) => {
1432
- if (!heatmapInfo) {
2027
+ if (!dataInfo) {
1433
2028
  reset();
1434
2029
  return;
1435
2030
  }
@@ -1438,19 +2033,19 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
1438
2033
  reset();
1439
2034
  return;
1440
2035
  }
1441
- const selector = heatmapInfo?.elementMapInfo?.[hash];
2036
+ const selector = dataInfo?.elementMapInfo?.[hash];
1442
2037
  const rect = getRect({ hash, selector });
1443
- const elementInfo = buildElementInfo(hash, rect, heatmapInfo);
2038
+ const elementInfo = buildElementInfo(hash, rect, dataInfo);
1444
2039
  if (!elementInfo) {
1445
2040
  reset();
1446
2041
  return;
1447
2042
  }
1448
2043
  setHoveredElement(elementInfo);
1449
- }, 16), [heatmapInfo, getRect, reset, getHashFromEvent]);
2044
+ }, 16), [dataInfo, getRect, reset, getHashFromEvent]);
1450
2045
  const handleClick = useCallback(() => {
1451
2046
  if (!hoveredElement?.hash)
1452
2047
  return;
1453
- onSelect(hoveredElement.hash);
2048
+ setSelectedElement(hoveredElement.hash);
1454
2049
  }, [hoveredElement?.hash]);
1455
2050
  return {
1456
2051
  hoveredElement,
@@ -1562,11 +2157,9 @@ function useVizLiveIframeMsg(options = {}) {
1562
2157
  }
1563
2158
 
1564
2159
  function useVizLiveRender() {
1565
- const wrapperHeight = useHeatmapLiveStore((state) => state.wrapperHeight);
1566
- const setIframeHeight = useHeatmapLiveStore((state) => state.setIframeHeight);
2160
+ const { setIframeHeight, wrapperHeight, setIsRenderViz, wrapperWidth } = useHeatmapViz();
1567
2161
  const contentWidth = useHeatmapConfigStore((state) => state.width);
1568
2162
  const htmlContent = useHeatmapLiveStore((state) => state.htmlContent);
1569
- const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
1570
2163
  const { iframeRef, isReady } = useVizLiveIframeMsg();
1571
2164
  useEffect(() => {
1572
2165
  if (!htmlContent || !iframeRef.current)
@@ -1582,17 +2175,20 @@ function useVizLiveRender() {
1582
2175
  useEffect(() => {
1583
2176
  if (!isReady)
1584
2177
  return;
2178
+ if (!wrapperHeight)
2179
+ return;
1585
2180
  if (!iframeRef.current)
1586
2181
  return;
2182
+ console.log(`🚀 🐥 ~ useVizLiveRender ~ wrapperHeight:`, wrapperHeight);
1587
2183
  const iframe = iframeRef.current;
1588
2184
  if (!iframe || !htmlContent)
1589
2185
  return;
1590
2186
  setIsRenderViz(false);
1591
- reset(iframe, { width: contentWidth, height: wrapperHeight }, (height) => {
2187
+ reset(iframe, { width: wrapperWidth, height: wrapperHeight }, (height) => {
1592
2188
  height && setIframeHeight(height);
1593
2189
  setIsRenderViz(true);
1594
2190
  });
1595
- }, [isReady, contentWidth, wrapperHeight]);
2191
+ }, [isReady, contentWidth, wrapperHeight, wrapperWidth]);
1596
2192
  return {
1597
2193
  iframeRef,
1598
2194
  };
@@ -1611,16 +2207,19 @@ function reset(iframe, rect, onSuccess) {
1611
2207
  fixer.enableNavigationBlocking();
1612
2208
  }
1613
2209
 
1614
- let visualizer = new Visualizer();
1615
2210
  const useHeatmapRender = () => {
1616
- const data = useHeatmapDataStore((state) => state.data);
1617
- const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
1618
- const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
1619
- const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
2211
+ const { data } = useHeatmapData();
2212
+ const { vizRef, setVizRef, setIsRenderViz, setIframeHeight, wrapperHeight, wrapperWidth } = useHeatmapViz();
2213
+ console.log(`🚀 🐥 ~ useHeatmapRender ~ wrapperHeight:`, wrapperHeight);
1620
2214
  const iframeRef = useRef(null);
1621
2215
  const renderHeatmap = useCallback(async (payloads) => {
1622
2216
  if (!payloads || payloads.length === 0)
1623
2217
  return;
2218
+ let visualizer = vizRef;
2219
+ if (!visualizer) {
2220
+ visualizer = new Visualizer();
2221
+ setVizRef(visualizer);
2222
+ }
1624
2223
  setIsRenderViz(false);
1625
2224
  const iframe = iframeRef.current;
1626
2225
  if (!iframe?.contentWindow)
@@ -1629,7 +2228,6 @@ const useHeatmapRender = () => {
1629
2228
  initIframe(iframe, payloads, (height) => {
1630
2229
  height && setIframeHeight(height);
1631
2230
  setIsRenderViz(true);
1632
- setVizRef(visualizer);
1633
2231
  });
1634
2232
  }, []);
1635
2233
  useEffect(() => {
@@ -1644,7 +2242,9 @@ const useHeatmapRender = () => {
1644
2242
  iframeRef,
1645
2243
  };
1646
2244
  };
1647
- function initIframe(iframe, payloads, onSuccess) {
2245
+ function initIframe(iframe,
2246
+ // size: { width: number; height: number },
2247
+ payloads, onSuccess) {
1648
2248
  const { size } = findLastSizeOfDom(payloads);
1649
2249
  const docWidth = size.width ?? 0;
1650
2250
  const docHeight = size.height ?? 0;
@@ -1671,8 +2271,8 @@ function sortEvents(a, b) {
1671
2271
  }
1672
2272
 
1673
2273
  const useReplayRender = () => {
1674
- const data = useHeatmapDataStore((state) => state.data);
1675
2274
  const setWidth = useHeatmapConfigStore((state) => state.setWidth);
2275
+ const { data } = useHeatmapData();
1676
2276
  const visualizerRef = useRef(null);
1677
2277
  const iframeRef = useRef(null);
1678
2278
  const eventsRef = useRef([]);
@@ -1843,8 +2443,8 @@ const useContentDimensions = ({ iframeRef, }) => {
1843
2443
  };
1844
2444
 
1845
2445
  const useObserveIframeHeight = (props) => {
1846
- const { iframeRef, setIframeHeight } = props;
1847
- const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz);
2446
+ const { iframeRef, isRenderViz } = props;
2447
+ const { setIframeHeight } = useHeatmapViz();
1848
2448
  const resizeObserverRef = useRef(null);
1849
2449
  const mutationObserverRef = useRef(null);
1850
2450
  const debounceTimerRef = useRef(null);
@@ -1852,7 +2452,7 @@ const useObserveIframeHeight = (props) => {
1852
2452
  const animationFrameRef = useRef(null);
1853
2453
  const updateIframeHeight = useCallback(() => {
1854
2454
  const iframe = iframeRef.current;
1855
- if (!iframe || !setIframeHeight)
2455
+ if (!iframe)
1856
2456
  return;
1857
2457
  try {
1858
2458
  const iframeDocument = iframe.contentDocument;
@@ -1860,7 +2460,7 @@ const useObserveIframeHeight = (props) => {
1860
2460
  const iframeDocumentElement = iframeDocument?.documentElement;
1861
2461
  if (!iframeBody || !iframeDocumentElement)
1862
2462
  return;
1863
- iframe.style.height = 'auto';
2463
+ // iframe.style.height = 'auto'; // TODO: check if this is needed
1864
2464
  requestAnimationFrame(() => {
1865
2465
  const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
1866
2466
  const documentHeight = Math.max(iframeDocumentElement.scrollHeight, iframeDocumentElement.offsetHeight, iframeDocumentElement.clientHeight);
@@ -1876,7 +2476,7 @@ const useObserveIframeHeight = (props) => {
1876
2476
  catch (error) {
1877
2477
  console.warn('Cannot measure iframe content:', error);
1878
2478
  }
1879
- }, [iframeRef, setIframeHeight]);
2479
+ }, [iframeRef]);
1880
2480
  const debouncedUpdate = useCallback(() => {
1881
2481
  // Cancel pending updates
1882
2482
  if (debounceTimerRef.current) {
@@ -1969,13 +2569,8 @@ const useObserveIframeHeight = (props) => {
1969
2569
  };
1970
2570
 
1971
2571
  const useScaleCalculation = (props) => {
1972
- const scale = useHeatmapVizStore((state) => state.scale);
1973
- const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio);
1974
- const setScale = useHeatmapVizStore((state) => state.setScale);
1975
- const isScaledToFit = useHeatmapVizStore((state) => state.isScaledToFit);
1976
- const setIsScaledToFit = useHeatmapVizStore((state) => state.setIsScaledToFit);
1977
- const minZoomRatio = useHeatmapVizStore((state) => state.minZoomRatio);
1978
- const setMinZoomRatio = useHeatmapVizStore((state) => state.setMinZoomRatio);
2572
+ const { widthScale, zoomRatio, isScaledToFit, minZoomRatio } = useHeatmapViz();
2573
+ const { setScale, setIsScaledToFit, setMinZoomRatio } = useHeatmapViz();
1979
2574
  const { containerWidth, containerHeight, contentWidth, contentHeight } = props;
1980
2575
  const calculateScaleResult = useCallback(() => {
1981
2576
  if (containerWidth > 0 && contentWidth > 0 && containerHeight > 0 && contentHeight > 0) {
@@ -1983,7 +2578,7 @@ const useScaleCalculation = (props) => {
1983
2578
  const availableWidth = containerWidth - HEATMAP_CONFIG['padding'] * 2;
1984
2579
  const widthScale = Math.min(availableWidth / contentWidth, 1);
1985
2580
  // 2. Calculate available height
1986
- const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] ;
2581
+ const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] || 0;
1987
2582
  const paddingTotal = HEATMAP_CONFIG['padding'] * 2;
1988
2583
  const availableHeight = containerHeight - toolbarHeight - paddingTotal; // 10px buffer to avoid scroll bar
1989
2584
  // 3. Calculate minZoomRatio (zoom ratio minimum to fit iframe in container)
@@ -2015,11 +2610,10 @@ const useScaleCalculation = (props) => {
2015
2610
  useEffect(() => {
2016
2611
  calculateScaleResult();
2017
2612
  }, [calculateScaleResult]);
2018
- return { scale, isScaledToFit, minZoomRatio };
2613
+ return { widthScale, isScaledToFit, minZoomRatio };
2019
2614
  };
2020
2615
 
2021
- const useScrollSync = ({ iframeRef }) => {
2022
- const widthScale = useHeatmapVizStore((state) => state.scale);
2616
+ const useScrollSync = ({ widthScale, iframeRef, }) => {
2023
2617
  const handleScroll = useCallback((scrollTop) => {
2024
2618
  const iframe = iframeRef.current;
2025
2619
  if (!iframe || widthScale <= 0)
@@ -2040,24 +2634,24 @@ const useScrollSync = ({ iframeRef }) => {
2040
2634
  };
2041
2635
 
2042
2636
  const useHeatmapScale = (props) => {
2043
- const { wrapperRef, iframeRef, iframeHeight, setIframeHeight } = props;
2637
+ const { wrapperRef, iframeRef, iframeHeight, isRenderViz } = props;
2044
2638
  // 1. Observe container dimensions
2045
2639
  const { containerWidth, containerHeight } = useContainerDimensions({ wrapperRef });
2046
2640
  // 2. Get content dimensions from config
2047
2641
  const { contentWidth } = useContentDimensions({ iframeRef });
2048
2642
  // 3. Observe iframe height (now reacts to width changes)
2049
- useObserveIframeHeight({ iframeRef, setIframeHeight });
2643
+ useObserveIframeHeight({ iframeRef, isRenderViz });
2050
2644
  // 4. Calculate scale
2051
- const { scale } = useScaleCalculation({
2645
+ const { widthScale } = useScaleCalculation({
2052
2646
  containerWidth,
2053
2647
  containerHeight,
2054
2648
  contentWidth,
2055
2649
  contentHeight: iframeHeight,
2056
2650
  });
2057
2651
  // 5. Setup scroll sync
2058
- const { handleScroll } = useScrollSync({ iframeRef });
2059
- const scaledHeight = iframeHeight * scale;
2060
- const scaledWidth = contentWidth * scale;
2652
+ const { handleScroll } = useScrollSync({ widthScale, iframeRef });
2653
+ const scaledHeight = iframeHeight * widthScale;
2654
+ const scaledWidth = contentWidth * widthScale;
2061
2655
  return {
2062
2656
  containerWidth,
2063
2657
  containerHeight,
@@ -2068,27 +2662,29 @@ const useHeatmapScale = (props) => {
2068
2662
  };
2069
2663
 
2070
2664
  const useWrapperRefHeight = (props) => {
2071
- const { isActive, wrapperRef, setWrapperHeight } = props;
2072
- const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz);
2665
+ const { isActive, wrapperRef } = props;
2073
2666
  const resizeObserverRef = useRef(null);
2074
2667
  const mutationObserverRef = useRef(null);
2668
+ const { setWrapperHeight, setWrapperWidth } = useHeatmapViz();
2075
2669
  const updateWrapperHeight = useCallback(() => {
2076
2670
  const wrapper = wrapperRef.current;
2077
2671
  if (!wrapper)
2078
2672
  return;
2079
2673
  try {
2080
2674
  const wrapperHeight = wrapper.offsetHeight;
2675
+ const wrapperWidth = wrapper.offsetWidth;
2081
2676
  if (wrapperHeight > 0) {
2082
- setWrapperHeight?.(wrapperHeight);
2677
+ setWrapperHeight(wrapperHeight);
2678
+ setWrapperWidth(wrapperWidth);
2083
2679
  }
2084
2680
  }
2085
2681
  catch (error) {
2086
2682
  console.warn('Cannot measure iframe content:', error);
2087
2683
  }
2088
- }, [wrapperRef, setWrapperHeight]);
2684
+ }, [wrapperRef]);
2089
2685
  useEffect(() => {
2090
2686
  const wrapper = wrapperRef.current;
2091
- if (!wrapper || !isRenderViz)
2687
+ if (!wrapper)
2092
2688
  return;
2093
2689
  const setupObservers = () => {
2094
2690
  try {
@@ -2118,6 +2714,7 @@ const useWrapperRefHeight = (props) => {
2118
2714
  updateWrapperHeight();
2119
2715
  }
2120
2716
  catch (error) {
2717
+ console.log(`🚀 🐥 ~ setupObservers ~ error:`, error);
2121
2718
  console.warn('Cannot access wrapper content:', error);
2122
2719
  }
2123
2720
  };
@@ -2132,12 +2729,12 @@ const useWrapperRefHeight = (props) => {
2132
2729
  mutationObserverRef.current.disconnect();
2133
2730
  }
2134
2731
  };
2135
- }, [wrapperRef, isRenderViz, updateWrapperHeight, isActive]);
2732
+ }, [wrapperRef, updateWrapperHeight, isActive]);
2136
2733
  return {};
2137
2734
  };
2138
2735
 
2139
2736
  const useZonePositions = (options) => {
2140
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2737
+ const { iframeHeight } = useHeatmapViz();
2141
2738
  const getZonePosition = useCallback((zone) => {
2142
2739
  if (!iframeHeight) {
2143
2740
  return null;
@@ -2163,12 +2760,13 @@ const useScrollmapZones = (options) => {
2163
2760
  const { mode = 'basic', enabled = true, iframeRef, wrapperRef } = options;
2164
2761
  const [isReady, setIsReady] = useState(false);
2165
2762
  const [zones, setZones] = useState([]);
2166
- const scrollmap = useHeatmapDataStore((state) => state.scrollmap);
2167
- const scrollMapInfo = useHeatmapDataStore((state) => state.dataInfo?.scrollMapInfo);
2763
+ const { scrollmap } = useHeatmapData();
2764
+ const { dataInfo } = useHeatmapData();
2168
2765
  const { getZonePosition } = useZonePositions();
2766
+ const scrollMapInfo = dataInfo?.scrollMapInfo;
2169
2767
  const maxUsers = useMemo(() => {
2170
2768
  if (!scrollmap || scrollmap.length === 0)
2171
- return 100;
2769
+ return 0;
2172
2770
  return Math.max(...scrollmap.map((d) => d.percUsers));
2173
2771
  }, [scrollmap]);
2174
2772
  const createZones = useCallback((data) => {
@@ -2265,6 +2863,514 @@ const getScrollGradientColor = (normalized) => {
2265
2863
  return `rgb(${r}, ${g}, ${b})`;
2266
2864
  };
2267
2865
 
2866
+ class PerformanceLogger {
2867
+ static instance;
2868
+ metrics = [];
2869
+ sessionId;
2870
+ sessionStartTime;
2871
+ config;
2872
+ constructor() {
2873
+ this.sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2874
+ this.sessionStartTime = Date.now();
2875
+ this.config = {
2876
+ enabled: false,
2877
+ logToConsole: false,
2878
+ logLevel: 'normal',
2879
+ thresholds: {
2880
+ slowRenderMs: 16, // > 16ms = slower than 60fps
2881
+ slowHookMs: 5,
2882
+ excessiveRenderCount: 10,
2883
+ },
2884
+ };
2885
+ }
2886
+ static getInstance() {
2887
+ if (!PerformanceLogger.instance) {
2888
+ PerformanceLogger.instance = new PerformanceLogger();
2889
+ }
2890
+ return PerformanceLogger.instance;
2891
+ }
2892
+ configure(config) {
2893
+ this.config = { ...this.config, ...config };
2894
+ if (this.config.enabled && this.config.logToConsole) {
2895
+ console.log('[Performance Monitor] Enabled', {
2896
+ sessionId: this.sessionId,
2897
+ config: this.config,
2898
+ });
2899
+ }
2900
+ }
2901
+ log(metric) {
2902
+ if (!this.config.enabled)
2903
+ return;
2904
+ this.metrics.push(metric);
2905
+ // Log to console based on level
2906
+ if (this.config.logToConsole) {
2907
+ this.logToConsole(metric);
2908
+ }
2909
+ // Send to external logger if configured
2910
+ if (this.config.externalLogger) {
2911
+ this.config.externalLogger(metric);
2912
+ }
2913
+ // Check thresholds and warn
2914
+ this.checkThresholds(metric);
2915
+ }
2916
+ logToConsole(metric) {
2917
+ const { logLevel } = this.config;
2918
+ if (logLevel === 'minimal') {
2919
+ // Only log warnings
2920
+ return;
2921
+ }
2922
+ const style = this.getConsoleStyle(metric.type);
2923
+ const label = `[${metric.type.toUpperCase()}] ${metric.name}`;
2924
+ if (logLevel === 'verbose') {
2925
+ console.log(`%c${label}`, style, metric);
2926
+ }
2927
+ else {
2928
+ // Normal: Log compact info
2929
+ const info = { name: metric.name };
2930
+ if (metric.duration)
2931
+ info.duration = `${metric.duration.toFixed(2)}ms`;
2932
+ if ('viewId' in metric && metric.viewId)
2933
+ info.viewId = metric.viewId;
2934
+ if ('renderCount' in metric)
2935
+ info.renderCount = metric.renderCount;
2936
+ console.log(`%c${label}`, style, info);
2937
+ }
2938
+ }
2939
+ getConsoleStyle(type) {
2940
+ const styles = {
2941
+ render: 'color: #61dafb; font-weight: bold',
2942
+ hook: 'color: #ffa500; font-weight: bold',
2943
+ store: 'color: #9c27b0; font-weight: bold',
2944
+ function: 'color: #4caf50; font-weight: bold',
2945
+ };
2946
+ return styles[type] || '';
2947
+ }
2948
+ checkThresholds(metric) {
2949
+ const { thresholds } = this.config;
2950
+ // Check slow render
2951
+ if (metric.type === 'render' && metric.duration && metric.duration > thresholds.slowRenderMs) {
2952
+ console.warn(`[Performance] Slow render detected: ${metric.name} took ${metric.duration.toFixed(2)}ms`, metric);
2953
+ }
2954
+ // Check slow hook
2955
+ if (metric.type === 'hook' && metric.duration && metric.duration > thresholds.slowHookMs) {
2956
+ console.warn(`[Performance] Slow hook detected: ${metric.name} took ${metric.duration.toFixed(2)}ms`, metric);
2957
+ }
2958
+ // Check excessive renders
2959
+ if (metric.type === 'render' && 'renderCount' in metric) {
2960
+ const renderMetric = metric;
2961
+ if (renderMetric.renderCount > thresholds.excessiveRenderCount) {
2962
+ console.warn(`[Performance] Excessive renders: ${metric.name} has rendered ${renderMetric.renderCount} times`, metric);
2963
+ }
2964
+ }
2965
+ }
2966
+ generateReport() {
2967
+ const now = Date.now();
2968
+ const duration = now - this.sessionStartTime;
2969
+ // Calculate summary
2970
+ const renderMetrics = this.metrics.filter((m) => m.type === 'render');
2971
+ const hookMetrics = this.metrics.filter((m) => m.type === 'hook');
2972
+ const storeMetrics = this.metrics.filter((m) => m.type === 'store');
2973
+ const totalRenders = renderMetrics.length;
2974
+ const totalHookCalls = hookMetrics.length;
2975
+ const totalStoreUpdates = storeMetrics.length;
2976
+ const renderDurations = renderMetrics
2977
+ .filter((m) => m.duration)
2978
+ .map((m) => m.duration);
2979
+ const averageRenderTime = renderDurations.length > 0
2980
+ ? renderDurations.reduce((a, b) => a + b, 0) / renderDurations.length
2981
+ : 0;
2982
+ // View-specific metrics
2983
+ const viewMetrics = {};
2984
+ this.metrics.forEach((metric) => {
2985
+ const viewId = 'viewId' in metric && metric.viewId ? metric.viewId : DEFAULT_VIEW_ID;
2986
+ if (!viewMetrics[viewId]) {
2987
+ viewMetrics[viewId] = { renders: 0, hookCalls: 0, storeUpdates: 0 };
2988
+ }
2989
+ if (metric.type === 'render')
2990
+ viewMetrics[viewId].renders++;
2991
+ if (metric.type === 'hook')
2992
+ viewMetrics[viewId].hookCalls++;
2993
+ if (metric.type === 'store')
2994
+ viewMetrics[viewId].storeUpdates++;
2995
+ });
2996
+ // Find warnings
2997
+ const renderCounts = new Map();
2998
+ renderMetrics.forEach((m) => {
2999
+ const key = `${m.name}-${'viewId' in m ? m.viewId : DEFAULT_VIEW_ID}`;
3000
+ renderCounts.set(key, (renderCounts.get(key) || 0) + 1);
3001
+ });
3002
+ const excessiveRenders = Array.from(renderCounts.entries())
3003
+ .filter(([, count]) => count > this.config.thresholds.excessiveRenderCount)
3004
+ .map(([key, count]) => {
3005
+ const [component, viewId] = key.split('-');
3006
+ return { component, count, viewId: viewId !== DEFAULT_VIEW_ID ? viewId : undefined };
3007
+ });
3008
+ const slowOperations = this.metrics
3009
+ .filter((m) => m.duration &&
3010
+ ((m.type === 'render' && m.duration > this.config.thresholds.slowRenderMs) ||
3011
+ (m.type === 'hook' && m.duration > this.config.thresholds.slowHookMs)))
3012
+ .map((m) => ({
3013
+ name: m.name,
3014
+ duration: m.duration,
3015
+ type: m.type,
3016
+ }));
3017
+ return {
3018
+ session: {
3019
+ id: this.sessionId,
3020
+ startTime: this.sessionStartTime,
3021
+ endTime: now,
3022
+ duration,
3023
+ },
3024
+ summary: {
3025
+ totalRenders,
3026
+ totalHookCalls,
3027
+ totalStoreUpdates,
3028
+ averageRenderTime,
3029
+ viewMetrics,
3030
+ },
3031
+ metrics: this.metrics,
3032
+ warnings: {
3033
+ excessiveRenders,
3034
+ slowOperations,
3035
+ },
3036
+ };
3037
+ }
3038
+ clearMetrics() {
3039
+ this.metrics = [];
3040
+ this.sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
3041
+ this.sessionStartTime = Date.now();
3042
+ }
3043
+ getMetrics() {
3044
+ return [...this.metrics];
3045
+ }
3046
+ isEnabled() {
3047
+ return this.config.enabled;
3048
+ }
3049
+ }
3050
+ const performanceLogger = PerformanceLogger.getInstance();
3051
+
3052
+ /**
3053
+ * Hook to track render count of a component
3054
+ * @param componentName - Name of the component
3055
+ * @param viewId - Optional viewId for compare mode
3056
+ */
3057
+ function useRenderCount(componentName, viewId) {
3058
+ const renderCount = useRef(0);
3059
+ const startTime = useRef(0);
3060
+ // Increment before render
3061
+ renderCount.current += 1;
3062
+ startTime.current = performance.now();
3063
+ useEffect(() => {
3064
+ const duration = performance.now() - startTime.current;
3065
+ const metric = {
3066
+ id: `render-${componentName}-${Date.now()}`,
3067
+ type: 'render',
3068
+ name: componentName,
3069
+ componentName,
3070
+ renderCount: renderCount.current,
3071
+ timestamp: Date.now(),
3072
+ duration,
3073
+ viewId,
3074
+ };
3075
+ performanceLogger.log(metric);
3076
+ });
3077
+ return renderCount.current;
3078
+ }
3079
+ /**
3080
+ * Hook to detect why a component re-rendered (which props changed)
3081
+ * @param componentName - Name of the component
3082
+ * @param props - Props object to track
3083
+ * @param viewId - Optional viewId
3084
+ */
3085
+ function useWhyDidYouUpdate(componentName, props, viewId) {
3086
+ const previousProps = useRef();
3087
+ const renderCount = useRef(0);
3088
+ const startTime = useRef(0);
3089
+ renderCount.current += 1;
3090
+ startTime.current = performance.now();
3091
+ useEffect(() => {
3092
+ if (previousProps.current) {
3093
+ const duration = performance.now() - startTime.current;
3094
+ const allKeys = Object.keys({ ...previousProps.current, ...props });
3095
+ const changedProps = [];
3096
+ allKeys.forEach((key) => {
3097
+ if (previousProps.current[key] !== props[key]) {
3098
+ changedProps.push(key);
3099
+ }
3100
+ });
3101
+ if (changedProps.length > 0) {
3102
+ const metric = {
3103
+ id: `render-${componentName}-${Date.now()}`,
3104
+ type: 'render',
3105
+ name: componentName,
3106
+ componentName,
3107
+ renderCount: renderCount.current,
3108
+ timestamp: Date.now(),
3109
+ duration,
3110
+ viewId,
3111
+ reason: 'Props changed',
3112
+ propsChanged: changedProps,
3113
+ metadata: {
3114
+ changes: changedProps.reduce((acc, key) => {
3115
+ acc[key] = {
3116
+ from: previousProps.current[key],
3117
+ to: props[key],
3118
+ };
3119
+ return acc;
3120
+ }, {}),
3121
+ },
3122
+ };
3123
+ performanceLogger.log(metric);
3124
+ }
3125
+ }
3126
+ previousProps.current = props;
3127
+ });
3128
+ }
3129
+ /**
3130
+ * Hook to measure execution time of a function
3131
+ * @param functionName - Name of the function
3132
+ * @param fn - Function to measure
3133
+ * @param viewId - Optional viewId
3134
+ */
3135
+ function useMeasureFunction(functionName, fn, viewId) {
3136
+ const measuredFn = ((...args) => {
3137
+ const startTime = performance.now();
3138
+ const result = fn(...args);
3139
+ const duration = performance.now() - startTime;
3140
+ performanceLogger.log({
3141
+ id: `function-${functionName}-${Date.now()}`,
3142
+ type: 'function',
3143
+ name: functionName,
3144
+ functionName,
3145
+ timestamp: Date.now(),
3146
+ duration,
3147
+ viewId,
3148
+ metadata: {
3149
+ args: args.length,
3150
+ },
3151
+ });
3152
+ return result;
3153
+ });
3154
+ return measuredFn;
3155
+ }
3156
+ /**
3157
+ * Hook to track when a hook is called
3158
+ * @param hookName - Name of the hook
3159
+ * @param viewId - Optional viewId
3160
+ * @param storeSlice - Optional store slice being accessed
3161
+ */
3162
+ function useTrackHookCall(hookName, viewId, storeSlice) {
3163
+ const startTime = useRef(0);
3164
+ startTime.current = performance.now();
3165
+ useEffect(() => {
3166
+ const duration = performance.now() - startTime.current;
3167
+ performanceLogger.log({
3168
+ id: `hook-${hookName}-${Date.now()}`,
3169
+ type: 'hook',
3170
+ name: hookName,
3171
+ hookName,
3172
+ timestamp: Date.now(),
3173
+ duration,
3174
+ viewId,
3175
+ storeSlice,
3176
+ });
3177
+ });
3178
+ }
3179
+
3180
+ /**
3181
+ * HOC to track component performance
3182
+ * @param Component - Component to wrap
3183
+ * @param options - Tracking options
3184
+ */
3185
+ function withPerformanceTracking(Component, options = {}) {
3186
+ const { trackProps = true, componentName, viewIdProp = 'viewId' } = options;
3187
+ const WrappedComponent = (props) => {
3188
+ const name = componentName || Component.displayName || Component.name || 'Unknown';
3189
+ const viewId = viewIdProp in props ? props[viewIdProp] : undefined;
3190
+ if (trackProps) {
3191
+ useWhyDidYouUpdate(name, props, viewId);
3192
+ }
3193
+ return jsx(Component, { ...props });
3194
+ };
3195
+ WrappedComponent.displayName = `withPerformanceTracking(${componentName || Component.displayName || Component.name})`;
3196
+ return WrappedComponent;
3197
+ }
3198
+
3199
+ /**
3200
+ * Middleware để track store updates
3201
+ */
3202
+ function createStorePerformanceTracker(storeName) {
3203
+ return (config) => (set, get, api) => {
3204
+ const wrappedSet = (partial, replace) => {
3205
+ const startTime = performance.now();
3206
+ const prevState = get();
3207
+ // Call original set
3208
+ set(partial, replace);
3209
+ const duration = performance.now() - startTime;
3210
+ const nextState = get();
3211
+ // Detect which viewIds were affected
3212
+ const affectedViews = new Set();
3213
+ // Check Record<string, any> properties for viewId keys
3214
+ Object.keys(nextState).forEach((key) => {
3215
+ if (typeof prevState[key] === 'object' &&
3216
+ typeof nextState[key] === 'object' &&
3217
+ prevState[key] !== nextState[key]) {
3218
+ // Check if this is a Record<viewId, value> structure
3219
+ const prevKeys = Object.keys(prevState[key] || {});
3220
+ const nextKeys = Object.keys(nextState[key] || {});
3221
+ const allKeys = new Set([...prevKeys, ...nextKeys]);
3222
+ allKeys.forEach((viewId) => {
3223
+ if (prevState[key]?.[viewId] !== nextState[key]?.[viewId]) {
3224
+ affectedViews.add(viewId);
3225
+ }
3226
+ });
3227
+ }
3228
+ });
3229
+ const metric = {
3230
+ id: `store-${storeName}-${Date.now()}`,
3231
+ type: 'store',
3232
+ name: `${storeName} update`,
3233
+ storeName,
3234
+ action: typeof partial === 'function' ? 'function update' : 'direct update',
3235
+ timestamp: Date.now(),
3236
+ duration,
3237
+ affectedViews: Array.from(affectedViews),
3238
+ metadata: {
3239
+ stateKeys: Object.keys(nextState),
3240
+ },
3241
+ };
3242
+ performanceLogger.log(metric);
3243
+ };
3244
+ return config(wrappedSet, get, api);
3245
+ };
3246
+ }
3247
+ /**
3248
+ * Track specific store action
3249
+ */
3250
+ function trackStoreAction(storeName, action, viewId, metadata) {
3251
+ const metric = {
3252
+ id: `store-${storeName}-${action}-${Date.now()}`,
3253
+ type: 'store',
3254
+ name: `${storeName}.${action}`,
3255
+ storeName,
3256
+ action,
3257
+ timestamp: Date.now(),
3258
+ viewId,
3259
+ metadata,
3260
+ };
3261
+ performanceLogger.log(metric);
3262
+ }
3263
+
3264
+ /**
3265
+ * Get performance report as JSON string
3266
+ */
3267
+ function getPerformanceReportJSON() {
3268
+ const report = performanceLogger.generateReport();
3269
+ return JSON.stringify(report, null, 2);
3270
+ }
3271
+ /**
3272
+ * Download performance report as JSON file
3273
+ */
3274
+ function downloadPerformanceReport(filename = 'heatmap-performance-report.json') {
3275
+ const report = performanceLogger.generateReport();
3276
+ const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
3277
+ const url = URL.createObjectURL(blob);
3278
+ const link = document.createElement('a');
3279
+ link.href = url;
3280
+ link.download = filename;
3281
+ link.click();
3282
+ URL.revokeObjectURL(url);
3283
+ }
3284
+ /**
3285
+ * Send performance report to external endpoint
3286
+ */
3287
+ async function sendPerformanceReport(endpoint) {
3288
+ const report = performanceLogger.generateReport();
3289
+ try {
3290
+ const response = await fetch(endpoint, {
3291
+ method: 'POST',
3292
+ headers: {
3293
+ 'Content-Type': 'application/json',
3294
+ },
3295
+ body: JSON.stringify(report),
3296
+ });
3297
+ if (!response.ok) {
3298
+ throw new Error(`Failed to send report: ${response.statusText}`);
3299
+ }
3300
+ }
3301
+ catch (error) {
3302
+ console.error('[Performance] Failed to send report:', error);
3303
+ throw error;
3304
+ }
3305
+ }
3306
+ /**
3307
+ * Print performance summary to console
3308
+ */
3309
+ function printPerformanceSummary() {
3310
+ const report = performanceLogger.generateReport();
3311
+ console.group('📊 Performance Summary');
3312
+ console.log('Session:', report.session);
3313
+ console.log('Total Renders:', report.summary.totalRenders);
3314
+ console.log('Total Hook Calls:', report.summary.totalHookCalls);
3315
+ console.log('Total Store Updates:', report.summary.totalStoreUpdates);
3316
+ console.log('Average Render Time:', `${report.summary.averageRenderTime.toFixed(2)}ms`);
3317
+ console.log('View Metrics:', report.summary.viewMetrics);
3318
+ if (report.warnings.excessiveRenders.length > 0) {
3319
+ console.group('⚠️ Excessive Renders');
3320
+ report.warnings.excessiveRenders.forEach((warning) => {
3321
+ console.warn(`${warning.component}${warning.viewId ? ` (${warning.viewId})` : ''}: ${warning.count} renders`);
3322
+ });
3323
+ console.groupEnd();
3324
+ }
3325
+ if (report.warnings.slowOperations.length > 0) {
3326
+ console.group('🐌 Slow Operations');
3327
+ report.warnings.slowOperations.forEach((warning) => {
3328
+ console.warn(`${warning.name} (${warning.type}): ${warning.duration.toFixed(2)}ms`);
3329
+ });
3330
+ console.groupEnd();
3331
+ }
3332
+ console.groupEnd();
3333
+ }
3334
+ /**
3335
+ * Get performance metrics filtered by viewId
3336
+ */
3337
+ function getMetricsByViewId(viewId) {
3338
+ const allMetrics = performanceLogger.getMetrics();
3339
+ const filteredMetrics = allMetrics.filter((m) => {
3340
+ return 'viewId' in m && m.viewId === viewId;
3341
+ });
3342
+ const report = performanceLogger.generateReport();
3343
+ return {
3344
+ ...report,
3345
+ metrics: filteredMetrics,
3346
+ summary: {
3347
+ ...report.summary,
3348
+ totalRenders: filteredMetrics.filter((m) => m.type === 'render').length,
3349
+ totalHookCalls: filteredMetrics.filter((m) => m.type === 'hook').length,
3350
+ totalStoreUpdates: filteredMetrics.filter((m) => m.type === 'store').length,
3351
+ averageRenderTime: 0, // Recalculate if needed
3352
+ viewMetrics: { [viewId]: report.summary.viewMetrics[viewId] || { renders: 0, hookCalls: 0, storeUpdates: 0 } },
3353
+ },
3354
+ };
3355
+ }
3356
+ /**
3357
+ * Compare performance between two viewIds
3358
+ */
3359
+ function compareViewPerformance(viewId1, viewId2) {
3360
+ 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 };
3363
+ return {
3364
+ view1: view1Metrics,
3365
+ view2: view2Metrics,
3366
+ difference: {
3367
+ renders: view1Metrics.renders - view2Metrics.renders,
3368
+ hookCalls: view1Metrics.hookCalls - view2Metrics.hookCalls,
3369
+ storeUpdates: view1Metrics.storeUpdates - view2Metrics.storeUpdates,
3370
+ },
3371
+ };
3372
+ }
3373
+
2268
3374
  const BoxStack = forwardRef(({ children, ...props }, ref) => {
2269
3375
  const id = props.id;
2270
3376
  const flexDirection = props.flexDirection;
@@ -2310,7 +3416,12 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
2310
3416
 
2311
3417
  const ContentTopBar = () => {
2312
3418
  const controls = useHeatmapControlStore((state) => state.controls);
3419
+ useHeatmapConfigStore((state) => state.mode);
2313
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
+ // }
2314
3425
  return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
2315
3426
  borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
2316
3427
  }, children: TopBar && jsx(TopBar, {}) }));
@@ -2318,8 +3429,9 @@ const ContentTopBar = () => {
2318
3429
 
2319
3430
  const ContentMetricBar = () => {
2320
3431
  const controls = useHeatmapControlStore((state) => state.controls);
3432
+ const borderBottom = `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`;
2321
3433
  return (jsx(BoxStack, { id: "gx-hm-content-metric-bar", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
2322
- borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3434
+ borderBottom,
2323
3435
  }, children: controls.MetricBar ?? null }));
2324
3436
  };
2325
3437
 
@@ -2335,31 +3447,88 @@ const ContentToolbar = () => {
2335
3447
  }, children: controls.Toolbar ?? null }));
2336
3448
  };
2337
3449
 
2338
- const VizContainer = ({ children, setWrapperHeight }) => {
3450
+ const ContentSidebar = () => {
3451
+ const controls = useHeatmapControlStore((state) => state.controls);
3452
+ const { state } = useHeatmapInteraction();
3453
+ const isHideSidebar = state.hideSidebar;
3454
+ const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3455
+ const mode = useHeatmapConfigStore((state) => state.mode);
3456
+ const SidebarComponent = controls.Sidebar ?? null;
3457
+ const isCompareMode = mode === 'compare';
3458
+ if (isCompareMode)
3459
+ return null;
3460
+ if (!SidebarComponent)
3461
+ return null;
3462
+ return (jsx("div", { className: "gx-hm-sidebar", style: {
3463
+ height: '100%',
3464
+ display: 'flex',
3465
+ zIndex: 1,
3466
+ ...(isHideSidebar
3467
+ ? {
3468
+ width: '0',
3469
+ transform: 'translateX(-100%)',
3470
+ visibility: 'hidden',
3471
+ }
3472
+ : { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
3473
+ }, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
3474
+ height: '100%',
3475
+ width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
3476
+ borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3477
+ }, children: jsx(SidebarComponent, {}) }) }));
3478
+ };
3479
+
3480
+ const PopoverSidebar = () => {
3481
+ const mode = useHeatmapConfigStore((state) => state.mode);
3482
+ const CompSidebar = useHeatmapControlStore((state) => state.controls.Sidebar);
3483
+ const CompSidebarActivator = useHeatmapControlStore((state) => state.controls.SidebarActivator);
3484
+ const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3485
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
3486
+ const { state } = useHeatmapInteraction();
3487
+ const isCompareMode = mode === 'compare';
3488
+ const isHideSidebar = state.hideSidebar;
3489
+ const stylePopover = {
3490
+ position: 'absolute',
3491
+ top: '24px',
3492
+ left: '24px',
3493
+ zIndex: Z_INDEX.SIDEBAR_POPOVER,
3494
+ height: `calc(100% - ${HEATMAP_CONFIG.heightToolbar}px - ${HEATMAP_CONFIG.padding}px - 24px)`,
3495
+ width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
3496
+ };
3497
+ if (isHideSidebar || !isCompareMode)
3498
+ return null;
3499
+ if (!CompSidebar || !CompSidebarActivator)
3500
+ return null;
3501
+ return (jsxs("div", { children: [!isPopoverOpen && (jsx("div", { style: { ...stylePopover, width: 'auto', height: 'auto' }, children: jsx(CompSidebarActivator, { onClick: () => setIsPopoverOpen(true) }) })), isPopoverOpen && (jsx(Fragment, { children: jsx("div", { className: "gx-hm-sidebar-popover", style: {
3502
+ ...stylePopover,
3503
+ backgroundColor: '#fff',
3504
+ borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3505
+ borderRadius: '8px',
3506
+ boxShadow: '4px 0 12px rgba(0, 0, 0, 0.15)',
3507
+ overflow: 'auto',
3508
+ }, children: jsx(CompSidebar, { closeAction: { onClick: () => setIsPopoverOpen(false) } }) }) }))] }));
3509
+ };
3510
+
3511
+ const VizContainer = ({ children, isActive = false }) => {
2339
3512
  const wrapperRef = useRef(null);
3513
+ const viewId = useViewId();
2340
3514
  useWrapperRefHeight({
2341
- isActive: !!setWrapperHeight,
3515
+ isActive,
2342
3516
  wrapperRef,
2343
- setWrapperHeight,
2344
3517
  });
2345
- return (jsx(BoxStack, { ref: wrapperRef, id: "gx-hm-viz-container", flexDirection: "column", flex: "1 1 auto", overflow: "auto", zIndex: 1, children: jsx(BoxStack, { id: "gx-hm-content", flexDirection: "column", flex: "1 1 auto", overflow: "hidden", style: {
2346
- minWidth: '394px',
2347
- }, children: children }) }));
3518
+ return (jsxs(BoxStack, { ref: wrapperRef, id: `gx-hm-viz-container-${viewId}`, flexDirection: "column", flex: "1 1 auto", overflow: "auto", zIndex: 1, children: [jsx(BoxStack, { id: "gx-hm-content", flexDirection: "column", flex: "1 1 auto", overflow: "hidden", style: {
3519
+ minWidth: '394px',
3520
+ }, children: children }), jsx(PopoverSidebar, {})] }));
2348
3521
  };
2349
3522
 
2350
3523
  const useClickmap = () => {
2351
- const [isInitialized, setIsInitialized] = useState(false);
2352
- const clickmap = useHeatmapDataStore((state) => state.clickmap);
2353
- const vizRef = useHeatmapSingleStore((state) => state.vizRef);
3524
+ const { vizRef } = useHeatmapViz();
3525
+ const { clickmap } = useHeatmapData();
2354
3526
  const start = useCallback(() => {
2355
- if (isInitialized)
2356
- return;
2357
3527
  if (!vizRef || !clickmap || clickmap.length === 0)
2358
3528
  return;
2359
3529
  try {
2360
3530
  vizRef?.clearmap?.();
2361
3531
  vizRef?.clickmap?.(clickmap);
2362
- setIsInitialized(true);
2363
3532
  }
2364
3533
  catch (error) {
2365
3534
  console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
@@ -2369,8 +3538,8 @@ const useClickmap = () => {
2369
3538
  };
2370
3539
 
2371
3540
  const useScrollmap = () => {
2372
- const vizRef = useHeatmapSingleStore((state) => state.vizRef);
2373
- const scrollmap = useHeatmapDataStore((state) => state.scrollmap);
3541
+ const { vizRef } = useHeatmapViz();
3542
+ const { scrollmap } = useHeatmapData();
2374
3543
  const start = useCallback(() => {
2375
3544
  // if (isInitialized) return;
2376
3545
  if (!vizRef || !scrollmap || scrollmap.length === 0)
@@ -2387,7 +3556,7 @@ const useScrollmap = () => {
2387
3556
  return { start };
2388
3557
  };
2389
3558
 
2390
- const useHeatmapCanvas = ({ iframeRef, }) => {
3559
+ const useHeatmapCanvas = () => {
2391
3560
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
2392
3561
  const { start: startClickmap } = useClickmap();
2393
3562
  const { start: startScrollmap } = useScrollmap();
@@ -2403,10 +3572,28 @@ const useHeatmapCanvas = ({ iframeRef, }) => {
2403
3572
  }, [heatmapType, startClickmap, startScrollmap]);
2404
3573
  };
2405
3574
 
2406
- const CLICKED_ELEMENT_ID = 'gx-hm-clicked-element';
2407
- const SECONDARY_CLICKED_ELEMENT_ID = 'gx-hm-secondary-clicked-element';
2408
- const HOVERED_ELEMENT_ID = 'gx-hm-hovered-element';
2409
- const SECONDARY_HOVERED_ELEMENT_ID = 'gx-hm-secondary-hovered-element';
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);
3596
+ };
2410
3597
 
2411
3598
  const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
2412
3599
  const style = calculateRankPosition(elementRect, widthScale);
@@ -2415,9 +3602,9 @@ const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
2415
3602
 
2416
3603
  const NUMBER_OF_TOP_ELEMENTS = 10;
2417
3604
  const DefaultRankBadges = ({ getRect, hidden }) => {
2418
- const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
2419
- const widthScale = useHeatmapVizStore((state) => state.scale);
2420
- const elements = heatmapInfo?.sortedElements?.slice(0, NUMBER_OF_TOP_ELEMENTS) ?? [];
3605
+ const { dataInfo } = useHeatmapData();
3606
+ const { widthScale } = useHeatmapViz();
3607
+ const elements = dataInfo?.sortedElements?.slice(0, NUMBER_OF_TOP_ELEMENTS) ?? [];
2421
3608
  if (hidden || elements.length === 0)
2422
3609
  return null;
2423
3610
  return (jsx(Fragment, { children: elements.map((element, index) => {
@@ -2436,6 +3623,7 @@ const DEFAULT_POSITION = {
2436
3623
  };
2437
3624
  const ElementCallout = (props) => {
2438
3625
  const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
3626
+ const viewId = useViewId();
2439
3627
  const { element, target, visualRef, hozOffset, alignment = 'left' } = props;
2440
3628
  const calloutRef = useRef(null);
2441
3629
  const [position, setPosition] = useState(DEFAULT_POSITION);
@@ -2468,15 +3656,15 @@ const ElementCallout = (props) => {
2468
3656
  position: 'fixed',
2469
3657
  top: position.top,
2470
3658
  left: position.left,
2471
- zIndex: 2147483647,
3659
+ zIndex: Z_INDEX.CALLOUT,
2472
3660
  }, "aria-live": "assertive", role: "tooltip", children: CompElementCallout && jsx(CompElementCallout, { elementHash: element.hash }) }));
2473
- return createPortal(calloutContent, document.getElementById('gx-hm-viz-container'));
3661
+ return createPortal(calloutContent, document.getElementById(`gx-hm-viz-container-${viewId}`));
2474
3662
  };
2475
3663
 
2476
3664
  const ElementMissing = ({ show = true }) => {
3665
+ const { widthScale } = useHeatmapViz();
2477
3666
  if (!show)
2478
3667
  return null;
2479
- const widthScale = useHeatmapVizStore((state) => state.scale);
2480
3668
  return (jsx("div", { className: "missingElement", style: {
2481
3669
  position: 'fixed',
2482
3670
  top: '50%',
@@ -2494,18 +3682,9 @@ const ElementMissing = ({ show = true }) => {
2494
3682
  }, "aria-live": "assertive", children: "Element not visible on current screen" }));
2495
3683
  };
2496
3684
 
2497
- const TARGET_ID_BY_TYPE = {
2498
- hovered: {
2499
- default: HOVERED_ELEMENT_ID,
2500
- secondary: SECONDARY_HOVERED_ELEMENT_ID,
2501
- },
2502
- clicked: {
2503
- default: CLICKED_ELEMENT_ID,
2504
- secondary: SECONDARY_CLICKED_ELEMENT_ID,
2505
- },
2506
- };
2507
- const ElementOverlay = ({ type, element, onClick, isSecondary }) => {
2508
- const widthScale = useHeatmapVizStore((state) => state.scale);
3685
+ const ElementOverlay = ({ type, element, onClick, isSecondary, elementId, }) => {
3686
+ // useRenderCount('ElementOverlay');
3687
+ const { widthScale } = useHeatmapViz();
2509
3688
  if (!element || (element.width === 0 && element.height === 0))
2510
3689
  return null;
2511
3690
  // Iframe has border, so we need to add it to the top position
@@ -2513,8 +3692,7 @@ const ElementOverlay = ({ type, element, onClick, isSecondary }) => {
2513
3692
  const left = element.left + HEATMAP_CONFIG['borderWidthIframe'];
2514
3693
  const width = element.width;
2515
3694
  const height = element.height;
2516
- const targetId = TARGET_ID_BY_TYPE[type][isSecondary ? 'secondary' : 'default'];
2517
- return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: "heatmapElement", id: targetId, style: {
3695
+ return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: "heatmapElement", id: elementId, style: {
2518
3696
  top,
2519
3697
  left,
2520
3698
  width,
@@ -2528,14 +3706,17 @@ const ELEMENT_CALLOUT = {
2528
3706
  alignment: 'left',
2529
3707
  };
2530
3708
  const HeatmapElements = (props) => {
2531
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2532
- const { iframeRef, wrapperRef, visualRef, visualizer, iframeDimensions, isElementSidebarOpen, isVisible = true, areDefaultRanksHidden, isSecondary, } = props;
3709
+ const viewId = useViewId();
3710
+ const { iframeHeight } = useHeatmapViz();
3711
+ const clickedElementId = getClickedElementId(viewId, props.isSecondary);
3712
+ const hoveredElementId = getHoveredElementId(viewId, props.isSecondary);
3713
+ const { iframeRef, wrapperRef, visualRef, visualizer, iframeDimensions, isVisible = true, areDefaultRanksHidden, isSecondary, } = props;
2533
3714
  const getRect = useHeatmapElementPosition({
2534
3715
  iframeRef,
2535
3716
  wrapperRef,
2536
3717
  visualizer,
2537
3718
  });
2538
- const { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout } = useClickedElement({
3719
+ const { clickedElement, showMissingElement, shouldShowCallout } = useClickedElement({
2539
3720
  visualRef,
2540
3721
  getRect,
2541
3722
  });
@@ -2543,53 +3724,29 @@ const HeatmapElements = (props) => {
2543
3724
  iframeRef,
2544
3725
  getRect,
2545
3726
  });
2546
- useElementCalloutVisible({
2547
- visualRef,
2548
- getRect,
2549
- });
2550
- const resetAll = () => {
2551
- // setShouldShowCallout(false);
2552
- };
2553
- useHeatmapEffects({
2554
- isVisible,
2555
- isElementSidebarOpen,
2556
- setShouldShowCallout,
2557
- resetAll,
2558
- });
3727
+ useElementCalloutVisible({ visualRef, getRect });
3728
+ useHeatmapEffects({ isVisible });
3729
+ useRenderCount('HeatmapElements');
2559
3730
  if (!isVisible)
2560
3731
  return null;
2561
- return (jsxs("div", { onMouseMove: (event) => {
2562
- handleMouseMove(event);
2563
- // handleMouseMove2(event as any);
2564
- }, onMouseLeave: handleMouseLeave, className: "gx-hm-elements", style: { ...iframeDimensions, height: `${iframeHeight}px` }, children: [jsx(ElementMissing, { show: showMissingElement }), jsx(DefaultRankBadges, { getRect: getRect, hidden: areDefaultRanksHidden }), jsx(ElementOverlay, { type: "clicked", element: clickedElement, isSecondary: isSecondary }), jsx(ElementOverlay, { type: "hovered", element: hoveredElement, isSecondary: isSecondary, onClick: handleClick }), hoveredElement?.hash !== clickedElement?.hash && hoveredElement && (jsx(ElementCallout, { element: hoveredElement, target: `#${HOVERED_ELEMENT_ID}`, visualRef: visualRef, ...ELEMENT_CALLOUT })), shouldShowCallout && clickedElement && (jsx(ElementCallout, { element: clickedElement, target: `#${CLICKED_ELEMENT_ID}`, visualRef: visualRef, ...ELEMENT_CALLOUT }))] }));
3732
+ return (jsxs("div", { onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, className: "gx-hm-elements", style: { ...iframeDimensions, height: `${iframeHeight}px` }, children: [jsx(ElementMissing, { show: showMissingElement }), jsx(DefaultRankBadges, { getRect: getRect, hidden: areDefaultRanksHidden }), jsx(ElementOverlay, { type: "clicked", element: clickedElement, isSecondary: isSecondary, elementId: clickedElementId }), jsx(ElementOverlay, { type: "hovered", element: hoveredElement, isSecondary: isSecondary, onClick: handleClick, elementId: hoveredElementId }), hoveredElement?.hash !== clickedElement?.hash && hoveredElement && (jsx(ElementCallout, { element: hoveredElement, target: `#${hoveredElementId}`, visualRef: visualRef, ...ELEMENT_CALLOUT })), shouldShowCallout && clickedElement && (jsx(ElementCallout, { element: clickedElement, target: `#${clickedElementId}`, visualRef: visualRef, ...ELEMENT_CALLOUT }))] }));
2565
3733
  };
2566
3734
 
2567
3735
  const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
2568
- const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
2569
3736
  const contentWidth = useHeatmapConfigStore((state) => state.width);
2570
- const vizRef = useHeatmapSingleStore((state) => state.vizRef);
3737
+ const { dataInfo } = useHeatmapData();
3738
+ const { vizRef } = useHeatmapViz();
2571
3739
  const visualizer = {
2572
3740
  get: (hash) => {
2573
- if (vizRef) {
2574
- return vizRef.get(hash);
2575
- }
2576
- const doc = iframeRef.current?.contentDocument;
2577
- if (!doc)
3741
+ if (!vizRef)
2578
3742
  return null;
2579
- const elmHashAlpha = doc.querySelector(`[data-clarity-hashalpha="${hash}"]`);
2580
- if (elmHashAlpha) {
2581
- return elmHashAlpha;
2582
- }
2583
- const elmHash = doc.querySelector(`[data-clarity-hash="${hash}"]`);
2584
- if (elmHash) {
2585
- return elmHash;
2586
- }
2587
- return null;
3743
+ return vizRef.get(hash);
2588
3744
  },
2589
3745
  };
3746
+ // useRenderCount('VizElements');
2590
3747
  if (!iframeRef.current)
2591
3748
  return null;
2592
- return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo: heatmapInfo, isVisible: true, iframeDimensions: {
3749
+ return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo: dataInfo, isVisible: true, iframeDimensions: {
2593
3750
  width: contentWidth,
2594
3751
  position: 'absolute',
2595
3752
  top: 0,
@@ -2599,8 +3756,9 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
2599
3756
  };
2600
3757
 
2601
3758
  const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
2602
- const averageFold = useHeatmapDataStore((state) => state.dataInfo?.averageFold || 50);
3759
+ const { dataInfo } = useHeatmapData();
2603
3760
  const { getZonePosition } = useZonePositions();
3761
+ const averageFold = dataInfo?.averageFold || 50;
2604
3762
  const position = getZonePosition({
2605
3763
  startY: averageFold,
2606
3764
  endY: averageFold,
@@ -2635,8 +3793,8 @@ const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
2635
3793
  };
2636
3794
 
2637
3795
  const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
2638
- const scrollmap = useHeatmapDataStore((state) => state.scrollmap);
2639
3796
  const scrollType = useHeatmapConfigStore((state) => state.scrollType);
3797
+ const { scrollmap } = useHeatmapData();
2640
3798
  const { getZonePosition } = useZonePositions();
2641
3799
  if (!scrollmap || scrollmap.length === 0)
2642
3800
  return null;
@@ -2710,8 +3868,8 @@ const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
2710
3868
  };
2711
3869
 
2712
3870
  const ScrollMapMinimap = ({ zones, maxUsers }) => {
2713
- const showMinimap = useHeatmapVizScrollmapStore((state) => state.showMinimap);
2714
3871
  const scrollType = useHeatmapConfigStore((state) => state.scrollType);
3872
+ const { showMinimap } = useHeatmapVizScrollmap();
2715
3873
  const isScrollType = [IScrollType.Attention].includes(scrollType);
2716
3874
  if (!showMinimap || !isScrollType)
2717
3875
  return null;
@@ -2816,7 +3974,7 @@ const MetricsTooltipContent = ({ zone }) => {
2816
3974
  const MetricRow = ({ label, value }) => (jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [jsxs("span", { style: { color: '#605E5C' }, children: [label, ":"] }), jsx("span", { style: { fontWeight: 600 }, children: value })] }));
2817
3975
 
2818
3976
  const HoverZones = ({ iframeRef, wrapperRef, position, currentScrollPercent, }) => {
2819
- const scrollmap = useHeatmapDataStore((state) => state.scrollmap);
3977
+ const { scrollmap } = useHeatmapData();
2820
3978
  // const hoveredZone = useHeatmapVizScrollmapStore((state) => state.hoveredZone);
2821
3979
  // const setHoveredZone = useHeatmapVizScrollmapStore((state) => state.setHoveredZone);
2822
3980
  const { zones, isReady, maxUsers } = useScrollmapZones({
@@ -2834,8 +3992,7 @@ const ScrollMapOverlay = ({ wrapperRef, iframeRef }) => {
2834
3992
  const overlayRef = useRef(null);
2835
3993
  const [position, setPosition] = useState();
2836
3994
  const [currentScrollPercent, setCurrentScrollPercent] = useState(0);
2837
- const widthScale = useHeatmapVizStore((state) => state.scale);
2838
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
3995
+ const { widthScale, iframeHeight } = useHeatmapViz();
2839
3996
  const handleMouseMove = (event) => {
2840
3997
  if (!iframeRef.current || !wrapperRef.current)
2841
3998
  return;
@@ -2870,8 +4027,9 @@ const ScrollMapOverlay = ({ wrapperRef, iframeRef }) => {
2870
4027
 
2871
4028
  const SCROLL_TYPES = [IHeatmapType.Scroll];
2872
4029
  const VizScrollMap = ({ iframeRef, wrapperRef }) => {
2873
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2874
4030
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
4031
+ const { iframeHeight } = useHeatmapViz();
4032
+ // useRenderCount('VizScrollMap');
2875
4033
  const isHeatmapScroll = SCROLL_TYPES.includes(heatmapType);
2876
4034
  if (!iframeHeight || !isHeatmapScroll)
2877
4035
  return null;
@@ -2887,10 +4045,8 @@ const VizScrollMap = ({ iframeRef, wrapperRef }) => {
2887
4045
 
2888
4046
  const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHeight, onScroll, }) => {
2889
4047
  const contentWidth = useHeatmapConfigStore((state) => state.width);
2890
- const widthScale = useHeatmapVizStore((state) => state.scale);
2891
- const contentHeight = scaledHeight > 0
2892
- ? `${scaledHeight + HEATMAP_CONFIG['heightToolbar'] + HEATMAP_CONFIG['padding'] * 2}px`
2893
- : 'auto';
4048
+ const { widthScale } = useHeatmapViz();
4049
+ const contentHeight = calcContentHeight();
2894
4050
  return (jsx("div", { ref: visualRef, className: "gx-hm-visual", onScroll: onScroll, style: {
2895
4051
  overflow: 'hidden scroll',
2896
4052
  display: 'flex',
@@ -2915,29 +4071,34 @@ const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHe
2915
4071
  transformOrigin: 'top center',
2916
4072
  paddingBlockEnd: '0',
2917
4073
  }, children: children }) }) }));
4074
+ function calcContentHeight() {
4075
+ return scaledHeight > 0
4076
+ ? `${scaledHeight + HEATMAP_CONFIG['heightToolbar'] + HEATMAP_CONFIG['padding'] * 2}px`
4077
+ : 'auto';
4078
+ }
2918
4079
  };
2919
4080
 
2920
4081
  const VizDomRenderer = ({ mode = 'heatmap' }) => {
2921
- const width = useHeatmapConfigStore((state) => state.width);
4082
+ const contentWidth = useHeatmapConfigStore((state) => state.width || 0);
2922
4083
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
2923
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2924
- const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
2925
- const setSelectedElement = useHeatmapInteractionStore((state) => state.setSelectedElement);
2926
4084
  const wrapperRef = useRef(null);
2927
4085
  const visualRef = useRef(null);
4086
+ const { setSelectedElement } = useHeatmapInteraction();
4087
+ const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
2928
4088
  const { iframeRef } = useHeatmapVizRender(mode);
2929
4089
  const { scaledHeight, handleScroll } = useHeatmapScale({
2930
4090
  wrapperRef,
2931
4091
  iframeRef,
2932
4092
  visualRef,
2933
4093
  iframeHeight,
4094
+ isRenderViz,
2934
4095
  });
2935
- const contentWidth = width ?? 0;
4096
+ useHeatmapCanvas();
4097
+ useRenderCount('VizDomRenderer');
2936
4098
  const onScroll = (e) => {
2937
4099
  const scrollTop = e.currentTarget.scrollTop;
2938
4100
  handleScroll(scrollTop);
2939
4101
  };
2940
- useHeatmapCanvas({ iframeRef: iframeRef });
2941
4102
  const cleanUp = () => {
2942
4103
  setIframeHeight(0);
2943
4104
  setSelectedElement(null);
@@ -2953,26 +4114,20 @@ const VizLoading = () => {
2953
4114
  };
2954
4115
 
2955
4116
  const VizDomHeatmap = () => {
2956
- const controls = useHeatmapControlStore((state) => state.controls);
2957
- const isRendering = useHeatmapDataStore((state) => state.isRendering);
2958
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2959
- const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
2960
- const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
4117
+ const { iframeHeight, setIframeHeight } = useHeatmapViz();
4118
+ useRenderCount('VizDomHeatmap');
2961
4119
  useEffect(() => {
2962
4120
  return () => {
2963
- setVizRef(null);
4121
+ console.log('🚀 🐥 ~ useEffect ~ return:');
2964
4122
  setIframeHeight(0);
2965
4123
  };
2966
4124
  }, []);
2967
- if (isRendering)
2968
- return controls.VizLoading ?? null;
2969
- return (jsxs(VizContainer, { children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
4125
+ return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
2970
4126
  };
2971
4127
 
2972
4128
  const VizLiveRenderer = () => {
2973
4129
  const contentWidth = useHeatmapConfigStore((state) => state.width);
2974
- const iframeHeight = useHeatmapLiveStore((state) => state.iframeHeight);
2975
- const setIframeHeight = useHeatmapLiveStore((state) => state.setIframeHeight);
4130
+ const { isRenderViz, iframeHeight, setIframeHeight } = useHeatmapViz();
2976
4131
  const visualRef = useRef(null);
2977
4132
  const wrapperRef = useRef(null);
2978
4133
  const { iframeRef } = useVizLiveRender();
@@ -2981,7 +4136,7 @@ const VizLiveRenderer = () => {
2981
4136
  iframeRef,
2982
4137
  visualRef,
2983
4138
  iframeHeight,
2984
- setIframeHeight,
4139
+ isRenderViz,
2985
4140
  });
2986
4141
  const onScroll = (e) => {
2987
4142
  const scrollTop = e.currentTarget.scrollTop;
@@ -2993,62 +4148,38 @@ const VizLiveRenderer = () => {
2993
4148
  };
2994
4149
 
2995
4150
  const VizLiveHeatmap = () => {
2996
- const controls = useHeatmapControlStore((state) => state.controls);
2997
- const isRendering = useHeatmapDataStore((state) => state.isRendering);
2998
- const iframeHeight = useHeatmapLiveStore((state) => state.iframeHeight);
2999
- const wrapperHeight = useHeatmapLiveStore((state) => state.wrapperHeight);
3000
- const setWrapperHeight = useHeatmapLiveStore((state) => state.setWrapperHeight);
4151
+ const { iframeHeight, wrapperHeight } = useHeatmapViz();
3001
4152
  const reset = useHeatmapLiveStore((state) => state.reset);
3002
4153
  useEffect(() => {
3003
4154
  return () => {
3004
4155
  reset();
3005
4156
  };
3006
- }, [reset]);
3007
- if (isRendering)
3008
- return controls.VizLoading ?? null;
3009
- return (jsxs(VizContainer, { setWrapperHeight: setWrapperHeight, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
4157
+ }, []);
4158
+ return (jsxs(VizContainer, { isActive: true, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
3010
4159
  };
3011
4160
 
3012
4161
  const ContentVizByMode = () => {
3013
4162
  const mode = useHeatmapConfigStore((state) => state.mode);
4163
+ const isRendering = useHeatmapConfigStore((state) => state.isRendering);
4164
+ const controls = useHeatmapControlStore((state) => state.controls);
4165
+ if (isRendering)
4166
+ return controls.VizLoading ?? null;
3014
4167
  switch (mode) {
3015
4168
  case 'live':
3016
4169
  return jsx(VizLiveHeatmap, {});
4170
+ // case 'compare':
4171
+ // return <VizCompareHeatmap />;
3017
4172
  default:
3018
4173
  return jsx(VizDomHeatmap, {});
3019
4174
  }
3020
4175
  };
3021
4176
 
3022
- const LeftSidebar = () => {
3023
- const controls = useHeatmapControlStore((state) => state.controls);
3024
- const isHideSidebar = useHeatmapInteractionStore((state) => state.state.hideSidebar);
3025
- const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3026
- if (isHideSidebar) {
3027
- return null;
3028
- }
3029
- return (jsx("div", { className: "gx-hm-sidebar", style: {
3030
- height: '100%',
3031
- display: 'flex',
3032
- zIndex: 1,
3033
- ...(isHideSidebar
3034
- ? {
3035
- width: '0',
3036
- transform: 'translateX(-100%)',
3037
- visibility: 'hidden',
3038
- }
3039
- : { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
3040
- }, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
3041
- height: '100%',
3042
- width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
3043
- borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3044
- }, children: controls.Sidebar ?? null }) }));
3045
- };
3046
-
3047
4177
  const WrapperPreview = () => {
3048
- return (jsxs(BoxStack, { id: "gx-hm-container", flexDirection: "row", overflow: "hidden", flex: "1", position: "relative", children: [jsx(LeftSidebar, {}), jsxs(BoxStack, { flexDirection: "column", flex: "1", children: [jsx(ContentMetricBar, {}), jsx(ContentVizByMode, {}), jsx(ContentToolbar, {})] })] }));
4178
+ 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, {})] })] }));
3049
4179
  };
3050
4180
 
3051
4181
  const WrapperLayout = () => {
4182
+ useRenderCount('WrapperLayout');
3052
4183
  return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(WrapperPreview, {})] }));
3053
4184
  };
3054
4185
 
@@ -3057,6 +4188,19 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
3057
4188
  useRegisterData(data, dataInfo);
3058
4189
  useRegisterHeatmap({ clickmap, scrollmap });
3059
4190
  useRegisterConfig();
4191
+ performanceLogger.configure({
4192
+ enabled: true,
4193
+ logToConsole: false,
4194
+ logLevel: 'normal', // 'verbose' | 'normal' | 'minimal'
4195
+ 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
4199
+ },
4200
+ externalLogger: (metric) => {
4201
+ if (metric.name === 'VizDomRenderer') ;
4202
+ },
4203
+ });
3060
4204
  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: {
3061
4205
  minHeight: '100%',
3062
4206
  display: 'flex',
@@ -3070,4 +4214,4 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
3070
4214
  }
3071
4215
  };
3072
4216
 
3073
- export { GraphView, HeatmapLayout, IClickType, IHeatmapType, IScrollType, useHeatmapConfigStore, useHeatmapDataStore, useHeatmapInteractionStore, useHeatmapLiveStore, useHeatmapVizStore };
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 };