@gemx-dev/heatmap-react 3.5.44 → 3.5.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/dist/esm/components/Layout/ContentHeader.d.ts +4 -0
  2. package/dist/esm/components/Layout/ContentHeader.d.ts.map +1 -0
  3. package/dist/esm/components/Layout/VizMode.d.ts +2 -0
  4. package/dist/esm/components/Layout/VizMode.d.ts.map +1 -0
  5. package/dist/esm/components/Test.d.ts +121 -0
  6. package/dist/esm/components/Test.d.ts.map +1 -0
  7. package/dist/esm/components/VizDom/VizDomContainer.d.ts +6 -0
  8. package/dist/esm/components/VizDom/VizDomContainer.d.ts.map +1 -0
  9. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  10. package/dist/esm/components/VizElement/ClickedElementOverlay.d.ts +17 -0
  11. package/dist/esm/components/VizElement/ClickedElementOverlay.d.ts.map +1 -0
  12. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
  13. package/dist/esm/components/VizElement/HoveredElementOverlay.d.ts +12 -0
  14. package/dist/esm/components/VizElement/HoveredElementOverlay.d.ts.map +1 -0
  15. package/dist/esm/components/VizElement/MissingElementMessage.d.ts +7 -0
  16. package/dist/esm/components/VizElement/MissingElementMessage.d.ts.map +1 -0
  17. package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
  18. package/dist/esm/components/VizElement/temp/ClarityVisualizer.d.ts +150 -0
  19. package/dist/esm/components/VizElement/temp/ClarityVisualizer.d.ts.map +1 -0
  20. package/dist/esm/components/VizElement/temp/VizElementRank.d.ts +74 -0
  21. package/dist/esm/components/VizElement/temp/VizElementRank.d.ts.map +1 -0
  22. package/dist/esm/components/VizLive/VizLive.d.ts +2 -0
  23. package/dist/esm/components/VizLive/VizLive.d.ts.map +1 -0
  24. package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  25. package/dist/esm/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  26. package/dist/esm/configs/style.d.ts +0 -2
  27. package/dist/esm/configs/style.d.ts.map +1 -1
  28. package/dist/esm/helpers/index.d.ts +2 -1
  29. package/dist/esm/helpers/index.d.ts.map +1 -1
  30. package/dist/esm/helpers/viewport-fixer.d.ts +13 -0
  31. package/dist/esm/helpers/viewport-fixer.d.ts.map +1 -0
  32. package/dist/esm/helpers/viewport-replacer.d.ts +26 -0
  33. package/dist/esm/helpers/viewport-replacer.d.ts.map +1 -0
  34. package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
  35. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts +1 -3
  36. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  37. package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +1 -1
  38. package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -1
  39. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts +1 -3
  40. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
  41. package/dist/esm/hooks/viz-live/index.d.ts +1 -1
  42. package/dist/esm/hooks/viz-live/{useVizLiveIframeMsg.d.ts → useIframeMessage.d.ts} +10 -2
  43. package/dist/esm/hooks/viz-live/useIframeMessage.d.ts.map +1 -0
  44. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  45. package/dist/esm/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -1
  46. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
  47. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  48. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts +10 -0
  49. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts.map +1 -0
  50. package/dist/esm/index.d.ts +1 -1
  51. package/dist/esm/index.d.ts.map +1 -1
  52. package/dist/esm/index.js +203 -910
  53. package/dist/esm/index.mjs +203 -910
  54. package/dist/esm/stores/index.d.ts +0 -1
  55. package/dist/esm/stores/index.d.ts.map +1 -1
  56. package/dist/esm/stores/interaction.d.ts.map +1 -1
  57. package/dist/esm/stores/mode-live.d.ts +0 -4
  58. package/dist/esm/stores/mode-live.d.ts.map +1 -1
  59. package/dist/esm/stores/viz.d.ts +4 -0
  60. package/dist/esm/stores/viz.d.ts.map +1 -1
  61. package/dist/esm/types/index.d.ts +1 -1
  62. package/dist/esm/types/index.d.ts.map +1 -1
  63. package/dist/esm/types/viewport-fixer.d.ts +31 -0
  64. package/dist/esm/types/viewport-fixer.d.ts.map +1 -0
  65. package/dist/umd/components/Layout/ContentHeader.d.ts +4 -0
  66. package/dist/umd/components/Layout/ContentHeader.d.ts.map +1 -0
  67. package/dist/umd/components/Test.d.ts +121 -0
  68. package/dist/umd/components/Test.d.ts.map +1 -0
  69. package/dist/umd/components/VizDom/VizDomContainer.d.ts +2 -0
  70. package/dist/umd/components/VizDom/VizDomContainer.d.ts.map +1 -0
  71. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  72. package/dist/umd/components/VizElement/ClickedElementOverlay.d.ts +17 -0
  73. package/dist/umd/components/VizElement/ClickedElementOverlay.d.ts.map +1 -0
  74. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
  75. package/dist/umd/components/VizElement/HoveredElementOverlay.d.ts +12 -0
  76. package/dist/umd/components/VizElement/HoveredElementOverlay.d.ts.map +1 -0
  77. package/dist/umd/components/VizElement/MissingElementMessage.d.ts +7 -0
  78. package/dist/umd/components/VizElement/MissingElementMessage.d.ts.map +1 -0
  79. package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
  80. package/dist/umd/components/VizElement/temp/ClarityVisualizer.d.ts +150 -0
  81. package/dist/umd/components/VizElement/temp/ClarityVisualizer.d.ts.map +1 -0
  82. package/dist/umd/components/VizElement/temp/VizElementRank.d.ts +74 -0
  83. package/dist/umd/components/VizElement/temp/VizElementRank.d.ts.map +1 -0
  84. package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  85. package/dist/umd/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  86. package/dist/umd/configs/style.d.ts +0 -2
  87. package/dist/umd/configs/style.d.ts.map +1 -1
  88. package/dist/umd/helpers/index.d.ts +2 -1
  89. package/dist/umd/helpers/index.d.ts.map +1 -1
  90. package/dist/umd/helpers/viewport-fixer.d.ts +13 -0
  91. package/dist/umd/helpers/viewport-fixer.d.ts.map +1 -0
  92. package/dist/umd/helpers/viewport-replacer.d.ts +26 -0
  93. package/dist/umd/helpers/viewport-replacer.d.ts.map +1 -0
  94. package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
  95. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts +1 -3
  96. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  97. package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +1 -1
  98. package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -1
  99. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts +1 -3
  100. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
  101. package/dist/umd/hooks/viz-live/index.d.ts +1 -1
  102. package/dist/umd/hooks/viz-live/{useVizLiveIframeMsg.d.ts → useIframeMessage.d.ts} +10 -2
  103. package/dist/umd/hooks/viz-live/useIframeMessage.d.ts.map +1 -0
  104. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  105. package/dist/umd/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -1
  106. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
  107. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  108. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts +10 -0
  109. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts.map +1 -0
  110. package/dist/umd/index.d.ts +1 -1
  111. package/dist/umd/index.d.ts.map +1 -1
  112. package/dist/umd/index.js +2 -2
  113. package/dist/umd/stores/index.d.ts +0 -1
  114. package/dist/umd/stores/index.d.ts.map +1 -1
  115. package/dist/umd/stores/interaction.d.ts.map +1 -1
  116. package/dist/umd/stores/mode-live.d.ts +0 -4
  117. package/dist/umd/stores/mode-live.d.ts.map +1 -1
  118. package/dist/umd/stores/viz.d.ts +4 -0
  119. package/dist/umd/stores/viz.d.ts.map +1 -1
  120. package/dist/umd/types/index.d.ts +1 -1
  121. package/dist/umd/types/index.d.ts.map +1 -1
  122. package/dist/umd/types/viewport-fixer.d.ts +31 -0
  123. package/dist/umd/types/viewport-fixer.d.ts.map +1 -0
  124. package/package.json +13 -15
  125. package/src/components/GraphView.tsx +58 -0
  126. package/src/components/Layout/ContentMetricBar.tsx +23 -0
  127. package/src/components/Layout/ContentToolbar.tsx +22 -0
  128. package/src/components/Layout/ContentTopBar.tsx +24 -0
  129. package/src/components/Layout/ContentVizByMode.tsx +14 -0
  130. package/src/components/Layout/HeatmapLayout.tsx +60 -0
  131. package/src/components/Layout/LeftSidebar.tsx +44 -0
  132. package/src/components/Layout/WrapperLayout.tsx +12 -0
  133. package/src/components/Layout/WrapperPreview.tsx +24 -0
  134. package/src/components/Layout/index.ts +1 -0
  135. package/src/components/VizDom/ReplayControls.tsx +48 -0
  136. package/src/components/VizDom/VizContainer.tsx +40 -0
  137. package/src/components/VizDom/VizDomHeatmap.tsx +28 -0
  138. package/src/components/VizDom/VizDomRenderer.tsx +82 -0
  139. package/src/components/VizDom/VizLoading.tsx +8 -0
  140. package/src/components/VizDom/WrapperVisual.tsx +73 -0
  141. package/src/components/VizDom/index.ts +5 -0
  142. package/src/components/VizElement/DefaultRankBadges.tsx +36 -0
  143. package/src/components/VizElement/ElementCallout.tsx +82 -0
  144. package/src/components/VizElement/ElementMissing.tsx +35 -0
  145. package/src/components/VizElement/ElementOverlay.tsx +66 -0
  146. package/src/components/VizElement/HeatmapElements.tsx +127 -0
  147. package/src/components/VizElement/HeatmapExample.tsx +70 -0
  148. package/src/components/VizElement/RankBadge.tsx +25 -0
  149. package/src/components/VizElement/VizElements.tsx +57 -0
  150. package/src/components/VizElement/index.ts +1 -0
  151. package/src/components/VizLive/VizLiveHeatmap.tsx +27 -0
  152. package/src/components/VizLive/VizLiveRenderer.tsx +47 -0
  153. package/src/components/VizLive/index.ts +1 -0
  154. package/src/components/VizScrollmap/AverageFoldLine.tsx +57 -0
  155. package/src/components/VizScrollmap/HoverZones.tsx +58 -0
  156. package/src/components/VizScrollmap/MetricRow.tsx +0 -0
  157. package/src/components/VizScrollmap/ScrollMapMinimap.tsx +64 -0
  158. package/src/components/VizScrollmap/ScrollMapOverlay.tsx +79 -0
  159. package/src/components/VizScrollmap/ScrollZoneHoverArea.tsx +35 -0
  160. package/src/components/VizScrollmap/ScrollZoneTooltip.tsx +146 -0
  161. package/src/components/VizScrollmap/ScrollmapMarker.tsx +106 -0
  162. package/src/components/VizScrollmap/VizScrollMap.tsx +36 -0
  163. package/src/components/VizScrollmap/index.ts +1 -0
  164. package/src/components/VizScrollmapV2/ScrollmapOverlay.css +94 -0
  165. package/src/components/VizScrollmapV2/ScrollmapOverlayV2.tsx +130 -0
  166. package/src/components/VizScrollmapV2/index.ts +1 -0
  167. package/src/components/VizScrollmapV2/scrollmap.types.ts +21 -0
  168. package/src/components/VizScrollmapV2/useScrollmapOverlay.ts +187 -0
  169. package/src/components/index.tsx +2 -0
  170. package/src/configs/iframe.ts +15 -0
  171. package/src/configs/index.ts +2 -0
  172. package/src/configs/style.ts +21 -0
  173. package/src/constants/index.ts +4 -0
  174. package/src/global.d.ts +5 -0
  175. package/src/helpers/elm-callout.ts +347 -0
  176. package/src/helpers/elm-getter.ts +70 -0
  177. package/src/helpers/iframe-helper/fixer.ts +100 -0
  178. package/src/helpers/iframe-helper/index.ts +1 -0
  179. package/src/helpers/iframe-helper/init.ts +56 -0
  180. package/src/helpers/iframe-helper/navigation-blocker-v2.ts +371 -0
  181. package/src/helpers/iframe-helper/navigation-blocker.ts +367 -0
  182. package/src/helpers/iframe-helper/style-replacer.ts +231 -0
  183. package/src/helpers/iframe.ts +42 -0
  184. package/src/helpers/index.ts +8 -0
  185. package/src/helpers/viz-canvas/area-clustering.ts +234 -0
  186. package/src/helpers/viz-canvas/area-overlay-manager-v2.ts +176 -0
  187. package/src/helpers/viz-canvas/area-overlay-manager.ts +273 -0
  188. package/src/helpers/viz-canvas/hierarchical-area-clustering.ts +420 -0
  189. package/src/helpers/viz-canvas/index.ts +2 -0
  190. package/src/helpers/viz-elements.ts +43 -0
  191. package/src/hooks/index.ts +8 -0
  192. package/src/hooks/register/index.ts +4 -0
  193. package/src/hooks/register/useRegisterConfig.ts +17 -0
  194. package/src/hooks/register/useRegisterControl.ts +13 -0
  195. package/src/hooks/register/useRegisterData.ts +36 -0
  196. package/src/hooks/register/useRegisterHeatmap.ts +38 -0
  197. package/src/hooks/viz-area/useAreaHeatmap.ts +336 -0
  198. package/src/hooks/viz-area/useAreaHeatmapManager.ts +692 -0
  199. package/src/hooks/viz-canvas/index.ts +1 -0
  200. package/src/hooks/viz-canvas/useAreamap.ts +162 -0
  201. package/src/hooks/viz-canvas/useClickmap.ts +24 -0
  202. package/src/hooks/viz-canvas/useHeatmapCanvas.ts +27 -0
  203. package/src/hooks/viz-canvas/useScrollmap.ts +22 -0
  204. package/src/hooks/viz-elements/index.ts +5 -0
  205. package/src/hooks/viz-elements/useClickedElement.ts +86 -0
  206. package/src/hooks/viz-elements/useElementCalloutVisible.ts +45 -0
  207. package/src/hooks/viz-elements/useHeatmapEffects.ts +30 -0
  208. package/src/hooks/viz-elements/useHeatmapElementPosition.ts +60 -0
  209. package/src/hooks/viz-elements/useHeatmapMouseHandler.ts +255 -0
  210. package/src/hooks/viz-elements/useHoveredElement.ts +170 -0
  211. package/src/hooks/viz-live/index.ts +1 -0
  212. package/src/hooks/viz-live/useVizLiveIframeMsg.ts +88 -0
  213. package/src/hooks/viz-live/useVizLiveRender.ts +67 -0
  214. package/src/hooks/viz-render/index.ts +1 -0
  215. package/src/hooks/viz-render/useHeatmapRender.ts +71 -0
  216. package/src/hooks/viz-render/useHeatmapVizRender.ts +20 -0
  217. package/src/hooks/viz-render/useReplayRender.ts +160 -0
  218. package/src/hooks/viz-scale/index.ts +2 -0
  219. package/src/hooks/viz-scale/useContainerDimensions.ts +48 -0
  220. package/src/hooks/viz-scale/useContentDimensions.ts +25 -0
  221. package/src/hooks/viz-scale/useHeatmapScale.ts +52 -0
  222. package/src/hooks/viz-scale/useObserveIframeHeight.ts +162 -0
  223. package/src/hooks/viz-scale/useScaleCalculation.ts +31 -0
  224. package/src/hooks/viz-scale/useScrollSync.ts +36 -0
  225. package/src/hooks/viz-scale/useWrapperRefHeight.ts +91 -0
  226. package/src/hooks/viz-scrollmap/index.ts +2 -0
  227. package/src/hooks/viz-scrollmap/useScrollmapZones.ts +165 -0
  228. package/src/hooks/viz-scrollmap/useZonePositions.ts +38 -0
  229. package/src/index.ts +10 -0
  230. package/src/stores/comp.ts +31 -0
  231. package/src/stores/config.ts +37 -0
  232. package/src/stores/data.ts +30 -0
  233. package/src/stores/index.ts +10 -0
  234. package/src/stores/interaction.ts +32 -0
  235. package/src/stores/mode-live.ts +38 -0
  236. package/src/stores/mode-single.ts +18 -0
  237. package/src/stores/viz-scrollmap.ts +22 -0
  238. package/src/stores/viz.ts +17 -0
  239. package/src/styles/base.css +1 -0
  240. package/src/styles/style.css +137 -0
  241. package/src/types/clarity.ts +45 -0
  242. package/src/types/control.ts +10 -0
  243. package/src/types/elm-callout.ts +9 -0
  244. package/src/types/heatmap-info.ts +11 -0
  245. package/src/types/heatmap.ts +25 -0
  246. package/src/types/iframe-helper.ts +18 -0
  247. package/src/types/index.ts +12 -0
  248. package/src/types/viz-canvas.ts +20 -0
  249. package/src/types/viz-element.ts +34 -0
  250. package/src/types/viz-scrollmap.ts +28 -0
  251. package/src/ui/BoxStack/BoxStack.tsx +136 -0
  252. package/src/ui/BoxStack/index.ts +1 -0
  253. package/src/ui/index.ts +1 -0
  254. package/src/utils/debounce.ts +10 -0
  255. package/src/utils/device.ts +7 -0
  256. package/src/utils/retry.ts +20 -0
  257. package/src/utils/sort.ts +5 -0
  258. package/dist/esm/helpers/iframe-helper/fixer.d.ts +0 -18
  259. package/dist/esm/helpers/iframe-helper/fixer.d.ts.map +0 -1
  260. package/dist/esm/helpers/iframe-helper/index.d.ts +0 -2
  261. package/dist/esm/helpers/iframe-helper/index.d.ts.map +0 -1
  262. package/dist/esm/helpers/iframe-helper/init.d.ts +0 -5
  263. package/dist/esm/helpers/iframe-helper/init.d.ts.map +0 -1
  264. package/dist/esm/helpers/iframe-helper/navigation-blocker-v2.d.ts +0 -28
  265. package/dist/esm/helpers/iframe-helper/navigation-blocker-v2.d.ts.map +0 -1
  266. package/dist/esm/helpers/iframe-helper/navigation-blocker.d.ts +0 -20
  267. package/dist/esm/helpers/iframe-helper/navigation-blocker.d.ts.map +0 -1
  268. package/dist/esm/helpers/iframe-helper/style-replacer.d.ts +0 -25
  269. package/dist/esm/helpers/iframe-helper/style-replacer.d.ts.map +0 -1
  270. package/dist/esm/hooks/vix-elements/useHeatmapMouseHandler.d.ts +0 -34
  271. package/dist/esm/hooks/vix-elements/useHeatmapMouseHandler.d.ts.map +0 -1
  272. package/dist/esm/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +0 -1
  273. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts +0 -4
  274. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +0 -1
  275. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts +0 -10
  276. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts.map +0 -1
  277. package/dist/esm/stores/mode-single.d.ts +0 -9
  278. package/dist/esm/stores/mode-single.d.ts.map +0 -1
  279. package/dist/esm/types/iframe-helper.d.ts +0 -20
  280. package/dist/esm/types/iframe-helper.d.ts.map +0 -1
  281. package/dist/umd/helpers/iframe-helper/fixer.d.ts +0 -18
  282. package/dist/umd/helpers/iframe-helper/fixer.d.ts.map +0 -1
  283. package/dist/umd/helpers/iframe-helper/index.d.ts +0 -2
  284. package/dist/umd/helpers/iframe-helper/index.d.ts.map +0 -1
  285. package/dist/umd/helpers/iframe-helper/init.d.ts +0 -5
  286. package/dist/umd/helpers/iframe-helper/init.d.ts.map +0 -1
  287. package/dist/umd/helpers/iframe-helper/navigation-blocker-v2.d.ts +0 -28
  288. package/dist/umd/helpers/iframe-helper/navigation-blocker-v2.d.ts.map +0 -1
  289. package/dist/umd/helpers/iframe-helper/navigation-blocker.d.ts +0 -20
  290. package/dist/umd/helpers/iframe-helper/navigation-blocker.d.ts.map +0 -1
  291. package/dist/umd/helpers/iframe-helper/style-replacer.d.ts +0 -25
  292. package/dist/umd/helpers/iframe-helper/style-replacer.d.ts.map +0 -1
  293. package/dist/umd/hooks/vix-elements/useHeatmapMouseHandler.d.ts +0 -34
  294. package/dist/umd/hooks/vix-elements/useHeatmapMouseHandler.d.ts.map +0 -1
  295. package/dist/umd/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +0 -1
  296. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts +0 -4
  297. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +0 -1
  298. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts +0 -10
  299. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts.map +0 -1
  300. package/dist/umd/stores/mode-single.d.ts +0 -9
  301. package/dist/umd/stores/mode-single.d.ts.map +0 -1
  302. package/dist/umd/types/iframe-helper.d.ts +0 -20
  303. package/dist/umd/types/iframe-helper.d.ts.map +0 -1
