@gemx-dev/heatmap-react 3.5.49 → 3.5.51

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 (445) hide show
  1. package/dist/esm/components/Layout/ContentMetricBar.d.ts.map +1 -1
  2. package/dist/esm/components/Layout/ContentVizByMode.d.ts.map +1 -1
  3. package/dist/esm/components/Layout/HeatmapLayout.d.ts +2 -2
  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/VizDom/VizContainer.d.ts.map +1 -1
  14. package/dist/esm/components/VizDom/VizDomHeatmap.d.ts.map +1 -1
  15. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  16. package/dist/esm/components/VizDom/WrapperVisual.d.ts.map +1 -1
  17. package/dist/esm/components/VizElement/DefaultRankBadges.d.ts.map +1 -1
  18. package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
  19. package/dist/esm/components/VizElement/ElementOverlay.d.ts +1 -0
  20. package/dist/esm/components/VizElement/ElementOverlay.d.ts.map +1 -1
  21. package/dist/esm/components/VizElement/HeatmapElements.d.ts +0 -1
  22. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
  23. package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
  24. package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  25. package/dist/esm/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  26. package/dist/esm/components/VizScrollmap/HoverZones.d.ts +1 -1
  27. package/dist/esm/components/VizScrollmap/HoverZones.d.ts.map +1 -1
  28. package/dist/esm/components/VizScrollmap/ScrollMapMinimap.d.ts +1 -1
  29. package/dist/esm/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
  30. package/dist/esm/components/VizScrollmap/ScrollMapOverlay.d.ts.map +1 -1
  31. package/dist/esm/components/VizScrollmap/ScrollZoneHoverArea.d.ts +1 -2
  32. package/dist/esm/components/VizScrollmap/ScrollZoneHoverArea.d.ts.map +1 -1
  33. package/dist/esm/components/VizScrollmap/ScrollZoneTooltip.d.ts +1 -1
  34. package/dist/esm/components/VizScrollmap/ScrollZoneTooltip.d.ts.map +1 -1
  35. package/dist/esm/components/VizScrollmap/VizScrollMap.d.ts.map +1 -1
  36. package/dist/esm/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts +1 -2
  37. package/dist/esm/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts.map +1 -1
  38. package/dist/esm/components/VizScrollmapV2/useScrollmapOverlay.d.ts +1 -1
  39. package/dist/esm/components/VizScrollmapV2/useScrollmapOverlay.d.ts.map +1 -1
  40. package/dist/esm/configs/index.d.ts +2 -0
  41. package/dist/esm/configs/index.d.ts.map +1 -1
  42. package/dist/esm/configs/viewId.d.ts +21 -0
  43. package/dist/esm/configs/viewId.d.ts.map +1 -0
  44. package/dist/esm/configs/z-index.d.ts +8 -0
  45. package/dist/esm/configs/z-index.d.ts.map +1 -0
  46. package/dist/esm/constants/index.d.ts +13 -4
  47. package/dist/esm/constants/index.d.ts.map +1 -1
  48. package/dist/esm/contexts/CompareViewContext.d.ts +1 -1
  49. package/dist/esm/contexts/CompareViewContext.d.ts.map +1 -1
  50. package/dist/esm/helpers/iframe-helper/fixer.d.ts +1 -1
  51. package/dist/esm/helpers/iframe-helper/fixer.d.ts.map +1 -1
  52. package/dist/esm/helpers/iframe-helper/init.d.ts +1 -1
  53. package/dist/esm/helpers/iframe-helper/init.d.ts.map +1 -1
  54. package/dist/esm/helpers/iframe-helper/style-replacer.d.ts.map +1 -1
  55. package/dist/esm/helpers/iframe.d.ts +1 -1
  56. package/dist/esm/helpers/iframe.d.ts.map +1 -1
  57. package/dist/esm/helpers/index.d.ts +2 -3
  58. package/dist/esm/helpers/index.d.ts.map +1 -1
  59. package/dist/esm/helpers/viz-canvas/area-clustering.d.ts +1 -1
  60. package/dist/esm/helpers/viz-canvas/area-clustering.d.ts.map +1 -1
  61. package/dist/esm/helpers/viz-canvas/area-overlay-manager-v2.d.ts +2 -2
  62. package/dist/esm/helpers/viz-canvas/area-overlay-manager-v2.d.ts.map +1 -1
  63. package/dist/esm/helpers/viz-canvas/area-overlay-manager.d.ts +1 -1
  64. package/dist/esm/helpers/viz-canvas/area-overlay-manager.d.ts.map +1 -1
  65. package/dist/esm/helpers/viz-canvas/hierarchical-area-clustering.d.ts +1 -1
  66. package/dist/esm/helpers/viz-canvas/hierarchical-area-clustering.d.ts.map +1 -1
  67. package/dist/esm/helpers/viz-elm-callout/constants.d.ts +4 -0
  68. package/dist/esm/helpers/viz-elm-callout/constants.d.ts.map +1 -0
  69. package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts +4 -0
  70. package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts.map +1 -0
  71. package/dist/esm/helpers/{elm-getter.d.ts → viz-elm-callout/getter.d.ts} +2 -2
  72. package/dist/esm/helpers/viz-elm-callout/getter.d.ts.map +1 -0
  73. package/dist/esm/helpers/viz-elm-callout/index.d.ts +3 -0
  74. package/dist/esm/helpers/viz-elm-callout/index.d.ts.map +1 -0
  75. package/dist/esm/helpers/viz-elm-callout/position-calculator.d.ts +14 -0
  76. package/dist/esm/helpers/viz-elm-callout/position-calculator.d.ts.map +1 -0
  77. package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts +6 -0
  78. package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -0
  79. package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts +10 -0
  80. package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts.map +1 -0
  81. package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts +4 -0
  82. package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts.map +1 -0
  83. package/dist/esm/helpers/viz-elm-callout/types.d.ts +17 -0
  84. package/dist/esm/helpers/viz-elm-callout/types.d.ts.map +1 -0
  85. package/dist/esm/helpers/{elm-callout.d.ts → viz-elm-callout/viz-elm.d.ts} +4 -3
  86. package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -0
  87. package/dist/esm/helpers/{viz-elements.d.ts → viz-elm.d.ts} +1 -1
  88. package/dist/esm/helpers/viz-elm.d.ts.map +1 -0
  89. package/dist/esm/hooks/common/index.d.ts +2 -0
  90. package/dist/esm/hooks/common/index.d.ts.map +1 -0
  91. package/dist/esm/hooks/common/useDebounceCallback.d.ts +17 -0
  92. package/dist/esm/hooks/common/useDebounceCallback.d.ts.map +1 -0
  93. package/dist/esm/hooks/index.d.ts +3 -5
  94. package/dist/esm/hooks/index.d.ts.map +1 -1
  95. package/dist/esm/hooks/register/useRegisterControl.d.ts +1 -1
  96. package/dist/esm/hooks/register/useRegisterControl.d.ts.map +1 -1
  97. package/dist/esm/hooks/register/useRegisterData.d.ts +1 -1
  98. package/dist/esm/hooks/register/useRegisterData.d.ts.map +1 -1
  99. package/dist/esm/hooks/register/useRegisterHeatmap.d.ts +1 -1
  100. package/dist/esm/hooks/register/useRegisterHeatmap.d.ts.map +1 -1
  101. package/dist/esm/hooks/view-context/index.d.ts +7 -0
  102. package/dist/esm/hooks/view-context/index.d.ts.map +1 -0
  103. package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts +33 -0
  104. package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -0
  105. package/dist/esm/hooks/{useHeatmapData.d.ts → view-context/useHeatmapData.d.ts} +1 -1
  106. package/dist/esm/hooks/view-context/useHeatmapData.d.ts.map +1 -0
  107. package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts +16 -0
  108. package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts.map +1 -0
  109. package/dist/{umd/hooks → esm/hooks/view-context}/useHeatmapViz.d.ts +3 -1
  110. package/dist/esm/hooks/view-context/useHeatmapViz.d.ts.map +1 -0
  111. package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts +13 -0
  112. package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +1 -0
  113. package/dist/esm/hooks/{useViewId.d.ts → view-context/useViewId.d.ts} +1 -1
  114. package/dist/esm/hooks/view-context/useViewId.d.ts.map +1 -0
  115. package/dist/esm/hooks/viz-area/useAreaHeatmap.d.ts +1 -1
  116. package/dist/esm/hooks/viz-area/useAreaHeatmap.d.ts.map +1 -1
  117. package/dist/esm/hooks/viz-area/useAreaHeatmapManager.d.ts.map +1 -1
  118. package/dist/esm/hooks/viz-canvas/useAreamap.d.ts +1 -1
  119. package/dist/esm/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
  120. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  121. package/dist/esm/hooks/viz-canvas/useHeatmapCanvas.d.ts +1 -3
  122. package/dist/esm/hooks/viz-canvas/useHeatmapCanvas.d.ts.map +1 -1
  123. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
  124. package/dist/esm/hooks/viz-elm/index.d.ts.map +1 -0
  125. package/dist/esm/hooks/{viz-elements → viz-elm}/useClickedElement.d.ts +0 -1
  126. package/dist/esm/hooks/viz-elm/useClickedElement.d.ts.map +1 -0
  127. package/dist/esm/hooks/viz-elm/useElementCalloutVisible.d.ts.map +1 -0
  128. package/dist/esm/hooks/viz-elm/useHeatmapEffects.d.ts +4 -0
  129. package/dist/esm/hooks/viz-elm/useHeatmapEffects.d.ts.map +1 -0
  130. package/dist/esm/hooks/viz-elm/useHeatmapElementPosition.d.ts.map +1 -0
  131. package/dist/{umd/hooks/viz-elements → esm/hooks/viz-elm}/useHeatmapMouseHandler.d.ts +1 -1
  132. package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts.map +1 -1
  133. package/dist/esm/hooks/viz-elm/useHoveredElement.d.ts.map +1 -0
  134. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
  135. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  136. package/dist/esm/hooks/viz-render/useReplayRender.d.ts.map +1 -1
  137. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +0 -1
  138. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  139. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts +0 -1
  140. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -1
  141. package/dist/esm/hooks/viz-scale/useWrapperRefHeight.d.ts.map +1 -1
  142. package/dist/esm/hooks/viz-scrollmap/useScrollmapZones.d.ts +1 -1
  143. package/dist/esm/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +1 -1
  144. package/dist/esm/hooks/viz-scrollmap/useZonePositions.d.ts +2 -2
  145. package/dist/esm/hooks/viz-scrollmap/useZonePositions.d.ts.map +1 -1
  146. package/dist/esm/index.d.ts +3 -2
  147. package/dist/esm/index.d.ts.map +1 -1
  148. package/dist/esm/index.js +1280 -635
  149. package/dist/esm/index.mjs +1280 -635
  150. package/dist/esm/performance/hooks.d.ts +28 -0
  151. package/dist/esm/performance/hooks.d.ts.map +1 -0
  152. package/dist/esm/performance/index.d.ts +7 -0
  153. package/dist/esm/performance/index.d.ts.map +1 -0
  154. package/dist/esm/performance/performance-logger.d.ts +22 -0
  155. package/dist/esm/performance/performance-logger.d.ts.map +1 -0
  156. package/dist/esm/performance/storeTracker.d.ts +10 -0
  157. package/dist/esm/performance/storeTracker.d.ts.map +1 -0
  158. package/dist/esm/performance/types.d.ts +79 -0
  159. package/dist/esm/performance/types.d.ts.map +1 -0
  160. package/dist/esm/performance/utils.d.ts +24 -0
  161. package/dist/esm/performance/utils.d.ts.map +1 -0
  162. package/dist/esm/performance/withPerformanceTracking.d.ts +14 -0
  163. package/dist/esm/performance/withPerformanceTracking.d.ts.map +1 -0
  164. package/dist/esm/stores/comp.d.ts +1 -1
  165. package/dist/esm/stores/comp.d.ts.map +1 -1
  166. package/dist/esm/stores/data.d.ts +10 -2
  167. package/dist/esm/stores/data.d.ts.map +1 -1
  168. package/dist/esm/stores/interaction.d.ts +21 -10
  169. package/dist/esm/stores/interaction.d.ts.map +1 -1
  170. package/dist/esm/stores/mode-compare.d.ts +1 -1
  171. package/dist/esm/stores/mode-compare.d.ts.map +1 -1
  172. package/dist/esm/stores/mode-live.d.ts +1 -1
  173. package/dist/esm/stores/mode-live.d.ts.map +1 -1
  174. package/dist/esm/stores/mode-single.d.ts +12 -2
  175. package/dist/esm/stores/mode-single.d.ts.map +1 -1
  176. package/dist/esm/stores/viz-scrollmap.d.ts +19 -8
  177. package/dist/esm/stores/viz-scrollmap.d.ts.map +1 -1
  178. package/dist/esm/stores/viz.d.ts +9 -1
  179. package/dist/esm/stores/viz.d.ts.map +1 -1
  180. package/dist/esm/types/control.d.ts +9 -1
  181. package/dist/esm/types/control.d.ts.map +1 -1
  182. package/dist/esm/types/heatmap-info.d.ts +2 -2
  183. package/dist/esm/types/heatmap-info.d.ts.map +1 -1
  184. package/dist/esm/types/index.d.ts +3 -2
  185. package/dist/esm/types/index.d.ts.map +1 -1
  186. package/dist/{umd/types/elm-callout.d.ts → esm/types/viz-elm-callout.d.ts} +1 -1
  187. package/dist/esm/types/viz-elm-callout.d.ts.map +1 -0
  188. package/dist/esm/types/{viz-element.d.ts → viz-elm.d.ts} +1 -1
  189. package/dist/esm/types/viz-elm.d.ts.map +1 -0
  190. package/dist/esm/utils/sort.d.ts +1 -1
  191. package/dist/esm/utils/sort.d.ts.map +1 -1
  192. package/dist/style.css +7 -1
  193. package/dist/umd/components/Layout/ContentMetricBar.d.ts.map +1 -1
  194. package/dist/umd/components/Layout/ContentVizByMode.d.ts.map +1 -1
  195. package/dist/umd/components/Layout/HeatmapLayout.d.ts +2 -2
  196. package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
  197. package/dist/umd/components/Layout/Sidebar/ContentSidebar.d.ts +2 -0
  198. package/dist/umd/components/Layout/Sidebar/ContentSidebar.d.ts.map +1 -0
  199. package/dist/umd/components/Layout/Sidebar/PopoverSidebar.d.ts +2 -0
  200. package/dist/umd/components/Layout/Sidebar/PopoverSidebar.d.ts.map +1 -0
  201. package/dist/umd/components/Layout/Sidebar/index.d.ts +3 -0
  202. package/dist/umd/components/Layout/Sidebar/index.d.ts.map +1 -0
  203. package/dist/umd/components/Layout/WrapperLayout.d.ts.map +1 -1
  204. package/dist/umd/components/Layout/WrapperPreview.d.ts.map +1 -1
  205. package/dist/umd/components/VizDom/VizContainer.d.ts.map +1 -1
  206. package/dist/umd/components/VizDom/VizDomHeatmap.d.ts.map +1 -1
  207. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  208. package/dist/umd/components/VizDom/WrapperVisual.d.ts.map +1 -1
  209. package/dist/umd/components/VizElement/DefaultRankBadges.d.ts.map +1 -1
  210. package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
  211. package/dist/umd/components/VizElement/ElementOverlay.d.ts +1 -0
  212. package/dist/umd/components/VizElement/ElementOverlay.d.ts.map +1 -1
  213. package/dist/umd/components/VizElement/HeatmapElements.d.ts +0 -1
  214. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
  215. package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
  216. package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  217. package/dist/umd/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  218. package/dist/umd/components/VizScrollmap/HoverZones.d.ts +1 -1
  219. package/dist/umd/components/VizScrollmap/HoverZones.d.ts.map +1 -1
  220. package/dist/umd/components/VizScrollmap/ScrollMapMinimap.d.ts +1 -1
  221. package/dist/umd/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
  222. package/dist/umd/components/VizScrollmap/ScrollMapOverlay.d.ts.map +1 -1
  223. package/dist/umd/components/VizScrollmap/ScrollZoneHoverArea.d.ts +1 -2
  224. package/dist/umd/components/VizScrollmap/ScrollZoneHoverArea.d.ts.map +1 -1
  225. package/dist/umd/components/VizScrollmap/ScrollZoneTooltip.d.ts +1 -1
  226. package/dist/umd/components/VizScrollmap/ScrollZoneTooltip.d.ts.map +1 -1
  227. package/dist/umd/components/VizScrollmap/VizScrollMap.d.ts.map +1 -1
  228. package/dist/umd/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts +1 -2
  229. package/dist/umd/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts.map +1 -1
  230. package/dist/umd/components/VizScrollmapV2/useScrollmapOverlay.d.ts +1 -1
  231. package/dist/umd/components/VizScrollmapV2/useScrollmapOverlay.d.ts.map +1 -1
  232. package/dist/umd/configs/index.d.ts +2 -0
  233. package/dist/umd/configs/index.d.ts.map +1 -1
  234. package/dist/umd/configs/viewId.d.ts +21 -0
  235. package/dist/umd/configs/viewId.d.ts.map +1 -0
  236. package/dist/umd/configs/z-index.d.ts +8 -0
  237. package/dist/umd/configs/z-index.d.ts.map +1 -0
  238. package/dist/umd/constants/index.d.ts +13 -4
  239. package/dist/umd/constants/index.d.ts.map +1 -1
  240. package/dist/umd/contexts/CompareViewContext.d.ts +1 -1
  241. package/dist/umd/contexts/CompareViewContext.d.ts.map +1 -1
  242. package/dist/umd/helpers/iframe-helper/fixer.d.ts +1 -1
  243. package/dist/umd/helpers/iframe-helper/fixer.d.ts.map +1 -1
  244. package/dist/umd/helpers/iframe-helper/init.d.ts +1 -1
  245. package/dist/umd/helpers/iframe-helper/init.d.ts.map +1 -1
  246. package/dist/umd/helpers/iframe-helper/style-replacer.d.ts.map +1 -1
  247. package/dist/umd/helpers/iframe.d.ts +1 -1
  248. package/dist/umd/helpers/iframe.d.ts.map +1 -1
  249. package/dist/umd/helpers/index.d.ts +2 -3
  250. package/dist/umd/helpers/index.d.ts.map +1 -1
  251. package/dist/umd/helpers/viz-canvas/area-clustering.d.ts +1 -1
  252. package/dist/umd/helpers/viz-canvas/area-clustering.d.ts.map +1 -1
  253. package/dist/umd/helpers/viz-canvas/area-overlay-manager-v2.d.ts +2 -2
  254. package/dist/umd/helpers/viz-canvas/area-overlay-manager-v2.d.ts.map +1 -1
  255. package/dist/umd/helpers/viz-canvas/area-overlay-manager.d.ts +1 -1
  256. package/dist/umd/helpers/viz-canvas/area-overlay-manager.d.ts.map +1 -1
  257. package/dist/umd/helpers/viz-canvas/hierarchical-area-clustering.d.ts +1 -1
  258. package/dist/umd/helpers/viz-canvas/hierarchical-area-clustering.d.ts.map +1 -1
  259. package/dist/umd/helpers/viz-elm-callout/constants.d.ts +4 -0
  260. package/dist/umd/helpers/viz-elm-callout/constants.d.ts.map +1 -0
  261. package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts +4 -0
  262. package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts.map +1 -0
  263. package/dist/umd/helpers/{elm-getter.d.ts → viz-elm-callout/getter.d.ts} +2 -2
  264. package/dist/umd/helpers/viz-elm-callout/getter.d.ts.map +1 -0
  265. package/dist/umd/helpers/viz-elm-callout/index.d.ts +3 -0
  266. package/dist/umd/helpers/viz-elm-callout/index.d.ts.map +1 -0
  267. package/dist/umd/helpers/viz-elm-callout/position-calculator.d.ts +14 -0
  268. package/dist/umd/helpers/viz-elm-callout/position-calculator.d.ts.map +1 -0
  269. package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts +6 -0
  270. package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -0
  271. package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts +10 -0
  272. package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts.map +1 -0
  273. package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts +4 -0
  274. package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts.map +1 -0
  275. package/dist/umd/helpers/viz-elm-callout/types.d.ts +17 -0
  276. package/dist/umd/helpers/viz-elm-callout/types.d.ts.map +1 -0
  277. package/dist/umd/helpers/{elm-callout.d.ts → viz-elm-callout/viz-elm.d.ts} +4 -3
  278. package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -0
  279. package/dist/umd/helpers/{viz-elements.d.ts → viz-elm.d.ts} +1 -1
  280. package/dist/umd/helpers/viz-elm.d.ts.map +1 -0
  281. package/dist/umd/hooks/common/index.d.ts +2 -0
  282. package/dist/umd/hooks/common/index.d.ts.map +1 -0
  283. package/dist/umd/hooks/common/useDebounceCallback.d.ts +17 -0
  284. package/dist/umd/hooks/common/useDebounceCallback.d.ts.map +1 -0
  285. package/dist/umd/hooks/index.d.ts +3 -5
  286. package/dist/umd/hooks/index.d.ts.map +1 -1
  287. package/dist/umd/hooks/register/useRegisterControl.d.ts +1 -1
  288. package/dist/umd/hooks/register/useRegisterControl.d.ts.map +1 -1
  289. package/dist/umd/hooks/register/useRegisterData.d.ts +1 -1
  290. package/dist/umd/hooks/register/useRegisterData.d.ts.map +1 -1
  291. package/dist/umd/hooks/register/useRegisterHeatmap.d.ts +1 -1
  292. package/dist/umd/hooks/register/useRegisterHeatmap.d.ts.map +1 -1
  293. package/dist/umd/hooks/view-context/index.d.ts +7 -0
  294. package/dist/umd/hooks/view-context/index.d.ts.map +1 -0
  295. package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts +33 -0
  296. package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -0
  297. package/dist/umd/hooks/{useHeatmapData.d.ts → view-context/useHeatmapData.d.ts} +1 -1
  298. package/dist/umd/hooks/view-context/useHeatmapData.d.ts.map +1 -0
  299. package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts +16 -0
  300. package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts.map +1 -0
  301. package/dist/{esm/hooks → umd/hooks/view-context}/useHeatmapViz.d.ts +3 -1
  302. package/dist/umd/hooks/view-context/useHeatmapViz.d.ts.map +1 -0
  303. package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts +13 -0
  304. package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +1 -0
  305. package/dist/umd/hooks/{useViewId.d.ts → view-context/useViewId.d.ts} +1 -1
  306. package/dist/umd/hooks/view-context/useViewId.d.ts.map +1 -0
  307. package/dist/umd/hooks/viz-area/useAreaHeatmap.d.ts +1 -1
  308. package/dist/umd/hooks/viz-area/useAreaHeatmap.d.ts.map +1 -1
  309. package/dist/umd/hooks/viz-area/useAreaHeatmapManager.d.ts.map +1 -1
  310. package/dist/umd/hooks/viz-canvas/useAreamap.d.ts +1 -1
  311. package/dist/umd/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
  312. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  313. package/dist/umd/hooks/viz-canvas/useHeatmapCanvas.d.ts +1 -3
  314. package/dist/umd/hooks/viz-canvas/useHeatmapCanvas.d.ts.map +1 -1
  315. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
  316. package/dist/umd/hooks/viz-elm/index.d.ts.map +1 -0
  317. package/dist/umd/hooks/{viz-elements → viz-elm}/useClickedElement.d.ts +0 -1
  318. package/dist/umd/hooks/viz-elm/useClickedElement.d.ts.map +1 -0
  319. package/dist/umd/hooks/viz-elm/useElementCalloutVisible.d.ts.map +1 -0
  320. package/dist/umd/hooks/viz-elm/useHeatmapEffects.d.ts +4 -0
  321. package/dist/umd/hooks/viz-elm/useHeatmapEffects.d.ts.map +1 -0
  322. package/dist/umd/hooks/viz-elm/useHeatmapElementPosition.d.ts.map +1 -0
  323. package/dist/{esm/hooks/viz-elements → umd/hooks/viz-elm}/useHeatmapMouseHandler.d.ts +1 -1
  324. package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts.map +1 -1
  325. package/dist/umd/hooks/viz-elm/useHoveredElement.d.ts.map +1 -0
  326. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
  327. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  328. package/dist/umd/hooks/viz-render/useReplayRender.d.ts.map +1 -1
  329. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +0 -1
  330. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  331. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts +0 -1
  332. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -1
  333. package/dist/umd/hooks/viz-scale/useWrapperRefHeight.d.ts.map +1 -1
  334. package/dist/umd/hooks/viz-scrollmap/useScrollmapZones.d.ts +1 -1
  335. package/dist/umd/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +1 -1
  336. package/dist/umd/hooks/viz-scrollmap/useZonePositions.d.ts +2 -2
  337. package/dist/umd/hooks/viz-scrollmap/useZonePositions.d.ts.map +1 -1
  338. package/dist/umd/index.d.ts +3 -2
  339. package/dist/umd/index.d.ts.map +1 -1
  340. package/dist/umd/index.js +2 -2
  341. package/dist/umd/performance/hooks.d.ts +28 -0
  342. package/dist/umd/performance/hooks.d.ts.map +1 -0
  343. package/dist/umd/performance/index.d.ts +7 -0
  344. package/dist/umd/performance/index.d.ts.map +1 -0
  345. package/dist/umd/performance/performance-logger.d.ts +22 -0
  346. package/dist/umd/performance/performance-logger.d.ts.map +1 -0
  347. package/dist/umd/performance/storeTracker.d.ts +10 -0
  348. package/dist/umd/performance/storeTracker.d.ts.map +1 -0
  349. package/dist/umd/performance/types.d.ts +79 -0
  350. package/dist/umd/performance/types.d.ts.map +1 -0
  351. package/dist/umd/performance/utils.d.ts +24 -0
  352. package/dist/umd/performance/utils.d.ts.map +1 -0
  353. package/dist/umd/performance/withPerformanceTracking.d.ts +14 -0
  354. package/dist/umd/performance/withPerformanceTracking.d.ts.map +1 -0
  355. package/dist/umd/stores/comp.d.ts +1 -1
  356. package/dist/umd/stores/comp.d.ts.map +1 -1
  357. package/dist/umd/stores/data.d.ts +10 -2
  358. package/dist/umd/stores/data.d.ts.map +1 -1
  359. package/dist/umd/stores/interaction.d.ts +21 -10
  360. package/dist/umd/stores/interaction.d.ts.map +1 -1
  361. package/dist/umd/stores/mode-compare.d.ts +1 -1
  362. package/dist/umd/stores/mode-compare.d.ts.map +1 -1
  363. package/dist/umd/stores/mode-live.d.ts +1 -1
  364. package/dist/umd/stores/mode-live.d.ts.map +1 -1
  365. package/dist/umd/stores/mode-single.d.ts +12 -2
  366. package/dist/umd/stores/mode-single.d.ts.map +1 -1
  367. package/dist/umd/stores/viz-scrollmap.d.ts +19 -8
  368. package/dist/umd/stores/viz-scrollmap.d.ts.map +1 -1
  369. package/dist/umd/stores/viz.d.ts +9 -1
  370. package/dist/umd/stores/viz.d.ts.map +1 -1
  371. package/dist/umd/types/control.d.ts +9 -1
  372. package/dist/umd/types/control.d.ts.map +1 -1
  373. package/dist/umd/types/heatmap-info.d.ts +2 -2
  374. package/dist/umd/types/heatmap-info.d.ts.map +1 -1
  375. package/dist/umd/types/index.d.ts +3 -2
  376. package/dist/umd/types/index.d.ts.map +1 -1
  377. package/dist/{esm/types/elm-callout.d.ts → umd/types/viz-elm-callout.d.ts} +1 -1
  378. package/dist/umd/types/viz-elm-callout.d.ts.map +1 -0
  379. package/dist/umd/types/{viz-element.d.ts → viz-elm.d.ts} +1 -1
  380. package/dist/umd/types/viz-elm.d.ts.map +1 -0
  381. package/dist/umd/utils/sort.d.ts +1 -1
  382. package/dist/umd/utils/sort.d.ts.map +1 -1
  383. package/package.json +4 -1
  384. package/dist/esm/components/Layout/LeftSidebar.d.ts +0 -2
  385. package/dist/esm/components/Layout/LeftSidebar.d.ts.map +0 -1
  386. package/dist/esm/components/VizElement/HeatmapExample.d.ts +0 -2
  387. package/dist/esm/components/VizElement/HeatmapExample.d.ts.map +0 -1
  388. package/dist/esm/helpers/elm-callout.d.ts.map +0 -1
  389. package/dist/esm/helpers/elm-getter.d.ts.map +0 -1
  390. package/dist/esm/helpers/viz-elements.d.ts.map +0 -1
  391. package/dist/esm/hooks/compare/index.d.ts +0 -4
  392. package/dist/esm/hooks/compare/index.d.ts.map +0 -1
  393. package/dist/esm/hooks/compare/useCompareAwareConfig.d.ts +0 -21
  394. package/dist/esm/hooks/compare/useCompareAwareConfig.d.ts.map +0 -1
  395. package/dist/esm/hooks/compare/useCompareAwareData.d.ts +0 -28
  396. package/dist/esm/hooks/compare/useCompareAwareData.d.ts.map +0 -1
  397. package/dist/esm/hooks/compare/useCompareAwareViz.d.ts +0 -34
  398. package/dist/esm/hooks/compare/useCompareAwareViz.d.ts.map +0 -1
  399. package/dist/esm/hooks/useHeatmapData.d.ts.map +0 -1
  400. package/dist/esm/hooks/useHeatmapViz.d.ts.map +0 -1
  401. package/dist/esm/hooks/useViewId.d.ts.map +0 -1
  402. package/dist/esm/hooks/viz-elements/index.d.ts.map +0 -1
  403. package/dist/esm/hooks/viz-elements/useClickedElement.d.ts.map +0 -1
  404. package/dist/esm/hooks/viz-elements/useElementCalloutVisible.d.ts.map +0 -1
  405. package/dist/esm/hooks/viz-elements/useHeatmapEffects.d.ts +0 -7
  406. package/dist/esm/hooks/viz-elements/useHeatmapEffects.d.ts.map +0 -1
  407. package/dist/esm/hooks/viz-elements/useHeatmapElementPosition.d.ts.map +0 -1
  408. package/dist/esm/hooks/viz-elements/useHoveredElement.d.ts.map +0 -1
  409. package/dist/esm/types/elm-callout.d.ts.map +0 -1
  410. package/dist/esm/types/viz-element.d.ts.map +0 -1
  411. package/dist/umd/components/Layout/LeftSidebar.d.ts +0 -2
  412. package/dist/umd/components/Layout/LeftSidebar.d.ts.map +0 -1
  413. package/dist/umd/components/VizElement/HeatmapExample.d.ts +0 -2
  414. package/dist/umd/components/VizElement/HeatmapExample.d.ts.map +0 -1
  415. package/dist/umd/helpers/elm-callout.d.ts.map +0 -1
  416. package/dist/umd/helpers/elm-getter.d.ts.map +0 -1
  417. package/dist/umd/helpers/viz-elements.d.ts.map +0 -1
  418. package/dist/umd/hooks/compare/index.d.ts +0 -4
  419. package/dist/umd/hooks/compare/index.d.ts.map +0 -1
  420. package/dist/umd/hooks/compare/useCompareAwareConfig.d.ts +0 -21
  421. package/dist/umd/hooks/compare/useCompareAwareConfig.d.ts.map +0 -1
  422. package/dist/umd/hooks/compare/useCompareAwareData.d.ts +0 -28
  423. package/dist/umd/hooks/compare/useCompareAwareData.d.ts.map +0 -1
  424. package/dist/umd/hooks/compare/useCompareAwareViz.d.ts +0 -34
  425. package/dist/umd/hooks/compare/useCompareAwareViz.d.ts.map +0 -1
  426. package/dist/umd/hooks/useHeatmapData.d.ts.map +0 -1
  427. package/dist/umd/hooks/useHeatmapViz.d.ts.map +0 -1
  428. package/dist/umd/hooks/useViewId.d.ts.map +0 -1
  429. package/dist/umd/hooks/viz-elements/index.d.ts.map +0 -1
  430. package/dist/umd/hooks/viz-elements/useClickedElement.d.ts.map +0 -1
  431. package/dist/umd/hooks/viz-elements/useElementCalloutVisible.d.ts.map +0 -1
  432. package/dist/umd/hooks/viz-elements/useHeatmapEffects.d.ts +0 -7
  433. package/dist/umd/hooks/viz-elements/useHeatmapEffects.d.ts.map +0 -1
  434. package/dist/umd/hooks/viz-elements/useHeatmapElementPosition.d.ts.map +0 -1
  435. package/dist/umd/hooks/viz-elements/useHoveredElement.d.ts.map +0 -1
  436. package/dist/umd/types/elm-callout.d.ts.map +0 -1
  437. package/dist/umd/types/viz-element.d.ts.map +0 -1
  438. /package/dist/esm/hooks/{viz-elements → viz-elm}/index.d.ts +0 -0
  439. /package/dist/esm/hooks/{viz-elements → viz-elm}/useElementCalloutVisible.d.ts +0 -0
  440. /package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapElementPosition.d.ts +0 -0
  441. /package/dist/esm/hooks/{viz-elements → viz-elm}/useHoveredElement.d.ts +0 -0
  442. /package/dist/umd/hooks/{viz-elements → viz-elm}/index.d.ts +0 -0
  443. /package/dist/umd/hooks/{viz-elements → viz-elm}/useElementCalloutVisible.d.ts +0 -0
  444. /package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapElementPosition.d.ts +0 -0
  445. /package/dist/umd/hooks/{viz-elements → viz-elm}/useHoveredElement.d.ts +0 -0
