@gemx-dev/heatmap-react 3.5.19 → 3.5.23

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 (456) hide show
  1. package/dist/base.css +3 -0
  2. package/dist/esm/components/GraphView.d.ts +9 -0
  3. package/dist/esm/components/GraphView.d.ts.map +1 -0
  4. package/dist/esm/components/HeatmapLayout/ContentHeader.d.ts +4 -0
  5. package/dist/esm/components/HeatmapLayout/ContentHeader.d.ts.map +1 -0
  6. package/dist/esm/components/HeatmapLayout/HeatmapLayout.d.ts +7 -0
  7. package/dist/esm/components/HeatmapLayout/HeatmapLayout.d.ts.map +1 -0
  8. package/dist/esm/components/HeatmapLayout/LeftSidebar.d.ts +4 -0
  9. package/dist/esm/components/HeatmapLayout/LeftSidebar.d.ts.map +1 -0
  10. package/dist/esm/components/HeatmapLayout/ReplayControls.d.ts +2 -0
  11. package/dist/esm/components/HeatmapLayout/ReplayControls.d.ts.map +1 -0
  12. package/dist/esm/components/HeatmapLayout/VizDomContainer.d.ts +2 -0
  13. package/dist/esm/components/HeatmapLayout/VizDomContainer.d.ts.map +1 -0
  14. package/dist/esm/components/HeatmapLayout/VizDomRenderer.d.ts +6 -0
  15. package/dist/esm/components/HeatmapLayout/VizDomRenderer.d.ts.map +1 -0
  16. package/dist/esm/components/HeatmapLayout/WrapperLayout.d.ts +7 -0
  17. package/dist/esm/components/HeatmapLayout/WrapperLayout.d.ts.map +1 -0
  18. package/dist/esm/components/HeatmapLayout/WrapperPreview.d.ts +4 -0
  19. package/dist/esm/components/HeatmapLayout/WrapperPreview.d.ts.map +1 -0
  20. package/dist/esm/components/HeatmapLayout/index.d.ts +2 -0
  21. package/dist/esm/components/HeatmapLayout/index.d.ts.map +1 -0
  22. package/dist/esm/components/Layout/ContentHeader.d.ts +4 -0
  23. package/dist/esm/components/Layout/ContentHeader.d.ts.map +1 -0
  24. package/dist/esm/components/Layout/HeatmapLayout.d.ts +11 -0
  25. package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -0
  26. package/dist/esm/components/Layout/LeftSidebar.d.ts +4 -0
  27. package/dist/esm/components/Layout/LeftSidebar.d.ts.map +1 -0
  28. package/dist/esm/components/Layout/WrapperLayout.d.ts +8 -0
  29. package/dist/esm/components/Layout/WrapperLayout.d.ts.map +1 -0
  30. package/dist/esm/components/Layout/WrapperPreview.d.ts +4 -0
  31. package/dist/esm/components/Layout/WrapperPreview.d.ts.map +1 -0
  32. package/dist/esm/components/Layout/index.d.ts +2 -0
  33. package/dist/esm/components/Layout/index.d.ts.map +1 -0
  34. package/dist/esm/components/Test.d.ts +121 -0
  35. package/dist/esm/components/Test.d.ts.map +1 -0
  36. package/dist/esm/components/VizDom/ReplayControls.d.ts +2 -0
  37. package/dist/esm/components/VizDom/ReplayControls.d.ts.map +1 -0
  38. package/dist/esm/components/VizDom/VizDomContainer.d.ts +2 -0
  39. package/dist/esm/components/VizDom/VizDomContainer.d.ts.map +1 -0
  40. package/dist/esm/components/VizDom/VizDomRenderer.d.ts +6 -0
  41. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -0
  42. package/dist/esm/components/VizDom/index.d.ts +2 -0
  43. package/dist/esm/components/VizDom/index.d.ts.map +1 -0
  44. package/dist/esm/components/VizElement/ClarityVisualizer.d.ts +150 -0
  45. package/dist/esm/components/VizElement/ClarityVisualizer.d.ts.map +1 -0
  46. package/dist/esm/components/VizElement/ClickedElementOverlay.d.ts +17 -0
  47. package/dist/esm/components/VizElement/ClickedElementOverlay.d.ts.map +1 -0
  48. package/dist/esm/components/VizElement/DefaultRankBadges.d.ts +11 -0
  49. package/dist/esm/components/VizElement/DefaultRankBadges.d.ts.map +1 -0
  50. package/dist/esm/components/VizElement/ElementCallout.d.ts +17 -0
  51. package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -0
  52. package/dist/esm/components/VizElement/HeatmapElementV2.d.ts +45 -0
  53. package/dist/esm/components/VizElement/HeatmapElementV2.d.ts.map +1 -0
  54. package/dist/esm/components/VizElement/HeatmapElements.d.ts +23 -0
  55. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -0
  56. package/dist/esm/components/VizElement/HoveredElementOverlay.d.ts +12 -0
  57. package/dist/esm/components/VizElement/HoveredElementOverlay.d.ts.map +1 -0
  58. package/dist/esm/components/VizElement/MissingElementMessage.d.ts +7 -0
  59. package/dist/esm/components/VizElement/MissingElementMessage.d.ts.map +1 -0
  60. package/dist/esm/components/VizElement/PayloadProcessor.d.ts +65 -0
  61. package/dist/esm/components/VizElement/PayloadProcessor.d.ts.map +1 -0
  62. package/dist/esm/components/VizElement/RankBadge.d.ts +10 -0
  63. package/dist/esm/components/VizElement/RankBadge.d.ts.map +1 -0
  64. package/dist/esm/components/VizElement/VizElementRank.d.ts +74 -0
  65. package/dist/esm/components/VizElement/VizElementRank.d.ts.map +1 -0
  66. package/dist/esm/components/VizElement/VizElements.d.ts +10 -0
  67. package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -0
  68. package/dist/esm/components/VizElement/constants.d.ts +5 -0
  69. package/dist/esm/components/VizElement/constants.d.ts.map +1 -0
  70. package/dist/esm/components/VizElement/helpers.d.ts +20 -0
  71. package/dist/esm/components/VizElement/helpers.d.ts.map +1 -0
  72. package/dist/esm/components/VizElement/index.d.ts +2 -0
  73. package/dist/esm/components/VizElement/index.d.ts.map +1 -0
  74. package/dist/esm/components/VizElement/temp/ClarityVisualizer.d.ts +150 -0
  75. package/dist/esm/components/VizElement/temp/ClarityVisualizer.d.ts.map +1 -0
  76. package/dist/esm/components/VizElement/temp/VizElementRank.d.ts +74 -0
  77. package/dist/esm/components/VizElement/temp/VizElementRank.d.ts.map +1 -0
  78. package/dist/esm/components/VizElement/types.d.ts +13 -0
  79. package/dist/esm/components/VizElement/types.d.ts.map +1 -0
  80. package/dist/esm/components/index.d.ts +4 -0
  81. package/dist/esm/components/index.d.ts.map +1 -0
  82. package/dist/esm/configs/iframe.d.ts +10 -0
  83. package/dist/esm/configs/iframe.d.ts.map +1 -0
  84. package/dist/esm/configs/index.d.ts +3 -0
  85. package/dist/esm/configs/index.d.ts.map +1 -0
  86. package/dist/esm/configs/style.d.ts +9 -0
  87. package/dist/esm/configs/style.d.ts.map +1 -0
  88. package/dist/esm/constants/index.d.ts +5 -0
  89. package/dist/esm/constants/index.d.ts.map +1 -0
  90. package/dist/esm/helpers/iframe.d.ts +3 -0
  91. package/dist/esm/helpers/iframe.d.ts.map +1 -0
  92. package/dist/esm/helpers/index.d.ts +2 -0
  93. package/dist/esm/helpers/index.d.ts.map +1 -0
  94. package/dist/esm/helpers/viz-elements.d.ts +10 -0
  95. package/dist/esm/helpers/viz-elements.d.ts.map +1 -0
  96. package/dist/esm/hooks/index.d.ts +4 -0
  97. package/dist/esm/hooks/index.d.ts.map +1 -0
  98. package/dist/esm/hooks/useHeatmapByMode.d.ts +6 -0
  99. package/dist/esm/hooks/useHeatmapByMode.d.ts.map +1 -0
  100. package/dist/esm/hooks/useHeatmapRender.d.ts +6 -0
  101. package/dist/esm/hooks/useHeatmapRender.d.ts.map +1 -0
  102. package/dist/esm/hooks/useHeatmapScale.d.ts +28 -0
  103. package/dist/esm/hooks/useHeatmapScale.d.ts.map +1 -0
  104. package/dist/esm/hooks/useReplayRender.d.ts +9 -0
  105. package/dist/esm/hooks/useReplayRender.d.ts.map +1 -0
  106. package/dist/esm/hooks/vix-elements/index.d.ts +5 -0
  107. package/dist/esm/hooks/vix-elements/index.d.ts.map +1 -0
  108. package/dist/esm/hooks/vix-elements/useClickedElement.d.ts +14 -0
  109. package/dist/esm/hooks/vix-elements/useClickedElement.d.ts.map +1 -0
  110. package/dist/esm/hooks/vix-elements/useHeatmapEffects.d.ts +8 -0
  111. package/dist/esm/hooks/vix-elements/useHeatmapEffects.d.ts.map +1 -0
  112. package/dist/esm/hooks/vix-elements/useHeatmapElementPosition.d.ts +13 -0
  113. package/dist/esm/hooks/vix-elements/useHeatmapElementPosition.d.ts.map +1 -0
  114. package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts +17 -0
  115. package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts.map +1 -0
  116. package/dist/esm/hooks/viz-render/index.d.ts +2 -0
  117. package/dist/esm/hooks/viz-render/index.d.ts.map +1 -0
  118. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts +8 -0
  119. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -0
  120. package/dist/esm/hooks/viz-render/useHeatmapVizRender.d.ts +8 -0
  121. package/dist/esm/hooks/viz-render/useHeatmapVizRender.d.ts.map +1 -0
  122. package/dist/esm/hooks/viz-render/useReplayRender.d.ts +11 -0
  123. package/dist/esm/hooks/viz-render/useReplayRender.d.ts.map +1 -0
  124. package/dist/esm/hooks/viz-scale/index.d.ts +2 -0
  125. package/dist/esm/hooks/viz-scale/index.d.ts.map +1 -0
  126. package/dist/esm/hooks/viz-scale/useContainerDimensions.d.ts +10 -0
  127. package/dist/esm/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -0
  128. package/dist/esm/hooks/viz-scale/useContentDimensions.d.ts +11 -0
  129. package/dist/esm/hooks/viz-scale/useContentDimensions.d.ts.map +1 -0
  130. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +19 -0
  131. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -0
  132. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts +10 -0
  133. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts.map +1 -0
  134. package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts +10 -0
  135. package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -0
  136. package/dist/esm/hooks/viz-scale/useScrollSync.d.ts +10 -0
  137. package/dist/esm/hooks/viz-scale/useScrollSync.d.ts.map +1 -0
  138. package/dist/esm/index.d.ts +4 -0
  139. package/dist/esm/index.d.ts.map +1 -0
  140. package/dist/esm/index.js +1303 -0
  141. package/dist/esm/index.mjs +1303 -0
  142. package/dist/esm/stores/data.d.ts +18 -0
  143. package/dist/esm/stores/data.d.ts.map +1 -0
  144. package/dist/esm/stores/index.d.ts +2 -0
  145. package/dist/esm/stores/index.d.ts.map +1 -0
  146. package/dist/esm/types/index.d.ts +3 -0
  147. package/dist/esm/types/index.d.ts.map +1 -0
  148. package/dist/esm/ui/BoxStack/BoxStack.d.ts +56 -0
  149. package/dist/esm/ui/BoxStack/BoxStack.d.ts.map +1 -0
  150. package/dist/esm/ui/BoxStack/index.d.ts +2 -0
  151. package/dist/esm/ui/BoxStack/index.d.ts.map +1 -0
  152. package/dist/esm/ui/index.d.ts +2 -0
  153. package/dist/esm/ui/index.d.ts.map +1 -0
  154. package/dist/esm/utils/device.d.ts +2 -0
  155. package/dist/esm/utils/device.d.ts.map +1 -0
  156. package/dist/esm/utils/retry.d.ts +2 -0
  157. package/dist/esm/utils/retry.d.ts.map +1 -0
  158. package/dist/esm/utils/sort.d.ts +3 -0
  159. package/dist/esm/utils/sort.d.ts.map +1 -0
  160. package/dist/style.css +32 -0
  161. package/dist/umd/components/GraphView.d.ts +9 -0
  162. package/dist/umd/components/GraphView.d.ts.map +1 -0
  163. package/dist/umd/components/Layout/ContentHeader.d.ts +4 -0
  164. package/dist/umd/components/Layout/ContentHeader.d.ts.map +1 -0
  165. package/dist/umd/components/Layout/HeatmapLayout.d.ts +11 -0
  166. package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -0
  167. package/dist/umd/components/Layout/LeftSidebar.d.ts +4 -0
  168. package/dist/umd/components/Layout/LeftSidebar.d.ts.map +1 -0
  169. package/dist/umd/components/Layout/WrapperLayout.d.ts +8 -0
  170. package/dist/umd/components/Layout/WrapperLayout.d.ts.map +1 -0
  171. package/dist/umd/components/Layout/WrapperPreview.d.ts +4 -0
  172. package/dist/umd/components/Layout/WrapperPreview.d.ts.map +1 -0
  173. package/dist/umd/components/Layout/index.d.ts +2 -0
  174. package/dist/umd/components/Layout/index.d.ts.map +1 -0
  175. package/dist/umd/components/Test.d.ts +121 -0
  176. package/dist/umd/components/Test.d.ts.map +1 -0
  177. package/dist/umd/components/VizDom/ReplayControls.d.ts +2 -0
  178. package/dist/umd/components/VizDom/ReplayControls.d.ts.map +1 -0
  179. package/dist/umd/components/VizDom/VizDomContainer.d.ts +2 -0
  180. package/dist/umd/components/VizDom/VizDomContainer.d.ts.map +1 -0
  181. package/dist/umd/components/VizDom/VizDomRenderer.d.ts +6 -0
  182. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -0
  183. package/dist/umd/components/VizDom/index.d.ts +2 -0
  184. package/dist/umd/components/VizDom/index.d.ts.map +1 -0
  185. package/dist/umd/components/VizElement/ClickedElementOverlay.d.ts +17 -0
  186. package/dist/umd/components/VizElement/ClickedElementOverlay.d.ts.map +1 -0
  187. package/dist/umd/components/VizElement/DefaultRankBadges.d.ts +11 -0
  188. package/dist/umd/components/VizElement/DefaultRankBadges.d.ts.map +1 -0
  189. package/dist/umd/components/VizElement/ElementCallout.d.ts +17 -0
  190. package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -0
  191. package/dist/umd/components/VizElement/HeatmapElements.d.ts +23 -0
  192. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -0
  193. package/dist/umd/components/VizElement/HoveredElementOverlay.d.ts +12 -0
  194. package/dist/umd/components/VizElement/HoveredElementOverlay.d.ts.map +1 -0
  195. package/dist/umd/components/VizElement/MissingElementMessage.d.ts +7 -0
  196. package/dist/umd/components/VizElement/MissingElementMessage.d.ts.map +1 -0
  197. package/dist/umd/components/VizElement/RankBadge.d.ts +10 -0
  198. package/dist/umd/components/VizElement/RankBadge.d.ts.map +1 -0
  199. package/dist/umd/components/VizElement/VizElementRank.d.ts +75 -0
  200. package/dist/umd/components/VizElement/VizElementRank.d.ts.map +1 -0
  201. package/dist/umd/components/VizElement/VizElements.d.ts +10 -0
  202. package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -0
  203. package/dist/umd/components/VizElement/index.d.ts +2 -0
  204. package/dist/umd/components/VizElement/index.d.ts.map +1 -0
  205. package/dist/umd/components/VizElement/temp/ClarityVisualizer.d.ts +150 -0
  206. package/dist/umd/components/VizElement/temp/ClarityVisualizer.d.ts.map +1 -0
  207. package/dist/umd/components/VizElement/temp/VizElementRank.d.ts +74 -0
  208. package/dist/umd/components/VizElement/temp/VizElementRank.d.ts.map +1 -0
  209. package/dist/umd/components/index.d.ts +4 -0
  210. package/dist/umd/components/index.d.ts.map +1 -0
  211. package/dist/umd/configs/iframe.d.ts +10 -0
  212. package/dist/umd/configs/iframe.d.ts.map +1 -0
  213. package/dist/umd/configs/index.d.ts +3 -0
  214. package/dist/umd/configs/index.d.ts.map +1 -0
  215. package/dist/umd/configs/style.d.ts +9 -0
  216. package/dist/umd/configs/style.d.ts.map +1 -0
  217. package/dist/umd/constants/index.d.ts +5 -0
  218. package/dist/umd/constants/index.d.ts.map +1 -0
  219. package/dist/umd/helpers/iframe.d.ts +3 -0
  220. package/dist/umd/helpers/iframe.d.ts.map +1 -0
  221. package/dist/umd/helpers/index.d.ts +2 -0
  222. package/dist/umd/helpers/index.d.ts.map +1 -0
  223. package/dist/umd/helpers/viz-elements.d.ts +10 -0
  224. package/dist/umd/helpers/viz-elements.d.ts.map +1 -0
  225. package/dist/umd/hooks/index.d.ts +4 -0
  226. package/dist/umd/hooks/index.d.ts.map +1 -0
  227. package/dist/umd/hooks/vix-elements/index.d.ts +5 -0
  228. package/dist/umd/hooks/vix-elements/index.d.ts.map +1 -0
  229. package/dist/umd/hooks/vix-elements/useClickedElement.d.ts +14 -0
  230. package/dist/umd/hooks/vix-elements/useClickedElement.d.ts.map +1 -0
  231. package/dist/umd/hooks/vix-elements/useHeatmapEffects.d.ts +8 -0
  232. package/dist/umd/hooks/vix-elements/useHeatmapEffects.d.ts.map +1 -0
  233. package/dist/umd/hooks/vix-elements/useHeatmapElementPosition.d.ts +13 -0
  234. package/dist/umd/hooks/vix-elements/useHeatmapElementPosition.d.ts.map +1 -0
  235. package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts +17 -0
  236. package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts.map +1 -0
  237. package/dist/umd/hooks/viz-render/index.d.ts +2 -0
  238. package/dist/umd/hooks/viz-render/index.d.ts.map +1 -0
  239. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts +8 -0
  240. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -0
  241. package/dist/umd/hooks/viz-render/useHeatmapVizRender.d.ts +8 -0
  242. package/dist/umd/hooks/viz-render/useHeatmapVizRender.d.ts.map +1 -0
  243. package/dist/umd/hooks/viz-render/useReplayRender.d.ts +11 -0
  244. package/dist/umd/hooks/viz-render/useReplayRender.d.ts.map +1 -0
  245. package/dist/umd/hooks/viz-scale/index.d.ts +2 -0
  246. package/dist/umd/hooks/viz-scale/index.d.ts.map +1 -0
  247. package/dist/umd/hooks/viz-scale/useContainerDimensions.d.ts +10 -0
  248. package/dist/umd/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -0
  249. package/dist/umd/hooks/viz-scale/useContentDimensions.d.ts +11 -0
  250. package/dist/umd/hooks/viz-scale/useContentDimensions.d.ts.map +1 -0
  251. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +19 -0
  252. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -0
  253. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts +10 -0
  254. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts.map +1 -0
  255. package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts +10 -0
  256. package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -0
  257. package/dist/umd/hooks/viz-scale/useScrollSync.d.ts +10 -0
  258. package/dist/umd/hooks/viz-scale/useScrollSync.d.ts.map +1 -0
  259. package/dist/umd/index.d.ts +4 -0
  260. package/dist/umd/index.d.ts.map +1 -0
  261. package/dist/umd/index.js +10 -0
  262. package/dist/umd/stores/data.d.ts +18 -0
  263. package/dist/umd/stores/data.d.ts.map +1 -0
  264. package/dist/umd/stores/index.d.ts +2 -0
  265. package/dist/umd/stores/index.d.ts.map +1 -0
  266. package/dist/umd/ui/BoxStack/BoxStack.d.ts +56 -0
  267. package/dist/umd/ui/BoxStack/BoxStack.d.ts.map +1 -0
  268. package/dist/umd/ui/BoxStack/index.d.ts +2 -0
  269. package/dist/umd/ui/BoxStack/index.d.ts.map +1 -0
  270. package/dist/umd/ui/index.d.ts +2 -0
  271. package/dist/umd/ui/index.d.ts.map +1 -0
  272. package/dist/umd/utils/device.d.ts +2 -0
  273. package/dist/umd/utils/device.d.ts.map +1 -0
  274. package/dist/umd/utils/retry.d.ts +2 -0
  275. package/dist/umd/utils/retry.d.ts.map +1 -0
  276. package/dist/umd/utils/sort.d.ts +3 -0
  277. package/dist/umd/utils/sort.d.ts.map +1 -0
  278. package/package.json +11 -14
  279. package/src/components/GraphView.tsx +58 -0
  280. package/src/components/Layout/ContentHeader.tsx +15 -0
  281. package/src/components/Layout/HeatmapLayout.tsx +63 -0
  282. package/src/components/Layout/LeftSidebar.tsx +32 -0
  283. package/src/components/Layout/WrapperLayout.tsx +18 -0
  284. package/src/components/Layout/WrapperPreview.tsx +14 -0
  285. package/src/components/Layout/index.ts +1 -0
  286. package/src/components/Test.tsx +1106 -0
  287. package/src/components/VizDom/ReplayControls.tsx +48 -0
  288. package/src/components/VizDom/VizDomContainer.tsx +25 -0
  289. package/src/components/VizDom/VizDomRenderer.tsx +87 -0
  290. package/src/components/VizDom/index.ts +1 -0
  291. package/src/components/VizElement/ClickedElementOverlay.tsx +65 -0
  292. package/src/components/VizElement/DefaultRankBadges.tsx +33 -0
  293. package/src/components/VizElement/ElementCallout.tsx +337 -0
  294. package/src/components/VizElement/HeatmapElements.tsx +147 -0
  295. package/src/components/VizElement/HoveredElementOverlay.tsx +46 -0
  296. package/src/components/VizElement/MissingElementMessage.tsx +32 -0
  297. package/src/components/VizElement/RankBadge.tsx +25 -0
  298. package/src/components/VizElement/VizElements.tsx +80 -0
  299. package/src/components/VizElement/index.ts +1 -0
  300. package/src/components/VizElement/temp/ClarityVisualizer.ts +764 -0
  301. package/src/components/VizElement/temp/VizElementRank.tsx +579 -0
  302. package/src/components/index.tsx +4 -0
  303. package/src/configs/iframe.ts +15 -0
  304. package/src/configs/index.ts +2 -0
  305. package/src/configs/style.ts +9 -0
  306. package/src/constants/index.ts +4 -0
  307. package/src/dist/components/GraphView.d.ts +9 -0
  308. package/src/dist/components/GraphView.d.ts.map +1 -0
  309. package/src/dist/components/Layout/ContentHeader.d.ts +4 -0
  310. package/src/dist/components/Layout/ContentHeader.d.ts.map +1 -0
  311. package/src/dist/components/Layout/HeatmapLayout.d.ts +11 -0
  312. package/src/dist/components/Layout/HeatmapLayout.d.ts.map +1 -0
  313. package/src/dist/components/Layout/LeftSidebar.d.ts +4 -0
  314. package/src/dist/components/Layout/LeftSidebar.d.ts.map +1 -0
  315. package/src/dist/components/Layout/WrapperLayout.d.ts +8 -0
  316. package/src/dist/components/Layout/WrapperLayout.d.ts.map +1 -0
  317. package/src/dist/components/Layout/WrapperPreview.d.ts +4 -0
  318. package/src/dist/components/Layout/WrapperPreview.d.ts.map +1 -0
  319. package/src/dist/components/Layout/index.d.ts +2 -0
  320. package/src/dist/components/Layout/index.d.ts.map +1 -0
  321. package/src/dist/components/Test.d.ts +121 -0
  322. package/src/dist/components/Test.d.ts.map +1 -0
  323. package/src/dist/components/VizDom/ReplayControls.d.ts +2 -0
  324. package/src/dist/components/VizDom/ReplayControls.d.ts.map +1 -0
  325. package/src/dist/components/VizDom/VizDomContainer.d.ts +2 -0
  326. package/src/dist/components/VizDom/VizDomContainer.d.ts.map +1 -0
  327. package/src/dist/components/VizDom/VizDomRenderer.d.ts +6 -0
  328. package/src/dist/components/VizDom/VizDomRenderer.d.ts.map +1 -0
  329. package/src/dist/components/VizDom/index.d.ts +2 -0
  330. package/src/dist/components/VizDom/index.d.ts.map +1 -0
  331. package/src/dist/components/VizElement/ClickedElementOverlay.d.ts +17 -0
  332. package/src/dist/components/VizElement/ClickedElementOverlay.d.ts.map +1 -0
  333. package/src/dist/components/VizElement/DefaultRankBadges.d.ts +11 -0
  334. package/src/dist/components/VizElement/DefaultRankBadges.d.ts.map +1 -0
  335. package/src/dist/components/VizElement/ElementCallout.d.ts +17 -0
  336. package/src/dist/components/VizElement/ElementCallout.d.ts.map +1 -0
  337. package/src/dist/components/VizElement/HeatmapElements.d.ts +23 -0
  338. package/src/dist/components/VizElement/HeatmapElements.d.ts.map +1 -0
  339. package/src/dist/components/VizElement/HoveredElementOverlay.d.ts +12 -0
  340. package/src/dist/components/VizElement/HoveredElementOverlay.d.ts.map +1 -0
  341. package/src/dist/components/VizElement/MissingElementMessage.d.ts +7 -0
  342. package/src/dist/components/VizElement/MissingElementMessage.d.ts.map +1 -0
  343. package/src/dist/components/VizElement/RankBadge.d.ts +10 -0
  344. package/src/dist/components/VizElement/RankBadge.d.ts.map +1 -0
  345. package/src/dist/components/VizElement/VizElements.d.ts +10 -0
  346. package/src/dist/components/VizElement/VizElements.d.ts.map +1 -0
  347. package/src/dist/components/VizElement/index.d.ts +2 -0
  348. package/src/dist/components/VizElement/index.d.ts.map +1 -0
  349. package/src/dist/components/VizElement/temp/ClarityVisualizer.d.ts +150 -0
  350. package/src/dist/components/VizElement/temp/ClarityVisualizer.d.ts.map +1 -0
  351. package/src/dist/components/VizElement/temp/VizElementRank.d.ts +74 -0
  352. package/src/dist/components/VizElement/temp/VizElementRank.d.ts.map +1 -0
  353. package/src/dist/components/index.d.ts +4 -0
  354. package/src/dist/components/index.d.ts.map +1 -0
  355. package/src/dist/configs/iframe.d.ts +10 -0
  356. package/src/dist/configs/iframe.d.ts.map +1 -0
  357. package/src/dist/configs/index.d.ts +3 -0
  358. package/src/dist/configs/index.d.ts.map +1 -0
  359. package/src/dist/configs/style.d.ts +9 -0
  360. package/src/dist/configs/style.d.ts.map +1 -0
  361. package/src/dist/constants/index.d.ts +5 -0
  362. package/src/dist/constants/index.d.ts.map +1 -0
  363. package/src/dist/helpers/iframe.d.ts +3 -0
  364. package/src/dist/helpers/iframe.d.ts.map +1 -0
  365. package/src/dist/helpers/index.d.ts +2 -0
  366. package/src/dist/helpers/index.d.ts.map +1 -0
  367. package/src/dist/helpers/viz-elements.d.ts +10 -0
  368. package/src/dist/helpers/viz-elements.d.ts.map +1 -0
  369. package/src/dist/hooks/index.d.ts +4 -0
  370. package/src/dist/hooks/index.d.ts.map +1 -0
  371. package/src/dist/hooks/vix-elements/index.d.ts +5 -0
  372. package/src/dist/hooks/vix-elements/index.d.ts.map +1 -0
  373. package/src/dist/hooks/vix-elements/useClickedElement.d.ts +14 -0
  374. package/src/dist/hooks/vix-elements/useClickedElement.d.ts.map +1 -0
  375. package/src/dist/hooks/vix-elements/useHeatmapEffects.d.ts +8 -0
  376. package/src/dist/hooks/vix-elements/useHeatmapEffects.d.ts.map +1 -0
  377. package/src/dist/hooks/vix-elements/useHeatmapElementPosition.d.ts +13 -0
  378. package/src/dist/hooks/vix-elements/useHeatmapElementPosition.d.ts.map +1 -0
  379. package/src/dist/hooks/vix-elements/useHoveredElement.d.ts +17 -0
  380. package/src/dist/hooks/vix-elements/useHoveredElement.d.ts.map +1 -0
  381. package/src/dist/hooks/viz-render/index.d.ts +2 -0
  382. package/src/dist/hooks/viz-render/index.d.ts.map +1 -0
  383. package/src/dist/hooks/viz-render/useHeatmapRender.d.ts +8 -0
  384. package/src/dist/hooks/viz-render/useHeatmapRender.d.ts.map +1 -0
  385. package/src/dist/hooks/viz-render/useHeatmapVizRender.d.ts +8 -0
  386. package/src/dist/hooks/viz-render/useHeatmapVizRender.d.ts.map +1 -0
  387. package/src/dist/hooks/viz-render/useReplayRender.d.ts +11 -0
  388. package/src/dist/hooks/viz-render/useReplayRender.d.ts.map +1 -0
  389. package/src/dist/hooks/viz-scale/index.d.ts +2 -0
  390. package/src/dist/hooks/viz-scale/index.d.ts.map +1 -0
  391. package/src/dist/hooks/viz-scale/useContainerDimensions.d.ts +10 -0
  392. package/src/dist/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -0
  393. package/src/dist/hooks/viz-scale/useContentDimensions.d.ts +11 -0
  394. package/src/dist/hooks/viz-scale/useContentDimensions.d.ts.map +1 -0
  395. package/src/dist/hooks/viz-scale/useHeatmapScale.d.ts +19 -0
  396. package/src/dist/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -0
  397. package/src/dist/hooks/viz-scale/useIframeHeight.d.ts +10 -0
  398. package/src/dist/hooks/viz-scale/useIframeHeight.d.ts.map +1 -0
  399. package/src/dist/hooks/viz-scale/useScaleCalculation.d.ts +10 -0
  400. package/src/dist/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -0
  401. package/src/dist/hooks/viz-scale/useScrollSync.d.ts +10 -0
  402. package/src/dist/hooks/viz-scale/useScrollSync.d.ts.map +1 -0
  403. package/src/dist/index.d.ts +4 -0
  404. package/src/dist/index.d.ts.map +1 -0
  405. package/src/dist/stores/data.d.ts +18 -0
  406. package/src/dist/stores/data.d.ts.map +1 -0
  407. package/src/dist/stores/index.d.ts +2 -0
  408. package/src/dist/stores/index.d.ts.map +1 -0
  409. package/src/dist/ui/BoxStack/BoxStack.d.ts +56 -0
  410. package/src/dist/ui/BoxStack/BoxStack.d.ts.map +1 -0
  411. package/src/dist/ui/BoxStack/index.d.ts +2 -0
  412. package/src/dist/ui/BoxStack/index.d.ts.map +1 -0
  413. package/src/dist/ui/index.d.ts +2 -0
  414. package/src/dist/ui/index.d.ts.map +1 -0
  415. package/src/dist/utils/device.d.ts +2 -0
  416. package/src/dist/utils/device.d.ts.map +1 -0
  417. package/src/dist/utils/retry.d.ts +2 -0
  418. package/src/dist/utils/retry.d.ts.map +1 -0
  419. package/src/dist/utils/sort.d.ts +3 -0
  420. package/src/dist/utils/sort.d.ts.map +1 -0
  421. package/src/global.d.ts +5 -0
  422. package/src/helpers/iframe.ts +33 -0
  423. package/src/helpers/index.ts +1 -0
  424. package/src/helpers/viz-elements.ts +34 -0
  425. package/src/hooks/index.ts +3 -0
  426. package/src/hooks/vix-elements/index.ts +4 -0
  427. package/src/hooks/vix-elements/useClickedElement.ts +62 -0
  428. package/src/hooks/vix-elements/useHeatmapEffects.ts +29 -0
  429. package/src/hooks/vix-elements/useHeatmapElementPosition.ts +66 -0
  430. package/src/hooks/vix-elements/useHoveredElement.ts +135 -0
  431. package/src/hooks/viz-render/index.ts +1 -0
  432. package/src/hooks/viz-render/useHeatmapRender.ts +98 -0
  433. package/src/hooks/viz-render/useHeatmapVizRender.ts +22 -0
  434. package/src/hooks/viz-render/useReplayRender.ts +162 -0
  435. package/src/hooks/viz-scale/index.ts +1 -0
  436. package/src/hooks/viz-scale/useContainerDimensions.ts +46 -0
  437. package/src/hooks/viz-scale/useContentDimensions.ts +29 -0
  438. package/src/hooks/viz-scale/useHeatmapScale.ts +53 -0
  439. package/src/hooks/viz-scale/useIframeHeight.ts +119 -0
  440. package/src/hooks/viz-scale/useScaleCalculation.ts +28 -0
  441. package/src/hooks/viz-scale/useScrollSync.ts +36 -0
  442. package/src/index.ts +5 -0
  443. package/src/stores/data.ts +34 -0
  444. package/src/stores/index.ts +1 -0
  445. package/src/styles/base.css +1 -0
  446. package/src/styles/style.css +30 -0
  447. package/src/types/clarity.d.ts +38 -0
  448. package/src/types/heatmap.d.ts +3 -0
  449. package/src/types/index.d.ts +3 -0
  450. package/src/types/viz-element.d.ts +39 -0
  451. package/src/ui/BoxStack/BoxStack.tsx +120 -0
  452. package/src/ui/BoxStack/index.ts +1 -0
  453. package/src/ui/index.ts +1 -0
  454. package/src/utils/device.ts +7 -0
  455. package/src/utils/retry.ts +20 -0
  456. package/src/utils/sort.ts +5 -0
@@ -0,0 +1,1303 @@
1
+ "use client"
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
+ import { useNodesState, ReactFlow, Controls, Background } from '@xyflow/react';
4
+ import { useEffect, useMemo, useState, useCallback, useRef, Fragment as Fragment$1 } from 'react';
5
+ import { create } from 'zustand';
6
+ import { Visualizer } from '@gemx-dev/clarity-visualize';
7
+ import { createPortal } from 'react-dom';
8
+
9
+ const initialNodes = { id: '1', position: { x: 0, y: 0 }, data: { label: '1' } };
10
+ const GraphView = ({ children, width, height }) => {
11
+ const [nodes, setNodes, onNodesChange] = useNodesState([
12
+ {
13
+ ...initialNodes,
14
+ width: width,
15
+ height: height,
16
+ },
17
+ ]);
18
+ const CustomNode = () => {
19
+ return jsx(Fragment, { children: children });
20
+ };
21
+ const nodeTypes = {
22
+ default: CustomNode,
23
+ };
24
+ useEffect(() => {
25
+ if (!width)
26
+ return;
27
+ setNodes((prev) => {
28
+ const node = prev.find((node) => node.id === '1');
29
+ const newNode = {
30
+ ...node,
31
+ measured: { height: height, width: width },
32
+ height: height,
33
+ width: width,
34
+ };
35
+ return [newNode];
36
+ });
37
+ }, [width, height, setNodes]);
38
+ return (jsxs(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, onNodesChange: onNodesChange, debug: true, minZoom: 0.5, maxZoom: 2, fitView: true, children: [jsx(Controls, {}), jsx(Background, {})] }));
39
+ };
40
+
41
+ const useHeatmapDataStore = create()((set, get) => ({
42
+ data: undefined,
43
+ clickmap: undefined,
44
+ config: undefined,
45
+ iframeHeight: 0,
46
+ state: {
47
+ hideSidebar: false,
48
+ },
49
+ setData: (data) => set({ data }),
50
+ setClickmap: (clickmap) => set({ clickmap }),
51
+ setState: (state) => set({ state: { ...get().state, ...state } }),
52
+ setConfig: (config) => set({ config: { ...get().config, ...config } }),
53
+ setIframeHeight: (iframeHeight) => set({ iframeHeight }),
54
+ }));
55
+
56
+ const BoxStack = ({ children, ...props }) => {
57
+ const id = props.id;
58
+ const flexDirection = props.flexDirection;
59
+ const overflow = props.overflow || 'hidden';
60
+ const position = props.position || 'relative';
61
+ const flex = props.flex || 'none';
62
+ const justifyContent = props.justifyContent;
63
+ const alignItems = props.alignItems;
64
+ const style = props.style || {};
65
+ const gap = props.gap || 0;
66
+ const height = props.height || 'auto';
67
+ const styleGap = useMemo(() => {
68
+ switch (flexDirection) {
69
+ case 'row':
70
+ return {
71
+ columnGap: gap,
72
+ };
73
+ case 'column':
74
+ return {
75
+ rowGap: gap,
76
+ };
77
+ }
78
+ }, [gap, flexDirection]);
79
+ const styleProps = {
80
+ display: 'flex',
81
+ flexDirection,
82
+ overflow,
83
+ position,
84
+ flex,
85
+ justifyContent,
86
+ alignItems,
87
+ height,
88
+ ...styleGap,
89
+ ...style,
90
+ };
91
+ return (jsx("div", { id: id, style: styleProps, children: children }));
92
+ };
93
+
94
+ const ContentHeader = ({ children }) => {
95
+ return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", children: children }));
96
+ };
97
+
98
+ const HEATMAP_IFRAME = {
99
+ id: 'clarity-iframe',
100
+ title: 'Clarity Session Replay',
101
+ sandbox: 'allow-same-origin allow-scripts',
102
+ scrolling: 'no',
103
+ height: '100%',
104
+ };
105
+
106
+ const HEATMAP_CONFIG = {
107
+ paddingBlock: 0,
108
+ };
109
+ const HEATMAP_STYLE = {
110
+ wrapper: {
111
+ padding: `${HEATMAP_CONFIG.paddingBlock}px 0`,
112
+ },
113
+ };
114
+
115
+ const useClickedElement = ({ selectedElement, heatmapInfo, getRect }) => {
116
+ const [clickedElement, setClickedElement] = useState(null);
117
+ const [showMissingElement, setShowMissingElement] = useState(false);
118
+ const [shouldShowCallout, setShouldShowCallout] = useState(false);
119
+ useEffect(() => {
120
+ if (!selectedElement || !heatmapInfo?.elementMapInfo) {
121
+ setClickedElement(null);
122
+ setShowMissingElement(false);
123
+ setShouldShowCallout(false);
124
+ return;
125
+ }
126
+ const info = heatmapInfo.elementMapInfo[selectedElement];
127
+ if (!info) {
128
+ setClickedElement(null);
129
+ return;
130
+ }
131
+ const rect = getRect({ hash: selectedElement, selector: info.selector });
132
+ if (rect && heatmapInfo.sortedElements) {
133
+ const rank = heatmapInfo.sortedElements.findIndex((e) => e.hash === selectedElement) + 1;
134
+ setClickedElement({
135
+ ...rect,
136
+ hash: selectedElement,
137
+ clicks: info.totalclicks ?? 0,
138
+ rank,
139
+ selector: info.selector ?? '',
140
+ });
141
+ setShowMissingElement(false);
142
+ setShouldShowCallout(true);
143
+ }
144
+ else {
145
+ const rank = (heatmapInfo.sortedElements?.findIndex((e) => e.hash === selectedElement) ?? -1) + 1;
146
+ setClickedElement({
147
+ hash: selectedElement,
148
+ clicks: info.totalclicks ?? 0,
149
+ rank,
150
+ selector: info.selector ?? '',
151
+ left: 0,
152
+ top: 0,
153
+ width: 0,
154
+ height: 0,
155
+ });
156
+ setShowMissingElement(true);
157
+ setShouldShowCallout(false);
158
+ }
159
+ }, [selectedElement, heatmapInfo, getRect]);
160
+ return { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout };
161
+ };
162
+
163
+ const useHeatmapEffects = ({ isVisible, isElementSidebarOpen, selectedElement, setShouldShowCallout, resetAll, }) => {
164
+ // Reset khi ẩn
165
+ useEffect(() => {
166
+ if (!isVisible)
167
+ resetAll();
168
+ }, [isVisible, resetAll]);
169
+ // Ẩn callout khi sidebar mở
170
+ useEffect(() => {
171
+ if (isElementSidebarOpen && selectedElement) {
172
+ setShouldShowCallout(false);
173
+ }
174
+ else if (!isElementSidebarOpen && selectedElement) {
175
+ setShouldShowCallout(true);
176
+ }
177
+ }, [isElementSidebarOpen, selectedElement, setShouldShowCallout]);
178
+ };
179
+
180
+ function getElementLayout(element) {
181
+ if (!element?.getBoundingClientRect)
182
+ return null;
183
+ const rect = element.getBoundingClientRect();
184
+ if (rect.width === 0 && rect.height === 0)
185
+ return null;
186
+ return {
187
+ top: rect.top,
188
+ left: rect.left,
189
+ width: rect.width,
190
+ height: rect.height,
191
+ };
192
+ }
193
+ function formatPercentage(value, decimals = 2) {
194
+ return value.toFixed(decimals);
195
+ }
196
+ function getSimpleSelector(selector) {
197
+ const parts = selector.split(' > ');
198
+ return parts[parts.length - 1] || selector;
199
+ }
200
+ function calculateRankPosition(rect, widthScale) {
201
+ const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
202
+ const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
203
+ return {
204
+ transform: `scale(${1.2 * widthScale})`,
205
+ top: Number.isNaN(top) ? undefined : top,
206
+ left: Number.isNaN(left) ? undefined : left,
207
+ };
208
+ }
209
+
210
+ const useHeatmapElementPosition = ({ iframeRef, parentRef, visualizer, heatmapWidth, iframeHeight, widthScale, projectId, }) => {
211
+ return useCallback((element) => {
212
+ const hash = element?.hash;
213
+ if (!iframeRef.current?.contentDocument || !hash || !visualizer)
214
+ return null;
215
+ let domElement = null;
216
+ try {
217
+ domElement = visualizer.get(hash);
218
+ }
219
+ catch (error) {
220
+ console.error('Visualizer error:', { projectId, hash, error });
221
+ return null;
222
+ }
223
+ if (!domElement)
224
+ return null;
225
+ const layout = getElementLayout(domElement);
226
+ if (!layout)
227
+ return null;
228
+ const parentEl = parentRef.current;
229
+ if (!parentEl)
230
+ return null;
231
+ const scrollOffset = parentEl.scrollTop / widthScale;
232
+ const adjustedTop = layout.top + scrollOffset;
233
+ const outOfBounds = adjustedTop < 0 ||
234
+ adjustedTop > (iframeHeight || Infinity) ||
235
+ layout.left < 0 ||
236
+ (typeof heatmapWidth === 'number' && layout.left > heatmapWidth);
237
+ if (outOfBounds)
238
+ return null;
239
+ return {
240
+ left: layout.left,
241
+ top: adjustedTop,
242
+ width: Math.min(layout.width, heatmapWidth || layout.width),
243
+ height: layout.height,
244
+ };
245
+ }, [iframeRef, parentRef, visualizer, heatmapWidth, iframeHeight, widthScale, projectId]);
246
+ };
247
+
248
+ const debounce = (fn, delay) => {
249
+ let timeout;
250
+ return (...args) => {
251
+ clearTimeout(timeout);
252
+ timeout = setTimeout(() => fn(...args), delay);
253
+ };
254
+ };
255
+ const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect, onSelect, }) => {
256
+ const [hoveredElement, setHoveredElement] = useState(null);
257
+ const handleMouseMove = useCallback(debounce((event) => {
258
+ if (!iframeRef.current?.contentDocument || !heatmapInfo?.elementMapInfo) {
259
+ setHoveredElement(null);
260
+ return;
261
+ }
262
+ const iframe = iframeRef.current;
263
+ const iframeRect = iframe.getBoundingClientRect();
264
+ let x = event.clientX - iframeRect.left;
265
+ console.log(`🚀 🐥 ~ useHoveredElement ~ iframeRect.left:`, iframeRect.left);
266
+ console.log(`🚀 🐥 ~ useHoveredElement ~ event.clientX:`, event.clientX);
267
+ let y = event.clientY - iframeRect.top;
268
+ if (widthScale !== 1) {
269
+ x /= widthScale;
270
+ y /= widthScale;
271
+ }
272
+ const doc = iframe.contentDocument;
273
+ if (!doc) {
274
+ setHoveredElement(null);
275
+ return;
276
+ }
277
+ let targetElement = null;
278
+ // Best: dùng caretPositionFromPoint nếu có (Chrome, Safari)
279
+ targetElement = getElementAtPoint(doc, x, y);
280
+ if (!targetElement) {
281
+ targetElement = doc.elementFromPoint(x, y);
282
+ }
283
+ if (!targetElement) {
284
+ setHoveredElement(null);
285
+ return;
286
+ }
287
+ // Lấy hash từ nhiều attribute khả dĩ
288
+ const hash = targetElement.getAttribute('data-clarity-hash') ||
289
+ targetElement.getAttribute('data-clarity-hashalpha') ||
290
+ targetElement.getAttribute('data-clarity-hashbeta');
291
+ if (!hash || !heatmapInfo.elementMapInfo[hash]) {
292
+ setHoveredElement(null);
293
+ return;
294
+ }
295
+ const info = heatmapInfo.elementMapInfo[hash];
296
+ const position = getRect({ hash, selector: info.selector });
297
+ if (position && heatmapInfo.sortedElements) {
298
+ const rank = heatmapInfo.sortedElements.findIndex((e) => e.hash === hash) + 1;
299
+ setHoveredElement({
300
+ ...position,
301
+ hash,
302
+ clicks: info.totalclicks ?? 0,
303
+ rank,
304
+ selector: info.selector ?? '',
305
+ });
306
+ }
307
+ else {
308
+ setHoveredElement(null);
309
+ }
310
+ }, 16), // ~60fps
311
+ [iframeRef, heatmapInfo, getRect]);
312
+ const handleMouseLeave = useCallback(() => {
313
+ setHoveredElement(null);
314
+ }, []);
315
+ const handleClick = useCallback(() => {
316
+ if (hoveredElement?.hash && onSelect) {
317
+ onSelect(hoveredElement.hash);
318
+ }
319
+ }, [hoveredElement, onSelect]);
320
+ return {
321
+ hoveredElement,
322
+ handleMouseMove,
323
+ handleMouseLeave,
324
+ handleClick,
325
+ };
326
+ };
327
+ const getElementAtPoint = (doc, x, y) => {
328
+ let el = null;
329
+ if ('caretPositionFromPoint' in doc) {
330
+ el = doc.caretPositionFromPoint(x, y)?.offsetNode ?? null;
331
+ }
332
+ el = el ?? doc.elementFromPoint(x, y);
333
+ let element = el;
334
+ while (element && element.nodeType === Node.TEXT_NODE) {
335
+ element = element.parentElement;
336
+ }
337
+ return element;
338
+ };
339
+
340
+ const recreateIframe = (iframeRef, config) => {
341
+ const container = iframeRef.current?.parentElement;
342
+ if (!container)
343
+ return;
344
+ const oldIframe = iframeRef.current;
345
+ if (!oldIframe?.contentDocument?.body.innerHTML) {
346
+ return oldIframe;
347
+ }
348
+ if (oldIframe && oldIframe.parentElement) {
349
+ oldIframe.parentElement.removeChild(oldIframe);
350
+ }
351
+ const newIframe = document.createElement('iframe');
352
+ newIframe.id = HEATMAP_IFRAME.id;
353
+ newIframe.title = HEATMAP_IFRAME.title;
354
+ newIframe.sandbox = HEATMAP_IFRAME.sandbox;
355
+ newIframe.scrolling = HEATMAP_IFRAME.scrolling;
356
+ newIframe.width = config?.width ? `${config.width}px` : '100%';
357
+ // Append to container
358
+ container.appendChild(newIframe);
359
+ // Update ref
360
+ iframeRef.current = newIframe;
361
+ return newIframe;
362
+ };
363
+
364
+ function isMobileDevice(userAgent) {
365
+ if (!userAgent)
366
+ return false;
367
+ return /android|webos|iphone|ipad|ipod|blackberry|windows phone|opera mini|iemobile|mobile|silk|fennec|bada|tizen|symbian|nokia|palmsource|meego|sailfish|kindle|playbook|bb10|rim/i.test(userAgent);
368
+ }
369
+
370
+ const useHeatmapRender = () => {
371
+ const data = useHeatmapDataStore((state) => state.data);
372
+ const config = useHeatmapDataStore((state) => state.config);
373
+ const setConfig = useHeatmapDataStore((state) => state.setConfig);
374
+ const clickmap = useHeatmapDataStore((state) => state.clickmap);
375
+ const visualizerRef = useRef(null);
376
+ const iframeRef = useRef(null);
377
+ const initializeVisualizer = useCallback((envelope, userAgent) => {
378
+ const iframe = iframeRef.current;
379
+ if (!iframe?.contentWindow)
380
+ return null;
381
+ const visualizer = new Visualizer();
382
+ const mobile = isMobileDevice(userAgent);
383
+ visualizer.setup(iframe.contentWindow, {
384
+ version: envelope.version,
385
+ onresize: (width) => {
386
+ setConfig({ width });
387
+ },
388
+ mobile,
389
+ vNext: true,
390
+ locale: 'en-us',
391
+ });
392
+ return visualizer;
393
+ }, []);
394
+ // Process and render heatmap HTML
395
+ const renderHeatmap = useCallback(async (payloads) => {
396
+ if (!payloads || payloads.length === 0)
397
+ return;
398
+ let visualizer = new Visualizer();
399
+ const iframe = recreateIframe(iframeRef, config);
400
+ // const merged = visualizer.merge(payloads);
401
+ // setIframeHeight(Number(iframeRef.current?.height || 0));
402
+ // for (const decoded of payloads) {
403
+ // // Initialize on first sequence
404
+ // if (decoded.envelope.sequence === 1) {
405
+ // const userAgent = (decoded.dimension?.[0]?.data[0]?.[0] as string) || '';
406
+ // visualizer = initializeVisualizer(decoded.envelope as any, userAgent);
407
+ // if (!visualizer) return;
408
+ // visualizerRef.current = visualizer;
409
+ // }
410
+ // if (!visualizer) continue;
411
+ // // Merge and process DOM
412
+ // const merged = visualizer.merge([decoded]);
413
+ // visualizer.dom(merged.dom);
414
+ // }
415
+ // Render static HTML
416
+ if (visualizer && iframe?.contentWindow) {
417
+ await visualizer.html(payloads, iframe.contentWindow);
418
+ visualizerRef.current = visualizer;
419
+ }
420
+ }, [initializeVisualizer]);
421
+ useEffect(() => {
422
+ if (!data || data.length === 0)
423
+ return;
424
+ renderHeatmap(data);
425
+ return () => {
426
+ visualizerRef.current = null;
427
+ };
428
+ }, [config, data, renderHeatmap]);
429
+ useEffect(() => {
430
+ if (!visualizerRef.current || !clickmap || clickmap.length === 0)
431
+ return;
432
+ visualizerRef.current.clearmap();
433
+ visualizerRef.current?.clickmap(clickmap);
434
+ }, [clickmap]);
435
+ return {
436
+ iframeRef,
437
+ clarityVisualizer: visualizerRef.current,
438
+ };
439
+ };
440
+
441
+ function sortEvents(a, b) {
442
+ return a.time - b.time;
443
+ }
444
+
445
+ const useReplayRender = () => {
446
+ const data = useHeatmapDataStore((state) => state.data);
447
+ const setConfig = useHeatmapDataStore((state) => state.setConfig);
448
+ const visualizerRef = useRef(null);
449
+ const iframeRef = useRef(null);
450
+ const eventsRef = useRef([]);
451
+ const animationFrameRef = useRef(null);
452
+ const isPlayingRef = useRef(false);
453
+ // Initialize visualizer for replay
454
+ const initializeVisualizer = useCallback((envelope, userAgent) => {
455
+ const iframe = iframeRef.current;
456
+ if (!iframe?.contentWindow)
457
+ return null;
458
+ // Clear previous events
459
+ eventsRef.current = [];
460
+ const visualizer = new Visualizer();
461
+ const mobile = isMobileDevice(userAgent);
462
+ visualizer.setup(iframe.contentWindow, {
463
+ version: envelope.version,
464
+ onresize: (width) => {
465
+ setConfig({ width });
466
+ },
467
+ mobile,
468
+ vNext: true,
469
+ locale: 'en-us',
470
+ });
471
+ return visualizer;
472
+ }, [setConfig]);
473
+ // Animation loop for replay
474
+ const replayLoop = useCallback(() => {
475
+ if (!isPlayingRef.current)
476
+ return;
477
+ const events = eventsRef.current;
478
+ const visualizer = visualizerRef.current;
479
+ if (!visualizer || events.length === 0) {
480
+ animationFrameRef.current = requestAnimationFrame(replayLoop);
481
+ return;
482
+ }
483
+ const event = events[0];
484
+ const end = event.time + 16; // 60FPS => 16ms per frame
485
+ let index = 0;
486
+ // Get events within current frame
487
+ while (events[index] && events[index].time < end) {
488
+ index++;
489
+ }
490
+ // Render events for this frame
491
+ if (index > 0) {
492
+ visualizer.render(events.splice(0, index));
493
+ }
494
+ animationFrameRef.current = requestAnimationFrame(replayLoop);
495
+ }, []);
496
+ // Start replay
497
+ const startReplay = useCallback(async (payloads) => {
498
+ if (!payloads || payloads.length === 0)
499
+ return;
500
+ let visualizer = visualizerRef.current;
501
+ for (const decoded of payloads) {
502
+ // Initialize on first sequence
503
+ if (decoded.envelope.sequence === 1) {
504
+ const userAgent = decoded.dimension?.[0]?.data[0]?.[0] || '';
505
+ visualizer = initializeVisualizer(decoded.envelope, userAgent);
506
+ if (!visualizer)
507
+ return;
508
+ visualizerRef.current = visualizer;
509
+ }
510
+ if (!visualizer)
511
+ continue;
512
+ // Merge events and DOM
513
+ const merged = visualizer.merge([decoded]);
514
+ eventsRef.current = eventsRef.current.concat(merged.events).sort(sortEvents);
515
+ visualizer.dom(merged.dom);
516
+ }
517
+ // Render HTML
518
+ if (visualizer && iframeRef.current?.contentWindow) {
519
+ await visualizer.html(payloads, iframeRef.current.contentWindow);
520
+ }
521
+ // Auto-start replay
522
+ isPlayingRef.current = true;
523
+ animationFrameRef.current = requestAnimationFrame(replayLoop);
524
+ }, [initializeVisualizer, replayLoop]);
525
+ // Play control
526
+ const play = useCallback(() => {
527
+ if (!isPlayingRef.current) {
528
+ isPlayingRef.current = true;
529
+ animationFrameRef.current = requestAnimationFrame(replayLoop);
530
+ }
531
+ }, [replayLoop]);
532
+ // Pause control
533
+ const pause = useCallback(() => {
534
+ isPlayingRef.current = false;
535
+ if (animationFrameRef.current) {
536
+ cancelAnimationFrame(animationFrameRef.current);
537
+ animationFrameRef.current = null;
538
+ }
539
+ }, []);
540
+ // Main effect: Start replay when data changes
541
+ useEffect(() => {
542
+ if (!data || data.length === 0)
543
+ return;
544
+ startReplay(data);
545
+ // Cleanup
546
+ return () => {
547
+ isPlayingRef.current = false;
548
+ if (animationFrameRef.current) {
549
+ cancelAnimationFrame(animationFrameRef.current);
550
+ animationFrameRef.current = null;
551
+ }
552
+ eventsRef.current = [];
553
+ visualizerRef.current = null;
554
+ };
555
+ }, [data, startReplay]);
556
+ return {
557
+ iframeRef,
558
+ isPlaying: isPlayingRef.current,
559
+ clarityVisualizer: visualizerRef.current,
560
+ play,
561
+ pause,
562
+ };
563
+ };
564
+
565
+ const useHeatmapVizRender = (mode) => {
566
+ const heatmapResult = useMemo(() => {
567
+ switch (mode) {
568
+ case 'heatmap':
569
+ return useHeatmapRender;
570
+ case 'replay':
571
+ return useReplayRender;
572
+ }
573
+ }, [mode]);
574
+ return heatmapResult();
575
+ };
576
+
577
+ const useContainerDimensions = (props) => {
578
+ const { wrapperRef } = props;
579
+ const [containerWidth, setContainerWidth] = useState(0);
580
+ const [containerHeight, setContainerHeight] = useState(0);
581
+ const resizeObserverRef = useRef(null);
582
+ const updateDimensions = useCallback(() => {
583
+ const scrollContainer = wrapperRef.current?.parentElement?.parentElement;
584
+ if (scrollContainer) {
585
+ setContainerWidth(scrollContainer.clientWidth);
586
+ setContainerHeight(scrollContainer.clientHeight);
587
+ }
588
+ }, [wrapperRef]);
589
+ useEffect(() => {
590
+ const scrollContainer = wrapperRef.current?.parentElement?.parentElement;
591
+ if (!scrollContainer || typeof window.ResizeObserver === 'undefined') {
592
+ return;
593
+ }
594
+ resizeObserverRef.current = new ResizeObserver(updateDimensions);
595
+ resizeObserverRef.current.observe(scrollContainer);
596
+ updateDimensions();
597
+ return () => {
598
+ if (resizeObserverRef.current && scrollContainer) {
599
+ resizeObserverRef.current.unobserve(scrollContainer);
600
+ }
601
+ };
602
+ }, [wrapperRef, updateDimensions]);
603
+ return { containerWidth, containerHeight };
604
+ };
605
+
606
+ const useContentDimensions = (props) => {
607
+ const { iframeRef, config } = props;
608
+ const [contentWidth, setContentWidth] = useState(0);
609
+ useEffect(() => {
610
+ if (config?.width) {
611
+ if (iframeRef.current) {
612
+ iframeRef.current.width = `${config.width}px`;
613
+ }
614
+ setContentWidth(config.width);
615
+ }
616
+ }, [config?.width, iframeRef]);
617
+ return { contentWidth };
618
+ };
619
+
620
+ // Hook 3: Iframe Height Observer
621
+ const useIframeHeight = (props) => {
622
+ const { iframeRef, contentWidth } = props;
623
+ const iframeHeight = useHeatmapDataStore((state) => state.iframeHeight);
624
+ const setIframeHeight = useHeatmapDataStore((state) => state.setIframeHeight);
625
+ const resizeObserverRef = useRef(null);
626
+ const mutationObserverRef = useRef(null);
627
+ const updateIframeHeight = useCallback(() => {
628
+ const iframe = iframeRef.current;
629
+ if (!iframe)
630
+ return;
631
+ try {
632
+ const iframeDocument = iframe.contentDocument;
633
+ const iframeBody = iframeDocument?.body;
634
+ if (!iframeBody)
635
+ return;
636
+ const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
637
+ if (bodyHeight > 0) {
638
+ iframe.height = `${bodyHeight}px`;
639
+ setIframeHeight(bodyHeight);
640
+ }
641
+ }
642
+ catch (error) {
643
+ console.warn('Cannot measure iframe content:', error);
644
+ }
645
+ }, [iframeRef, setIframeHeight]);
646
+ // Trigger height update when content width changes
647
+ useEffect(() => {
648
+ if (contentWidth > 0) {
649
+ // Delay to allow iframe content to reflow after width change
650
+ const timeoutId = setTimeout(() => {
651
+ updateIframeHeight();
652
+ }, 100);
653
+ return () => clearTimeout(timeoutId);
654
+ }
655
+ }, [contentWidth, updateIframeHeight]);
656
+ useEffect(() => {
657
+ const iframe = iframeRef.current;
658
+ if (!iframe)
659
+ return;
660
+ const setupObservers = () => {
661
+ try {
662
+ const iframeDocument = iframe.contentDocument;
663
+ const iframeBody = iframeDocument?.body;
664
+ if (!iframeBody)
665
+ return;
666
+ // Cleanup existing observers
667
+ if (resizeObserverRef.current) {
668
+ resizeObserverRef.current.disconnect();
669
+ }
670
+ if (mutationObserverRef.current) {
671
+ mutationObserverRef.current.disconnect();
672
+ }
673
+ // ResizeObserver for size changes
674
+ if (typeof window.ResizeObserver !== 'undefined') {
675
+ resizeObserverRef.current = new ResizeObserver(updateIframeHeight);
676
+ resizeObserverRef.current.observe(iframeBody);
677
+ }
678
+ // MutationObserver for DOM changes
679
+ if (typeof window.MutationObserver !== 'undefined') {
680
+ mutationObserverRef.current = new MutationObserver(updateIframeHeight);
681
+ mutationObserverRef.current.observe(iframeBody, {
682
+ childList: true,
683
+ subtree: true,
684
+ attributes: true,
685
+ characterData: true,
686
+ });
687
+ }
688
+ // Initial measurement
689
+ updateIframeHeight();
690
+ }
691
+ catch (error) {
692
+ console.warn('Cannot access iframe content:', error);
693
+ }
694
+ };
695
+ if (iframe.contentDocument?.readyState === 'complete') {
696
+ setupObservers();
697
+ }
698
+ else {
699
+ iframe.addEventListener('load', setupObservers, { once: true });
700
+ }
701
+ return () => {
702
+ if (resizeObserverRef.current) {
703
+ resizeObserverRef.current.disconnect();
704
+ }
705
+ if (mutationObserverRef.current) {
706
+ mutationObserverRef.current.disconnect();
707
+ }
708
+ iframe.removeEventListener('load', setupObservers);
709
+ };
710
+ }, [iframeRef, updateIframeHeight]);
711
+ return { iframeHeight };
712
+ };
713
+
714
+ const useScaleCalculation = (props) => {
715
+ const { containerWidth, contentWidth } = props;
716
+ const [scale, setScale] = useState(1);
717
+ useEffect(() => {
718
+ if (containerWidth > 0 && contentWidth > 0) {
719
+ const availableWidth = containerWidth - HEATMAP_CONFIG['paddingBlock'] * 2;
720
+ const calculatedScale = Math.min(availableWidth / contentWidth, 1);
721
+ setScale(calculatedScale);
722
+ }
723
+ }, [containerWidth, contentWidth]);
724
+ return { scale };
725
+ };
726
+
727
+ const useScrollSync = (props) => {
728
+ const { iframeRef, scale } = props;
729
+ const handleScroll = useCallback((scrollTop) => {
730
+ const iframe = iframeRef.current;
731
+ if (!iframe || scale <= 0)
732
+ return;
733
+ try {
734
+ const iframeWindow = iframe.contentWindow;
735
+ const iframeDocument = iframe.contentDocument;
736
+ if (iframeWindow && iframeDocument) {
737
+ const iframeScrollTop = scrollTop / scale;
738
+ iframe.style.top = `${iframeScrollTop}px`;
739
+ }
740
+ }
741
+ catch (error) {
742
+ console.warn('Cannot sync scroll to iframe:', error);
743
+ }
744
+ }, [iframeRef, scale]);
745
+ return { handleScroll };
746
+ };
747
+
748
+ const useHeatmapScale = (props) => {
749
+ const { wrapperRef, iframeRef, config } = props;
750
+ // 1. Observe container dimensions
751
+ const { containerWidth, containerHeight } = useContainerDimensions({ wrapperRef });
752
+ // 2. Get content dimensions from config
753
+ const { contentWidth } = useContentDimensions({ iframeRef, config });
754
+ // 3. Observe iframe height (now reacts to width changes)
755
+ const { iframeHeight } = useIframeHeight({ iframeRef, contentWidth });
756
+ // 4. Calculate scale
757
+ const { scale } = useScaleCalculation({ containerWidth, contentWidth });
758
+ // 5. Setup scroll sync
759
+ const { handleScroll } = useScrollSync({ iframeRef, scale });
760
+ return {
761
+ containerWidth,
762
+ containerHeight,
763
+ contentWidth,
764
+ iframeHeight,
765
+ scale,
766
+ scaledWidth: contentWidth * scale,
767
+ scaledHeight: iframeHeight * scale,
768
+ handleScroll,
769
+ };
770
+ };
771
+
772
+ const CLICKED_ELEMENT_ID = 'clickedElement';
773
+ const SECONDARY_CLICKED_ELEMENT_ID = 'secondaryClickedElementID';
774
+ const HOVERED_ELEMENT_ID = 'hoveredElement';
775
+ const SECONDARY_HOVERED_ELEMENT_ID = 'secondaryhoveredElementID';
776
+
777
+ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecordingView, isCompareMode, deviceType, heatmapType, language, widthScale, parentRef, }) => {
778
+ const calloutRef = useRef(null);
779
+ const [position, setPosition] = useState({
780
+ top: 0,
781
+ left: 0,
782
+ placement: 'top',
783
+ });
784
+ const percentage = formatPercentage(((element.clicks ?? 0) / totalClicks) * 100, 2);
785
+ // Calculate callout position
786
+ useEffect(() => {
787
+ const targetElement = document.querySelector(target);
788
+ const calloutElement = calloutRef.current;
789
+ if (!targetElement || !calloutElement)
790
+ return;
791
+ const calculatePosition = () => {
792
+ const targetRect = targetElement.getBoundingClientRect();
793
+ const calloutRect = calloutElement.getBoundingClientRect();
794
+ const viewportWidth = window.innerWidth;
795
+ const viewportHeight = window.innerHeight;
796
+ const padding = 12; // Space between target and callout
797
+ const arrowSize = 8;
798
+ let top = 0;
799
+ let left = 0;
800
+ let placement = 'top';
801
+ // Try positions in order: top, bottom, right, left
802
+ const positions = [
803
+ // Top
804
+ {
805
+ placement: 'top',
806
+ top: targetRect.top - calloutRect.height - padding - arrowSize,
807
+ left: targetRect.left + targetRect.width / 2 - calloutRect.width / 2,
808
+ valid: targetRect.top - calloutRect.height - padding - arrowSize > 0,
809
+ },
810
+ // Bottom
811
+ {
812
+ placement: 'bottom',
813
+ top: targetRect.bottom + padding + arrowSize,
814
+ left: targetRect.left + targetRect.width / 2 - calloutRect.width / 2,
815
+ valid: targetRect.bottom + calloutRect.height + padding + arrowSize < viewportHeight,
816
+ },
817
+ // Right
818
+ {
819
+ placement: 'right',
820
+ top: targetRect.top + targetRect.height / 2 - calloutRect.height / 2,
821
+ left: targetRect.right + padding + arrowSize,
822
+ valid: targetRect.right + calloutRect.width + padding + arrowSize < viewportWidth,
823
+ },
824
+ // Left
825
+ {
826
+ placement: 'left',
827
+ top: targetRect.top + targetRect.height / 2 - calloutRect.height / 2,
828
+ left: targetRect.left - calloutRect.width - padding - arrowSize,
829
+ valid: targetRect.left - calloutRect.width - padding - arrowSize > 0,
830
+ },
831
+ ];
832
+ // Find first valid position
833
+ const validPosition = positions.find((p) => p.valid) || positions[0];
834
+ top = validPosition.top;
835
+ left = validPosition.left;
836
+ placement = validPosition.placement;
837
+ // Keep within viewport bounds
838
+ left = Math.max(padding, Math.min(left, viewportWidth - calloutRect.width - padding));
839
+ top = Math.max(padding, Math.min(top, viewportHeight - calloutRect.height - padding));
840
+ setPosition({ top, left, placement });
841
+ };
842
+ // Initial calculation
843
+ calculatePosition();
844
+ // Recalculate on scroll/resize
845
+ const handleUpdate = () => {
846
+ requestAnimationFrame(calculatePosition);
847
+ };
848
+ window.addEventListener('scroll', handleUpdate, true);
849
+ window.addEventListener('resize', handleUpdate);
850
+ parentRef?.current?.addEventListener('scroll', handleUpdate);
851
+ return () => {
852
+ window.removeEventListener('scroll', handleUpdate, true);
853
+ window.removeEventListener('resize', handleUpdate);
854
+ parentRef?.current?.removeEventListener('scroll', handleUpdate);
855
+ };
856
+ }, [target, parentRef]);
857
+ const calloutContent = (jsxs("div", { ref: calloutRef, className: `clarity-callout clarity-callout--${position.placement} ${isSecondary ? 'clarity-callout--secondary' : ''}`, style: {
858
+ position: 'fixed',
859
+ top: position.top,
860
+ left: position.left,
861
+ zIndex: 2147483647,
862
+ }, "aria-live": "assertive", role: "tooltip", children: [jsx("div", { className: "clarity-callout__arrow" }), jsxs("div", { className: "clarity-callout__content", children: [jsx("div", { className: "clarity-callout__rank", children: element.rank }), jsxs("div", { className: "clarity-callout__stats", children: [jsx("div", { className: "clarity-callout__label", children: "Clicks" }), jsxs("div", { className: "clarity-callout__value", children: [jsx("span", { className: "clarity-callout__count", children: element.clicks?.toLocaleString(language) }), jsxs("span", { className: "clarity-callout__percentage", children: ["(", percentage, "%)"] })] })] }), !isRecordingView && !isCompareMode && (jsxs("button", { className: "clarity-callout__button", "data-clarity-id": "viewRecordingClickedFromHeatmapIframe", "data-element-hash": element.hash, "data-selector": getSimpleSelector(element.selector), "data-device-type": deviceType, "data-heatmap-type": heatmapType, children: [jsx("svg", { width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", children: jsx("path", { d: "M5 3L11 8L5 13V3Z", fill: "currentColor" }) }), "View Recording"] }))] }), jsx("style", { children: `
863
+ .clarity-callout {
864
+ background: white;
865
+ border-radius: 8px;
866
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
867
+ padding: 16px;
868
+ min-width: 200px;
869
+ max-width: 280px;
870
+ animation: clarity-callout-fade-in 0.2s ease-out;
871
+ pointer-events: auto;
872
+ }
873
+
874
+ @keyframes clarity-callout-fade-in {
875
+ from {
876
+ opacity: 0;
877
+ transform: scale(0.95);
878
+ }
879
+ to {
880
+ opacity: 1;
881
+ transform: scale(1);
882
+ }
883
+ }
884
+
885
+ .clarity-callout__arrow {
886
+ position: absolute;
887
+ width: 16px;
888
+ height: 16px;
889
+ background: white;
890
+ transform: rotate(45deg);
891
+ }
892
+
893
+ /* Arrow positions */
894
+ .clarity-callout--top .clarity-callout__arrow {
895
+ bottom: -8px;
896
+ left: 50%;
897
+ margin-left: -8px;
898
+ box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
899
+ }
900
+
901
+ .clarity-callout--bottom .clarity-callout__arrow {
902
+ top: -8px;
903
+ left: 50%;
904
+ margin-left: -8px;
905
+ box-shadow: -2px -2px 4px rgba(0, 0, 0, 0.1);
906
+ }
907
+
908
+ .clarity-callout--left .clarity-callout__arrow {
909
+ right: -8px;
910
+ top: 50%;
911
+ margin-top: -8px;
912
+ box-shadow: 2px -2px 4px rgba(0, 0, 0, 0.1);
913
+ }
914
+
915
+ .clarity-callout--right .clarity-callout__arrow {
916
+ left: -8px;
917
+ top: 50%;
918
+ margin-top: -8px;
919
+ box-shadow: -2px 2px 4px rgba(0, 0, 0, 0.1);
920
+ }
921
+
922
+ .clarity-callout__content {
923
+ position: relative;
924
+ z-index: 1;
925
+ }
926
+
927
+ .clarity-callout__rank {
928
+ display: inline-flex;
929
+ align-items: center;
930
+ justify-content: center;
931
+ width: 32px;
932
+ height: 32px;
933
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
934
+ color: white;
935
+ border-radius: 8px;
936
+ font-weight: 600;
937
+ font-size: 16px;
938
+ margin-bottom: 12px;
939
+ }
940
+
941
+ .clarity-callout--secondary .clarity-callout__rank {
942
+ background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
943
+ }
944
+
945
+ .clarity-callout__stats {
946
+ margin-bottom: 12px;
947
+ }
948
+
949
+ .clarity-callout__label {
950
+ font-size: 12px;
951
+ color: #6b7280;
952
+ margin-bottom: 4px;
953
+ font-weight: 500;
954
+ }
955
+
956
+ .clarity-callout__value {
957
+ display: flex;
958
+ align-items: baseline;
959
+ gap: 6px;
960
+ }
961
+
962
+ .clarity-callout__count {
963
+ font-size: 20px;
964
+ font-weight: 700;
965
+ color: #111827;
966
+ }
967
+
968
+ .clarity-callout__percentage {
969
+ font-size: 14px;
970
+ color: #6b7280;
971
+ font-weight: 500;
972
+ }
973
+
974
+ .clarity-callout__button {
975
+ display: flex;
976
+ align-items: center;
977
+ justify-content: center;
978
+ gap: 6px;
979
+ width: 100%;
980
+ padding: 8px 12px;
981
+ background: #667eea;
982
+ color: white;
983
+ border: none;
984
+ border-radius: 6px;
985
+ font-size: 13px;
986
+ font-weight: 600;
987
+ cursor: pointer;
988
+ transition: all 0.2s;
989
+ }
990
+
991
+ .clarity-callout__button:hover {
992
+ background: #5568d3;
993
+ transform: translateY(-1px);
994
+ box-shadow: 0 2px 8px rgba(102, 126, 234, 0.4);
995
+ }
996
+
997
+ .clarity-callout__button:active {
998
+ transform: translateY(0);
999
+ }
1000
+
1001
+ .clarity-callout__button svg {
1002
+ width: 14px;
1003
+ height: 14px;
1004
+ }
1005
+ ` })] }));
1006
+ // Render to body using Portal
1007
+ return createPortal(calloutContent, document.body);
1008
+ };
1009
+
1010
+ const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
1011
+ const style = calculateRankPosition(elementRect, widthScale);
1012
+ return (jsx("div", { className: "rankBadge", style: style, onClick: clickOnElement, "data-testid": "elementRank", children: index }));
1013
+ };
1014
+
1015
+ const ClickedElementOverlay = ({ element, shouldShowCallout, isSecondary, targetId, totalClicks = 1, isRecordingView, isCompareMode, deviceType, heatmapType, widthScale, }) => {
1016
+ if (!element || (element.width === 0 && element.height === 0))
1017
+ return null;
1018
+ return (jsxs(Fragment$1, { children: [jsx("div", { className: "heatmapElement", id: targetId, style: {
1019
+ top: element.top,
1020
+ left: element.left,
1021
+ width: element.width,
1022
+ height: element.height,
1023
+ } }), jsx(RankBadge, { index: element.rank, elementRect: element, widthScale: widthScale }), shouldShowCallout && (jsx(ElementCallout, { element: element, target: `#${targetId}`, totalClicks: totalClicks, isSecondary: isSecondary, isRecordingView: isRecordingView, isCompareMode: isCompareMode, deviceType: deviceType, heatmapType: heatmapType, widthScale: widthScale }))] }));
1024
+ };
1025
+
1026
+ const DefaultRankBadges = ({ elements, getRect, widthScale, hidden }) => {
1027
+ if (hidden || elements.length === 0)
1028
+ return null;
1029
+ return (jsx(Fragment, { children: elements.map((element, index) => {
1030
+ const rect = getRect(element);
1031
+ if (!rect)
1032
+ return null;
1033
+ return (jsx(RankBadge, { index: index + 1, elementRect: rect, widthScale: widthScale }, element.hash));
1034
+ }) }));
1035
+ };
1036
+
1037
+ const HoveredElementOverlay = ({ element, onClick, isSecondary, targetId, totalClicks = 1, }) => {
1038
+ if (!element)
1039
+ return null;
1040
+ return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: "heatmapElement hovered", id: targetId, style: {
1041
+ top: element.top,
1042
+ left: element.left,
1043
+ width: element.width,
1044
+ height: element.height,
1045
+ cursor: 'pointer',
1046
+ } }), jsx(RankBadge, { index: element.rank, elementRect: element, widthScale: 1, clickOnElement: onClick })] }));
1047
+ };
1048
+
1049
+ const MissingElementMessage = ({ widthScale }) => {
1050
+ return (jsx("div", { className: "missingElement", style: {
1051
+ position: 'absolute',
1052
+ top: '50%',
1053
+ left: '50%',
1054
+ transform: `translate(-50%, -50%) scale(${1 / widthScale})`,
1055
+ background: 'rgba(0, 0, 0, 0.8)',
1056
+ color: 'white',
1057
+ padding: '12px 20px',
1058
+ borderRadius: '8px',
1059
+ fontSize: '14px',
1060
+ fontWeight: '500',
1061
+ zIndex: 9999,
1062
+ pointerEvents: 'none',
1063
+ whiteSpace: 'nowrap',
1064
+ }, "aria-live": "assertive", children: "Element not visible on current screen" }));
1065
+ };
1066
+
1067
+ const HeatmapElements = (props) => {
1068
+ const { iframeRef, parentRef, visualizer, heatmapInfo, widthScale, iframeHeight, iframeDimensions, selectedElement, isElementSidebarOpen, isVisible = true, selectElement, areDefaultRanksHidden, isSecondary, ...rest } = props;
1069
+ const getRect = useHeatmapElementPosition({
1070
+ iframeRef,
1071
+ parentRef,
1072
+ visualizer,
1073
+ heatmapWidth: heatmapInfo?.width,
1074
+ iframeHeight,
1075
+ widthScale,
1076
+ projectId: props.projectId,
1077
+ });
1078
+ const { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout } = useClickedElement({
1079
+ selectedElement,
1080
+ heatmapInfo,
1081
+ getRect,
1082
+ });
1083
+ const { hoveredElement, handleMouseMove, handleMouseLeave, handleClick } = useHoveredElement({
1084
+ iframeRef,
1085
+ heatmapInfo,
1086
+ getRect,
1087
+ onSelect: selectElement,
1088
+ widthScale,
1089
+ });
1090
+ const resetAll = () => {
1091
+ // nếu cần reset thêm state ở đây
1092
+ // setShouldShowCallout(false);
1093
+ };
1094
+ useHeatmapEffects({
1095
+ isVisible,
1096
+ isElementSidebarOpen,
1097
+ selectedElement,
1098
+ setShouldShowCallout,
1099
+ resetAll,
1100
+ });
1101
+ if (!isVisible)
1102
+ return null;
1103
+ const top10 = heatmapInfo?.sortedElements?.slice(0, 10) ?? [];
1104
+ return (jsxs("div", { onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, className: "heatmapElements gx-hm-elements", style: iframeDimensions, children: [jsx(DefaultRankBadges, { elements: top10, getRect: getRect, widthScale: widthScale, hidden: areDefaultRanksHidden }), jsx(ClickedElementOverlay, { widthScale: widthScale, element: clickedElement, shouldShowCallout: shouldShowCallout, isSecondary: isSecondary, targetId: isSecondary ? SECONDARY_CLICKED_ELEMENT_ID : CLICKED_ELEMENT_ID, ...rest }), showMissingElement && jsx(MissingElementMessage, { widthScale: widthScale }), jsx(HoveredElementOverlay, { element: hoveredElement, onClick: handleClick, isSecondary: isSecondary, targetId: isSecondary ? SECONDARY_HOVERED_ELEMENT_ID : HOVERED_ELEMENT_ID, totalClicks: heatmapInfo?.totalClicks ?? 1 }), hoveredElement !== clickedElement && hoveredElement && (jsx(ElementCallout, { element: hoveredElement, target: `#${props.isSecondary ? SECONDARY_HOVERED_ELEMENT_ID : HOVERED_ELEMENT_ID}`, totalClicks: props.heatmapInfo?.totalClicks ?? 1, isSecondary: props.isSecondary, parentRef: props.parentRef }))] }));
1105
+ };
1106
+
1107
+ const VizElements = ({ width, height, iframeRef, wrapperRef, widthScale, }) => {
1108
+ useHeatmapDataStore((state) => state.data);
1109
+ const heatmapInfo = {
1110
+ sortedElements: [
1111
+ {
1112
+ hash: '9ebwu6a3',
1113
+ selector: 'Join our email list',
1114
+ },
1115
+ {
1116
+ hash: '350hde5d4',
1117
+ selector: 'Products',
1118
+ },
1119
+ ],
1120
+ elementMapInfo: {
1121
+ '9ebwu6a3': {
1122
+ totalclicks: 4,
1123
+ hash: '9ebwu6a3',
1124
+ },
1125
+ '350hde5d4': {
1126
+ totalclicks: 4,
1127
+ hash: '350hde5d4',
1128
+ },
1129
+ },
1130
+ totalClicks: 8,
1131
+ };
1132
+ const visualizer = {
1133
+ get: (hash) => {
1134
+ const doc = iframeRef.current?.contentDocument;
1135
+ if (!doc)
1136
+ return null;
1137
+ // Find element by hash attribute
1138
+ return doc.querySelector(`[data-clarity-hashalpha="${hash}"]`);
1139
+ },
1140
+ };
1141
+ const [selectedElement, setSelectedElement] = useState(null);
1142
+ if (!iframeRef.current)
1143
+ return null;
1144
+ return (jsx(HeatmapElements, { visualizer: visualizer, iframeRef: iframeRef, parentRef: wrapperRef, iframeHeight: window.innerHeight, widthScale: widthScale, heatmapInfo: heatmapInfo, selectedElement: selectedElement, selectElement: setSelectedElement, isVisible: true, iframeDimensions: {
1145
+ width,
1146
+ height,
1147
+ position: 'absolute',
1148
+ top: 0,
1149
+ left: 0,
1150
+ // pointerEvents: 'none',
1151
+ } }));
1152
+ };
1153
+
1154
+ const ReplayControls = () => {
1155
+ const replayResult = useReplayRender();
1156
+ return (jsxs("div", { style: {
1157
+ position: 'absolute',
1158
+ bottom: 20,
1159
+ left: '50%',
1160
+ transform: 'translateX(-50%)',
1161
+ display: 'flex',
1162
+ gap: 10,
1163
+ backgroundColor: 'rgba(0, 0, 0, 0.7)',
1164
+ padding: '10px 20px',
1165
+ borderRadius: 8,
1166
+ }, children: [jsx("button", { onClick: replayResult.play, style: {
1167
+ padding: '8px 16px',
1168
+ backgroundColor: '#4CAF50',
1169
+ color: 'white',
1170
+ border: 'none',
1171
+ borderRadius: 4,
1172
+ cursor: 'pointer',
1173
+ }, children: "Play" }), jsx("button", { onClick: replayResult.pause, style: {
1174
+ padding: '8px 16px',
1175
+ backgroundColor: '#f44336',
1176
+ color: 'white',
1177
+ border: 'none',
1178
+ borderRadius: 4,
1179
+ cursor: 'pointer',
1180
+ }, children: "Pause" })] }));
1181
+ };
1182
+
1183
+ const VizDomRenderer = ({ mode = 'heatmap' }) => {
1184
+ const config = useHeatmapDataStore((state) => state.config);
1185
+ const wrapperRef = useRef(null);
1186
+ const visualRef = useRef(null);
1187
+ const { iframeRef } = useHeatmapVizRender(mode);
1188
+ const { contentWidth, iframeHeight, scale, scaledHeight, handleScroll } = useHeatmapScale({
1189
+ wrapperRef,
1190
+ iframeRef,
1191
+ config,
1192
+ });
1193
+ const onScroll = (e) => {
1194
+ const scrollTop = e.currentTarget.scrollTop;
1195
+ handleScroll(scrollTop);
1196
+ };
1197
+ return (jsxs("div", { ref: visualRef, className: "gx-hm-visual", onScroll: onScroll, style: {
1198
+ overflow: 'hidden auto',
1199
+ display: 'flex',
1200
+ position: 'relative',
1201
+ justifyContent: 'center',
1202
+ flex: 1,
1203
+ backgroundColor: '#fff',
1204
+ // borderRadius: '8px',
1205
+ }, children: [jsx("div", { className: "gx-hm-visual-unscaled", style: {
1206
+ width: '100%',
1207
+ display: 'flex',
1208
+ justifyContent: 'center',
1209
+ alignItems: 'flex-start',
1210
+ height: scaledHeight > 0 ? `${scaledHeight + HEATMAP_CONFIG['paddingBlock']}px` : 'auto',
1211
+ padding: HEATMAP_STYLE['wrapper']['padding'],
1212
+ }, children: jsxs("div", { className: "gx-hm-wrapper", ref: wrapperRef, style: {
1213
+ width: contentWidth,
1214
+ height: iframeHeight,
1215
+ transform: `scale(${scale})`,
1216
+ transformOrigin: 'top center',
1217
+ }, children: [jsx(VizElements, { width: contentWidth, height: iframeHeight, widthScale: scale, iframeRef: iframeRef, wrapperRef: wrapperRef }), jsx("iframe", {
1218
+ // key={iframeKey}
1219
+ ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no" })] }) }), mode === 'replay' && jsx(ReplayControls, {})] }));
1220
+ };
1221
+
1222
+ const VizDomContainer = () => {
1223
+ return (jsx(BoxStack, { id: "gx-hm-viz-container", flexDirection: "column", flex: "1 1 auto", overflow: "auto", children: jsx(BoxStack, { id: "gx-hm-content", flexDirection: "column", flex: "1 1 auto", overflow: "hidden", style: {
1224
+ // margin: '0px 16px 0px 12px',
1225
+ // borderRadius: '8px 8px 0 0',
1226
+ // borderRadius: '8px',
1227
+ minWidth: '394px',
1228
+ padding: '12px',
1229
+ background: '#f1f1f1',
1230
+ }, children: jsx(VizDomRenderer, {}) }) }));
1231
+ };
1232
+
1233
+ const SIDEBAR_WIDTH = 280;
1234
+ const LeftSidebar = ({ children }) => {
1235
+ const isHideSidebar = useHeatmapDataStore((state) => state.state.hideSidebar);
1236
+ if (isHideSidebar) {
1237
+ return null;
1238
+ }
1239
+ return (jsx("div", { className: "gx-hm-sidebar", style: {
1240
+ height: '100%',
1241
+ display: 'flex',
1242
+ ...(isHideSidebar
1243
+ ? {
1244
+ width: '0',
1245
+ transform: 'translateX(-100%)',
1246
+ visibility: 'hidden',
1247
+ }
1248
+ : { width: `${SIDEBAR_WIDTH}px` }),
1249
+ }, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: { height: '100%', width: `${SIDEBAR_WIDTH}px` }, children: children }) }));
1250
+ };
1251
+
1252
+ const WrapperPreview = ({ children }) => {
1253
+ return (jsxs("div", { className: "gx-hm-container", style: { display: 'flex', overflowY: 'hidden', flex: '1', position: 'relative' }, children: [jsx(LeftSidebar, { children: children }), jsx(VizDomContainer, {})] }));
1254
+ };
1255
+
1256
+ const WrapperLayout = ({ header, toolbar, sidebar }) => {
1257
+ return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentHeader, { children: header }), jsx(ContentHeader, { children: toolbar }), jsx(WrapperPreview, { children: sidebar })] }));
1258
+ };
1259
+
1260
+ const HeatmapLayout = ({ data, clickmap, header, toolbar, sidebar, }) => {
1261
+ const setData = useHeatmapDataStore((state) => state.setData);
1262
+ const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
1263
+ const handleSetClickmap = useCallback((clickmap) => {
1264
+ if (!clickmap)
1265
+ return;
1266
+ setClickmap(clickmap);
1267
+ }, [clickmap]);
1268
+ const handleSetData = useCallback((data) => {
1269
+ if (!data)
1270
+ return;
1271
+ setData(data);
1272
+ }, [data]);
1273
+ useEffect(() => {
1274
+ handleSetData(data);
1275
+ }, [data]);
1276
+ useEffect(() => {
1277
+ handleSetClickmap(clickmap);
1278
+ }, [clickmap]);
1279
+ return (jsx(BoxStack, { id: "gx-hm-project", flexDirection: "column", flex: "1", height: "100%", children: jsx(BoxStack, { id: "gx-hm-project-content", flexDirection: "column", flex: "1", children: jsx("div", { style: {
1280
+ minHeight: '100%',
1281
+ display: 'flex',
1282
+ }, children: jsx(WrapperLayout, { header: header, toolbar: toolbar, sidebar: sidebar }) }) }) }));
1283
+ };
1284
+
1285
+ var PanelContent;
1286
+ (function (PanelContent) {
1287
+ PanelContent["Sessions"] = "Sessions";
1288
+ PanelContent["Timeline"] = "Timeline";
1289
+ PanelContent["Area"] = "Area";
1290
+ PanelContent["Click"] = "Click";
1291
+ PanelContent["Scroll"] = "Scroll";
1292
+ PanelContent["Attention"] = "Attention";
1293
+ })(PanelContent || (PanelContent = {}));
1294
+ var ErrorType;
1295
+ (function (ErrorType) {
1296
+ ErrorType["NoResults"] = "NoResults";
1297
+ ErrorType["NoClicks"] = "NoClicks";
1298
+ ErrorType["NoScroll"] = "NoScroll";
1299
+ ErrorType["ServerError"] = "ServerError";
1300
+ ErrorType["DataError"] = "DataError";
1301
+ })(ErrorType || (ErrorType = {}));
1302
+
1303
+ export { GraphView, HeatmapLayout, useHeatmapDataStore };