package/dist/esm/index.js CHANGED
@@ -61,8 +61,6 @@ const HEATMAP_STYLE = {
61
61
  },
62
62
  wrapper: {
63
63
  padding: `${HEATMAP_CONFIG.padding}px 0`,
64
- paddingBlock: `${HEATMAP_CONFIG.padding}px`,
65
- paddingInline: `${HEATMAP_CONFIG.padding}px`,
66
64
  },
67
65
  };
68
66
  const DEFAULT_SIDEBAR_WIDTH = 260;
@@ -141,12 +139,15 @@ const useHeatmapVizStore = create()((set, get) => {
141
139
  isRenderViz: false,
142
140
  setIsRenderViz: (isRenderViz) => set({ isRenderViz }),
143
141
  scale: 1,
142
+ vizRef: undefined,
143
+ iframeHeight: 0,
144
144
  setScale: (scale) => set({ scale }),
145
+ setVizRef: (vizRef) => set({ vizRef }),
146
+ setIframeHeight: (iframeHeight) => set({ iframeHeight }),
145
147
  };
146
148
  });
147
149
 
148
150
  const initialState = {
149
- payloads: [],
150
151
  htmlContent: '',
151
152
  wrapperHeight: 0,
152
153
  iframeHeight: 0,
@@ -155,23 +156,12 @@ const useHeatmapLiveStore = create()((set, get) => {
155
156
  return {
156
157
  ...initialState,
157
158
  reset: () => set(initialState),
158
- setPayloads: (payloads) => set({ payloads }),
159
- addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
160
159
  setHtmlContent: (htmlContent) => set({ htmlContent }),
161
160
  setWrapperHeight: (wrapperHeight) => set({ wrapperHeight }),
162
161
  setIframeHeight: (iframeHeight) => set({ iframeHeight }),
163
162
  };
164
163
  });
165
164
 
166
- const useHeatmapSingleStore = create()((set, get) => {
167
- return {
168
- vizRef: null,
169
- iframeHeight: 0,
170
- setVizRef: (vizRef) => set({ vizRef }),
171
- setIframeHeight: (iframeHeight) => set({ iframeHeight }),
172
- };
173
- });
174
-
175
165
  const useRegisterConfig = () => {
176
166
  const mode = useHeatmapConfigStore((state) => state.mode);
177
167
  const width = useHeatmapConfigStore((state) => state.width);
@@ -511,311 +501,91 @@ function isElementInViewport(elementRect, visualRef, scale) {
511
501
  return elementBottom > viewportTop && elementTop < viewportBottom;
512
502
  }
513
503
 
514
- class IframeNavigationBlockerV2 {
515
- doc;
516
- win;
517
- isEnabled = false;
518
- showMessage = false;
519
- originalWindowOpen;
520
- observers = [];
521
- constructor(iframe) {
522
- if (!iframe.contentDocument || !iframe.contentWindow) {
523
- throw new Error('Iframe document or window not accessible');
524
- }
525
- this.doc = iframe.contentDocument;
526
- this.win = iframe.contentWindow;
527
- this.originalWindowOpen = this.win.open.bind(this.win);
504
+ const getScriptInjectCode = async () => {
505
+ const moduleResult = (await Promise.resolve().then(function () { return viewportReplacer; }));
506
+ const ActualClass = moduleResult.default;
507
+ const classCode = ActualClass.toString();
508
+ const classInstantiateCode = ActualClass.name;
509
+ const scriptCode = `
510
+ (function() {
511
+ 'use strict';
512
+ ${classCode}
513
+ new ${classInstantiateCode}()
514
+ })();
515
+ `;
516
+ return scriptCode;
517
+ };
518
+ class ViewportUnitsFixer {
519
+ iframe = null;
520
+ config;
521
+ constructor(config) {
522
+ this.config = config;
523
+ this.iframe = config.iframe;
528
524
  this.init();
529
525
  }
530
- init() {
531
- console.log('[NavigationBlocker] Initializing...');
532
- try {
533
- // Chặn navigation qua links
534
- this.blockLinkNavigation();
535
- // Chặn form submissions
536
- this.blockFormSubmissions();
537
- // Chặn window.open (này an toàn)
538
- this.blockWindowOpen();
539
- // Chặn beforeunload để prevent navigation
540
- this.blockBeforeUnload();
541
- // Monitor DOM changes để block dynamic links
542
- this.monitorDOMChanges();
543
- // Inject CSP nếu có thể
544
- this.injectCSP();
545
- }
546
- catch (error) {
547
- console.error('[NavigationBlocker] Init error:', error);
548
- }
549
- }
550
- blockLinkNavigation() {
551
- // Sử dụng capture phase để chặn sớm nhất
552
- this.doc.addEventListener('click', (e) => {
553
- if (!this.isEnabled)
554
- return;
555
- const target = e.target;
556
- const link = target.closest('a');
557
- if (link) {
558
- const href = link.getAttribute('href');
559
- // Cho phép hash links và empty links
560
- if (!href || href === '' || href === '#' || href.startsWith('#')) {
561
- console.log('[NavigationBlocker] Allowed hash navigation:', href);
562
- return;
563
- }
564
- // Chặn tất cả các loại navigation
565
- console.log('[NavigationBlocker] Blocked link navigation to:', href);
566
- e.preventDefault();
567
- e.stopPropagation();
568
- e.stopImmediatePropagation();
569
- this.notifyBlockedNavigation(href);
570
- }
571
- }, true);
572
- // Chặn cả middle click và right click "open in new tab"
573
- this.doc.addEventListener('auxclick', (e) => {
574
- if (!this.isEnabled)
575
- return;
576
- const target = e.target;
577
- const link = target.closest('a');
578
- if (link) {
579
- const href = link.getAttribute('href');
580
- if (href && !href.startsWith('#')) {
581
- console.log('[NavigationBlocker] Blocked auxclick navigation');
582
- e.preventDefault();
583
- e.stopPropagation();
584
- e.stopImmediatePropagation();
585
- }
586
- }
587
- }, true);
588
- // Disable tất cả links ngay từ đầu
589
- this.disableAllLinks();
590
- }
591
- disableAllLinks() {
592
- this.doc.querySelectorAll('a[href]').forEach((link) => {
593
- const href = link.getAttribute('href');
594
- if (href && !href.startsWith('#')) {
595
- // Thêm pointer-events: none và cursor
596
- link.style.cursor = 'not-allowed';
597
- link.setAttribute('data-navigation-blocked', 'true');
598
- // Remove href để browser không hiện preview
599
- link.setAttribute('data-original-href', href);
600
- link.removeAttribute('href');
601
- // Hoặc giữ href nhưng disable
602
- // link.setAttribute('onclick', 'return false');
603
- }
604
- });
605
- }
606
- blockFormSubmissions() {
607
- this.doc.addEventListener('submit', (e) => {
608
- if (!this.isEnabled)
609
- return;
610
- const form = e.target;
611
- const action = form.getAttribute('action');
612
- // Cho phép forms không có action
613
- if (!action || action === '' || action === '#') {
614
- console.log('[NavigationBlocker] Allowed same-page form');
615
- e.preventDefault();
616
- this.handleFormSubmit(form);
617
- return;
618
- }
619
- // Chặn tất cả external submissions
620
- console.log('[NavigationBlocker] Blocked form submission to:', action);
621
- e.preventDefault();
622
- e.stopPropagation();
623
- e.stopImmediatePropagation();
624
- this.notifyBlockedNavigation(action);
625
- }, true);
626
- }
627
- blockWindowOpen() {
628
- // Override window.open - đây là safe
629
- this.win.open = ((...args) => {
630
- if (!this.isEnabled) {
631
- return this.originalWindowOpen(...args);
632
- }
633
- const url = args[0]?.toString() || 'popup';
634
- console.log('[NavigationBlocker] Blocked window.open:', url);
635
- this.notifyBlockedNavigation(url);
636
- return null;
637
- });
638
- }
639
- blockBeforeUnload() {
640
- // Chặn unload
641
- this.win.addEventListener('beforeunload', (e) => {
642
- if (!this.isEnabled)
643
- return;
644
- console.log('[NavigationBlocker] Blocked beforeunload');
645
- e.preventDefault();
646
- e.returnValue = '';
647
- return '';
648
- }, true);
649
- // Chặn unload
650
- this.win.addEventListener('unload', (e) => {
651
- if (!this.isEnabled)
652
- return;
653
- console.log('[NavigationBlocker] Blocked unload');
654
- e.preventDefault();
655
- e.stopPropagation();
656
- }, true);
657
- // Monitor popstate
658
- this.win.addEventListener('popstate', (e) => {
659
- if (!this.isEnabled)
660
- return;
661
- console.log('[NavigationBlocker] Blocked popstate');
662
- e.preventDefault();
663
- e.stopPropagation();
664
- }, true);
665
- }
666
- monitorDOMChanges() {
667
- // Monitor khi có links mới được thêm vào
668
- const observer = new MutationObserver((mutations) => {
669
- if (!this.isEnabled)
670
- return;
671
- mutations.forEach((mutation) => {
672
- mutation.addedNodes.forEach((node) => {
673
- if (node.nodeType === Node.ELEMENT_NODE) {
674
- const element = node;
675
- // Nếu là link
676
- if (element.tagName === 'A') {
677
- const href = element.getAttribute('href');
678
- if (href && !href.startsWith('#')) {
679
- element.style.cursor = 'not-allowed';
680
- element.setAttribute('data-navigation-blocked', 'true');
681
- element.setAttribute('data-original-href', href);
682
- element.removeAttribute('href');
683
- }
684
- }
685
- // Tìm links trong subtree
686
- element.querySelectorAll('a[href]').forEach((link) => {
687
- const href = link.getAttribute('href');
688
- if (href && !href.startsWith('#')) {
689
- link.style.cursor = 'not-allowed';
690
- link.setAttribute('data-navigation-blocked', 'true');
691
- link.setAttribute('data-original-href', href);
692
- link.removeAttribute('href');
693
- }
694
- });
695
- }
696
- });
697
- });
698
- });
699
- observer.observe(this.doc.body, {
700
- childList: true,
701
- subtree: true,
702
- });
703
- this.observers.push(observer);
704
- }
705
- injectCSP() {
706
- // Thêm CSP meta tag nếu chưa có (optional)
707
- try {
708
- const existingCSP = this.doc.querySelector('meta[http-equiv="Content-Security-Policy"]');
709
- if (!existingCSP) {
710
- const meta = this.doc.createElement('meta');
711
- meta.httpEquiv = 'Content-Security-Policy';
712
- meta.content = "navigate-to 'none'"; // Chặn tất cả navigation
713
- this.doc.head.appendChild(meta);
714
- console.log('[NavigationBlocker] Injected CSP');
715
- }
526
+ async init() {
527
+ if (!this.iframe) {
528
+ console.error('[Parent] Required elements not found');
529
+ return;
716
530
  }
717
- catch (error) {
718
- console.warn('[NavigationBlocker] Could not inject CSP:', error);
531
+ // this.injectScriptContent = await generateIframeInjectScript();
532
+ window.addEventListener('message', this.handleMessage.bind(this));
533
+ if (this.iframe.contentDocument?.readyState === 'complete') {
534
+ await this.injectScript();
719
535
  }
720
- }
721
- handleFormSubmit(form) {
722
- const formData = new FormData(form);
723
- const data = {};
724
- formData.forEach((value, key) => {
725
- data[key] = value;
726
- });
727
- console.log('[NavigationBlocker] Handling form data:', data);
728
- window.dispatchEvent(new CustomEvent('iframe-form-submit', {
729
- detail: { form, data },
730
- }));
731
- }
732
- notifyBlockedNavigation(url) {
733
- console.warn('[NavigationBlocker] Navigation blocked to:', url);
734
- window.dispatchEvent(new CustomEvent('iframe-navigation-blocked', {
735
- detail: { url, timestamp: Date.now() },
736
- }));
737
- if (this.shouldShowMessage(url)) {
738
- this.showBlockedMessage(url);
536
+ else {
537
+ this.iframe.addEventListener('load', () => this.injectScript());
739
538
  }
740
539
  }
741
- shouldShowMessage(url) {
742
- return !url.startsWith('#') && url !== 'reload' && url !== 'popup';
743
- }
744
- showBlockedMessage(url) {
745
- if (!this.showMessage)
540
+ async injectScript() {
541
+ if (!this.iframe?.contentWindow || !this.iframe.contentDocument)
746
542
  return;
747
- const message = this.doc.createElement('div');
748
- message.style.cssText = `
749
- position: fixed;
750
- top: 20px;
751
- right: 20px;
752
- background: #ff6b6b;
753
- color: white;
754
- padding: 12px 20px;
755
- border-radius: 8px;
756
- box-shadow: 0 4px 12px rgba(0,0,0,0.15);
757
- z-index: 999999;
758
- font-family: system-ui, -apple-system, sans-serif;
759
- font-size: 14px;
760
- max-width: 300px;
761
- word-break: break-word;
762
- pointer-events: none;
763
- `;
764
- const shortUrl = url.length > 50 ? url.substring(0, 47) + '...' : url;
765
- message.innerHTML = `
766
- <div style="font-weight: 600; margin-bottom: 4px;">🚫 Navigation Blocked</div>
767
- <div style="font-size: 12px; opacity: 0.9;">${this.escapeHtml(shortUrl)}</div>
768
- `;
769
- this.doc.body.appendChild(message);
770
- setTimeout(() => {
771
- message.style.opacity = '0';
772
- message.style.transition = 'opacity 0.3s';
773
- setTimeout(() => message.remove(), 300);
774
- }, 3000);
775
- }
776
- escapeHtml(text) {
777
- const div = this.doc.createElement('div');
778
- div.textContent = text;
779
- return div.innerHTML;
780
- }
781
- enable() {
782
- this.isEnabled = true;
783
- console.log('[NavigationBlocker] Enabled');
784
- }
785
- enableMessage() {
786
- this.showMessage = true;
787
- console.log('[NavigationBlocker] Enabled message');
788
- }
789
- disable() {
790
- this.isEnabled = false;
791
- console.log('[NavigationBlocker] Disabled');
543
+ const win = this.iframe.contentWindow;
544
+ const doc = this.iframe.contentDocument;
545
+ win.__viewportConfig = this.config;
546
+ const script = doc.createElement('script');
547
+ const codeInject = await getScriptInjectCode();
548
+ script.textContent = codeInject;
549
+ script.type = 'text/javascript';
550
+ script.id = 'viewport-replacer';
551
+ script.onload = () => console.log('[Parent] Viewport replacer module loaded');
552
+ script.onerror = () => {
553
+ this.config.onSuccess?.({
554
+ height: 1000,
555
+ });
556
+ };
557
+ doc.head.appendChild(script);
792
558
  }
793
- disableMessage() {
794
- this.showMessage = false;
795
- console.log('[NavigationBlocker] Disabled message');
559
+ handleMessage(event) {
560
+ const data = event.data;
561
+ if (!data || data.type !== 'IFRAME_HEIGHT_CALCULATED')
562
+ return;
563
+ this.config.onSuccess?.(data);
796
564
  }
797
- destroy() {
798
- this.isEnabled = false;
799
- this.showMessage = false;
800
- // Cleanup observers
801
- this.observers.forEach((observer) => observer.disconnect());
802
- this.observers = [];
803
- console.log('[NavigationBlocker] Destroyed');
565
+ recalculate() {
566
+ this.injectScript();
804
567
  }
805
568
  }
569
+ function initViewportFixer(config) {
570
+ const fixer = new ViewportUnitsFixer(config);
571
+ window.viewportFixer = fixer;
572
+ window.addEventListener('iframe-dimensions-applied', ((e) => {
573
+ const ev = e;
574
+ console.log('Iframe dimensions finalized:', ev.detail);
575
+ }));
576
+ return fixer;
577
+ }
806
578
 
807
- class IframeStyleReplacer {
808
- doc;
809
- win;
579
+ class ViewportUnitsReplacer {
810
580
  config;
811
581
  regex = /([-.\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
812
- constructor(iframe, config) {
813
- if (!iframe.contentDocument || !iframe.contentWindow) {
814
- throw new Error('Iframe document or window not accessible');
582
+ constructor() {
583
+ if (!window.__viewportConfig) {
584
+ throw new Error('[Iframe] Do not have viewport config');
815
585
  }
816
- this.doc = iframe.contentDocument;
817
- this.win = iframe.contentWindow;
818
- this.config = config;
586
+ this.config = window.__viewportConfig;
587
+ console.log('[Iframe] ViewportUnitsReplacer started with config:', this.config);
588
+ this.init();
819
589
  }
820
590
  px(value) {
821
591
  return `${value.toFixed(2)}px`;
@@ -841,28 +611,26 @@ class IframeStyleReplacer {
841
611
  }
842
612
  processInlineStyles() {
843
613
  let count = 0;
844
- this.doc.querySelectorAll('[style]').forEach((el) => {
614
+ document.querySelectorAll('[style]').forEach((el) => {
845
615
  const style = el.getAttribute('style');
846
616
  if (style && this.regex.test(style)) {
847
- this.regex.lastIndex = 0;
848
617
  el.setAttribute('style', this.replaceInText(style));
849
618
  count++;
850
619
  }
851
620
  });
852
- console.log(`[IframeStyleReplacer] Replaced ${count} inline style elements`);
621
+ console.log(`[Iframe] Replaced ${count} inline style elements`);
853
622
  return count;
854
623
  }
855
624
  processStyleTags() {
856
625
  let count = 0;
857
- this.doc.querySelectorAll('style').forEach((tag) => {
626
+ document.querySelectorAll('style').forEach((tag) => {
858
627
  const css = tag.textContent || '';
859
628
  if (this.regex.test(css)) {
860
- this.regex.lastIndex = 0;
861
629
  tag.textContent = this.replaceInText(css);
862
630
  count++;
863
631
  }
864
632
  });
865
- console.log(`[IframeStyleReplacer] Replaced ${count} <style> tags`);
633
+ console.log(`[Iframe] Replaced ${count} <style> tags`);
866
634
  return count;
867
635
  }
868
636
  processRule(rule) {
@@ -873,7 +641,6 @@ class IframeStyleReplacer {
873
641
  const prop = style[i];
874
642
  const value = style.getPropertyValue(prop);
875
643
  if (value && this.regex.test(value)) {
876
- this.regex.lastIndex = 0;
877
644
  style.setProperty(prop, this.replaceInText(value), style.getPropertyPriority(prop));
878
645
  count++;
879
646
  }
@@ -889,11 +656,11 @@ class IframeStyleReplacer {
889
656
  }
890
657
  processStylesheets() {
891
658
  let total = 0;
892
- Array.from(this.doc.styleSheets).forEach((sheet) => {
659
+ Array.from(document.styleSheets).forEach((sheet) => {
893
660
  try {
894
661
  // Bỏ qua external CSS (cross-origin)
895
- if (sheet.href && !sheet.href.startsWith(this.win.location.origin)) {
896
- console.log('[IframeStyleReplacer] Skipping external CSS:', sheet.href);
662
+ if (sheet.href && !sheet.href.startsWith(location.origin)) {
663
+ console.log('[Iframe] Skipping external CSS:', sheet.href);
897
664
  return;
898
665
  }
899
666
  const rules = sheet.cssRules || sheet.rules;
@@ -904,27 +671,26 @@ class IframeStyleReplacer {
904
671
  }
905
672
  }
906
673
  catch (e) {
907
- console.warn('[IframeStyleReplacer] Cannot read stylesheet (CORS?):', e.message);
674
+ console.warn('[Iframe] Cannot read stylesheet (CORS?):', e.message);
908
675
  }
909
676
  });
910
- console.log(`[IframeStyleReplacer] Replaced ${total} rules in stylesheets`);
677
+ console.log(`[Iframe] Replaced ${total} rules in stylesheets`);
911
678
  return total;
912
679
  }
913
680
  async processLinkedStylesheets() {
914
- const links = this.doc.querySelectorAll('link[rel="stylesheet"]');
681
+ const links = document.querySelectorAll('link[rel="stylesheet"]');
915
682
  let count = 0;
916
683
  for (const link of Array.from(links)) {
917
- if (!link.href.startsWith(this.win.location.origin)) {
918
- console.log('[IframeStyleReplacer] Skipping external CSS:', link.href);
684
+ if (!link.href.startsWith(location.origin)) {
685
+ console.log('[Iframe] Skipping external CSS:', link.href);
919
686
  continue;
920
687
  }
921
688
  try {
922
689
  const res = await fetch(link.href);
923
690
  let css = await res.text();
924
691
  if (this.regex.test(css)) {
925
- this.regex.lastIndex = 0;
926
692
  css = this.replaceInText(css);
927
- const style = this.doc.createElement('style');
693
+ const style = document.createElement('style');
928
694
  style.textContent = css;
929
695
  style.dataset.originalHref = link.href;
930
696
  link.parentNode?.insertBefore(style, link);
@@ -933,25 +699,30 @@ class IframeStyleReplacer {
933
699
  }
934
700
  }
935
701
  catch (e) {
936
- console.warn('[IframeStyleReplacer] Cannot load CSS:', link.href, e);
702
+ console.warn('[Iframe] Cannot load CSS:', link.href, e);
937
703
  }
938
704
  }
939
- console.log(`[IframeStyleReplacer] Replaced ${count} linked CSS files`);
705
+ console.log(`[Iframe] Replaced ${count} linked CSS files`);
940
706
  return count;
941
707
  }
942
708
  getFinalHeight() {
943
709
  // Trigger reflow
944
- void this.doc.body.offsetHeight;
945
- return Math.max(this.doc.body.scrollHeight, this.doc.body.offsetHeight, this.doc.documentElement.scrollHeight, this.doc.documentElement.offsetHeight, this.doc.documentElement.clientHeight);
710
+ void document.body.offsetHeight;
711
+ return Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight, document.documentElement.clientHeight);
946
712
  }
947
- getFinalWidth() {
948
- return Math.max(this.doc.body.scrollWidth, this.doc.body.offsetWidth, this.doc.documentElement.scrollWidth, this.doc.documentElement.offsetWidth, this.doc.documentElement.clientWidth);
713
+ notifyParent(height) {
714
+ window.parent.postMessage({
715
+ type: 'IFRAME_HEIGHT_CALCULATED',
716
+ height,
717
+ width: document.body.scrollWidth,
718
+ }, '*');
719
+ console.log('[Iframe] Sent height to parent:', height);
949
720
  }
950
721
  async waitForResources() {
951
- if ('fonts' in this.doc) {
952
- await this.doc.fonts.ready;
722
+ if ('fonts' in document) {
723
+ await document.fonts.ready;
953
724
  }
954
- const images = Array.from(this.doc.images).filter((img) => !img.complete);
725
+ const images = Array.from(document.images).filter((img) => !img.complete);
955
726
  if (images.length > 0) {
956
727
  await Promise.all(images.map((img) => new Promise((resolve) => {
957
728
  img.onload = img.onerror = resolve;
@@ -960,131 +731,35 @@ class IframeStyleReplacer {
960
731
  }
961
732
  async run() {
962
733
  try {
963
- console.log('[IframeStyleReplacer] Starting viewport units replacement...');
964
734
  this.processInlineStyles();
965
735
  this.processStyleTags();
966
736
  this.processStylesheets();
967
737
  await this.processLinkedStylesheets();
968
738
  // await this.waitForResources();
969
- return await new Promise((resolve) => {
970
- requestAnimationFrame(() => {
971
- const height = this.getFinalHeight();
972
- const width = this.getFinalWidth();
973
- console.log('[IframeStyleReplacer] Calculated dimensions:', { height, width });
974
- resolve({ height, width });
975
- });
739
+ requestAnimationFrame(() => {
740
+ const height = this.getFinalHeight();
741
+ this.notifyParent(height);
976
742
  });
977
743
  }
978
744
  catch (err) {
979
- console.error('[IframeStyleReplacer] Critical error:', err);
980
- return {
981
- height: this.doc.body.scrollHeight || 1000,
982
- width: this.doc.body.scrollWidth || 1000,
983
- };
745
+ console.error('[Iframe] Critical error:', err);
746
+ this.notifyParent(document.body.scrollHeight || 1000);
984
747
  }
985
748
  }
986
- updateConfig(config) {
987
- this.config = { ...this.config, ...config };
988
- }
989
- }
990
-
991
- class IframeHelperFixer {
992
- iframe;
993
- config;
994
- replacer = null;
995
- navigationBlocker = null;
996
- constructor(config) {
997
- this.config = config;
998
- this.iframe = config.iframe;
999
- this.init();
1000
- }
1001
- async init() {
1002
- if (!this.iframe) {
1003
- console.error('[IframeHelper] iframe not found');
1004
- this.config.onError?.(new Error('iframe not found'));
1005
- return;
1006
- }
1007
- // Wait for iframe to load completely
1008
- if (this.iframe.contentDocument?.readyState === 'complete') {
1009
- await this.process();
749
+ init() {
750
+ if (document.readyState === 'loading') {
751
+ document.addEventListener('DOMContentLoaded', () => this.run());
1010
752
  }
1011
753
  else {
1012
- this.iframe.addEventListener('load', () => this.process());
1013
- }
1014
- }
1015
- async process() {
1016
- if (!this.iframe.contentDocument || !this.iframe.contentWindow) {
1017
- console.error('[IframeHelper] Cannot access iframe document');
1018
- this.config.onError?.(new Error('Cannot access iframe document'));
1019
- return;
1020
- }
1021
- try {
1022
- console.log('[IframeHelper] Processing viewport units...');
1023
- // Create replacer instance
1024
- this.replacer = new IframeStyleReplacer(this.iframe, this.config);
1025
- // Create navigation blocker
1026
- this.navigationBlocker = new IframeNavigationBlockerV2(this.iframe);
1027
- // Run replacement
1028
- const result = await this.replacer.run();
1029
- console.log('[IframeHelper] Process completed:', result);
1030
- // Trigger success callback
1031
- this.config.onSuccess?.(result);
1032
- // Dispatch custom event
1033
- window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', {
1034
- detail: result,
1035
- }));
1036
- }
1037
- catch (error) {
1038
- console.error('[IframeHelper] Failed to process:', error);
1039
- this.config.onError?.(error);
1040
- }
1041
- }
1042
- async recalculate() {
1043
- console.log('[IframeHelper] Recalculating...');
1044
- await this.process();
1045
- }
1046
- updateConfig(config) {
1047
- this.config = { ...this.config, ...config };
1048
- if (this.replacer) {
1049
- this.replacer.updateConfig(config);
754
+ this.run();
1050
755
  }
1051
756
  }
1052
- enableNavigationBlocking() {
1053
- this.navigationBlocker?.enable();
1054
- }
1055
- enableNavigationBlockingMessage() {
1056
- this.navigationBlocker?.enableMessage();
1057
- }
1058
- disableNavigationBlocking() {
1059
- this.navigationBlocker?.disable();
1060
- }
1061
- disableNavigationBlockingMessage() {
1062
- this.navigationBlocker?.disableMessage();
1063
- }
1064
- destroy() {
1065
- this.replacer = null;
1066
- this.navigationBlocker?.destroy();
1067
- this.navigationBlocker = null;
1068
- console.log('[IframeHelper] Destroyed');
1069
- }
1070
757
  }
1071
758
 
1072
- function initIframeHelperFixer(config) {
1073
- const fixer = new IframeHelperFixer(config);
1074
- window.addEventListener('iframe-dimensions-applied', ((e) => {
1075
- const ev = e;
1076
- console.log('[IframeHelper] Iframe dimensions finalized:', ev.detail);
1077
- }));
1078
- window.addEventListener('iframe-navigation-blocked', ((e) => {
1079
- const ev = e;
1080
- console.warn('[IframeHelper] Iframe tried to navigate to:', ev.detail.url);
1081
- }));
1082
- window.addEventListener('iframe-form-submit', ((e) => {
1083
- const ev = e;
1084
- console.log('[IframeHelper] Iframe form submitted:', ev.detail.data);
1085
- }));
1086
- return fixer;
1087
- }
759
+ var viewportReplacer = /*#__PURE__*/Object.freeze({
760
+ __proto__: null,
761
+ default: ViewportUnitsReplacer
762
+ });
1088
763
 
1089
764
  const scrollToElementIfNeeded = (visualRef, rect, scale) => {
1090
765
  if (!visualRef.current)
@@ -1198,7 +873,7 @@ const useHeatmapEffects = ({ isVisible, isElementSidebarOpen, setShouldShowCallo
1198
873
 
1199
874
  const useHeatmapElementPosition = ({ iframeRef, wrapperRef, visualizer }) => {
1200
875
  const widthScale = useHeatmapVizStore((state) => state.scale);
1201
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
876
+ const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
1202
877
  const heatmapWidth = useHeatmapConfigStore((state) => state.width);
1203
878
  return useCallback((element) => {
1204
879
  const hash = element?.hash;
@@ -1246,179 +921,6 @@ const debounce = (fn, delay) => {
1246
921
  };
1247
922
  };
1248
923
 
1249
- // ===================== CONSTANTS =====================
1250
- const HEATMAP_ELEMENT_ATTRIBUTE = 'data-clarity-hashalpha'; // Hoặc attribute bạn đang dùng
1251
- // ===================== UTILITY FUNCTIONS =====================
1252
- /**
1253
- * Lấy bounding box tuyệt đối của element (relative to document)
1254
- */
1255
- function getBoundingBox(element) {
1256
- if (typeof element.getBoundingClientRect !== 'function') {
1257
- return null;
1258
- }
1259
- const rect = element.getBoundingClientRect();
1260
- // Lấy scroll offset (hỗ trợ cả cách cũ và mới)
1261
- const scrollLeft = 'pageXOffset' in window ? window.pageXOffset : document.documentElement.scrollLeft;
1262
- const scrollTop = 'pageYOffset' in window ? window.pageYOffset : document.documentElement.scrollTop;
1263
- // Kiểm tra element có kích thước hợp lệ
1264
- if (!rect || (rect.height === 0 && rect.width === 0)) {
1265
- return null;
1266
- }
1267
- // Trả về vị trí tuyệt đối
1268
- return {
1269
- left: Math.floor(rect.left + scrollLeft),
1270
- top: Math.floor(rect.top + scrollTop),
1271
- width: Math.floor(rect.width),
1272
- height: Math.floor(rect.height),
1273
- };
1274
- }
1275
- /**
1276
- * Lấy tất cả elements tại tọa độ (x, y), hỗ trợ Shadow DOM
1277
- */
1278
- function getElementsAtPoint(documentOrShadowRoot, x, y, filterFunction, visitedShadowRoots = new Set()) {
1279
- // Lấy tất cả elements tại vị trí
1280
- const elementsAtPoint = documentOrShadowRoot.elementsFromPoint(x, y);
1281
- if (!filterFunction) {
1282
- return elementsAtPoint;
1283
- }
1284
- // Tìm element đầu tiên match với filter
1285
- const matchedElement = elementsAtPoint.find(filterFunction);
1286
- // Nếu element có Shadow DOM và chưa visit -> đệ quy vào
1287
- if (matchedElement?.shadowRoot && !visitedShadowRoots.has(matchedElement.shadowRoot)) {
1288
- visitedShadowRoots.add(matchedElement.shadowRoot);
1289
- return getElementsAtPoint(matchedElement.shadowRoot, x, y, filterFunction, visitedShadowRoots);
1290
- }
1291
- return elementsAtPoint;
1292
- }
1293
- // ===================== MAIN HOOK =====================
1294
- function useHeatmapMouseHandler(props) {
1295
- const { heatmapWrapperRef, iframeRef, parentRef, heatmapInfo, scaleRatio, onElementHover } = props;
1296
- const handleMouseMove = useCallback((event) => {
1297
- // Kiểm tra tất cả refs và data cần thiết
1298
- if (!heatmapWrapperRef?.current ||
1299
- !iframeRef?.current ||
1300
- !iframeRef.current.contentDocument ||
1301
- !heatmapInfo?.elementMapInfo ||
1302
- !parentRef?.current) {
1303
- return;
1304
- }
1305
- try {
1306
- // Tính toán scroll position (đã scale)
1307
- const scrollTop = parentRef.current.scrollTop / scaleRatio;
1308
- console.log(`🚀 🐥 ~ useHeatmapMouseHandler ~ scrollTop:`, scrollTop);
1309
- // Lấy vị trí của heatmap wrapper
1310
- const wrapperRect = heatmapWrapperRef.current.getBoundingClientRect();
1311
- // Tính toán tọa độ chuột trong iframe (đã scale)
1312
- const mouseX = (event.clientX - wrapperRect.left) / scaleRatio;
1313
- const mouseY = (event.clientY - wrapperRect.top) / scaleRatio - scrollTop;
1314
- // Tìm elements tại vị trí chuột
1315
- const elementsAtPoint = getElementsAtPoint(iframeRef.current.contentDocument, Math.round(mouseX), Math.round(mouseY),
1316
- // Filter: chỉ lấy elements có heatmap attribute
1317
- (element) => element.hasAttribute(HEATMAP_ELEMENT_ATTRIBUTE));
1318
- if (!elementsAtPoint || elementsAtPoint.length === 0) {
1319
- return;
1320
- }
1321
- // Duyệt qua các elements tìm được
1322
- for (let i = 0; i < elementsAtPoint.length; i++) {
1323
- const element = elementsAtPoint[i];
1324
- // Lấy hash/id của element
1325
- const elementHash = element.getAttribute(HEATMAP_ELEMENT_ATTRIBUTE);
1326
- // Kiểm tra element có data trong heatmapInfo không
1327
- if (elementHash && heatmapInfo.elementMapInfo[elementHash]) {
1328
- const elementData = heatmapInfo.elementMapInfo[elementHash];
1329
- // Lấy bounding box của element
1330
- const boundingBox = getBoundingBox(element);
1331
- if (boundingBox) {
1332
- // Tính rank của element
1333
- const rank = Array.isArray(heatmapInfo.sortedElements) && elementData
1334
- ? heatmapInfo.sortedElements.indexOf(elementData) + 1
1335
- : NaN;
1336
- // Callback với thông tin element
1337
- onElementHover({
1338
- ...boundingBox,
1339
- // Giới hạn width không vượt quá width của heatmap
1340
- width: Math.min(boundingBox.width, heatmapInfo.width || 0),
1341
- // Adjust top position với scroll
1342
- top: boundingBox.top + scrollTop,
1343
- // Metadata
1344
- hash: elementHash,
1345
- clicks: elementData.totalclicks,
1346
- rank: rank,
1347
- selector: elementData.selector || '',
1348
- });
1349
- // Dừng loop khi tìm thấy element hợp lệ đầu tiên
1350
- break;
1351
- }
1352
- }
1353
- }
1354
- }
1355
- catch (error) {
1356
- console.warn('Error handling mouse move on heatmap:', error);
1357
- }
1358
- }, [heatmapWrapperRef, iframeRef, parentRef, heatmapInfo, scaleRatio, onElementHover]);
1359
- return { handleMouseMove };
1360
- }
1361
- // ===================== EXAMPLE USAGE =====================
1362
- /*
1363
- import { useRef, useState } from 'react';
1364
-
1365
- function HeatmapComponent() {
1366
- const heatmapWrapperRef = useRef<HTMLDivElement>(null);
1367
- const iframeRef = useRef<HTMLIFrameElement>(null);
1368
- const parentRef = useRef<HTMLDivElement>(null);
1369
-
1370
- const [hoveredElement, setHoveredElement] = useState<HoveredElementInfo | null>(null);
1371
-
1372
- const heatmapInfo = {
1373
- width: 1920,
1374
- elementMapInfo: {
1375
- 'hash123': {
1376
- totalclicks: 45,
1377
- selector: 'button.submit'
1378
- }
1379
- },
1380
- sortedElements: [...]
1381
- };
1382
-
1383
- const { handleMouseMove } = useHeatmapMouseHandler({
1384
- heatmapWrapperRef,
1385
- iframeRef,
1386
- parentRef,
1387
- heatmapInfo,
1388
- scaleRatio: 0.8, // 80% zoom
1389
- onElementHover: (info) => {
1390
- setHoveredElement(info);
1391
- console.log('Hovered element:', info);
1392
- }
1393
- });
1394
-
1395
- return (
1396
- <div ref={parentRef}>
1397
- <div
1398
- ref={heatmapWrapperRef}
1399
- onMouseMove={handleMouseMove}
1400
- >
1401
- <iframe ref={iframeRef} />
1402
-
1403
- {hoveredElement && (
1404
- <div className="tooltip" style={{
1405
- position: 'absolute',
1406
- left: hoveredElement.left,
1407
- top: hoveredElement.top
1408
- }}>
1409
- Clicks: {hoveredElement.clicks}
1410
- <br />
1411
- Rank: #{hoveredElement.rank}
1412
- <br />
1413
- Selector: {hoveredElement.selector}
1414
- </div>
1415
- )}
1416
- </div>
1417
- </div>
1418
- );
1419
- }
1420
- */
1421
-
1422
924
  const useHoveredElement = ({ iframeRef, getRect }) => {
1423
925
  const hoveredElement = useHeatmapInteractionStore((state) => state.hoveredElement);
1424
926
  const setHoveredElement = useHeatmapInteractionStore((state) => state.setHoveredElement);
@@ -1440,7 +942,7 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
1440
942
  const doc = iframe.contentDocument;
1441
943
  const iframeRect = iframe.getBoundingClientRect();
1442
944
  const { x, y } = convertViewportToIframeCoords(event.clientX, event.clientY, iframeRect, widthScale);
1443
- const targetElement = findTargetElement(doc, x, y, heatmapInfo);
945
+ const targetElement = findTargetElement(doc, x, y);
1444
946
  if (!targetElement || !isValidElement(targetElement, heatmapInfo)) {
1445
947
  reset();
1446
948
  return;
@@ -1491,25 +993,7 @@ const convertViewportToIframeCoords = (clientX, clientY, iframeRect, scale) => {
1491
993
  }
1492
994
  return { x, y };
1493
995
  };
1494
- const findTargetElement = (doc, x, y, heatmapInfo) => {
1495
- const HEATMAP_ELEMENT_ATTRIBUTE = 'data-clarity-hashalpha';
1496
- const elementsAtPoint = getElementsAtPoint(doc, Math.round(x), Math.round(y), (element) => element.hasAttribute(HEATMAP_ELEMENT_ATTRIBUTE));
1497
- let dataElement = null;
1498
- for (let i = 0; i < elementsAtPoint.length; i++) {
1499
- const element = elementsAtPoint[i];
1500
- const elementHash = element.getAttribute(HEATMAP_ELEMENT_ATTRIBUTE);
1501
- if (elementHash && heatmapInfo.elementMapInfo?.[elementHash]) {
1502
- heatmapInfo.elementMapInfo[elementHash];
1503
- const boundingBox = getBoundingBox(element);
1504
- if (boundingBox) {
1505
- dataElement = element;
1506
- break;
1507
- }
1508
- }
1509
- }
1510
- if (!!dataElement) {
1511
- return dataElement;
1512
- }
996
+ const findTargetElement = (doc, x, y) => {
1513
997
  let targetElement = getElementAtPoint(doc, x, y);
1514
998
  if (!targetElement) {
1515
999
  targetElement = doc.elementFromPoint(x, y);
@@ -1533,9 +1017,9 @@ var MessageType;
1533
1017
  MessageType["GX_DOM_TRACKING_PAYLOAD"] = "GX_DOM_TRACKING_PAYLOAD";
1534
1018
  MessageType["CLARITY_READY"] = "CLARITY_READY";
1535
1019
  })(MessageType || (MessageType = {}));
1536
- function useVizLiveIframeMsg(options = {}) {
1020
+ function useIframeMessage(options = {}) {
1537
1021
  const { trustedOrigins = [], onMessage } = options;
1538
- const addPayload = useHeatmapLiveStore((state) => state.addPayload);
1022
+ const [payloads, setPayloads] = useState([]);
1539
1023
  const [isReady, setIsReady] = useState(false);
1540
1024
  const iframeRef = useRef(null);
1541
1025
  const isValidOrigin = useCallback((origin) => {
@@ -1561,9 +1045,9 @@ function useVizLiveIframeMsg(options = {}) {
1561
1045
  switch (message.type) {
1562
1046
  case MessageType.GX_DOM_TRACKING_PAYLOAD:
1563
1047
  if (message.payload) {
1564
- const data = decodePayloads(message.payload);
1565
- if (data) {
1566
- addPayload(data);
1048
+ const decodedPayloads = decodePayloads(message.payload);
1049
+ if (decodedPayloads) {
1050
+ setPayloads((prev) => [...prev, decodedPayloads]);
1567
1051
  }
1568
1052
  }
1569
1053
  break;
@@ -1578,19 +1062,27 @@ function useVizLiveIframeMsg(options = {}) {
1578
1062
  window.removeEventListener('message', handleMessage);
1579
1063
  };
1580
1064
  }, [handleMessage]);
1065
+ const clearPayloads = useCallback(() => {
1066
+ setPayloads([]);
1067
+ }, []);
1068
+ const reset = useCallback(() => {
1069
+ setPayloads([]);
1070
+ setIsReady(false);
1071
+ }, []);
1581
1072
  return {
1582
1073
  iframeRef,
1074
+ payloads,
1583
1075
  isReady,
1076
+ clearPayloads,
1077
+ reset,
1584
1078
  };
1585
1079
  }
1586
-
1587
- function useVizLiveRender() {
1080
+ function useIframeRender() {
1588
1081
  const wrapperHeight = useHeatmapLiveStore((state) => state.wrapperHeight);
1589
- const setIframeHeight = useHeatmapLiveStore((state) => state.setIframeHeight);
1590
1082
  const contentWidth = useHeatmapConfigStore((state) => state.width);
1083
+ const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
1591
1084
  const htmlContent = useHeatmapLiveStore((state) => state.htmlContent);
1592
- const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
1593
- const { iframeRef, isReady } = useVizLiveIframeMsg();
1085
+ const { iframeRef, payloads, isReady } = useIframeMessage();
1594
1086
  useEffect(() => {
1595
1087
  if (!htmlContent || !iframeRef.current)
1596
1088
  return;
@@ -1610,50 +1102,50 @@ function useVizLiveRender() {
1610
1102
  const iframe = iframeRef.current;
1611
1103
  if (!iframe || !htmlContent)
1612
1104
  return;
1613
- setIsRenderViz(false);
1614
- reset(iframe, { width: contentWidth, height: wrapperHeight }, (height) => {
1615
- height && setIframeHeight(height);
1616
- setIsRenderViz(true);
1105
+ reset$1(iframe, { width: contentWidth, height: wrapperHeight }, (height) => {
1106
+ setIframeHeight(height);
1617
1107
  });
1618
1108
  }, [isReady, contentWidth, wrapperHeight]);
1619
1109
  return {
1620
1110
  iframeRef,
1111
+ payloads,
1112
+ isReady,
1621
1113
  };
1622
1114
  }
1623
- function reset(iframe, rect, onSuccess) {
1624
- const fixer = initIframeHelperFixer({
1625
- targetWidth: rect.width,
1626
- targetHeight: rect.height,
1115
+ function reset$1(iframe, payloads, onSuccess) {
1116
+ const viewportFixer = initViewportFixer({
1117
+ targetWidth: payloads.width,
1118
+ targetHeight: payloads.height,
1627
1119
  iframe: iframe,
1628
1120
  onSuccess: (data) => {
1629
- iframe.height = `${data.height}px`;
1630
1121
  onSuccess(data.height);
1122
+ iframe.height = `${data.height}px`;
1631
1123
  },
1632
1124
  });
1633
- // fixer.recalculate();
1634
- fixer.enableNavigationBlocking();
1125
+ viewportFixer.recalculate();
1126
+ return iframe;
1635
1127
  }
1636
1128
 
1637
- let visualizer = new Visualizer();
1638
1129
  const useHeatmapRender = () => {
1639
1130
  const data = useHeatmapDataStore((state) => state.data);
1640
- const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
1131
+ const setVizRef = useHeatmapVizStore((state) => state.setVizRef);
1641
1132
  const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
1642
- const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
1133
+ const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
1643
1134
  const iframeRef = useRef(null);
1644
1135
  const renderHeatmap = useCallback(async (payloads) => {
1645
1136
  if (!payloads || payloads.length === 0)
1646
1137
  return;
1647
1138
  setIsRenderViz(false);
1139
+ const visualizer = new Visualizer();
1648
1140
  const iframe = iframeRef.current;
1649
1141
  if (!iframe?.contentWindow)
1650
1142
  return;
1651
1143
  await visualizer.html(payloads, iframe.contentWindow);
1652
- initIframe(iframe, payloads, (height) => {
1144
+ reset(iframe, payloads, (height) => {
1653
1145
  height && setIframeHeight(height);
1654
1146
  setIsRenderViz(true);
1655
- setVizRef(visualizer);
1656
1147
  });
1148
+ setVizRef(visualizer);
1657
1149
  }, []);
1658
1150
  useEffect(() => {
1659
1151
  if (!data || data.length === 0)
@@ -1667,20 +1159,21 @@ const useHeatmapRender = () => {
1667
1159
  iframeRef,
1668
1160
  };
1669
1161
  };
1670
- function initIframe(iframe, payloads, onSuccess) {
1162
+ function reset(iframe, payloads, onSuccess) {
1671
1163
  const { size } = findLastSizeOfDom(payloads);
1672
1164
  const docWidth = size.width ?? 0;
1673
1165
  const docHeight = size.height ?? 0;
1674
- initIframeHelperFixer({
1166
+ const viewportFixer = initViewportFixer({
1675
1167
  targetWidth: docWidth,
1676
1168
  targetHeight: docHeight,
1677
1169
  iframe: iframe,
1678
1170
  onSuccess: (data) => {
1679
- iframe.height = `${data.height}px`;
1680
1171
  onSuccess(data.height);
1172
+ iframe.height = `${data.height}px`;
1681
1173
  },
1682
1174
  });
1683
- // fixer.recalculate();
1175
+ viewportFixer.recalculate();
1176
+ return iframe;
1684
1177
  }
1685
1178
 
1686
1179
  function isMobileDevice(userAgent) {
@@ -1865,64 +1358,30 @@ const useContentDimensions = ({ iframeRef, }) => {
1865
1358
  return { contentWidth };
1866
1359
  };
1867
1360
 
1868
- const useObserveIframeHeight = (props) => {
1361
+ const useIframeHeight = (props) => {
1869
1362
  const { iframeRef, setIframeHeight } = props;
1870
1363
  const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz);
1871
1364
  const resizeObserverRef = useRef(null);
1872
1365
  const mutationObserverRef = useRef(null);
1873
- const debounceTimerRef = useRef(null);
1874
- const lastHeightRef = useRef(0);
1875
- const animationFrameRef = useRef(null);
1876
1366
  const updateIframeHeight = useCallback(() => {
1877
1367
  const iframe = iframeRef.current;
1878
- if (!iframe || !setIframeHeight)
1368
+ if (!iframe)
1879
1369
  return;
1880
1370
  try {
1881
1371
  const iframeDocument = iframe.contentDocument;
1882
1372
  const iframeBody = iframeDocument?.body;
1883
- const iframeDocumentElement = iframeDocument?.documentElement;
1884
- if (!iframeBody || !iframeDocumentElement)
1373
+ if (!iframeBody)
1885
1374
  return;
1886
- iframe.style.height = 'auto';
1887
- requestAnimationFrame(() => {
1888
- const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
1889
- const documentHeight = Math.max(iframeDocumentElement.scrollHeight, iframeDocumentElement.offsetHeight, iframeDocumentElement.clientHeight);
1890
- const actualHeight = Math.max(bodyHeight, documentHeight);
1891
- if (actualHeight > 0) {
1892
- lastHeightRef.current = actualHeight;
1893
- iframe.height = `${actualHeight}px`;
1894
- iframe.style.height = `${actualHeight}px`;
1895
- setIframeHeight(actualHeight);
1896
- }
1897
- });
1375
+ const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
1376
+ if (bodyHeight > 0) {
1377
+ iframe.height = `${bodyHeight}px`;
1378
+ setIframeHeight(bodyHeight);
1379
+ }
1898
1380
  }
1899
1381
  catch (error) {
1900
1382
  console.warn('Cannot measure iframe content:', error);
1901
1383
  }
1902
1384
  }, [iframeRef, setIframeHeight]);
1903
- const debouncedUpdate = useCallback(() => {
1904
- // Cancel pending updates
1905
- if (debounceTimerRef.current) {
1906
- clearTimeout(debounceTimerRef.current);
1907
- }
1908
- if (animationFrameRef.current) {
1909
- cancelAnimationFrame(animationFrameRef.current);
1910
- }
1911
- debounceTimerRef.current = setTimeout(() => {
1912
- animationFrameRef.current = requestAnimationFrame(() => {
1913
- updateIframeHeight();
1914
- });
1915
- }, 50);
1916
- }, [updateIframeHeight]);
1917
- // Immediate update không debounce (cho ResizeObserver)
1918
- const immediateUpdate = useCallback(() => {
1919
- if (animationFrameRef.current) {
1920
- cancelAnimationFrame(animationFrameRef.current);
1921
- }
1922
- animationFrameRef.current = requestAnimationFrame(() => {
1923
- updateIframeHeight();
1924
- });
1925
- }, [updateIframeHeight]);
1926
1385
  useEffect(() => {
1927
1386
  const iframe = iframeRef.current;
1928
1387
  if (!iframe || !isRenderViz)
@@ -1940,24 +1399,22 @@ const useObserveIframeHeight = (props) => {
1940
1399
  if (mutationObserverRef.current) {
1941
1400
  mutationObserverRef.current.disconnect();
1942
1401
  }
1402
+ // ResizeObserver for size changes
1943
1403
  if (typeof window.ResizeObserver !== 'undefined') {
1944
- resizeObserverRef.current = new ResizeObserver(immediateUpdate);
1404
+ resizeObserverRef.current = new ResizeObserver(updateIframeHeight);
1945
1405
  resizeObserverRef.current.observe(iframeBody);
1946
- const iframeDocumentElement = iframeDocument?.documentElement;
1947
- if (iframeDocumentElement) {
1948
- resizeObserverRef.current.observe(iframeDocumentElement);
1949
- }
1950
1406
  }
1407
+ // MutationObserver for DOM changes
1951
1408
  if (typeof window.MutationObserver !== 'undefined') {
1952
- mutationObserverRef.current = new MutationObserver(immediateUpdate);
1409
+ mutationObserverRef.current = new MutationObserver(updateIframeHeight);
1953
1410
  mutationObserverRef.current.observe(iframeBody, {
1954
1411
  childList: true,
1955
1412
  subtree: true,
1956
1413
  attributes: true,
1957
- attributeFilter: ['style', 'class'],
1958
- characterData: false,
1414
+ characterData: true,
1959
1415
  });
1960
1416
  }
1417
+ // Initial measurement
1961
1418
  updateIframeHeight();
1962
1419
  }
1963
1420
  catch (error) {
@@ -1971,23 +1428,15 @@ const useObserveIframeHeight = (props) => {
1971
1428
  iframe.addEventListener('load', setupObservers, { once: true });
1972
1429
  }
1973
1430
  return () => {
1974
- // Cleanup observers
1975
1431
  if (resizeObserverRef.current) {
1976
1432
  resizeObserverRef.current.disconnect();
1977
1433
  }
1978
1434
  if (mutationObserverRef.current) {
1979
1435
  mutationObserverRef.current.disconnect();
1980
1436
  }
1981
- // Cleanup timers
1982
- if (debounceTimerRef.current) {
1983
- clearTimeout(debounceTimerRef.current);
1984
- }
1985
- if (animationFrameRef.current) {
1986
- cancelAnimationFrame(animationFrameRef.current);
1987
- }
1988
1437
  iframe.removeEventListener('load', setupObservers);
1989
1438
  };
1990
- }, [iframeRef, isRenderViz, updateIframeHeight, debouncedUpdate, immediateUpdate]);
1439
+ }, [iframeRef, isRenderViz, updateIframeHeight]);
1991
1440
  return {};
1992
1441
  };
1993
1442
 
@@ -2033,18 +1482,16 @@ const useHeatmapScale = (props) => {
2033
1482
  // 2. Get content dimensions from config
2034
1483
  const { contentWidth } = useContentDimensions({ iframeRef });
2035
1484
  // 3. Observe iframe height (now reacts to width changes)
2036
- useObserveIframeHeight({ iframeRef, setIframeHeight });
1485
+ useIframeHeight({ iframeRef, setIframeHeight });
2037
1486
  // 4. Calculate scale
2038
1487
  const { scale } = useScaleCalculation({ containerWidth, contentWidth });
2039
1488
  // 5. Setup scroll sync
2040
1489
  const { handleScroll } = useScrollSync({ iframeRef });
2041
- const scaledHeight = iframeHeight * scale;
2042
- const scaledWidth = contentWidth * scale;
2043
1490
  return {
2044
1491
  containerWidth,
2045
1492
  containerHeight,
2046
- scaledWidth,
2047
- scaledHeight,
1493
+ scaledWidth: contentWidth * scale,
1494
+ scaledHeight: iframeHeight * scale,
2048
1495
  handleScroll,
2049
1496
  };
2050
1497
  };
@@ -2204,159 +1651,35 @@ const VizContainer = ({ children, setWrapperHeight }) => {
2204
1651
  const useClickmap = () => {
2205
1652
  const [isInitialized, setIsInitialized] = useState(false);
2206
1653
  const clickmap = useHeatmapDataStore((state) => state.clickmap);
2207
- const vizRef = useHeatmapSingleStore((state) => state.vizRef);
2208
- const start = useCallback(() => {
1654
+ const vizRef = useHeatmapVizStore((state) => state.vizRef);
1655
+ useEffect(() => {
2209
1656
  if (isInitialized)
2210
1657
  return;
2211
1658
  if (!vizRef || !clickmap || clickmap.length === 0)
2212
1659
  return;
2213
- try {
2214
- vizRef?.clearmap?.();
2215
- vizRef?.clickmap?.(clickmap);
2216
- setIsInitialized(true);
2217
- }
2218
- catch (error) {
2219
- console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
2220
- }
1660
+ vizRef.clearmap();
1661
+ vizRef?.clickmap(clickmap);
1662
+ setIsInitialized(true);
2221
1663
  }, [vizRef, clickmap]);
2222
- return { start };
1664
+ return {};
2223
1665
  };
2224
1666
 
2225
- const DATA_SCROLLMAP = [
2226
- {
2227
- scrollReachY: 5,
2228
- cumulativeSum: 0,
2229
- percUsers: 0,
2230
- },
2231
- {
2232
- scrollReachY: 10,
2233
- cumulativeSum: 0,
2234
- percUsers: 0,
2235
- },
2236
- {
2237
- scrollReachY: 15,
2238
- cumulativeSum: 0,
2239
- percUsers: 0,
2240
- },
2241
- {
2242
- scrollReachY: 20,
2243
- cumulativeSum: 0,
2244
- percUsers: 0,
2245
- },
2246
- {
2247
- scrollReachY: 25,
2248
- cumulativeSum: 0,
2249
- percUsers: 0,
2250
- },
2251
- {
2252
- scrollReachY: 30,
2253
- cumulativeSum: 0,
2254
- percUsers: 0,
2255
- },
2256
- {
2257
- scrollReachY: 35,
2258
- cumulativeSum: 0,
2259
- percUsers: 0,
2260
- },
2261
- {
2262
- scrollReachY: 40,
2263
- cumulativeSum: 0,
2264
- percUsers: 0,
2265
- },
2266
- {
2267
- scrollReachY: 45,
2268
- cumulativeSum: 0,
2269
- percUsers: 0,
2270
- },
2271
- {
2272
- scrollReachY: 50,
2273
- cumulativeSum: 0,
2274
- percUsers: 0,
2275
- },
2276
- {
2277
- scrollReachY: 55,
2278
- cumulativeSum: 0,
2279
- percUsers: 0,
2280
- },
2281
- {
2282
- scrollReachY: 60,
2283
- cumulativeSum: 0,
2284
- percUsers: 0,
2285
- },
2286
- {
2287
- scrollReachY: 65,
2288
- cumulativeSum: 0,
2289
- percUsers: 0,
2290
- },
2291
- {
2292
- scrollReachY: 70,
2293
- cumulativeSum: 0,
2294
- percUsers: 0,
2295
- },
2296
- {
2297
- scrollReachY: 75,
2298
- cumulativeSum: 0,
2299
- percUsers: 0,
2300
- },
2301
- {
2302
- scrollReachY: 80,
2303
- cumulativeSum: 0,
2304
- percUsers: 0,
2305
- },
2306
- {
2307
- scrollReachY: 85,
2308
- cumulativeSum: 0,
2309
- percUsers: 0,
2310
- },
2311
- {
2312
- scrollReachY: 90,
2313
- cumulativeSum: 0,
2314
- percUsers: 0,
2315
- },
2316
- {
2317
- scrollReachY: 95,
2318
- cumulativeSum: 0,
2319
- percUsers: 0,
2320
- },
2321
- {
2322
- scrollReachY: 100,
2323
- cumulativeSum: 0,
2324
- percUsers: 0,
2325
- },
2326
- ];
2327
1667
  const useScrollmap = () => {
2328
- const vizRef = useHeatmapSingleStore((state) => state.vizRef);
2329
- const start = useCallback(() => {
2330
- // if (isInitialized) return;
2331
- const scrollmap = DATA_SCROLLMAP;
2332
- if (!vizRef || !scrollmap || scrollmap.length === 0)
2333
- return;
2334
- try {
2335
- vizRef?.clearmap?.();
2336
- vizRef?.scrollmap?.(scrollmap);
2337
- // setIsInitialized(true);
2338
- }
2339
- catch (error) {
2340
- console.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
2341
- }
2342
- }, [vizRef]);
2343
- return { start };
1668
+ useHeatmapDataStore((state) => state.clickmap);
1669
+ return {};
2344
1670
  };
2345
1671
 
2346
1672
  const useHeatmapVizCanvas = () => {
2347
1673
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
2348
- const { start: startClickmap } = useClickmap();
2349
- const { start: startScrollmap } = useScrollmap();
2350
- useEffect(() => {
1674
+ const heatmapRender = useMemo(() => {
2351
1675
  switch (heatmapType) {
2352
1676
  case IHeatmapType.Click:
2353
- startClickmap();
2354
- break;
1677
+ return useClickmap;
2355
1678
  case IHeatmapType.Scroll:
2356
- startClickmap();
2357
- break;
1679
+ return useScrollmap;
2358
1680
  }
2359
- }, [heatmapType, startClickmap, startScrollmap]);
1681
+ }, [heatmapType]);
1682
+ return heatmapRender?.();
2360
1683
  };
2361
1684
 
2362
1685
  const CLICKED_ELEMENT_ID = 'gx-hm-clicked-element';
@@ -2419,7 +1742,7 @@ const ElementCallout = (props) => {
2419
1742
  window.removeEventListener('resize', handleUpdate);
2420
1743
  visualRef?.current?.removeEventListener('scroll', handleUpdate);
2421
1744
  };
2422
- }, [element, target, visualRef, hozOffset, alignment]);
1745
+ }, [target, visualRef, hozOffset, alignment]);
2423
1746
  const calloutContent = (jsx("div", { ref: calloutRef, className: `clarity-callout clarity-callout--${position.placement} clarity-callout--align-${position.horizontalAlign}`, style: {
2424
1747
  position: 'fixed',
2425
1748
  top: position.top,
@@ -2484,8 +1807,7 @@ const ELEMENT_CALLOUT = {
2484
1807
  alignment: 'left',
2485
1808
  };
2486
1809
  const HeatmapElements = (props) => {
2487
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2488
- const setHoveredElement = useHeatmapInteractionStore((state) => state.setHoveredElement);
1810
+ const height = useHeatmapVizStore((state) => state.iframeHeight);
2489
1811
  const { iframeRef, wrapperRef, visualRef, visualizer, iframeDimensions, isElementSidebarOpen, isVisible = true, areDefaultRanksHidden, isSecondary, ...rest } = props;
2490
1812
  const getRect = useHeatmapElementPosition({
2491
1813
  iframeRef,
@@ -2500,27 +1822,6 @@ const HeatmapElements = (props) => {
2500
1822
  iframeRef,
2501
1823
  getRect,
2502
1824
  });
2503
- const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
2504
- useHeatmapMouseHandler({
2505
- heatmapWrapperRef: wrapperRef,
2506
- iframeRef,
2507
- parentRef: visualRef,
2508
- heatmapInfo: heatmapInfo || {},
2509
- scaleRatio: 0.8, // 80% zoom
2510
- onElementHover: (info) => {
2511
- setHoveredElement({
2512
- hash: info.hash,
2513
- clicks: info.clicks,
2514
- rank: info.rank,
2515
- selector: info.selector,
2516
- top: info.top,
2517
- left: info.left,
2518
- width: info.width,
2519
- height: info.height,
2520
- });
2521
- console.log(`🚀 🐥 ~ HeatmapElements ~ info:`, info);
2522
- },
2523
- });
2524
1825
  useElementCalloutVisible({
2525
1826
  visualRef,
2526
1827
  getRect,
@@ -2536,21 +1837,14 @@ const HeatmapElements = (props) => {
2536
1837
  });
2537
1838
  if (!isVisible)
2538
1839
  return null;
2539
- return (jsxs("div", { onMouseMove: (event) => {
2540
- handleMouseMove(event);
2541
- // handleMouseMove2(event as any);
2542
- }, 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 }))] }));
1840
+ return (jsxs("div", { onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, className: "gx-hm-elements", style: { ...iframeDimensions, height }, 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 }))] }));
2543
1841
  };
2544
1842
 
2545
1843
  const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
2546
1844
  const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
2547
- const contentWidth = useHeatmapConfigStore((state) => state.width);
2548
- const vizRef = useHeatmapSingleStore((state) => state.vizRef);
1845
+ const contentWidth = useHeatmapDataStore((state) => state.config?.width ?? 0);
2549
1846
  const visualizer = {
2550
1847
  get: (hash) => {
2551
- if (vizRef) {
2552
- return vizRef.get(hash);
2553
- }
2554
1848
  const doc = iframeRef.current?.contentDocument;
2555
1849
  if (!doc)
2556
1850
  return null;
@@ -2610,8 +1904,8 @@ const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHe
2610
1904
 
2611
1905
  const VizDomRenderer = ({ mode = 'heatmap' }) => {
2612
1906
  const width = useHeatmapConfigStore((state) => state.width);
2613
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2614
- const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
1907
+ const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
1908
+ const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
2615
1909
  const setSelectedElement = useHeatmapInteractionStore((state) => state.setSelectedElement);
2616
1910
  const wrapperRef = useRef(null);
2617
1911
  const visualRef = useRef(null);
@@ -2621,6 +1915,7 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
2621
1915
  iframeRef,
2622
1916
  visualRef,
2623
1917
  iframeHeight,
1918
+ setIframeHeight,
2624
1919
  });
2625
1920
  const contentWidth = width ?? 0;
2626
1921
  const onScroll = (e) => {
@@ -2635,7 +1930,7 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
2635
1930
  useEffect(() => {
2636
1931
  return cleanUp;
2637
1932
  }, []);
2638
- return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [jsx(VizElements, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef }), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, scrolling: "no" })] }));
1933
+ return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [jsx(VizElements, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef }), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no" })] }));
2639
1934
  };
2640
1935
 
2641
1936
  const VizLoading = () => {
@@ -2645,12 +1940,12 @@ const VizLoading = () => {
2645
1940
  const VizDomHeatmap = () => {
2646
1941
  const controls = useHeatmapControlStore((state) => state.controls);
2647
1942
  const isRendering = useHeatmapDataStore((state) => state.isRendering);
2648
- const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2649
- const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
2650
- const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
1943
+ const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
1944
+ const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
1945
+ const setVizRef = useHeatmapVizStore((state) => state.setVizRef);
2651
1946
  useEffect(() => {
2652
1947
  return () => {
2653
- setVizRef(null);
1948
+ setVizRef(undefined);
2654
1949
  setIframeHeight(0);
2655
1950
  };
2656
1951
  }, []);
@@ -2665,7 +1960,7 @@ const VizLiveRenderer = () => {
2665
1960
  const setIframeHeight = useHeatmapLiveStore((state) => state.setIframeHeight);
2666
1961
  const visualRef = useRef(null);
2667
1962
  const wrapperRef = useRef(null);
2668
- const { iframeRef } = useVizLiveRender();
1963
+ const { iframeRef } = useIframeRender();
2669
1964
  const { scaledHeight, handleScroll } = useHeatmapScale({
2670
1965
  wrapperRef,
2671
1966
  iframeRef,
@@ -2677,15 +1972,13 @@ const VizLiveRenderer = () => {
2677
1972
  const scrollTop = e.currentTarget.scrollTop;
2678
1973
  handleScroll(scrollTop);
2679
1974
  };
2680
- return (jsx(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, iframeHeight: iframeHeight, onScroll: onScroll, children: jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth,
2681
- // height={iframeHeight}
2682
- scrolling: "no", sandbox: "allow-scripts allow-same-origin" }) }));
1975
+ return (jsx(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, iframeHeight: iframeHeight, onScroll: onScroll, children: jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no", sandbox: "allow-scripts allow-same-origin" }) }));
2683
1976
  };
2684
1977
 
2685
1978
  const VizLiveHeatmap = () => {
2686
1979
  const controls = useHeatmapControlStore((state) => state.controls);
2687
1980
  const isRendering = useHeatmapDataStore((state) => state.isRendering);
2688
- const iframeHeight = useHeatmapLiveStore((state) => state.iframeHeight);
1981
+ const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
2689
1982
  const wrapperHeight = useHeatmapLiveStore((state) => state.wrapperHeight);
2690
1983
  const setWrapperHeight = useHeatmapLiveStore((state) => state.setWrapperHeight);
2691
1984
  const reset = useHeatmapLiveStore((state) => state.reset);
@@ -2760,4 +2053,4 @@ const HeatmapLayout = ({ data, clickmap, controls, dataInfo, }) => {
2760
2053
  }
2761
2054
  };
2762
2055
 
2763
- export { GraphView, HeatmapLayout, IHeatmapType, useHeatmapConfigStore, useHeatmapDataStore, useHeatmapInteractionStore, useHeatmapLiveStore, useHeatmapVizStore };
2056
+ export { GraphView, HeatmapLayout, IHeatmapType, useHeatmapConfigStore, useHeatmapDataStore, useHeatmapInteractionStore, useHeatmapLiveStore };