@@ -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, createContext, useContext, useCallback, useState, useRef, useMemo, forwardRef, Fragment as Fragment$1 } from 'react';
4
+ import { useEffect, useRef, useCallback, createContext, useContext, useMemo, useState, forwardRef, Fragment as Fragment$1 } from 'react';
5
5
  import { create } from 'zustand';
6
+ 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,10 +68,75 @@ 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
+
105
+ /**
106
+ * Creates a debounced version of a callback that delays invoking until after
107
+ * wait milliseconds have elapsed since the last time it was invoked.
108
+ *
109
+ * @param callback - The function to debounce
110
+ * @param delay - The number of milliseconds to delay
111
+ * @returns A debounced version of the callback
112
+ *
113
+ * @example
114
+ * ```tsx
115
+ * const handleSearch = useDebounceCallback((query: string) => {
116
+ * searchAPI(query);
117
+ * }, 300);
118
+ * ```
119
+ */
120
+ function useDebounceCallback(callback, delay) {
121
+ const timeoutRef = useRef();
122
+ const callbackRef = useRef(callback);
123
+ // Update callback ref when callback changes
124
+ callbackRef.current = callback;
125
+ return useCallback((...args) => {
126
+ if (timeoutRef.current) {
127
+ clearTimeout(timeoutRef.current);
128
+ }
129
+ timeoutRef.current = setTimeout(() => {
130
+ callbackRef.current(...args);
131
+ }, delay);
132
+ }, [delay]);
133
+ }
134
+
70
135
  const useHeatmapControlStore = create()((set, get) => {
71
136
  return {
72
137
  controls: {
73
- Sidebar: null,
138
+ Sidebar: undefined,
139
+ SidebarActivator: undefined,
74
140
  Toolbar: null,
75
141
  MetricBar: null,
76
142
  VizLoading: null,
@@ -109,12 +175,12 @@ var IScrollType;
109
175
  IScrollType["Revenue"] = "revenue-scroll";
110
176
  })(IScrollType || (IScrollType = {}));
111
177
 
112
- const useHeatmapConfigStore = create()((set, get) => {
178
+ const useHeatmapConfigStore = create()((set) => {
113
179
  return {
114
180
  mode: 'single',
115
181
  width: 1440,
116
182
  sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
117
- heatmapType: IHeatmapType.Scroll,
183
+ heatmapType: IHeatmapType.Click,
118
184
  clickType: IClickType.Total,
119
185
  scrollType: IScrollType.Depth,
120
186
  isRendering: true,
@@ -129,23 +195,22 @@ const useHeatmapConfigStore = create()((set, get) => {
129
195
  };
130
196
  });
131
197
 
132
- const DEFAULT_VIEW_ID$2 = 'default';
133
- const useHeatmapDataStore = create()((set, get) => {
198
+ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
134
199
  return {
135
- data: { [DEFAULT_VIEW_ID$2]: undefined },
136
- clickmap: { [DEFAULT_VIEW_ID$2]: undefined },
137
- dataInfo: { [DEFAULT_VIEW_ID$2]: undefined },
138
- scrollmap: { [DEFAULT_VIEW_ID$2]: undefined },
139
- setDataInfo: (dataInfo, viewId = DEFAULT_VIEW_ID$2) => set((state) => ({
200
+ data: { [DEFAULT_VIEW_ID]: undefined },
201
+ clickmap: { [DEFAULT_VIEW_ID]: undefined },
202
+ dataInfo: { [DEFAULT_VIEW_ID]: undefined },
203
+ scrollmap: { [DEFAULT_VIEW_ID]: undefined },
204
+ setDataInfo: (dataInfo, viewId = DEFAULT_VIEW_ID) => set((state) => ({
140
205
  dataInfo: { ...state.dataInfo, [viewId]: dataInfo },
141
206
  })),
142
- setData: (data, viewId = DEFAULT_VIEW_ID$2) => set((state) => ({
207
+ setData: (data, viewId = DEFAULT_VIEW_ID) => set((state) => ({
143
208
  data: { ...state.data, [viewId]: data },
144
209
  })),
145
- setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID$2) => set((state) => ({
210
+ setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID) => set((state) => ({
146
211
  clickmap: { ...state.clickmap, [viewId]: clickmap },
147
212
  })),
148
- setScrollmap: (scrollmap, viewId = DEFAULT_VIEW_ID$2) => set((state) => ({
213
+ setScrollmap: (scrollmap, viewId = DEFAULT_VIEW_ID) => set((state) => ({
149
214
  scrollmap: { ...state.scrollmap, [viewId]: scrollmap },
150
215
  })),
151
216
  copyView: (fromViewId, toViewId) => set((state) => ({
@@ -171,50 +236,98 @@ const useHeatmapDataStore = create()((set, get) => {
171
236
  };
172
237
  }),
173
238
  resetAll: () => set({
174
- data: { [DEFAULT_VIEW_ID$2]: undefined },
175
- clickmap: { [DEFAULT_VIEW_ID$2]: undefined },
176
- dataInfo: { [DEFAULT_VIEW_ID$2]: undefined },
177
- scrollmap: { [DEFAULT_VIEW_ID$2]: undefined },
239
+ data: { [DEFAULT_VIEW_ID]: undefined },
240
+ clickmap: { [DEFAULT_VIEW_ID]: undefined },
241
+ dataInfo: { [DEFAULT_VIEW_ID]: undefined },
242
+ scrollmap: { [DEFAULT_VIEW_ID]: undefined },
178
243
  }),
179
244
  };
180
- });
245
+ }));
181
246
 
182
- const useHeatmapInteractionStore = create()((set, get) => {
247
+ const DEFAULT_STATE = {
248
+ hideSidebar: false,
249
+ };
250
+ const useHeatmapInteractionStore = create()(subscribeWithSelector((set) => {
183
251
  return {
184
- state: {
185
- hideSidebar: false,
186
- },
187
- setState: (state) => set({ state }),
188
- selectedElement: null,
189
- setSelectedElement: (selectedElement) => set({ selectedElement }),
190
- hoveredElement: null,
191
- setHoveredElement: (hoveredElement) => set({ hoveredElement }),
192
- shouldShowCallout: false,
193
- setShouldShowCallout: (shouldShowCallout) => set({ shouldShowCallout }),
252
+ state: { [DEFAULT_VIEW_ID]: DEFAULT_STATE },
253
+ selectedElement: { [DEFAULT_VIEW_ID]: null },
254
+ hoveredElement: { [DEFAULT_VIEW_ID]: null },
255
+ shouldShowCallout: { [DEFAULT_VIEW_ID]: false },
256
+ setState: (state, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
257
+ state: { ...prev.state, [viewId]: state },
258
+ })),
259
+ setSelectedElement: (selectedElement, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
260
+ selectedElement: { ...prev.selectedElement, [viewId]: selectedElement },
261
+ })),
262
+ setHoveredElement: (hoveredElement, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
263
+ hoveredElement: { ...prev.hoveredElement, [viewId]: hoveredElement },
264
+ })),
265
+ setShouldShowCallout: (shouldShowCallout, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
266
+ shouldShowCallout: { ...prev.shouldShowCallout, [viewId]: shouldShowCallout },
267
+ })),
268
+ copyView: (fromViewId, toViewId) => set((state) => ({
269
+ state: {
270
+ ...state.state,
271
+ [toViewId]: state.state[fromViewId] ?? DEFAULT_STATE,
272
+ },
273
+ selectedElement: {
274
+ ...state.selectedElement,
275
+ [toViewId]: state.selectedElement[fromViewId] ?? null,
276
+ },
277
+ hoveredElement: {
278
+ ...state.hoveredElement,
279
+ [toViewId]: state.hoveredElement[fromViewId] ?? null,
280
+ },
281
+ shouldShowCallout: {
282
+ ...state.shouldShowCallout,
283
+ [toViewId]: state.shouldShowCallout[fromViewId] ?? false,
284
+ },
285
+ })),
286
+ clearView: (viewId) => set((state) => {
287
+ const newState = { ...state.state };
288
+ const newSelectedElement = { ...state.selectedElement };
289
+ const newHoveredElement = { ...state.hoveredElement };
290
+ const newShouldShowCallout = { ...state.shouldShowCallout };
291
+ delete newState[viewId];
292
+ delete newSelectedElement[viewId];
293
+ delete newHoveredElement[viewId];
294
+ delete newShouldShowCallout[viewId];
295
+ return {
296
+ state: newState,
297
+ selectedElement: newSelectedElement,
298
+ hoveredElement: newHoveredElement,
299
+ shouldShowCallout: newShouldShowCallout,
300
+ };
301
+ }),
302
+ resetAll: () => set({
303
+ state: { default: DEFAULT_STATE },
304
+ selectedElement: { default: null },
305
+ hoveredElement: { default: null },
306
+ shouldShowCallout: { default: false },
307
+ }),
194
308
  };
195
- });
309
+ }));
196
310
 
197
- const DEFAULT_VIEW_ID$1 = 'default';
198
- const useHeatmapVizStore = create()((set, get) => {
311
+ const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
199
312
  return {
200
- isRenderViz: { [DEFAULT_VIEW_ID$1]: false },
201
- zoomRatio: { [DEFAULT_VIEW_ID$1]: 100 },
202
- minZoomRatio: { [DEFAULT_VIEW_ID$1]: 10 },
203
- scale: { [DEFAULT_VIEW_ID$1]: 1 },
204
- isScaledToFit: { [DEFAULT_VIEW_ID$1]: false },
205
- setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID$1) => set((state) => ({
313
+ isRenderViz: { [DEFAULT_VIEW_ID]: false },
314
+ zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
315
+ minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
316
+ scale: { [DEFAULT_VIEW_ID]: 1 },
317
+ isScaledToFit: { [DEFAULT_VIEW_ID]: false },
318
+ setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID) => set((state) => ({
206
319
  isRenderViz: { ...state.isRenderViz, [viewId]: isRenderViz },
207
320
  })),
208
- setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID$1) => set((state) => ({
321
+ setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
209
322
  zoomRatio: { ...state.zoomRatio, [viewId]: zoomRatio },
210
323
  })),
211
- setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID$1) => set((state) => ({
324
+ setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
212
325
  minZoomRatio: { ...state.minZoomRatio, [viewId]: minZoomRatio },
213
326
  })),
214
- setScale: (scale, viewId = DEFAULT_VIEW_ID$1) => set((state) => ({
327
+ setScale: (scale, viewId = DEFAULT_VIEW_ID) => set((state) => ({
215
328
  scale: { ...state.scale, [viewId]: scale },
216
329
  })),
217
- setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID$1) => set((state) => ({
330
+ setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID) => set((state) => ({
218
331
  isScaledToFit: { ...state.isScaledToFit, [viewId]: isScaledToFit },
219
332
  })),
220
333
  copyView: (fromViewId, toViewId) => set((state) => ({
@@ -222,7 +335,10 @@ const useHeatmapVizStore = create()((set, get) => {
222
335
  zoomRatio: { ...state.zoomRatio, [toViewId]: state.zoomRatio[fromViewId] ?? 100 },
223
336
  minZoomRatio: { ...state.minZoomRatio, [toViewId]: state.minZoomRatio[fromViewId] ?? 10 },
224
337
  scale: { ...state.scale, [toViewId]: state.scale[fromViewId] ?? 1 },
225
- isScaledToFit: { ...state.isScaledToFit, [toViewId]: state.isScaledToFit[fromViewId] ?? false },
338
+ isScaledToFit: {
339
+ ...state.isScaledToFit,
340
+ [toViewId]: state.isScaledToFit[fromViewId] ?? false,
341
+ },
226
342
  })),
227
343
  clearView: (viewId) => set((state) => {
228
344
  const newIsRenderViz = { ...state.isRenderViz };
@@ -244,31 +360,69 @@ const useHeatmapVizStore = create()((set, get) => {
244
360
  };
245
361
  }),
246
362
  resetAll: () => set({
247
- isRenderViz: { [DEFAULT_VIEW_ID$1]: false },
248
- zoomRatio: { [DEFAULT_VIEW_ID$1]: 100 },
249
- minZoomRatio: { [DEFAULT_VIEW_ID$1]: 10 },
250
- scale: { [DEFAULT_VIEW_ID$1]: 1 },
251
- isScaledToFit: { [DEFAULT_VIEW_ID$1]: false },
363
+ isRenderViz: { [DEFAULT_VIEW_ID]: false },
364
+ zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
365
+ minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
366
+ scale: { [DEFAULT_VIEW_ID]: 1 },
367
+ isScaledToFit: { [DEFAULT_VIEW_ID]: false },
252
368
  }),
253
369
  };
254
- });
370
+ }));
255
371
 
256
- const useHeatmapVizScrollmapStore = create()((set, get) => {
372
+ const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set) => {
257
373
  return {
258
- zones: [],
259
- hoveredZone: null,
260
- showMinimap: true,
261
- setZones: (zones) => set({ zones }),
262
- setHoveredZone: (hoveredZone) => set({ hoveredZone }),
263
- setShowMinimap: (showMinimap) => set({ showMinimap }),
374
+ zones: { [DEFAULT_VIEW_ID]: [] },
375
+ hoveredZone: { [DEFAULT_VIEW_ID]: null },
376
+ showMinimap: { [DEFAULT_VIEW_ID]: true },
377
+ setZones: (zones, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
378
+ zones: { ...prev.zones, [viewId]: zones },
379
+ })),
380
+ setHoveredZone: (hoveredZone, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
381
+ hoveredZone: { ...prev.hoveredZone, [viewId]: hoveredZone },
382
+ })),
383
+ setShowMinimap: (showMinimap, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
384
+ showMinimap: { ...prev.showMinimap, [viewId]: showMinimap },
385
+ })),
386
+ copyView: (fromViewId, toViewId) => set((state) => ({
387
+ zones: {
388
+ ...state.zones,
389
+ [toViewId]: state.zones[fromViewId] ?? [],
390
+ },
391
+ hoveredZone: {
392
+ ...state.hoveredZone,
393
+ [toViewId]: state.hoveredZone[fromViewId] ?? null,
394
+ },
395
+ showMinimap: {
396
+ ...state.showMinimap,
397
+ [toViewId]: state.showMinimap[fromViewId] ?? true,
398
+ },
399
+ })),
400
+ clearView: (viewId) => set((state) => {
401
+ const newZones = { ...state.zones };
402
+ const newHoveredZone = { ...state.hoveredZone };
403
+ const newShowMinimap = { ...state.showMinimap };
404
+ delete newZones[viewId];
405
+ delete newHoveredZone[viewId];
406
+ delete newShowMinimap[viewId];
407
+ return {
408
+ zones: newZones,
409
+ hoveredZone: newHoveredZone,
410
+ showMinimap: newShowMinimap,
411
+ };
412
+ }),
413
+ resetAll: () => set({
414
+ zones: { default: [] },
415
+ hoveredZone: { default: null },
416
+ showMinimap: { default: true },
417
+ }),
264
418
  };
265
- });
419
+ }));
266
420
 
267
421
  const initialState = {
268
422
  payloads: [],
269
423
  htmlContent: '',
270
424
  };
271
- const useHeatmapLiveStore = create()((set, get) => {
425
+ const useHeatmapLiveStore = create()((set) => {
272
426
  return {
273
427
  ...initialState,
274
428
  reset: () => set(initialState),
@@ -278,12 +432,12 @@ const useHeatmapLiveStore = create()((set, get) => {
278
432
  };
279
433
  });
280
434
 
281
- const DEFAULT_VIEW_ID = 'default';
282
- const useHeatmapSingleStore = create()((set, get) => {
435
+ const useHeatmapSingleStore = create()(subscribeWithSelector((set) => {
283
436
  return {
284
437
  vizRef: { [DEFAULT_VIEW_ID]: null },
285
438
  iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
286
439
  wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
440
+ wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
287
441
  setVizRef: (vizRef, viewId = DEFAULT_VIEW_ID) => set((state) => ({
288
442
  vizRef: { ...state.vizRef, [viewId]: vizRef },
289
443
  })),
@@ -297,31 +451,43 @@ const useHeatmapSingleStore = create()((set, get) => {
297
451
  wrapperHeight: { ...state.wrapperHeight, [viewId]: wrapperHeight },
298
452
  }));
299
453
  },
454
+ setWrapperWidth: (wrapperWidth, viewId = DEFAULT_VIEW_ID) => {
455
+ set((state) => ({
456
+ wrapperWidth: { ...state.wrapperWidth, [viewId]: wrapperWidth },
457
+ }));
458
+ },
300
459
  copyView: (fromViewId, toViewId) => set((state) => ({
301
460
  // Don't copy vizRef - each view needs its own visualizer instance
302
461
  iframeHeight: { ...state.iframeHeight, [toViewId]: state.iframeHeight[fromViewId] ?? 0 },
303
- wrapperHeight: { ...state.wrapperHeight, [toViewId]: state.wrapperHeight[fromViewId] ?? 0 },
462
+ wrapperHeight: {
463
+ ...state.wrapperHeight,
464
+ [toViewId]: state.wrapperHeight[fromViewId] ?? 0,
465
+ },
304
466
  })),
305
467
  clearView: (viewId) => set((state) => {
306
468
  const newVizRef = { ...state.vizRef };
307
469
  const newIframeHeight = { ...state.iframeHeight };
308
470
  const newWrapperHeight = { ...state.wrapperHeight };
471
+ const newWrapperWidth = { ...state.wrapperWidth };
309
472
  delete newVizRef[viewId];
310
473
  delete newIframeHeight[viewId];
311
474
  delete newWrapperHeight[viewId];
475
+ delete newWrapperWidth[viewId];
312
476
  return {
313
477
  vizRef: newVizRef,
314
478
  iframeHeight: newIframeHeight,
315
479
  wrapperHeight: newWrapperHeight,
480
+ wrapperWidth: newWrapperWidth,
316
481
  };
317
482
  }),
318
483
  resetAll: () => set({
319
484
  vizRef: { [DEFAULT_VIEW_ID]: null },
320
485
  iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
321
486
  wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
487
+ wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
322
488
  }),
323
489
  };
324
- });
490
+ }));
325
491
 
326
492
  const createDefaultView = (id, label, options) => ({
327
493
  id,
@@ -468,6 +634,7 @@ const useRegisterConfig = () => {
468
634
  const useRegisterControl = (control) => {
469
635
  const registerControl = useHeatmapControlStore((state) => state.registerControl);
470
636
  registerControl('Sidebar', control.Sidebar);
637
+ registerControl('SidebarActivator', control.SidebarActivator);
471
638
  registerControl('TopBar', control.TopBar);
472
639
  registerControl('Toolbar', control.Toolbar);
473
640
  registerControl('MetricBar', control.MetricBar);
@@ -482,18 +649,18 @@ const useRegisterControl = (control) => {
482
649
  const ViewIdContext = createContext(undefined);
483
650
  /**
484
651
  * Hook to get current viewId
485
- * Returns 'default' if not in a ViewIdContext (single mode)
652
+ * Returns DEFAULT_VIEW_ID if not in a ViewIdContext (single mode)
486
653
  */
487
654
  const useViewId = () => {
488
655
  const viewId = useContext(ViewIdContext);
489
- return viewId || 'default';
656
+ return viewId || DEFAULT_VIEW_ID;
490
657
  };
491
658
  /**
492
659
  * Hook to check if currently in compare mode
493
660
  */
494
661
  const useIsCompareMode = () => {
495
662
  const viewId = useContext(ViewIdContext);
496
- return viewId !== undefined && viewId !== 'default';
663
+ return viewId !== undefined && viewId !== DEFAULT_VIEW_ID;
497
664
  };
498
665
 
499
666
  const useHeatmapData = (props) => {
@@ -506,17 +673,191 @@ const useHeatmapData = (props) => {
506
673
  const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
507
674
  const setScrollmap = useHeatmapDataStore((state) => state.setScrollmap);
508
675
  const setDataInfo = useHeatmapDataStore((state) => state.setDataInfo);
676
+ const memoizedSetters = useMemo(() => ({
677
+ setData: (newData) => setData(newData, viewId),
678
+ setClickmap: (newClickmap) => setClickmap(newClickmap, viewId),
679
+ setScrollmap: (newScrollmap) => setScrollmap(newScrollmap, viewId),
680
+ setDataInfo: (newDataInfo) => setDataInfo(newDataInfo, viewId),
681
+ }), [setData, setClickmap, setScrollmap, setDataInfo, viewId]);
509
682
  return {
510
- // Data
511
683
  data,
512
684
  clickmap,
513
685
  scrollmap,
514
686
  dataInfo,
515
687
  // Setters (auto-inject viewId)
516
- setData: (newData) => setData(newData, viewId),
517
- setClickmap: (newClickmap) => setClickmap(newClickmap, viewId),
518
- setScrollmap: (newScrollmap) => setScrollmap(newScrollmap, viewId),
519
- setDataInfo: (newDataInfo) => setDataInfo(newDataInfo, viewId),
688
+ ...memoizedSetters,
689
+ };
690
+ };
691
+
692
+ const useHeatmapInteraction = (props) => {
693
+ const viewId = props?.viewId || useViewId();
694
+ const state = useHeatmapInteractionStore((store) => store.state[viewId] ?? { hideSidebar: false });
695
+ const selectedElement = useHeatmapInteractionStore((store) => store.selectedElement[viewId] ?? null);
696
+ const hoveredElement = useHeatmapInteractionStore((store) => store.hoveredElement[viewId] ?? null);
697
+ const shouldShowCallout = useHeatmapInteractionStore((store) => store.shouldShowCallout[viewId] ?? false);
698
+ const setStateStore = useHeatmapInteractionStore((store) => store.setState);
699
+ const setSelectedElementStore = useHeatmapInteractionStore((store) => store.setSelectedElement);
700
+ const setHoveredElementStore = useHeatmapInteractionStore((store) => store.setHoveredElement);
701
+ const setShouldShowCalloutStore = useHeatmapInteractionStore((store) => store.setShouldShowCallout);
702
+ const memoizedSetters = useMemo(() => ({
703
+ setState: (newState) => setStateStore(newState, viewId),
704
+ setSelectedElement: (element) => setSelectedElementStore(element, viewId),
705
+ setHoveredElement: (element) => setHoveredElementStore(element, viewId),
706
+ setShouldShowCallout: (value) => setShouldShowCalloutStore(value, viewId),
707
+ }), [
708
+ setStateStore,
709
+ setSelectedElementStore,
710
+ setHoveredElementStore,
711
+ setShouldShowCalloutStore,
712
+ viewId,
713
+ ]);
714
+ return {
715
+ state,
716
+ selectedElement,
717
+ hoveredElement,
718
+ shouldShowCallout,
719
+ // Setters (auto-inject viewId)
720
+ ...memoizedSetters,
721
+ };
722
+ };
723
+
724
+ const useHeatmapViz = (props) => {
725
+ const viewId = props?.viewId || useViewId();
726
+ // Viz store
727
+ const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
728
+ const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
729
+ const minZoomRatio = useHeatmapVizStore((state) => state.minZoomRatio[viewId] ?? 10);
730
+ const widthScale = useHeatmapVizStore((state) => state.scale[viewId] ?? 1);
731
+ const isScaledToFit = useHeatmapVizStore((state) => state.isScaledToFit[viewId] ?? false);
732
+ const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
733
+ const setZoomRatio = useHeatmapVizStore((state) => state.setZoomRatio);
734
+ const setMinZoomRatio = useHeatmapVizStore((state) => state.setMinZoomRatio);
735
+ const setScale = useHeatmapVizStore((state) => state.setScale);
736
+ const setIsScaledToFit = useHeatmapVizStore((state) => state.setIsScaledToFit);
737
+ // Single store
738
+ const vizRef = useHeatmapSingleStore((state) => state.vizRef[viewId] ?? null);
739
+ const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight[viewId] ?? 0);
740
+ const wrapperHeight = useHeatmapSingleStore((state) => state.wrapperHeight[viewId] ?? 0);
741
+ const wrapperWidth = useHeatmapSingleStore((state) => state.wrapperWidth[viewId] ?? 0);
742
+ const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
743
+ const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
744
+ const setWrapperHeight = useHeatmapSingleStore((state) => state.setWrapperHeight);
745
+ const setWrapperWidth = useHeatmapSingleStore((state) => state.setWrapperWidth);
746
+ const memoizedSetters = useMemo(() => ({
747
+ setIsRenderViz: (value) => setIsRenderViz(value, viewId),
748
+ setZoomRatio: (value) => setZoomRatio(value, viewId),
749
+ setMinZoomRatio: (value) => setMinZoomRatio(value, viewId),
750
+ setScale: (value) => setScale(value, viewId),
751
+ setIsScaledToFit: (value) => setIsScaledToFit(value, viewId),
752
+ setVizRef: (value) => setVizRef(value, viewId),
753
+ setIframeHeight: (value) => setIframeHeight(value, viewId),
754
+ setWrapperHeight: (value) => setWrapperHeight(value, viewId),
755
+ setWrapperWidth: (value) => setWrapperWidth(value, viewId),
756
+ }), [
757
+ setIsRenderViz,
758
+ setZoomRatio,
759
+ setMinZoomRatio,
760
+ setScale,
761
+ setIsScaledToFit,
762
+ setVizRef,
763
+ setIframeHeight,
764
+ setWrapperHeight,
765
+ setWrapperWidth,
766
+ viewId,
767
+ ]);
768
+ return {
769
+ isRenderViz,
770
+ zoomRatio,
771
+ minZoomRatio,
772
+ widthScale,
773
+ isScaledToFit,
774
+ vizRef,
775
+ iframeHeight,
776
+ wrapperHeight,
777
+ wrapperWidth,
778
+ // Setters (auto-inject viewId)
779
+ ...memoizedSetters,
780
+ };
781
+ };
782
+
783
+ const useHeatmapVizScrollmap = (props) => {
784
+ const viewId = props?.viewId || useViewId();
785
+ const zones = useHeatmapVizScrollmapStore((store) => store.zones[viewId] ?? []);
786
+ const hoveredZone = useHeatmapVizScrollmapStore((store) => store.hoveredZone[viewId] ?? null);
787
+ const showMinimap = useHeatmapVizScrollmapStore((store) => store.showMinimap[viewId] ?? true);
788
+ const setZonesStore = useHeatmapVizScrollmapStore((store) => store.setZones);
789
+ const setHoveredZoneStore = useHeatmapVizScrollmapStore((store) => store.setHoveredZone);
790
+ const setShowMinimapStore = useHeatmapVizScrollmapStore((store) => store.setShowMinimap);
791
+ const memoizedSetters = useMemo(() => ({
792
+ setZones: (newZones) => setZonesStore(newZones, viewId),
793
+ setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
794
+ setShowMinimap: (value) => setShowMinimapStore(value, viewId),
795
+ }), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
796
+ return {
797
+ zones,
798
+ hoveredZone,
799
+ showMinimap,
800
+ // Setters (auto-inject viewId)
801
+ ...memoizedSetters,
802
+ };
803
+ };
804
+
805
+ /**
806
+ * Hook to handle copying and clearing view data across all stores
807
+ */
808
+ const useHeatmapCopyView = () => {
809
+ const copyDataView = useHeatmapDataStore((state) => state.copyView);
810
+ const copyVizView = useHeatmapVizStore((state) => state.copyView);
811
+ const copySingleView = useHeatmapSingleStore((state) => state.copyView);
812
+ const copyInteractionView = useHeatmapInteractionStore((state) => state.copyView);
813
+ const copyVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.copyView);
814
+ const clearDataView = useHeatmapDataStore((state) => state.clearView);
815
+ const clearVizView = useHeatmapVizStore((state) => state.clearView);
816
+ const clearSingleView = useHeatmapSingleStore((state) => state.clearView);
817
+ const clearInteractionView = useHeatmapInteractionStore((state) => state.clearView);
818
+ const clearVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.clearView);
819
+ const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
820
+ const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
821
+ const resetSingleAll = useHeatmapSingleStore((state) => state.resetAll);
822
+ const resetInteractionAll = useHeatmapInteractionStore((state) => state.resetAll);
823
+ const resetVizScrollmapAll = useHeatmapVizScrollmapStore((state) => state.resetAll);
824
+ const copyView = (fromViewId, toViewId) => {
825
+ copyDataView(fromViewId, toViewId);
826
+ copyVizView(fromViewId, toViewId);
827
+ copySingleView(fromViewId, toViewId);
828
+ copyInteractionView(fromViewId, toViewId);
829
+ copyVizScrollmapView(fromViewId, toViewId);
830
+ };
831
+ const copyViewToMultiple = (fromViewId, toViewIds) => {
832
+ toViewIds.forEach((toViewId) => {
833
+ copyView(fromViewId, toViewId);
834
+ });
835
+ };
836
+ const clearView = (viewId) => {
837
+ clearDataView(viewId);
838
+ clearVizView(viewId);
839
+ clearSingleView(viewId);
840
+ clearInteractionView(viewId);
841
+ clearVizScrollmapView(viewId);
842
+ };
843
+ const clearMultipleViews = (viewIds) => {
844
+ viewIds.forEach((viewId) => {
845
+ clearView(viewId);
846
+ });
847
+ };
848
+ const resetAll = () => {
849
+ resetDataAll();
850
+ resetVizAll();
851
+ resetSingleAll();
852
+ resetInteractionAll();
853
+ resetVizScrollmapAll();
854
+ };
855
+ return {
856
+ copyView,
857
+ copyViewToMultiple,
858
+ clearView,
859
+ clearMultipleViews,
860
+ resetAll,
520
861
  };
521
862
  };
522
863
 
@@ -562,23 +903,146 @@ const useRegisterHeatmap = ({ clickmap, scrollmap }) => {
562
903
  }, [scrollmap]);
563
904
  };
564
905
 
906
+ function findLastSizeOfDom(data) {
907
+ const listDocs = data
908
+ .filter((item) => item.doc?.find((doc) => doc.data.width && doc.data.height))
909
+ .flatMap((item) => item.doc?.flatMap((doc) => doc.data));
910
+ const lastDoc = listDocs?.[listDocs.length - 1];
911
+ const docSize = {
912
+ width: lastDoc?.width,
913
+ height: lastDoc?.height,
914
+ };
915
+ const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
916
+ const lastResizeEvent = listResizes?.[listResizes.length - 1];
917
+ const resize = {
918
+ width: lastResizeEvent?.data.width,
919
+ height: lastResizeEvent?.data.height,
920
+ };
921
+ return {
922
+ doc: docSize,
923
+ resize: resize,
924
+ size: {
925
+ width: resize.width || docSize.width,
926
+ height: resize.height || docSize.height,
927
+ },
928
+ };
929
+ }
930
+ function decodePayloads(payload) {
931
+ try {
932
+ return decode(payload);
933
+ }
934
+ catch (error) {
935
+ return null;
936
+ }
937
+ }
938
+
939
+ function calculateRankPosition(rect, widthScale) {
940
+ const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
941
+ const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
942
+ return {
943
+ transform: `scale(${1.2 * widthScale})`,
944
+ top: Number.isNaN(top) ? undefined : top,
945
+ left: Number.isNaN(left) ? undefined : left,
946
+ };
947
+ }
948
+ function isElementInViewport(elementRect, visualRef, scale) {
949
+ if (!elementRect)
950
+ return false;
951
+ const visualRect = visualRef.current?.getBoundingClientRect();
952
+ if (!visualRect)
953
+ return false;
954
+ // Element position relative to the document (or container's content)
955
+ const elementTop = elementRect.top * scale;
956
+ const elementBottom = (elementRect.top + elementRect.height) * scale;
957
+ // Current scroll position
958
+ const scrollTop = visualRef.current?.scrollTop || 0;
959
+ const viewportHeight = visualRect.height;
960
+ // Visible viewport range in the scrollable content
961
+ const viewportTop = scrollTop;
962
+ const viewportBottom = scrollTop + viewportHeight;
963
+ // Check if element is within the visible viewport
964
+ // Element is visible if it overlaps with the viewport
965
+ return elementBottom > viewportTop && elementTop < viewportBottom;
966
+ }
967
+
968
+ function getElementLayout(element) {
969
+ if (!element?.getBoundingClientRect)
970
+ return null;
971
+ const rect = element.getBoundingClientRect();
972
+ if (rect.width === 0 && rect.height === 0)
973
+ return null;
974
+ return {
975
+ top: rect.top,
976
+ left: rect.left,
977
+ width: rect.width,
978
+ height: rect.height,
979
+ };
980
+ }
981
+ const getElementAtPoint = (doc, x, y) => {
982
+ let el = null;
983
+ if ('caretPositionFromPoint' in doc) {
984
+ el = doc.caretPositionFromPoint(x, y)?.offsetNode ?? null;
985
+ }
986
+ el = el ?? doc.elementFromPoint(x, y);
987
+ let element = el;
988
+ while (element && element.nodeType === Node.TEXT_NODE) {
989
+ element = element.parentElement;
990
+ }
991
+ return element;
992
+ };
993
+ function getElementHash(element) {
994
+ return (element.getAttribute('data-clarity-hash') ||
995
+ element.getAttribute('data-clarity-hashalpha') ||
996
+ element.getAttribute('data-clarity-hashbeta'));
997
+ }
998
+ const getElementRank = (hash, elements) => {
999
+ if (!elements)
1000
+ return 0;
1001
+ return elements.findIndex((e) => e.hash === hash) + 1;
1002
+ };
1003
+ const buildElementInfo = (hash, rect, heatmapInfo) => {
1004
+ if (!rect || !heatmapInfo)
1005
+ return null;
1006
+ const info = heatmapInfo.elementMapInfo?.[hash];
1007
+ if (!info)
1008
+ return null;
1009
+ const rank = getElementRank(hash, heatmapInfo.sortedElements);
1010
+ const clicks = info.totalclicks ?? 0;
1011
+ const selector = info.selector ?? '';
1012
+ const baseInfo = {
1013
+ hash,
1014
+ clicks,
1015
+ rank,
1016
+ selector,
1017
+ };
1018
+ return {
1019
+ ...baseInfo,
1020
+ ...rect,
1021
+ };
1022
+ };
1023
+
565
1024
  const PADDING = 0;
566
1025
  const ARROW_SIZE = 8;
567
1026
  const HORIZONTAL_OFFSET = 0;
568
- // ============================================================================
569
- // Viewport & Dimensions
570
- // ============================================================================
571
- const getViewportDimensions = () => ({
572
- width: window.innerWidth,
573
- height: window.innerHeight,
574
- });
1027
+
1028
+ const getViewportDimensions = (containerElm) => {
1029
+ if (containerElm) {
1030
+ const containerRect = containerElm.getBoundingClientRect();
1031
+ return {
1032
+ width: containerRect.width,
1033
+ height: containerRect.height,
1034
+ };
1035
+ }
1036
+ return {
1037
+ width: window.innerWidth,
1038
+ height: window.innerHeight,
1039
+ };
1040
+ };
575
1041
  const getElementDimensions = (targetElm, calloutElm) => ({
576
1042
  targetRect: targetElm.getBoundingClientRect(),
577
1043
  calloutRect: calloutElm.getBoundingClientRect(),
578
1044
  });
579
- // ============================================================================
580
- // Alignment Order
581
- // ============================================================================
1045
+
582
1046
  const getAlignmentOrder = (alignment) => {
583
1047
  switch (alignment) {
584
1048
  case 'center':
@@ -589,9 +1053,6 @@ const getAlignmentOrder = (alignment) => {
589
1053
  return ['right', 'center', 'left'];
590
1054
  }
591
1055
  };
592
- // ============================================================================
593
- // Position Calculation
594
- // ============================================================================
595
1056
  const calculateLeftPosition = ({ targetRect, calloutRect, hozOffset, align, }) => {
596
1057
  switch (align) {
597
1058
  case 'left':
@@ -615,31 +1076,40 @@ const calculateHorizontalPlacementPosition = (targetRect, calloutRect, placement
615
1076
  : targetRect.left - calloutRect.width - padding - arrowSize;
616
1077
  return { top, left };
617
1078
  };
618
- // ============================================================================
619
- // Validation
620
- // ============================================================================
621
- const isLeftPositionValid = (leftPos, calloutWidth, viewportWidth, padding) => {
1079
+
1080
+ const isLeftPositionValid = (leftPos, calloutWidth, viewportWidth, padding, containerRect) => {
1081
+ if (containerRect) {
1082
+ return leftPos >= containerRect.left + padding && leftPos + calloutWidth <= containerRect.right - padding;
1083
+ }
622
1084
  return leftPos >= padding && leftPos + calloutWidth <= viewportWidth - padding;
623
1085
  };
624
- const isVerticalPositionValid = (targetRect, calloutRect, placement, viewportHeight, padding, arrowSize) => {
1086
+ const isVerticalPositionValid = (targetRect, calloutRect, placement, viewportHeight, padding, arrowSize, containerRect) => {
1087
+ if (containerRect) {
1088
+ return placement === 'top'
1089
+ ? targetRect.top - calloutRect.height - padding - arrowSize >= containerRect.top
1090
+ : targetRect.bottom + calloutRect.height + padding + arrowSize <= containerRect.bottom;
1091
+ }
625
1092
  return placement === 'top'
626
1093
  ? targetRect.top - calloutRect.height - padding - arrowSize > 0
627
1094
  : targetRect.bottom + calloutRect.height + padding + arrowSize < viewportHeight;
628
1095
  };
629
- const isHorizontalPlacementValid = (targetRect, calloutRect, placement, viewportWidth, padding, arrowSize) => {
1096
+ const isHorizontalPlacementValid = (targetRect, calloutRect, placement, viewportWidth, padding, arrowSize, containerRect) => {
1097
+ if (containerRect) {
1098
+ return placement === 'right'
1099
+ ? targetRect.right + calloutRect.width + padding + arrowSize <= containerRect.right
1100
+ : targetRect.left - calloutRect.width - padding - arrowSize >= containerRect.left;
1101
+ }
630
1102
  return placement === 'right'
631
1103
  ? targetRect.right + calloutRect.width + padding + arrowSize < viewportWidth
632
1104
  : targetRect.left - calloutRect.width - padding - arrowSize > 0;
633
1105
  };
634
- // ============================================================================
635
- // Position Candidates Generation
636
- // ============================================================================
637
- const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHeight, viewportWidth, alignment, hozOffset, padding, arrowSize) => {
1106
+
1107
+ const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHeight, viewportWidth, alignment, hozOffset, padding, arrowSize, containerRect) => {
638
1108
  const candidates = [];
639
1109
  const placements = ['top', 'bottom'];
640
1110
  placements.forEach((placement) => {
641
1111
  const verticalPos = calculateVerticalPosition(targetRect, calloutRect, placement, padding, arrowSize);
642
- const verticalValid = isVerticalPositionValid(targetRect, calloutRect, placement, viewportHeight, padding, arrowSize);
1112
+ const verticalValid = isVerticalPositionValid(targetRect, calloutRect, placement, viewportHeight, padding, arrowSize, containerRect);
643
1113
  const alignmentOrder = getAlignmentOrder(alignment);
644
1114
  alignmentOrder.forEach((align) => {
645
1115
  const horizontalPos = calculateLeftPosition({
@@ -654,13 +1124,13 @@ const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHei
654
1124
  left: horizontalPos,
655
1125
  horizontalAlign: align,
656
1126
  valid: verticalValid &&
657
- isLeftPositionValid(horizontalPos, calloutRect.width, viewportWidth, padding),
1127
+ isLeftPositionValid(horizontalPos, calloutRect.width, viewportWidth, padding, containerRect),
658
1128
  });
659
1129
  });
660
1130
  });
661
1131
  return candidates;
662
1132
  };
663
- const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportWidth, padding, arrowSize) => {
1133
+ const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportWidth, padding, arrowSize, containerRect) => {
664
1134
  const placements = ['left', 'right'];
665
1135
  return placements.map((placement) => {
666
1136
  const { top, left } = calculateHorizontalPlacementPosition(targetRect, calloutRect, placement, padding, arrowSize);
@@ -669,46 +1139,45 @@ const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportW
669
1139
  top,
670
1140
  left,
671
1141
  horizontalAlign: 'center',
672
- valid: isHorizontalPlacementValid(targetRect, calloutRect, placement, viewportWidth, padding, arrowSize),
1142
+ valid: isHorizontalPlacementValid(targetRect, calloutRect, placement, viewportWidth, padding, arrowSize, containerRect),
673
1143
  };
674
1144
  });
675
1145
  };
676
- const generateAllPositionCandidates = (rectDimensions, viewport, alignment, hozOffset, padding, arrowSize) => {
1146
+ const generateAllPositionCandidates = (rectDimensions, viewport, alignment, hozOffset, padding, arrowSize, containerRect) => {
677
1147
  const { targetRect, calloutRect } = rectDimensions;
678
- const verticalCandidates = generateVerticalPositionCandidates(targetRect, calloutRect, viewport.height, viewport.width, alignment, hozOffset, padding, arrowSize);
679
- const horizontalCandidates = generateHorizontalPositionCandidates(targetRect, calloutRect, viewport.width, padding, arrowSize);
1148
+ const verticalCandidates = generateVerticalPositionCandidates(targetRect, calloutRect, viewport.height, viewport.width, alignment, hozOffset, padding, arrowSize, containerRect);
1149
+ const horizontalCandidates = generateHorizontalPositionCandidates(targetRect, calloutRect, viewport.width, padding, arrowSize, containerRect);
680
1150
  return [...verticalCandidates, ...horizontalCandidates];
681
1151
  };
682
- // ============================================================================
683
- // Position Selection
684
- // ============================================================================
1152
+
685
1153
  const selectBestPosition = (candidates) => {
686
1154
  return candidates.find((p) => p.valid) || candidates[0];
687
1155
  };
688
- // ============================================================================
689
- // Viewport Boundary Adjustment
690
- // ============================================================================
691
- const constrainToViewport = (position, calloutRect, viewport, padding) => {
1156
+ const constrainToViewport = (position, calloutRect, viewport, padding, containerRect) => {
1157
+ if (containerRect) {
1158
+ const left = Math.max(containerRect.left + padding, Math.min(position.left, containerRect.right - calloutRect.width - padding));
1159
+ const top = Math.max(containerRect.top + padding, Math.min(position.top, containerRect.bottom - calloutRect.height - padding));
1160
+ return { top, left };
1161
+ }
692
1162
  const left = Math.max(padding, Math.min(position.left, viewport.width - calloutRect.width - padding));
693
1163
  const top = Math.max(padding, Math.min(position.top, viewport.height - calloutRect.height - padding));
694
1164
  return { top, left };
695
1165
  };
696
- // ============================================================================
697
- // Main Function
698
- // ============================================================================
699
- const calcCalloutPosition = ({ targetElm, calloutElm, setPosition, hozOffset = HORIZONTAL_OFFSET, alignment = 'center', }) => {
1166
+
1167
+ const calcCalloutPosition = ({ targetElm, calloutElm, setPosition, hozOffset = HORIZONTAL_OFFSET, alignment = 'center', containerElm, }) => {
700
1168
  return () => {
701
1169
  // 1. Get dimensions
702
1170
  const rectDimensions = getElementDimensions(targetElm, calloutElm);
703
- const viewport = getViewportDimensions();
1171
+ const viewport = getViewportDimensions(containerElm);
1172
+ const containerRect = containerElm?.getBoundingClientRect();
704
1173
  const padding = PADDING;
705
1174
  const arrowSize = ARROW_SIZE;
706
1175
  // 2. Generate all position candidates
707
- const candidates = generateAllPositionCandidates(rectDimensions, viewport, alignment, hozOffset, padding, arrowSize);
1176
+ const candidates = generateAllPositionCandidates(rectDimensions, viewport, alignment, hozOffset, padding, arrowSize, containerRect);
708
1177
  // 3. Select best position
709
1178
  const bestPosition = selectBestPosition(candidates);
710
1179
  // 4. Constrain to viewport
711
- const constrainedPosition = constrainToViewport({ top: bestPosition.top, left: bestPosition.left }, rectDimensions.calloutRect, viewport, padding);
1180
+ const constrainedPosition = constrainToViewport({ top: bestPosition.top, left: bestPosition.left }, rectDimensions.calloutRect, viewport, padding, containerRect);
712
1181
  // 5. Create final position object
713
1182
  const finalPosition = {
714
1183
  top: constrainedPosition.top,
@@ -720,128 +1189,6 @@ const calcCalloutPosition = ({ targetElm, calloutElm, setPosition, hozOffset = H
720
1189
  };
721
1190
  };
722
1191
 
723
- function getElementLayout(element) {
724
- if (!element?.getBoundingClientRect)
725
- return null;
726
- const rect = element.getBoundingClientRect();
727
- if (rect.width === 0 && rect.height === 0)
728
- return null;
729
- return {
730
- top: rect.top,
731
- left: rect.left,
732
- width: rect.width,
733
- height: rect.height,
734
- };
735
- }
736
- const getElementAtPoint = (doc, x, y) => {
737
- let el = null;
738
- if ('caretPositionFromPoint' in doc) {
739
- el = doc.caretPositionFromPoint(x, y)?.offsetNode ?? null;
740
- }
741
- el = el ?? doc.elementFromPoint(x, y);
742
- let element = el;
743
- while (element && element.nodeType === Node.TEXT_NODE) {
744
- element = element.parentElement;
745
- }
746
- return element;
747
- };
748
- function getElementHash(element) {
749
- return (element.getAttribute('data-clarity-hash') ||
750
- element.getAttribute('data-clarity-hashalpha') ||
751
- element.getAttribute('data-clarity-hashbeta'));
752
- }
753
- const getElementRank = (hash, elements) => {
754
- if (!elements)
755
- return 0;
756
- return elements.findIndex((e) => e.hash === hash) + 1;
757
- };
758
- const buildElementInfo = (hash, rect, heatmapInfo) => {
759
- if (!rect || !heatmapInfo)
760
- return null;
761
- const info = heatmapInfo.elementMapInfo?.[hash];
762
- if (!info)
763
- return null;
764
- const rank = getElementRank(hash, heatmapInfo.sortedElements);
765
- const clicks = info.totalclicks ?? 0;
766
- const selector = info.selector ?? '';
767
- const baseInfo = {
768
- hash,
769
- clicks,
770
- rank,
771
- selector,
772
- };
773
- return {
774
- ...baseInfo,
775
- ...rect,
776
- };
777
- };
778
-
779
- function findLastSizeOfDom(data) {
780
- const firstDoc = data.find((item) => item.envelope.sequence === 1)?.doc;
781
- const docSorted = firstDoc?.sort(sort);
782
- const firstEvent = docSorted?.[0];
783
- const docSize = {
784
- width: firstEvent?.data.width,
785
- height: firstEvent?.data.height,
786
- };
787
- const newData = JSON.parse(JSON.stringify(data));
788
- const reversedData = newData.reverse();
789
- const lastResizeEvent = reversedData.find((item) => !!item.resize);
790
- const firstEventResize = lastResizeEvent?.resize?.[0];
791
- const resize = {
792
- width: firstEventResize?.data.width,
793
- height: firstEventResize?.data.height,
794
- };
795
- return {
796
- doc: docSize,
797
- resize: resize,
798
- size: {
799
- width: resize.width ?? docSize.width,
800
- height: resize.height ?? docSize.height,
801
- },
802
- };
803
- }
804
- function sort(a, b) {
805
- return a.time - b.time;
806
- }
807
- function decodePayloads(payload) {
808
- try {
809
- return decode(payload);
810
- }
811
- catch (error) {
812
- return null;
813
- }
814
- }
815
-
816
- function calculateRankPosition(rect, widthScale) {
817
- const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
818
- const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
819
- return {
820
- transform: `scale(${1.2 * widthScale})`,
821
- top: Number.isNaN(top) ? undefined : top,
822
- left: Number.isNaN(left) ? undefined : left,
823
- };
824
- }
825
- function isElementInViewport(elementRect, visualRef, scale) {
826
- if (!elementRect)
827
- return false;
828
- const visualRect = visualRef.current?.getBoundingClientRect();
829
- if (!visualRect)
830
- return false;
831
- // Element position relative to the document (or container's content)
832
- const elementTop = elementRect.top * scale;
833
- const elementBottom = (elementRect.top + elementRect.height) * scale;
834
- // Current scroll position
835
- const scrollTop = visualRef.current?.scrollTop || 0;
836
- const viewportHeight = visualRect.height;
837
- // Visible viewport range in the scrollable content
838
- const viewportTop = scrollTop;
839
- const viewportBottom = scrollTop + viewportHeight;
840
- // Check if element is within the visible viewport
841
- // Element is visible if it overlaps with the viewport
842
- return elementBottom > viewportTop && elementTop < viewportBottom;
843
- }
844
-
845
1192
  class IframeNavigationBlockerV2 {
846
1193
  doc;
847
1194
  win;
@@ -1139,7 +1486,7 @@ class IframeStyleReplacer {
1139
1486
  doc;
1140
1487
  win;
1141
1488
  config;
1142
- regex = /([-.\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
1489
+ regex = /([-.\d]+)(vh|svh|lvh|dvh)/gi; //vw|svw|lvw|dvw
1143
1490
  constructor(iframe, config) {
1144
1491
  if (!iframe.contentDocument || !iframe.contentWindow) {
1145
1492
  throw new Error('Iframe document or window not accessible');
@@ -1417,48 +1764,6 @@ function initIframeHelperFixer(config) {
1417
1764
  return fixer;
1418
1765
  }
1419
1766
 
1420
- const useHeatmapViz = (props) => {
1421
- const viewId = props?.viewId || useViewId();
1422
- // Viz store
1423
- const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
1424
- const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
1425
- const minZoomRatio = useHeatmapVizStore((state) => state.minZoomRatio[viewId] ?? 10);
1426
- const widthScale = useHeatmapVizStore((state) => state.scale[viewId] ?? 1);
1427
- const isScaledToFit = useHeatmapVizStore((state) => state.isScaledToFit[viewId] ?? false);
1428
- const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
1429
- const setZoomRatio = useHeatmapVizStore((state) => state.setZoomRatio);
1430
- const setMinZoomRatio = useHeatmapVizStore((state) => state.setMinZoomRatio);
1431
- const setScale = useHeatmapVizStore((state) => state.setScale);
1432
- const setIsScaledToFit = useHeatmapVizStore((state) => state.setIsScaledToFit);
1433
- // Single store
1434
- const vizRef = useHeatmapSingleStore((state) => state.vizRef[viewId] ?? null);
1435
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight[viewId] ?? 0);
1436
- const wrapperHeight = useHeatmapSingleStore((state) => state.wrapperHeight[viewId] ?? 0);
1437
- const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
1438
- const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
1439
- const setWrapperHeight = useHeatmapSingleStore((state) => state.setWrapperHeight);
1440
- return {
1441
- // State
1442
- isRenderViz,
1443
- zoomRatio,
1444
- minZoomRatio,
1445
- widthScale,
1446
- isScaledToFit,
1447
- vizRef,
1448
- iframeHeight,
1449
- wrapperHeight,
1450
- // Setters (auto-inject viewId)
1451
- setIsRenderViz: (value) => setIsRenderViz(value, viewId),
1452
- setZoomRatio: (value) => setZoomRatio(value, viewId),
1453
- setMinZoomRatio: (value) => setMinZoomRatio(value, viewId),
1454
- setScale: (value) => setScale(value, viewId),
1455
- setIsScaledToFit: (value) => setIsScaledToFit(value, viewId),
1456
- setVizRef: (value) => setVizRef(value, viewId),
1457
- setIframeHeight: (value) => setIframeHeight(value, viewId),
1458
- setWrapperHeight: (value) => setWrapperHeight(value, viewId),
1459
- };
1460
- };
1461
-
1462
1767
  const scrollToElementIfNeeded = (visualRef, rect, scale) => {
1463
1768
  if (!visualRef.current)
1464
1769
  return;
@@ -1477,9 +1782,7 @@ const scrollToElementIfNeeded = (visualRef, rect, scale) => {
1477
1782
  });
1478
1783
  };
1479
1784
  const useClickedElement = ({ visualRef, getRect }) => {
1480
- const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
1481
- const shouldShowCallout = useHeatmapInteractionStore((state) => state.shouldShowCallout);
1482
- const setShouldShowCallout = useHeatmapInteractionStore((state) => state.setShouldShowCallout);
1785
+ const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapInteraction();
1483
1786
  const { widthScale } = useHeatmapViz();
1484
1787
  const { dataInfo } = useHeatmapData();
1485
1788
  const [clickedElement, setClickedElement] = useState(null);
@@ -1490,6 +1793,8 @@ const useClickedElement = ({ visualRef, getRect }) => {
1490
1793
  setShouldShowCallout(false);
1491
1794
  };
1492
1795
  useEffect(() => {
1796
+ if (selectedElement === clickedElement?.hash)
1797
+ return;
1493
1798
  if (!selectedElement || !dataInfo?.elementMapInfo) {
1494
1799
  reset();
1495
1800
  return;
@@ -1515,13 +1820,12 @@ const useClickedElement = ({ visualRef, getRect }) => {
1515
1820
  requestAnimationFrame(() => {
1516
1821
  setClickedElement(elementInfo);
1517
1822
  });
1518
- }, [selectedElement, dataInfo, getRect, visualRef, widthScale]);
1519
- return { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout };
1823
+ }, [selectedElement, dataInfo, visualRef, widthScale]);
1824
+ return { clickedElement, showMissingElement, shouldShowCallout };
1520
1825
  };
1521
1826
 
1522
1827
  const useElementCalloutVisible = ({ visualRef, getRect }) => {
1523
- const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
1524
- const setShouldShowCallout = useHeatmapInteractionStore((state) => state.setShouldShowCallout);
1828
+ const { selectedElement, setShouldShowCallout } = useHeatmapInteraction();
1525
1829
  const { widthScale } = useHeatmapViz();
1526
1830
  const { dataInfo } = useHeatmapData();
1527
1831
  useEffect(() => {
@@ -1547,26 +1851,26 @@ const useElementCalloutVisible = ({ visualRef, getRect }) => {
1547
1851
  window.removeEventListener('resize', handleUpdate);
1548
1852
  visualRef?.current?.removeEventListener('scroll', handleUpdate);
1549
1853
  };
1550
- }, [selectedElement, visualRef, getRect, widthScale, dataInfo, setShouldShowCallout]);
1854
+ }, [selectedElement, visualRef, widthScale, dataInfo]);
1551
1855
  return {};
1552
1856
  };
1553
1857
 
1554
- const useHeatmapEffects = ({ isVisible, isElementSidebarOpen, setShouldShowCallout, resetAll, }) => {
1555
- const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
1858
+ const useHeatmapEffects = ({ isVisible }) => {
1859
+ useHeatmapInteraction();
1860
+ const resetAll = () => {
1861
+ // setShouldShowCallout(false);
1862
+ };
1556
1863
  // Reset khi ẩn
1557
1864
  useEffect(() => {
1558
- if (!isVisible)
1559
- resetAll();
1560
1865
  }, [isVisible, resetAll]);
1561
1866
  // Ẩn callout khi sidebar mở
1562
- useEffect(() => {
1563
- if (isElementSidebarOpen && selectedElement) {
1564
- setShouldShowCallout(false);
1565
- }
1566
- else if (!isElementSidebarOpen && selectedElement) {
1567
- setShouldShowCallout(true);
1568
- }
1569
- }, [isElementSidebarOpen, selectedElement, setShouldShowCallout]);
1867
+ // useEffect(() => {
1868
+ // if (isElementSidebarOpen && selectedElement) {
1869
+ // setShouldShowCallout(false);
1870
+ // } else if (!isElementSidebarOpen && selectedElement) {
1871
+ // setShouldShowCallout(true);
1872
+ // }
1873
+ // }, [isElementSidebarOpen, selectedElement]);
1570
1874
  };
1571
1875
 
1572
1876
  const useHeatmapElementPosition = ({ iframeRef, wrapperRef, visualizer }) => {
@@ -1722,9 +2026,7 @@ function HeatmapComponent() {
1722
2026
  */
1723
2027
 
1724
2028
  const useHoveredElement = ({ iframeRef, getRect }) => {
1725
- const hoveredElement = useHeatmapInteractionStore((state) => state.hoveredElement);
1726
- const setHoveredElement = useHeatmapInteractionStore((state) => state.setHoveredElement);
1727
- const onSelect = useHeatmapInteractionStore((state) => state.setSelectedElement);
2029
+ const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapInteraction();
1728
2030
  const { widthScale } = useHeatmapViz();
1729
2031
  const { dataInfo } = useHeatmapData();
1730
2032
  const reset = useCallback(() => {
@@ -1739,7 +2041,15 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
1739
2041
  return;
1740
2042
  }
1741
2043
  const iframe = iframeRef.current;
2044
+ if (!iframe) {
2045
+ reset();
2046
+ return;
2047
+ }
1742
2048
  const doc = iframe.contentDocument;
2049
+ if (!doc) {
2050
+ reset();
2051
+ return;
2052
+ }
1743
2053
  const iframeRect = iframe.getBoundingClientRect();
1744
2054
  const { x, y } = convertViewportToIframeCoords(event.clientX, event.clientY, iframeRect, widthScale);
1745
2055
  const targetElement = findTargetElement(doc, x, y, dataInfo);
@@ -1748,11 +2058,12 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
1748
2058
  return;
1749
2059
  }
1750
2060
  const hash = getElementHash(targetElement);
1751
- if (!!hash)
2061
+ if (hash)
1752
2062
  return hash;
1753
2063
  reset();
1754
2064
  return;
1755
- }, [dataInfo, iframeRef, getRect, widthScale, reset]);
2065
+ }, [dataInfo, iframeRef, widthScale, reset]);
2066
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1756
2067
  const handleMouseMove = useCallback(debounce((event) => {
1757
2068
  if (!dataInfo) {
1758
2069
  reset();
@@ -1775,7 +2086,8 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
1775
2086
  const handleClick = useCallback(() => {
1776
2087
  if (!hoveredElement?.hash)
1777
2088
  return;
1778
- onSelect(hoveredElement.hash);
2089
+ setSelectedElement(hoveredElement.hash);
2090
+ // eslint-disable-next-line react-hooks/exhaustive-deps
1779
2091
  }, [hoveredElement?.hash]);
1780
2092
  return {
1781
2093
  hoveredElement,
@@ -1801,7 +2113,6 @@ const findTargetElement = (doc, x, y, heatmapInfo) => {
1801
2113
  const element = elementsAtPoint[i];
1802
2114
  const elementHash = element.getAttribute(HEATMAP_ELEMENT_ATTRIBUTE);
1803
2115
  if (elementHash && heatmapInfo.elementMapInfo?.[elementHash]) {
1804
- heatmapInfo.elementMapInfo[elementHash];
1805
2116
  const boundingBox = getBoundingBox(element);
1806
2117
  if (boundingBox) {
1807
2118
  dataElement = element;
@@ -1809,7 +2120,7 @@ const findTargetElement = (doc, x, y, heatmapInfo) => {
1809
2120
  }
1810
2121
  }
1811
2122
  }
1812
- if (!!dataElement) {
2123
+ if (dataElement) {
1813
2124
  return dataElement;
1814
2125
  }
1815
2126
  let targetElement = getElementAtPoint(doc, x, y);
@@ -1887,7 +2198,7 @@ function useVizLiveIframeMsg(options = {}) {
1887
2198
  }
1888
2199
 
1889
2200
  function useVizLiveRender() {
1890
- const { setIframeHeight, wrapperHeight, setIsRenderViz } = useHeatmapViz();
2201
+ const { setIframeHeight, wrapperHeight, setIsRenderViz, wrapperWidth } = useHeatmapViz();
1891
2202
  const contentWidth = useHeatmapConfigStore((state) => state.width);
1892
2203
  const htmlContent = useHeatmapLiveStore((state) => state.htmlContent);
1893
2204
  const { iframeRef, isReady } = useVizLiveIframeMsg();
@@ -1905,17 +2216,20 @@ function useVizLiveRender() {
1905
2216
  useEffect(() => {
1906
2217
  if (!isReady)
1907
2218
  return;
2219
+ if (!wrapperHeight)
2220
+ return;
1908
2221
  if (!iframeRef.current)
1909
2222
  return;
2223
+ console.log(`🚀 🐥 ~ useVizLiveRender ~ wrapperHeight:`, wrapperHeight);
1910
2224
  const iframe = iframeRef.current;
1911
2225
  if (!iframe || !htmlContent)
1912
2226
  return;
1913
2227
  setIsRenderViz(false);
1914
- reset(iframe, { width: contentWidth, height: wrapperHeight }, (height) => {
2228
+ reset(iframe, { width: wrapperWidth, height: wrapperHeight }, (height) => {
1915
2229
  height && setIframeHeight(height);
1916
2230
  setIsRenderViz(true);
1917
2231
  });
1918
- }, [isReady, contentWidth, wrapperHeight]);
2232
+ }, [isReady, contentWidth, wrapperHeight, wrapperWidth]);
1919
2233
  return {
1920
2234
  iframeRef,
1921
2235
  };
@@ -1936,12 +2250,17 @@ function reset(iframe, rect, onSuccess) {
1936
2250
 
1937
2251
  const useHeatmapRender = () => {
1938
2252
  const { data } = useHeatmapData();
1939
- const { vizRef, setVizRef, setIsRenderViz, setIframeHeight } = useHeatmapViz();
2253
+ const { vizRef, setVizRef, setIsRenderViz, setIframeHeight, wrapperHeight, wrapperWidth } = useHeatmapViz();
2254
+ console.log(`🚀 🐥 ~ useHeatmapRender ~ wrapperHeight:`, wrapperHeight);
1940
2255
  const iframeRef = useRef(null);
1941
2256
  const renderHeatmap = useCallback(async (payloads) => {
1942
2257
  if (!payloads || payloads.length === 0)
1943
2258
  return;
1944
- const visualizer = vizRef || new Visualizer();
2259
+ let visualizer = vizRef;
2260
+ if (!visualizer) {
2261
+ visualizer = new Visualizer();
2262
+ setVizRef(visualizer);
2263
+ }
1945
2264
  setIsRenderViz(false);
1946
2265
  const iframe = iframeRef.current;
1947
2266
  if (!iframe?.contentWindow)
@@ -1950,7 +2269,6 @@ const useHeatmapRender = () => {
1950
2269
  initIframe(iframe, payloads, (height) => {
1951
2270
  height && setIframeHeight(height);
1952
2271
  setIsRenderViz(true);
1953
- setVizRef(visualizer);
1954
2272
  });
1955
2273
  }, []);
1956
2274
  useEffect(() => {
@@ -1965,7 +2283,9 @@ const useHeatmapRender = () => {
1965
2283
  iframeRef,
1966
2284
  };
1967
2285
  };
1968
- function initIframe(iframe, payloads, onSuccess) {
2286
+ function initIframe(iframe,
2287
+ // size: { width: number; height: number },
2288
+ payloads, onSuccess) {
1969
2289
  const { size } = findLastSizeOfDom(payloads);
1970
2290
  const docWidth = size.width ?? 0;
1971
2291
  const docHeight = size.height ?? 0;
@@ -2164,7 +2484,8 @@ const useContentDimensions = ({ iframeRef, }) => {
2164
2484
  };
2165
2485
 
2166
2486
  const useObserveIframeHeight = (props) => {
2167
- const { iframeRef, setIframeHeight, isRenderViz } = props;
2487
+ const { iframeRef, isRenderViz } = props;
2488
+ const { setIframeHeight } = useHeatmapViz();
2168
2489
  const resizeObserverRef = useRef(null);
2169
2490
  const mutationObserverRef = useRef(null);
2170
2491
  const debounceTimerRef = useRef(null);
@@ -2172,7 +2493,7 @@ const useObserveIframeHeight = (props) => {
2172
2493
  const animationFrameRef = useRef(null);
2173
2494
  const updateIframeHeight = useCallback(() => {
2174
2495
  const iframe = iframeRef.current;
2175
- if (!iframe || !setIframeHeight)
2496
+ if (!iframe)
2176
2497
  return;
2177
2498
  try {
2178
2499
  const iframeDocument = iframe.contentDocument;
@@ -2180,7 +2501,7 @@ const useObserveIframeHeight = (props) => {
2180
2501
  const iframeDocumentElement = iframeDocument?.documentElement;
2181
2502
  if (!iframeBody || !iframeDocumentElement)
2182
2503
  return;
2183
- iframe.style.height = 'auto';
2504
+ // iframe.style.height = 'auto'; // TODO: check if this is needed
2184
2505
  requestAnimationFrame(() => {
2185
2506
  const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
2186
2507
  const documentHeight = Math.max(iframeDocumentElement.scrollHeight, iframeDocumentElement.offsetHeight, iframeDocumentElement.clientHeight);
@@ -2196,7 +2517,7 @@ const useObserveIframeHeight = (props) => {
2196
2517
  catch (error) {
2197
2518
  console.warn('Cannot measure iframe content:', error);
2198
2519
  }
2199
- }, [iframeRef, setIframeHeight]);
2520
+ }, [iframeRef]);
2200
2521
  const debouncedUpdate = useCallback(() => {
2201
2522
  // Cancel pending updates
2202
2523
  if (debounceTimerRef.current) {
@@ -2298,7 +2619,7 @@ const useScaleCalculation = (props) => {
2298
2619
  const availableWidth = containerWidth - HEATMAP_CONFIG['padding'] * 2;
2299
2620
  const widthScale = Math.min(availableWidth / contentWidth, 1);
2300
2621
  // 2. Calculate available height
2301
- const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] ;
2622
+ const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] || 0;
2302
2623
  const paddingTotal = HEATMAP_CONFIG['padding'] * 2;
2303
2624
  const availableHeight = containerHeight - toolbarHeight - paddingTotal; // 10px buffer to avoid scroll bar
2304
2625
  // 3. Calculate minZoomRatio (zoom ratio minimum to fit iframe in container)
@@ -2354,13 +2675,13 @@ const useScrollSync = ({ widthScale, iframeRef, }) => {
2354
2675
  };
2355
2676
 
2356
2677
  const useHeatmapScale = (props) => {
2357
- const { wrapperRef, iframeRef, iframeHeight, setIframeHeight, isRenderViz } = props;
2678
+ const { wrapperRef, iframeRef, iframeHeight, isRenderViz } = props;
2358
2679
  // 1. Observe container dimensions
2359
2680
  const { containerWidth, containerHeight } = useContainerDimensions({ wrapperRef });
2360
2681
  // 2. Get content dimensions from config
2361
2682
  const { contentWidth } = useContentDimensions({ iframeRef });
2362
2683
  // 3. Observe iframe height (now reacts to width changes)
2363
- useObserveIframeHeight({ iframeRef, setIframeHeight, isRenderViz });
2684
+ useObserveIframeHeight({ iframeRef, isRenderViz });
2364
2685
  // 4. Calculate scale
2365
2686
  const { widthScale } = useScaleCalculation({
2366
2687
  containerWidth,
@@ -2385,15 +2706,17 @@ const useWrapperRefHeight = (props) => {
2385
2706
  const { isActive, wrapperRef } = props;
2386
2707
  const resizeObserverRef = useRef(null);
2387
2708
  const mutationObserverRef = useRef(null);
2388
- const { isRenderViz, setWrapperHeight } = useHeatmapViz();
2709
+ const { setWrapperHeight, setWrapperWidth } = useHeatmapViz();
2389
2710
  const updateWrapperHeight = useCallback(() => {
2390
2711
  const wrapper = wrapperRef.current;
2391
2712
  if (!wrapper)
2392
2713
  return;
2393
2714
  try {
2394
2715
  const wrapperHeight = wrapper.offsetHeight;
2716
+ const wrapperWidth = wrapper.offsetWidth;
2395
2717
  if (wrapperHeight > 0) {
2396
2718
  setWrapperHeight(wrapperHeight);
2719
+ setWrapperWidth(wrapperWidth);
2397
2720
  }
2398
2721
  }
2399
2722
  catch (error) {
@@ -2402,7 +2725,7 @@ const useWrapperRefHeight = (props) => {
2402
2725
  }, [wrapperRef]);
2403
2726
  useEffect(() => {
2404
2727
  const wrapper = wrapperRef.current;
2405
- if (!wrapper || !isRenderViz)
2728
+ if (!wrapper)
2406
2729
  return;
2407
2730
  const setupObservers = () => {
2408
2731
  try {
@@ -2432,6 +2755,7 @@ const useWrapperRefHeight = (props) => {
2432
2755
  updateWrapperHeight();
2433
2756
  }
2434
2757
  catch (error) {
2758
+ console.log(`🚀 🐥 ~ setupObservers ~ error:`, error);
2435
2759
  console.warn('Cannot access wrapper content:', error);
2436
2760
  }
2437
2761
  };
@@ -2446,11 +2770,11 @@ const useWrapperRefHeight = (props) => {
2446
2770
  mutationObserverRef.current.disconnect();
2447
2771
  }
2448
2772
  };
2449
- }, [wrapperRef, isRenderViz, updateWrapperHeight, isActive]);
2773
+ }, [wrapperRef, updateWrapperHeight, isActive]);
2450
2774
  return {};
2451
2775
  };
2452
2776
 
2453
- const useZonePositions = (options) => {
2777
+ const useZonePositions = (_options) => {
2454
2778
  const { iframeHeight } = useHeatmapViz();
2455
2779
  const getZonePosition = useCallback((zone) => {
2456
2780
  if (!iframeHeight) {
@@ -2580,184 +2904,509 @@ const getScrollGradientColor = (normalized) => {
2580
2904
  return `rgb(${r}, ${g}, ${b})`;
2581
2905
  };
2582
2906
 
2583
- const CompareViewContext = createContext(null);
2584
- /**
2585
- * Hook to safely access compare view context (returns null if not in compare mode)
2586
- */
2587
- const useCompareViewContextSafe = () => {
2588
- return useContext(CompareViewContext);
2589
- };
2590
-
2591
- /**
2592
- * Hook that returns data from compare view context if in compare mode,
2593
- * otherwise returns data from global store
2594
- */
2595
- const useCompareAwareData = () => {
2596
- const compareContext = useCompareViewContextSafe();
2597
- // Global store values
2598
- const globalData = useHeatmapDataStore((state) => state.data);
2599
- const globalClickmap = useHeatmapDataStore((state) => state.clickmap);
2600
- const globalScrollmap = useHeatmapDataStore((state) => state.scrollmap);
2601
- const globalDataInfo = useHeatmapDataStore((state) => state.dataInfo);
2602
- // If in compare mode, use view context
2603
- if (compareContext) {
2604
- return {
2605
- data: compareContext.view.data,
2606
- clickmap: compareContext.view.clickmap,
2607
- scrollmap: compareContext.view.scrollmap,
2608
- dataInfo: compareContext.view.dataInfo,
2907
+ class PerformanceLogger {
2908
+ static instance;
2909
+ metrics = [];
2910
+ sessionId;
2911
+ sessionStartTime;
2912
+ config;
2913
+ constructor() {
2914
+ this.sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
2915
+ this.sessionStartTime = Date.now();
2916
+ this.config = {
2917
+ enabled: false,
2918
+ logToConsole: false,
2919
+ logLevel: 'normal',
2920
+ thresholds: {
2921
+ slowRenderMs: 16, // > 16ms = slower than 60fps
2922
+ slowHookMs: 5,
2923
+ excessiveRenderCount: 10,
2924
+ },
2609
2925
  };
2610
2926
  }
2611
- // Otherwise use global store
2612
- return {
2613
- data: globalData,
2614
- clickmap: globalClickmap,
2615
- scrollmap: globalScrollmap,
2616
- dataInfo: globalDataInfo,
2617
- };
2618
- };
2619
- /**
2620
- * Hook that returns setters for data
2621
- * In compare mode, updates the view context
2622
- * In single/live mode, updates the global store
2623
- */
2624
- const useCompareAwareDataSetters = () => {
2625
- const compareContext = useCompareViewContextSafe();
2626
- const setDataGlobal = useHeatmapDataStore((state) => state.setData);
2627
- const setClickmapGlobal = useHeatmapDataStore((state) => state.setClickmap);
2628
- const setScrollmapGlobal = useHeatmapDataStore((state) => state.setScrollmap);
2629
- const setDataInfoGlobal = useHeatmapDataStore((state) => state.setDataInfo);
2630
- if (compareContext) {
2927
+ static getInstance() {
2928
+ if (!PerformanceLogger.instance) {
2929
+ PerformanceLogger.instance = new PerformanceLogger();
2930
+ }
2931
+ return PerformanceLogger.instance;
2932
+ }
2933
+ configure(config) {
2934
+ this.config = { ...this.config, ...config };
2935
+ if (this.config.enabled && this.config.logToConsole) {
2936
+ console.log('[Performance Monitor] Enabled', {
2937
+ sessionId: this.sessionId,
2938
+ config: this.config,
2939
+ });
2940
+ }
2941
+ }
2942
+ log(metric) {
2943
+ if (!this.config.enabled)
2944
+ return;
2945
+ this.metrics.push(metric);
2946
+ // Log to console based on level
2947
+ if (this.config.logToConsole) {
2948
+ this.logToConsole(metric);
2949
+ }
2950
+ // Send to external logger if configured
2951
+ if (this.config.externalLogger) {
2952
+ this.config.externalLogger(metric);
2953
+ }
2954
+ // Check thresholds and warn
2955
+ this.checkThresholds(metric);
2956
+ }
2957
+ logToConsole(metric) {
2958
+ const { logLevel } = this.config;
2959
+ if (logLevel === 'minimal') {
2960
+ // Only log warnings
2961
+ return;
2962
+ }
2963
+ const style = this.getConsoleStyle(metric.type);
2964
+ const label = `[${metric.type.toUpperCase()}] ${metric.name}`;
2965
+ if (logLevel === 'verbose') {
2966
+ console.log(`%c${label}`, style, metric);
2967
+ }
2968
+ else {
2969
+ // Normal: Log compact info
2970
+ const info = { name: metric.name };
2971
+ if (metric.duration)
2972
+ info.duration = `${metric.duration.toFixed(2)}ms`;
2973
+ if ('viewId' in metric && metric.viewId)
2974
+ info.viewId = metric.viewId;
2975
+ if ('renderCount' in metric)
2976
+ info.renderCount = metric.renderCount;
2977
+ console.log(`%c${label}`, style, info);
2978
+ }
2979
+ }
2980
+ getConsoleStyle(type) {
2981
+ const styles = {
2982
+ render: 'color: #61dafb; font-weight: bold',
2983
+ hook: 'color: #ffa500; font-weight: bold',
2984
+ store: 'color: #9c27b0; font-weight: bold',
2985
+ function: 'color: #4caf50; font-weight: bold',
2986
+ };
2987
+ return styles[type] || '';
2988
+ }
2989
+ checkThresholds(metric) {
2990
+ const { thresholds } = this.config;
2991
+ // Check slow render
2992
+ if (metric.type === 'render' && metric.duration && metric.duration > thresholds.slowRenderMs) {
2993
+ console.warn(`[Performance] Slow render detected: ${metric.name} took ${metric.duration.toFixed(2)}ms`, metric);
2994
+ }
2995
+ // Check slow hook
2996
+ if (metric.type === 'hook' && metric.duration && metric.duration > thresholds.slowHookMs) {
2997
+ console.warn(`[Performance] Slow hook detected: ${metric.name} took ${metric.duration.toFixed(2)}ms`, metric);
2998
+ }
2999
+ // Check excessive renders
3000
+ if (metric.type === 'render' && 'renderCount' in metric) {
3001
+ const renderMetric = metric;
3002
+ if (renderMetric.renderCount > thresholds.excessiveRenderCount) {
3003
+ console.warn(`[Performance] Excessive renders: ${metric.name} has rendered ${renderMetric.renderCount} times`, metric);
3004
+ }
3005
+ }
3006
+ }
3007
+ generateReport() {
3008
+ const now = Date.now();
3009
+ const duration = now - this.sessionStartTime;
3010
+ // Calculate summary
3011
+ const renderMetrics = this.metrics.filter((m) => m.type === 'render');
3012
+ const hookMetrics = this.metrics.filter((m) => m.type === 'hook');
3013
+ const storeMetrics = this.metrics.filter((m) => m.type === 'store');
3014
+ const totalRenders = renderMetrics.length;
3015
+ const totalHookCalls = hookMetrics.length;
3016
+ const totalStoreUpdates = storeMetrics.length;
3017
+ const renderDurations = renderMetrics
3018
+ .filter((m) => m.duration)
3019
+ .map((m) => m.duration);
3020
+ const averageRenderTime = renderDurations.length > 0
3021
+ ? renderDurations.reduce((a, b) => a + b, 0) / renderDurations.length
3022
+ : 0;
3023
+ // View-specific metrics
3024
+ const viewMetrics = {};
3025
+ this.metrics.forEach((metric) => {
3026
+ const viewId = 'viewId' in metric && metric.viewId ? metric.viewId : DEFAULT_VIEW_ID;
3027
+ if (!viewMetrics[viewId]) {
3028
+ viewMetrics[viewId] = { renders: 0, hookCalls: 0, storeUpdates: 0 };
3029
+ }
3030
+ if (metric.type === 'render')
3031
+ viewMetrics[viewId].renders++;
3032
+ if (metric.type === 'hook')
3033
+ viewMetrics[viewId].hookCalls++;
3034
+ if (metric.type === 'store')
3035
+ viewMetrics[viewId].storeUpdates++;
3036
+ });
3037
+ // Find warnings
3038
+ const renderCounts = new Map();
3039
+ renderMetrics.forEach((m) => {
3040
+ const key = `${m.name}-${'viewId' in m ? m.viewId : DEFAULT_VIEW_ID}`;
3041
+ renderCounts.set(key, (renderCounts.get(key) || 0) + 1);
3042
+ });
3043
+ const excessiveRenders = Array.from(renderCounts.entries())
3044
+ .filter(([, count]) => count > this.config.thresholds.excessiveRenderCount)
3045
+ .map(([key, count]) => {
3046
+ const [component, viewId] = key.split('-');
3047
+ return { component, count, viewId: viewId !== DEFAULT_VIEW_ID ? viewId : undefined };
3048
+ });
3049
+ const slowOperations = this.metrics
3050
+ .filter((m) => m.duration &&
3051
+ ((m.type === 'render' && m.duration > this.config.thresholds.slowRenderMs) ||
3052
+ (m.type === 'hook' && m.duration > this.config.thresholds.slowHookMs)))
3053
+ .map((m) => ({
3054
+ name: m.name,
3055
+ duration: m.duration,
3056
+ type: m.type,
3057
+ }));
2631
3058
  return {
2632
- setData: (data) => compareContext.updateView({ data }),
2633
- setClickmap: (clickmap) => compareContext.updateView({ clickmap }),
2634
- setScrollmap: (scrollmap) => compareContext.updateView({ scrollmap }),
2635
- setDataInfo: (dataInfo) => compareContext.updateView({ dataInfo }),
3059
+ session: {
3060
+ id: this.sessionId,
3061
+ startTime: this.sessionStartTime,
3062
+ endTime: now,
3063
+ duration,
3064
+ },
3065
+ summary: {
3066
+ totalRenders,
3067
+ totalHookCalls,
3068
+ totalStoreUpdates,
3069
+ averageRenderTime,
3070
+ viewMetrics,
3071
+ },
3072
+ metrics: this.metrics,
3073
+ warnings: {
3074
+ excessiveRenders,
3075
+ slowOperations,
3076
+ },
2636
3077
  };
2637
3078
  }
2638
- return {
2639
- setData: setDataGlobal,
2640
- setClickmap: setClickmapGlobal,
2641
- setScrollmap: setScrollmapGlobal,
2642
- setDataInfo: setDataInfoGlobal,
2643
- };
2644
- };
3079
+ clearMetrics() {
3080
+ this.metrics = [];
3081
+ this.sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
3082
+ this.sessionStartTime = Date.now();
3083
+ }
3084
+ getMetrics() {
3085
+ return [...this.metrics];
3086
+ }
3087
+ isEnabled() {
3088
+ return this.config.enabled;
3089
+ }
3090
+ }
3091
+ const performanceLogger = PerformanceLogger.getInstance();
2645
3092
 
2646
3093
  /**
2647
- * Hook that returns config from compare view context if in compare mode,
2648
- * otherwise returns config from global store
3094
+ * Hook to track render count of a component
3095
+ * @param componentName - Name of the component
3096
+ * @param viewId - Optional viewId for compare mode
2649
3097
  */
2650
- const useCompareAwareConfig = () => {
2651
- const compareContext = useCompareViewContextSafe();
2652
- // Global store values
2653
- const globalHeatmapType = useHeatmapConfigStore((state) => state.heatmapType);
2654
- const globalClickType = useHeatmapConfigStore((state) => state.clickType);
2655
- const globalScrollType = useHeatmapConfigStore((state) => state.scrollType);
2656
- // If in compare mode, use view context
2657
- if (compareContext) {
2658
- return {
2659
- heatmapType: compareContext.view.heatmapType,
2660
- clickType: compareContext.view.clickType,
2661
- scrollType: compareContext.view.scrollType,
3098
+ function useRenderCount(componentName, viewId) {
3099
+ const renderCount = useRef(0);
3100
+ const startTime = useRef(0);
3101
+ // Increment before render
3102
+ renderCount.current += 1;
3103
+ startTime.current = performance.now();
3104
+ useEffect(() => {
3105
+ const duration = performance.now() - startTime.current;
3106
+ const metric = {
3107
+ id: `render-${componentName}-${Date.now()}`,
3108
+ type: 'render',
3109
+ name: componentName,
3110
+ componentName,
3111
+ renderCount: renderCount.current,
3112
+ timestamp: Date.now(),
3113
+ duration,
3114
+ viewId,
2662
3115
  };
2663
- }
2664
- // Otherwise use global store
2665
- return {
2666
- heatmapType: globalHeatmapType,
2667
- clickType: globalClickType,
2668
- scrollType: globalScrollType,
2669
- };
2670
- };
3116
+ performanceLogger.log(metric);
3117
+ });
3118
+ return renderCount.current;
3119
+ }
2671
3120
  /**
2672
- * Hook that returns config setters
2673
- * In compare mode, updates the view context
2674
- * In single/live mode, updates the global store
3121
+ * Hook to detect why a component re-rendered (which props changed)
3122
+ * @param componentName - Name of the component
3123
+ * @param props - Props object to track
3124
+ * @param viewId - Optional viewId
2675
3125
  */
2676
- const useCompareAwareConfigSetters = () => {
2677
- const compareContext = useCompareViewContextSafe();
2678
- const setHeatmapTypeGlobal = useHeatmapConfigStore((state) => state.setHeatmapType);
2679
- const setClickTypeGlobal = useHeatmapConfigStore((state) => state.setClickType);
2680
- const setScrollTypeGlobal = useHeatmapConfigStore((state) => state.setScrollType);
2681
- if (compareContext) {
2682
- return {
2683
- setHeatmapType: (heatmapType) => compareContext.updateView({ heatmapType }),
2684
- setClickType: (clickType) => compareContext.updateView({ clickType }),
2685
- setScrollType: (scrollType) => compareContext.updateView({ scrollType }),
2686
- };
2687
- }
2688
- return {
2689
- setHeatmapType: setHeatmapTypeGlobal,
2690
- setClickType: setClickTypeGlobal,
2691
- setScrollType: setScrollTypeGlobal,
3126
+ function useWhyDidYouUpdate(componentName, props, viewId) {
3127
+ const previousProps = useRef();
3128
+ const renderCount = useRef(0);
3129
+ const startTime = useRef(0);
3130
+ renderCount.current += 1;
3131
+ startTime.current = performance.now();
3132
+ useEffect(() => {
3133
+ if (previousProps.current) {
3134
+ const duration = performance.now() - startTime.current;
3135
+ const allKeys = Object.keys({ ...previousProps.current, ...props });
3136
+ const changedProps = [];
3137
+ allKeys.forEach((key) => {
3138
+ if (previousProps.current[key] !== props[key]) {
3139
+ changedProps.push(key);
3140
+ }
3141
+ });
3142
+ if (changedProps.length > 0) {
3143
+ const metric = {
3144
+ id: `render-${componentName}-${Date.now()}`,
3145
+ type: 'render',
3146
+ name: componentName,
3147
+ componentName,
3148
+ renderCount: renderCount.current,
3149
+ timestamp: Date.now(),
3150
+ duration,
3151
+ viewId,
3152
+ reason: 'Props changed',
3153
+ propsChanged: changedProps,
3154
+ metadata: {
3155
+ changes: changedProps.reduce((acc, key) => {
3156
+ acc[key] = {
3157
+ from: previousProps.current[key],
3158
+ to: props[key],
3159
+ };
3160
+ return acc;
3161
+ }, {}),
3162
+ },
3163
+ };
3164
+ performanceLogger.log(metric);
3165
+ }
3166
+ }
3167
+ previousProps.current = props;
3168
+ });
3169
+ }
3170
+ /**
3171
+ * Hook to measure execution time of a function
3172
+ * @param functionName - Name of the function
3173
+ * @param fn - Function to measure
3174
+ * @param viewId - Optional viewId
3175
+ */
3176
+ function useMeasureFunction(functionName, fn, viewId) {
3177
+ const measuredFn = ((...args) => {
3178
+ const startTime = performance.now();
3179
+ const result = fn(...args);
3180
+ const duration = performance.now() - startTime;
3181
+ performanceLogger.log({
3182
+ id: `function-${functionName}-${Date.now()}`,
3183
+ type: 'function',
3184
+ name: functionName,
3185
+ functionName,
3186
+ timestamp: Date.now(),
3187
+ duration,
3188
+ viewId,
3189
+ metadata: {
3190
+ args: args.length,
3191
+ },
3192
+ });
3193
+ return result;
3194
+ });
3195
+ return measuredFn;
3196
+ }
3197
+ /**
3198
+ * Hook to track when a hook is called
3199
+ * @param hookName - Name of the hook
3200
+ * @param viewId - Optional viewId
3201
+ * @param storeSlice - Optional store slice being accessed
3202
+ */
3203
+ function useTrackHookCall(hookName, viewId, storeSlice) {
3204
+ const startTime = useRef(0);
3205
+ startTime.current = performance.now();
3206
+ useEffect(() => {
3207
+ const duration = performance.now() - startTime.current;
3208
+ performanceLogger.log({
3209
+ id: `hook-${hookName}-${Date.now()}`,
3210
+ type: 'hook',
3211
+ name: hookName,
3212
+ hookName,
3213
+ timestamp: Date.now(),
3214
+ duration,
3215
+ viewId,
3216
+ storeSlice,
3217
+ });
3218
+ });
3219
+ }
3220
+
3221
+ /**
3222
+ * HOC to track component performance
3223
+ * @param Component - Component to wrap
3224
+ * @param options - Tracking options
3225
+ */
3226
+ function withPerformanceTracking(Component, options = {}) {
3227
+ const { trackProps = true, componentName, viewIdProp = 'viewId' } = options;
3228
+ const WrappedComponent = (props) => {
3229
+ const name = componentName || Component.displayName || Component.name || 'Unknown';
3230
+ const viewId = viewIdProp in props ? props[viewIdProp] : undefined;
3231
+ if (trackProps) {
3232
+ useWhyDidYouUpdate(name, props, viewId); // eslint-disable-line react-hooks/rules-of-hooks
3233
+ }
3234
+ return jsx(Component, { ...props });
2692
3235
  };
2693
- };
3236
+ WrappedComponent.displayName = `withPerformanceTracking(${componentName || Component.displayName || Component.name})`;
3237
+ return WrappedComponent;
3238
+ }
2694
3239
 
2695
3240
  /**
2696
- * Hook that returns viz state from compare view context if in compare mode,
2697
- * otherwise returns viz state from global store
3241
+ * Middleware để track store updates
2698
3242
  */
2699
- const useCompareAwareViz = () => {
2700
- const compareContext = useCompareViewContextSafe();
2701
- // Global store values
2702
- const globalZoomRatio = useHeatmapVizStore((state) => state.zoomRatio);
2703
- const globalScale = useHeatmapVizStore((state) => state.scale);
2704
- const globalIsScaledToFit = useHeatmapVizStore((state) => state.isScaledToFit);
2705
- const globalIsRenderViz = useHeatmapVizStore((state) => state.isRenderViz);
2706
- const globalIframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2707
- const globalVizRef = useHeatmapSingleStore((state) => state.vizRef);
2708
- // If in compare mode, use view context
2709
- if (compareContext) {
2710
- return {
2711
- zoomRatio: compareContext.view.zoomRatio,
2712
- scale: compareContext.view.scale,
2713
- isScaledToFit: compareContext.view.isScaledToFit,
2714
- isRenderViz: compareContext.view.isRenderViz,
2715
- iframeHeight: compareContext.view.iframeHeight,
2716
- vizRef: compareContext.view.vizRef,
3243
+ function createStorePerformanceTracker(storeName) {
3244
+ return (config) => (set, get, api) => {
3245
+ const wrappedSet = (partial, replace) => {
3246
+ const startTime = performance.now();
3247
+ const prevState = get();
3248
+ // Call original set
3249
+ set(partial, replace);
3250
+ const duration = performance.now() - startTime;
3251
+ const nextState = get();
3252
+ // Detect which viewIds were affected
3253
+ const affectedViews = new Set();
3254
+ // Check Record<string, any> properties for viewId keys
3255
+ Object.keys(nextState).forEach((key) => {
3256
+ if (typeof prevState[key] === 'object' &&
3257
+ typeof nextState[key] === 'object' &&
3258
+ prevState[key] !== nextState[key]) {
3259
+ // Check if this is a Record<viewId, value> structure
3260
+ const prevKeys = Object.keys(prevState[key] || {});
3261
+ const nextKeys = Object.keys(nextState[key] || {});
3262
+ const allKeys = new Set([...prevKeys, ...nextKeys]);
3263
+ allKeys.forEach((viewId) => {
3264
+ if (prevState[key]?.[viewId] !== nextState[key]?.[viewId]) {
3265
+ affectedViews.add(viewId);
3266
+ }
3267
+ });
3268
+ }
3269
+ });
3270
+ const metric = {
3271
+ id: `store-${storeName}-${Date.now()}`,
3272
+ type: 'store',
3273
+ name: `${storeName} update`,
3274
+ storeName,
3275
+ action: typeof partial === 'function' ? 'function update' : 'direct update',
3276
+ timestamp: Date.now(),
3277
+ duration,
3278
+ affectedViews: Array.from(affectedViews),
3279
+ metadata: {
3280
+ stateKeys: Object.keys(nextState),
3281
+ },
3282
+ };
3283
+ performanceLogger.log(metric);
2717
3284
  };
2718
- }
2719
- // Otherwise use global store
2720
- return {
2721
- zoomRatio: globalZoomRatio,
2722
- scale: globalScale,
2723
- isScaledToFit: globalIsScaledToFit,
2724
- isRenderViz: globalIsRenderViz,
2725
- iframeHeight: globalIframeHeight,
2726
- vizRef: globalVizRef,
3285
+ return config(wrappedSet, get, api);
2727
3286
  };
2728
- };
3287
+ }
2729
3288
  /**
2730
- * Hook that returns viz setters
2731
- * In compare mode, updates the view context
2732
- * In single/live mode, updates the global store
3289
+ * Track specific store action
2733
3290
  */
2734
- const useCompareAwareVizSetters = () => {
2735
- const compareContext = useCompareViewContextSafe();
2736
- const setZoomRatioGlobal = useHeatmapVizStore((state) => state.setZoomRatio);
2737
- const setScaleGlobal = useHeatmapVizStore((state) => state.setScale);
2738
- const setIsScaledToFitGlobal = useHeatmapVizStore((state) => state.setIsScaledToFit);
2739
- const setIsRenderVizGlobal = useHeatmapVizStore((state) => state.setIsRenderViz);
2740
- const setIframeHeightGlobal = useHeatmapSingleStore((state) => state.setIframeHeight);
2741
- const setVizRefGlobal = useHeatmapSingleStore((state) => state.setVizRef);
2742
- if (compareContext) {
2743
- return {
2744
- setZoomRatio: (zoomRatio) => compareContext.updateView({ zoomRatio }),
2745
- setScale: (scale) => compareContext.updateView({ scale }),
2746
- setIsScaledToFit: (isScaledToFit) => compareContext.updateView({ isScaledToFit }),
2747
- setIsRenderViz: (isRenderViz) => compareContext.updateView({ isRenderViz }),
2748
- setIframeHeight: (iframeHeight) => compareContext.updateView({ iframeHeight }),
2749
- setVizRef: (vizRef) => compareContext.updateView({ vizRef }),
2750
- };
3291
+ function trackStoreAction(storeName, action, viewId, metadata) {
3292
+ const metric = {
3293
+ id: `store-${storeName}-${action}-${Date.now()}`,
3294
+ type: 'store',
3295
+ name: `${storeName}.${action}`,
3296
+ storeName,
3297
+ action,
3298
+ timestamp: Date.now(),
3299
+ viewId,
3300
+ metadata,
3301
+ };
3302
+ performanceLogger.log(metric);
3303
+ }
3304
+
3305
+ function getPerformanceReportJSON() {
3306
+ const report = performanceLogger.generateReport();
3307
+ return JSON.stringify(report, null, 2);
3308
+ }
3309
+ function downloadPerformanceReport(filename = 'heatmap-performance-report.json') {
3310
+ const report = performanceLogger.generateReport();
3311
+ const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
3312
+ const url = URL.createObjectURL(blob);
3313
+ const link = document.createElement('a');
3314
+ link.href = url;
3315
+ link.download = filename;
3316
+ link.click();
3317
+ URL.revokeObjectURL(url);
3318
+ }
3319
+ async function sendPerformanceReport(endpoint) {
3320
+ const report = performanceLogger.generateReport();
3321
+ try {
3322
+ const response = await fetch(endpoint, {
3323
+ method: 'POST',
3324
+ headers: {
3325
+ 'Content-Type': 'application/json',
3326
+ },
3327
+ body: JSON.stringify(report),
3328
+ });
3329
+ if (!response.ok) {
3330
+ throw new Error(`Failed to send report: ${response.statusText}`);
3331
+ }
3332
+ }
3333
+ catch (error) {
3334
+ console.error('[Performance] Failed to send report:', error);
3335
+ throw error;
3336
+ }
3337
+ }
3338
+ function printPerformanceSummary() {
3339
+ const report = performanceLogger.generateReport();
3340
+ console.group('📊 Performance Summary');
3341
+ console.log('Session:', report.session);
3342
+ console.log('Total Renders:', report.summary.totalRenders);
3343
+ console.log('Total Hook Calls:', report.summary.totalHookCalls);
3344
+ console.log('Total Store Updates:', report.summary.totalStoreUpdates);
3345
+ console.log('Average Render Time:', `${report.summary.averageRenderTime.toFixed(2)}ms`);
3346
+ console.log('View Metrics:', report.summary.viewMetrics);
3347
+ if (report.warnings.excessiveRenders.length > 0) {
3348
+ console.group('⚠️ Excessive Renders');
3349
+ report.warnings.excessiveRenders.forEach((warning) => {
3350
+ console.warn(`${warning.component}${warning.viewId ? ` (${warning.viewId})` : ''}: ${warning.count} renders`);
3351
+ });
3352
+ console.groupEnd();
3353
+ }
3354
+ if (report.warnings.slowOperations.length > 0) {
3355
+ console.group('🐌 Slow Operations');
3356
+ report.warnings.slowOperations.forEach((warning) => {
3357
+ console.warn(`${warning.name} (${warning.type}): ${warning.duration.toFixed(2)}ms`);
3358
+ });
3359
+ console.groupEnd();
2751
3360
  }
3361
+ console.groupEnd();
3362
+ }
3363
+ function getMetricsByViewId(viewId) {
3364
+ const allMetrics = performanceLogger.getMetrics();
3365
+ const filteredMetrics = allMetrics.filter((m) => {
3366
+ return 'viewId' in m && m.viewId === viewId;
3367
+ });
3368
+ const report = performanceLogger.generateReport();
2752
3369
  return {
2753
- setZoomRatio: setZoomRatioGlobal,
2754
- setScale: setScaleGlobal,
2755
- setIsScaledToFit: setIsScaledToFitGlobal,
2756
- setIsRenderViz: setIsRenderVizGlobal,
2757
- setIframeHeight: setIframeHeightGlobal,
2758
- setVizRef: setVizRefGlobal,
3370
+ ...report,
3371
+ metrics: filteredMetrics,
3372
+ summary: {
3373
+ ...report.summary,
3374
+ totalRenders: filteredMetrics.filter((m) => m.type === 'render').length,
3375
+ totalHookCalls: filteredMetrics.filter((m) => m.type === 'hook').length,
3376
+ totalStoreUpdates: filteredMetrics.filter((m) => m.type === 'store').length,
3377
+ averageRenderTime: 0, // Recalculate if needed
3378
+ viewMetrics: {
3379
+ [viewId]: report.summary.viewMetrics[viewId] || {
3380
+ renders: 0,
3381
+ hookCalls: 0,
3382
+ storeUpdates: 0,
3383
+ },
3384
+ },
3385
+ },
2759
3386
  };
2760
- };
3387
+ }
3388
+ function compareViewPerformance(viewId1, viewId2) {
3389
+ const report = performanceLogger.generateReport();
3390
+ const view1Metrics = report.summary.viewMetrics[viewId1] || {
3391
+ renders: 0,
3392
+ hookCalls: 0,
3393
+ storeUpdates: 0,
3394
+ };
3395
+ const view2Metrics = report.summary.viewMetrics[viewId2] || {
3396
+ renders: 0,
3397
+ hookCalls: 0,
3398
+ storeUpdates: 0,
3399
+ };
3400
+ return {
3401
+ view1: view1Metrics,
3402
+ view2: view2Metrics,
3403
+ difference: {
3404
+ renders: view1Metrics.renders - view2Metrics.renders,
3405
+ hookCalls: view1Metrics.hookCalls - view2Metrics.hookCalls,
3406
+ storeUpdates: view1Metrics.storeUpdates - view2Metrics.storeUpdates,
3407
+ },
3408
+ };
3409
+ }
2761
3410
 
2762
3411
  const BoxStack = forwardRef(({ children, ...props }, ref) => {
2763
3412
  const id = props.id;
@@ -2770,7 +3419,7 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
2770
3419
  const style = props.style || {};
2771
3420
  const gap = props.gap || 0;
2772
3421
  const height = props.height || 'auto';
2773
- const isZIndexDefined = typeof props.zIndex !== undefined;
3422
+ const isZIndexDefined = typeof props.zIndex !== 'undefined';
2774
3423
  const zIndex = props.zIndex;
2775
3424
  const backgroundColor = props.backgroundColor || 'transparent';
2776
3425
  const styleGap = useMemo(() => {
@@ -2801,6 +3450,7 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
2801
3450
  };
2802
3451
  return (jsx("div", { id: id, style: styleProps, ref: ref, children: children }));
2803
3452
  });
3453
+ BoxStack.displayName = 'BoxStack';
2804
3454
 
2805
3455
  const ContentTopBar = () => {
2806
3456
  const controls = useHeatmapControlStore((state) => state.controls);
@@ -2817,8 +3467,9 @@ const ContentTopBar = () => {
2817
3467
 
2818
3468
  const ContentMetricBar = () => {
2819
3469
  const controls = useHeatmapControlStore((state) => state.controls);
3470
+ const borderBottom = `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`;
2820
3471
  return (jsx(BoxStack, { id: "gx-hm-content-metric-bar", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
2821
- borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3472
+ borderBottom,
2822
3473
  }, children: controls.MetricBar ?? null }));
2823
3474
  };
2824
3475
 
@@ -2834,30 +3485,88 @@ const ContentToolbar = () => {
2834
3485
  }, children: controls.Toolbar ?? null }));
2835
3486
  };
2836
3487
 
3488
+ const ContentSidebar = () => {
3489
+ const controls = useHeatmapControlStore((state) => state.controls);
3490
+ const { state } = useHeatmapInteraction();
3491
+ const isHideSidebar = state.hideSidebar;
3492
+ const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3493
+ const mode = useHeatmapConfigStore((state) => state.mode);
3494
+ const SidebarComponent = controls.Sidebar ?? null;
3495
+ const isCompareMode = mode === 'compare';
3496
+ if (isCompareMode)
3497
+ return null;
3498
+ if (!SidebarComponent)
3499
+ return null;
3500
+ return (jsx("div", { className: "gx-hm-sidebar", style: {
3501
+ height: '100%',
3502
+ display: 'flex',
3503
+ zIndex: 1,
3504
+ ...(isHideSidebar
3505
+ ? {
3506
+ width: '0',
3507
+ transform: 'translateX(-100%)',
3508
+ visibility: 'hidden',
3509
+ }
3510
+ : { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
3511
+ }, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
3512
+ height: '100%',
3513
+ width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
3514
+ borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3515
+ }, children: jsx(SidebarComponent, {}) }) }));
3516
+ };
3517
+
3518
+ const PopoverSidebar = () => {
3519
+ const mode = useHeatmapConfigStore((state) => state.mode);
3520
+ const CompSidebar = useHeatmapControlStore((state) => state.controls.Sidebar);
3521
+ const CompSidebarActivator = useHeatmapControlStore((state) => state.controls.SidebarActivator);
3522
+ const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3523
+ const [isPopoverOpen, setIsPopoverOpen] = useState(false);
3524
+ const { state } = useHeatmapInteraction();
3525
+ const isCompareMode = mode === 'compare';
3526
+ const isHideSidebar = state.hideSidebar;
3527
+ const stylePopover = {
3528
+ position: 'absolute',
3529
+ top: '24px',
3530
+ left: '24px',
3531
+ zIndex: Z_INDEX.SIDEBAR_POPOVER,
3532
+ height: `calc(100% - ${HEATMAP_CONFIG.heightToolbar}px - ${HEATMAP_CONFIG.padding}px - 24px)`,
3533
+ width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
3534
+ };
3535
+ if (isHideSidebar || !isCompareMode)
3536
+ return null;
3537
+ if (!CompSidebar || !CompSidebarActivator)
3538
+ return null;
3539
+ 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: {
3540
+ ...stylePopover,
3541
+ backgroundColor: '#fff',
3542
+ borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3543
+ borderRadius: '8px',
3544
+ boxShadow: '4px 0 12px rgba(0, 0, 0, 0.15)',
3545
+ overflow: 'auto',
3546
+ }, children: jsx(CompSidebar, { closeAction: { onClick: () => setIsPopoverOpen(false) } }) }) }))] }));
3547
+ };
3548
+
2837
3549
  const VizContainer = ({ children, isActive = false }) => {
2838
3550
  const wrapperRef = useRef(null);
3551
+ const viewId = useViewId();
2839
3552
  useWrapperRefHeight({
2840
3553
  isActive,
2841
3554
  wrapperRef,
2842
3555
  });
2843
- 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: {
2844
- minWidth: '394px',
2845
- }, children: children }) }));
3556
+ 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: {
3557
+ minWidth: '394px',
3558
+ }, children: children }), jsx(PopoverSidebar, {})] }));
2846
3559
  };
2847
3560
 
2848
3561
  const useClickmap = () => {
2849
- const [isInitialized, setIsInitialized] = useState(false);
2850
- const { clickmap } = useHeatmapData();
2851
3562
  const { vizRef } = useHeatmapViz();
3563
+ const { clickmap } = useHeatmapData();
2852
3564
  const start = useCallback(() => {
2853
- if (isInitialized)
2854
- return;
2855
3565
  if (!vizRef || !clickmap || clickmap.length === 0)
2856
3566
  return;
2857
3567
  try {
2858
3568
  vizRef?.clearmap?.();
2859
3569
  vizRef?.clickmap?.(clickmap);
2860
- setIsInitialized(true);
2861
3570
  }
2862
3571
  catch (error) {
2863
3572
  console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
@@ -2885,7 +3594,7 @@ const useScrollmap = () => {
2885
3594
  return { start };
2886
3595
  };
2887
3596
 
2888
- const useHeatmapCanvas = ({ iframeRef, }) => {
3597
+ const useHeatmapCanvas = () => {
2889
3598
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
2890
3599
  const { start: startClickmap } = useClickmap();
2891
3600
  const { start: startScrollmap } = useScrollmap();
@@ -2901,10 +3610,28 @@ const useHeatmapCanvas = ({ iframeRef, }) => {
2901
3610
  }, [heatmapType, startClickmap, startScrollmap]);
2902
3611
  };
2903
3612
 
2904
- const CLICKED_ELEMENT_ID = 'gx-hm-clicked-element';
2905
- const SECONDARY_CLICKED_ELEMENT_ID = 'gx-hm-secondary-clicked-element';
2906
- const HOVERED_ELEMENT_ID = 'gx-hm-hovered-element';
2907
- const SECONDARY_HOVERED_ELEMENT_ID = 'gx-hm-secondary-hovered-element';
3613
+ // Base IDs for elements (without viewId suffix)
3614
+ const CLICKED_ELEMENT_ID_BASE = 'gx-hm-clicked-element';
3615
+ const SECONDARY_CLICKED_ELEMENT_ID_BASE = 'gx-hm-secondary-clicked-element';
3616
+ const HOVERED_ELEMENT_ID_BASE = 'gx-hm-hovered-element';
3617
+ const SECONDARY_HOVERED_ELEMENT_ID_BASE = 'gx-hm-secondary-hovered-element';
3618
+ /**
3619
+ * Generate unique element ID for a specific view
3620
+ * @param baseId - Base element ID
3621
+ * @param viewId - View ID
3622
+ * @returns Unique element ID (e.g., 'gx-hm-clicked-element-view-0')
3623
+ */
3624
+ const getElementId = (baseId, viewId) => {
3625
+ return `${baseId}-${viewId}`;
3626
+ };
3627
+ const getClickedElementId = (viewId, isSecondary = false) => {
3628
+ const baseId = isSecondary ? SECONDARY_CLICKED_ELEMENT_ID_BASE : CLICKED_ELEMENT_ID_BASE;
3629
+ return getElementId(baseId, viewId);
3630
+ };
3631
+ const getHoveredElementId = (viewId, isSecondary = false) => {
3632
+ const baseId = isSecondary ? SECONDARY_HOVERED_ELEMENT_ID_BASE : HOVERED_ELEMENT_ID_BASE;
3633
+ return getElementId(baseId, viewId);
3634
+ };
2908
3635
 
2909
3636
  const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
2910
3637
  const style = calculateRankPosition(elementRect, widthScale);
@@ -2934,6 +3661,7 @@ const DEFAULT_POSITION = {
2934
3661
  };
2935
3662
  const ElementCallout = (props) => {
2936
3663
  const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
3664
+ const viewId = useViewId();
2937
3665
  const { element, target, visualRef, hozOffset, alignment = 'left' } = props;
2938
3666
  const calloutRef = useRef(null);
2939
3667
  const [position, setPosition] = useState(DEFAULT_POSITION);
@@ -2948,6 +3676,7 @@ const ElementCallout = (props) => {
2948
3676
  setPosition,
2949
3677
  hozOffset,
2950
3678
  alignment,
3679
+ containerElm: visualRef?.current,
2951
3680
  });
2952
3681
  positionFn();
2953
3682
  const handleUpdate = () => {
@@ -2966,9 +3695,9 @@ const ElementCallout = (props) => {
2966
3695
  position: 'fixed',
2967
3696
  top: position.top,
2968
3697
  left: position.left,
2969
- zIndex: 2147483647,
3698
+ zIndex: Z_INDEX.CALLOUT,
2970
3699
  }, "aria-live": "assertive", role: "tooltip", children: CompElementCallout && jsx(CompElementCallout, { elementHash: element.hash }) }));
2971
- return createPortal(calloutContent, document.getElementById('gx-hm-viz-container'));
3700
+ return createPortal(calloutContent, document.getElementById(`gx-hm-viz-container-${viewId}`));
2972
3701
  };
2973
3702
 
2974
3703
  const ElementMissing = ({ show = true }) => {
@@ -2992,17 +3721,8 @@ const ElementMissing = ({ show = true }) => {
2992
3721
  }, "aria-live": "assertive", children: "Element not visible on current screen" }));
2993
3722
  };
2994
3723
 
2995
- const TARGET_ID_BY_TYPE = {
2996
- hovered: {
2997
- default: HOVERED_ELEMENT_ID,
2998
- secondary: SECONDARY_HOVERED_ELEMENT_ID,
2999
- },
3000
- clicked: {
3001
- default: CLICKED_ELEMENT_ID,
3002
- secondary: SECONDARY_CLICKED_ELEMENT_ID,
3003
- },
3004
- };
3005
- const ElementOverlay = ({ type, element, onClick, isSecondary }) => {
3724
+ const ElementOverlay = ({ type, element, onClick, isSecondary, elementId, }) => {
3725
+ // useRenderCount('ElementOverlay');
3006
3726
  const { widthScale } = useHeatmapViz();
3007
3727
  if (!element || (element.width === 0 && element.height === 0))
3008
3728
  return null;
@@ -3011,8 +3731,7 @@ const ElementOverlay = ({ type, element, onClick, isSecondary }) => {
3011
3731
  const left = element.left + HEATMAP_CONFIG['borderWidthIframe'];
3012
3732
  const width = element.width;
3013
3733
  const height = element.height;
3014
- const targetId = TARGET_ID_BY_TYPE[type][isSecondary ? 'secondary' : 'default'];
3015
- return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: "heatmapElement", id: targetId, style: {
3734
+ return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: "heatmapElement", id: elementId, style: {
3016
3735
  top,
3017
3736
  left,
3018
3737
  width,
@@ -3026,14 +3745,17 @@ const ELEMENT_CALLOUT = {
3026
3745
  alignment: 'left',
3027
3746
  };
3028
3747
  const HeatmapElements = (props) => {
3748
+ const viewId = useViewId();
3029
3749
  const { iframeHeight } = useHeatmapViz();
3030
- const { iframeRef, wrapperRef, visualRef, visualizer, iframeDimensions, isElementSidebarOpen, isVisible = true, areDefaultRanksHidden, isSecondary, } = props;
3750
+ const clickedElementId = getClickedElementId(viewId, props.isSecondary);
3751
+ const hoveredElementId = getHoveredElementId(viewId, props.isSecondary);
3752
+ const { iframeRef, wrapperRef, visualRef, visualizer, iframeDimensions, isVisible = true, areDefaultRanksHidden, isSecondary, } = props;
3031
3753
  const getRect = useHeatmapElementPosition({
3032
3754
  iframeRef,
3033
3755
  wrapperRef,
3034
3756
  visualizer,
3035
3757
  });
3036
- const { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout } = useClickedElement({
3758
+ const { clickedElement, showMissingElement, shouldShowCallout } = useClickedElement({
3037
3759
  visualRef,
3038
3760
  getRect,
3039
3761
  });
@@ -3041,25 +3763,12 @@ const HeatmapElements = (props) => {
3041
3763
  iframeRef,
3042
3764
  getRect,
3043
3765
  });
3044
- useElementCalloutVisible({
3045
- visualRef,
3046
- getRect,
3047
- });
3048
- const resetAll = () => {
3049
- // setShouldShowCallout(false);
3050
- };
3051
- useHeatmapEffects({
3052
- isVisible,
3053
- isElementSidebarOpen,
3054
- setShouldShowCallout,
3055
- resetAll,
3056
- });
3766
+ useElementCalloutVisible({ visualRef, getRect });
3767
+ useHeatmapEffects({ isVisible });
3768
+ useRenderCount('HeatmapElements');
3057
3769
  if (!isVisible)
3058
3770
  return null;
3059
- return (jsxs("div", { onMouseMove: (event) => {
3060
- handleMouseMove(event);
3061
- // handleMouseMove2(event as any);
3062
- }, 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 }))] }));
3771
+ 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 }))] }));
3063
3772
  };
3064
3773
 
3065
3774
  const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
@@ -3068,23 +3777,12 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
3068
3777
  const { vizRef } = useHeatmapViz();
3069
3778
  const visualizer = {
3070
3779
  get: (hash) => {
3071
- if (vizRef) {
3072
- return vizRef.get(hash);
3073
- }
3074
- const doc = iframeRef.current?.contentDocument;
3075
- if (!doc)
3780
+ if (!vizRef)
3076
3781
  return null;
3077
- const elmHashAlpha = doc.querySelector(`[data-clarity-hashalpha="${hash}"]`);
3078
- if (elmHashAlpha) {
3079
- return elmHashAlpha;
3080
- }
3081
- const elmHash = doc.querySelector(`[data-clarity-hash="${hash}"]`);
3082
- if (elmHash) {
3083
- return elmHash;
3084
- }
3085
- return null;
3782
+ return vizRef.get(hash);
3086
3783
  },
3087
3784
  };
3785
+ // useRenderCount('VizElements');
3088
3786
  if (!iframeRef.current)
3089
3787
  return null;
3090
3788
  return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo: dataInfo, isVisible: true, iframeDimensions: {
@@ -3209,8 +3907,8 @@ const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
3209
3907
  };
3210
3908
 
3211
3909
  const ScrollMapMinimap = ({ zones, maxUsers }) => {
3212
- const showMinimap = useHeatmapVizScrollmapStore((state) => state.showMinimap);
3213
3910
  const scrollType = useHeatmapConfigStore((state) => state.scrollType);
3911
+ const { showMinimap } = useHeatmapVizScrollmap();
3214
3912
  const isScrollType = [IScrollType.Attention].includes(scrollType);
3215
3913
  if (!showMinimap || !isScrollType)
3216
3914
  return null;
@@ -3370,6 +4068,7 @@ const SCROLL_TYPES = [IHeatmapType.Scroll];
3370
4068
  const VizScrollMap = ({ iframeRef, wrapperRef }) => {
3371
4069
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
3372
4070
  const { iframeHeight } = useHeatmapViz();
4071
+ // useRenderCount('VizScrollMap');
3373
4072
  const isHeatmapScroll = SCROLL_TYPES.includes(heatmapType);
3374
4073
  if (!iframeHeight || !isHeatmapScroll)
3375
4074
  return null;
@@ -3386,9 +4085,7 @@ const VizScrollMap = ({ iframeRef, wrapperRef }) => {
3386
4085
  const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHeight, onScroll, }) => {
3387
4086
  const contentWidth = useHeatmapConfigStore((state) => state.width);
3388
4087
  const { widthScale } = useHeatmapViz();
3389
- const contentHeight = scaledHeight > 0
3390
- ? `${scaledHeight + HEATMAP_CONFIG['heightToolbar'] + HEATMAP_CONFIG['padding'] * 2}px`
3391
- : 'auto';
4088
+ const contentHeight = calcContentHeight();
3392
4089
  return (jsx("div", { ref: visualRef, className: "gx-hm-visual", onScroll: onScroll, style: {
3393
4090
  overflow: 'hidden scroll',
3394
4091
  display: 'flex',
@@ -3413,15 +4110,20 @@ const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHe
3413
4110
  transformOrigin: 'top center',
3414
4111
  paddingBlockEnd: '0',
3415
4112
  }, children: children }) }) }));
4113
+ function calcContentHeight() {
4114
+ return scaledHeight > 0
4115
+ ? `${scaledHeight + HEATMAP_CONFIG['heightToolbar'] + HEATMAP_CONFIG['padding'] * 2}px`
4116
+ : 'auto';
4117
+ }
3416
4118
  };
3417
4119
 
3418
4120
  const VizDomRenderer = ({ mode = 'heatmap' }) => {
3419
- const width = useHeatmapConfigStore((state) => state.width);
4121
+ const contentWidth = useHeatmapConfigStore((state) => state.width || 0);
3420
4122
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
3421
- const setSelectedElement = useHeatmapInteractionStore((state) => state.setSelectedElement);
3422
- const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
3423
4123
  const wrapperRef = useRef(null);
3424
4124
  const visualRef = useRef(null);
4125
+ const { setSelectedElement } = useHeatmapInteraction();
4126
+ const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
3425
4127
  const { iframeRef } = useHeatmapVizRender(mode);
3426
4128
  const { scaledHeight, handleScroll } = useHeatmapScale({
3427
4129
  wrapperRef,
@@ -3430,12 +4132,12 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
3430
4132
  iframeHeight,
3431
4133
  isRenderViz,
3432
4134
  });
3433
- const contentWidth = width ?? 0;
4135
+ useHeatmapCanvas();
4136
+ useRenderCount('VizDomRenderer');
3434
4137
  const onScroll = (e) => {
3435
4138
  const scrollTop = e.currentTarget.scrollTop;
3436
4139
  handleScroll(scrollTop);
3437
4140
  };
3438
- useHeatmapCanvas({ iframeRef: iframeRef });
3439
4141
  const cleanUp = () => {
3440
4142
  setIframeHeight(0);
3441
4143
  setSelectedElement(null);
@@ -3451,21 +4153,15 @@ const VizLoading = () => {
3451
4153
  };
3452
4154
 
3453
4155
  const VizDomHeatmap = () => {
3454
- const controls = useHeatmapControlStore((state) => state.controls);
3455
- const { isRendering } = useHeatmapData();
3456
- console.log(`🚀 🐥 ~ VizDomHeatmap ~ isRendering:`, isRendering);
3457
- const { iframeHeight, setIframeHeight, setVizRef } = useHeatmapViz();
3458
- const viewId = useViewId();
4156
+ const { iframeHeight, setIframeHeight } = useHeatmapViz();
4157
+ useRenderCount('VizDomHeatmap');
3459
4158
  useEffect(() => {
3460
4159
  return () => {
3461
- console.log(`🚀 🐥 ~ useEffect ~ return:`, viewId, iframeHeight);
3462
- setVizRef(null);
4160
+ console.log('🚀 🐥 ~ useEffect ~ return:');
3463
4161
  setIframeHeight(0);
3464
4162
  };
3465
4163
  }, []);
3466
- if (isRendering)
3467
- return controls.VizLoading ?? null;
3468
- return (jsxs(VizContainer, { children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
4164
+ return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
3469
4165
  };
3470
4166
 
3471
4167
  const VizLiveRenderer = () => {
@@ -3480,7 +4176,6 @@ const VizLiveRenderer = () => {
3480
4176
  visualRef,
3481
4177
  iframeHeight,
3482
4178
  isRenderViz,
3483
- setIframeHeight,
3484
4179
  });
3485
4180
  const onScroll = (e) => {
3486
4181
  const scrollTop = e.currentTarget.scrollTop;
@@ -3492,8 +4187,6 @@ const VizLiveRenderer = () => {
3492
4187
  };
3493
4188
 
3494
4189
  const VizLiveHeatmap = () => {
3495
- const controls = useHeatmapControlStore((state) => state.controls);
3496
- const isRendering = useHeatmapConfigStore((state) => state.isRendering);
3497
4190
  const { iframeHeight, wrapperHeight } = useHeatmapViz();
3498
4191
  const reset = useHeatmapLiveStore((state) => state.reset);
3499
4192
  useEffect(() => {
@@ -3501,13 +4194,15 @@ const VizLiveHeatmap = () => {
3501
4194
  reset();
3502
4195
  };
3503
4196
  }, []);
3504
- if (isRendering)
3505
- return controls.VizLoading ?? null;
3506
4197
  return (jsxs(VizContainer, { isActive: true, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
3507
4198
  };
3508
4199
 
3509
4200
  const ContentVizByMode = () => {
3510
4201
  const mode = useHeatmapConfigStore((state) => state.mode);
4202
+ const isRendering = useHeatmapConfigStore((state) => state.isRendering);
4203
+ const controls = useHeatmapControlStore((state) => state.controls);
4204
+ if (isRendering)
4205
+ return controls.VizLoading ?? null;
3511
4206
  switch (mode) {
3512
4207
  case 'live':
3513
4208
  return jsx(VizLiveHeatmap, {});
@@ -3518,75 +4213,12 @@ const ContentVizByMode = () => {
3518
4213
  }
3519
4214
  };
3520
4215
 
3521
- const LeftSidebar = () => {
3522
- const controls = useHeatmapControlStore((state) => state.controls);
3523
- const isHideSidebar = useHeatmapInteractionStore((state) => state.state.hideSidebar);
3524
- const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
3525
- const mode = useHeatmapConfigStore((state) => state.mode);
3526
- const [isPopoverOpen, setIsPopoverOpen] = useState(false);
3527
- // In compare mode, render as popover
3528
- if (mode === 'compare') {
3529
- return (jsxs("div", { style: { position: 'relative', zIndex: 10 }, children: [jsx("button", { onClick: () => setIsPopoverOpen(!isPopoverOpen), style: {
3530
- position: 'absolute',
3531
- top: '8px',
3532
- left: '8px',
3533
- padding: '8px 16px',
3534
- backgroundColor: '#fff',
3535
- border: '1px solid #c9cccf',
3536
- borderRadius: '8px',
3537
- cursor: 'pointer',
3538
- fontSize: '14px',
3539
- fontWeight: 500,
3540
- zIndex: 11,
3541
- boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)',
3542
- }, children: "Click data" }), isPopoverOpen && (jsxs(Fragment, { children: [jsx("div", { onClick: () => setIsPopoverOpen(false), style: {
3543
- position: 'fixed',
3544
- top: 0,
3545
- left: 0,
3546
- right: 0,
3547
- bottom: 0,
3548
- backgroundColor: 'rgba(0, 0, 0, 0.3)',
3549
- zIndex: 12,
3550
- } }), jsx("div", { className: "gx-hm-sidebar-popover", style: {
3551
- position: 'fixed',
3552
- top: 0,
3553
- left: 0,
3554
- height: '100vh',
3555
- width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
3556
- backgroundColor: '#fff',
3557
- borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3558
- zIndex: 13,
3559
- boxShadow: '4px 0 12px rgba(0, 0, 0, 0.15)',
3560
- overflow: 'auto',
3561
- }, children: controls.Sidebar ?? null })] }))] }));
3562
- }
3563
- // Normal sidebar rendering for single mode
3564
- if (isHideSidebar) {
3565
- return null;
3566
- }
3567
- return (jsx("div", { className: "gx-hm-sidebar", style: {
3568
- height: '100%',
3569
- display: 'flex',
3570
- zIndex: 1,
3571
- ...(isHideSidebar
3572
- ? {
3573
- width: '0',
3574
- transform: 'translateX(-100%)',
3575
- visibility: 'hidden',
3576
- }
3577
- : { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
3578
- }, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
3579
- height: '100%',
3580
- width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
3581
- borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
3582
- }, children: controls.Sidebar ?? null }) }));
3583
- };
3584
-
3585
4216
  const WrapperPreview = () => {
3586
- 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, {})] })] }));
4217
+ 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, {})] })] }));
3587
4218
  };
3588
4219
 
3589
4220
  const WrapperLayout = () => {
4221
+ useRenderCount('WrapperLayout');
3590
4222
  return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(WrapperPreview, {})] }));
3591
4223
  };
3592
4224
 
@@ -3595,6 +4227,19 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
3595
4227
  useRegisterData(data, dataInfo);
3596
4228
  useRegisterHeatmap({ clickmap, scrollmap });
3597
4229
  useRegisterConfig();
4230
+ performanceLogger.configure({
4231
+ enabled: true,
4232
+ logToConsole: false,
4233
+ logLevel: 'normal', // 'verbose' | 'normal' | 'minimal'
4234
+ thresholds: {
4235
+ slowRenderMs: 16, // Warn if render > 16ms (60fps)
4236
+ slowHookMs: 5, // Warn if hook > 5ms
4237
+ excessiveRenderCount: 10, // Warn if component renders > 10 times
4238
+ },
4239
+ externalLogger: (metric) => {
4240
+ if (metric.name === 'VizDomRenderer') ;
4241
+ },
4242
+ });
3598
4243
  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: {
3599
4244
  minHeight: '100%',
3600
4245
  display: 'flex',
@@ -3608,4 +4253,4 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
3608
4253
  }
3609
4254
  };
3610
4255
 
3611
- export { GraphView, HeatmapLayout, IClickType, IHeatmapType, IScrollType, ViewIdContext, convertViewportToIframeCoords, getScrollGradientColor, useClickedElement, useCompareAwareConfig, useCompareAwareConfigSetters, useCompareAwareData, useCompareAwareDataSetters, useCompareAwareViz, useCompareAwareVizSetters, useElementCalloutVisible, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapData, useHeatmapDataStore, useHeatmapEffects, useHeatmapElementPosition, useHeatmapInteractionStore, useHeatmapLiveStore, useHeatmapScale, useHeatmapSingleStore, useHeatmapViz, useHeatmapVizRender, useHeatmapVizStore, useHoveredElement, useIsCompareMode, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useScrollmapZones, useViewId, useVizLiveRender, useWrapperRefHeight, useZonePositions };
4256
+ export { DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, IClickType, IHeatmapType, IScrollType, ViewIdContext, Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, downloadPerformanceReport, getCompareViewId, getCompareViewIndex, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, isCompareViewId, performanceLogger, printPerformanceSummary, sendPerformanceReport, trackStoreAction, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapData, useHeatmapDataStore, useHeatmapEffects, useHeatmapElementPosition, useHeatmapInteraction, useHeatmapInteractionStore, useHeatmapLiveStore, useHeatmapScale, useHeatmapSingleStore, useHeatmapViz, useHeatmapVizRender, useHeatmapVizScrollmap, useHeatmapVizScrollmapStore, useHeatmapVizStore, useHoveredElement, useIsCompareMode, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewId, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };