@gemx-dev/heatmap-react 3.5.31 → 3.5.32

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 (62) hide show
  1. package/dist/esm/components/Layout/HeatmapLayout.d.ts +2 -1
  2. package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
  3. package/dist/esm/components/Layout/LeftSidebar.d.ts.map +1 -1
  4. package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
  5. package/dist/esm/components/VizElement/HeatmapElements.d.ts +0 -3
  6. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
  7. package/dist/esm/components/VizElement/VizElements.d.ts +0 -1
  8. package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
  9. package/dist/esm/hooks/register/useRegisterData.d.ts +2 -2
  10. package/dist/esm/hooks/register/useRegisterData.d.ts.map +1 -1
  11. package/dist/esm/hooks/vix-elements/useClickedElement.d.ts +1 -2
  12. package/dist/esm/hooks/vix-elements/useClickedElement.d.ts.map +1 -1
  13. package/dist/esm/hooks/vix-elements/useHeatmapEffects.d.ts +1 -2
  14. package/dist/esm/hooks/vix-elements/useHeatmapEffects.d.ts.map +1 -1
  15. package/dist/esm/hooks/vix-elements/useHeatmapElementPosition.d.ts +1 -2
  16. package/dist/esm/hooks/vix-elements/useHeatmapElementPosition.d.ts.map +1 -1
  17. package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts +3 -4
  18. package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
  19. package/dist/esm/hooks/viz-scale/useContentDimensions.d.ts.map +1 -1
  20. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts.map +1 -1
  21. package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -1
  22. package/dist/esm/index.js +84 -107
  23. package/dist/esm/index.mjs +84 -107
  24. package/dist/esm/stores/data.d.ts +3 -8
  25. package/dist/esm/stores/data.d.ts.map +1 -1
  26. package/dist/esm/stores/index.d.ts +1 -0
  27. package/dist/esm/stores/index.d.ts.map +1 -1
  28. package/dist/esm/stores/interaction.d.ts +14 -0
  29. package/dist/esm/stores/interaction.d.ts.map +1 -0
  30. package/dist/esm/stores/viz.d.ts +4 -0
  31. package/dist/esm/stores/viz.d.ts.map +1 -1
  32. package/dist/umd/components/Layout/HeatmapLayout.d.ts +2 -1
  33. package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
  34. package/dist/umd/components/Layout/LeftSidebar.d.ts.map +1 -1
  35. package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
  36. package/dist/umd/components/VizElement/HeatmapElements.d.ts +0 -3
  37. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
  38. package/dist/umd/components/VizElement/VizElements.d.ts +0 -1
  39. package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
  40. package/dist/umd/hooks/register/useRegisterData.d.ts +2 -2
  41. package/dist/umd/hooks/register/useRegisterData.d.ts.map +1 -1
  42. package/dist/umd/hooks/vix-elements/useClickedElement.d.ts +1 -2
  43. package/dist/umd/hooks/vix-elements/useClickedElement.d.ts.map +1 -1
  44. package/dist/umd/hooks/vix-elements/useHeatmapEffects.d.ts +1 -2
  45. package/dist/umd/hooks/vix-elements/useHeatmapEffects.d.ts.map +1 -1
  46. package/dist/umd/hooks/vix-elements/useHeatmapElementPosition.d.ts +1 -2
  47. package/dist/umd/hooks/vix-elements/useHeatmapElementPosition.d.ts.map +1 -1
  48. package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts +3 -4
  49. package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
  50. package/dist/umd/hooks/viz-scale/useContentDimensions.d.ts.map +1 -1
  51. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts.map +1 -1
  52. package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -1
  53. package/dist/umd/index.js +2 -2
  54. package/dist/umd/stores/data.d.ts +3 -8
  55. package/dist/umd/stores/data.d.ts.map +1 -1
  56. package/dist/umd/stores/index.d.ts +1 -0
  57. package/dist/umd/stores/index.d.ts.map +1 -1
  58. package/dist/umd/stores/interaction.d.ts +14 -0
  59. package/dist/umd/stores/interaction.d.ts.map +1 -0
  60. package/dist/umd/stores/viz.d.ts +4 -0
  61. package/dist/umd/stores/viz.d.ts.map +1 -1
  62. package/package.json +1 -1
package/dist/esm/index.js CHANGED
@@ -4,6 +4,7 @@ import { useNodesState, ReactFlow, Controls, Background } from '@xyflow/react';
4
4
  import { useEffect, useCallback, useState, useRef, useMemo, Fragment as Fragment$1 } from 'react';
5
5
  import { create } from 'zustand';
6
6
  import { Visualizer } from '@gemx-dev/clarity-visualize';
7
+ import { createPortal } from 'react-dom';
7
8
 
8
9
  const initialNodes = { id: '1', position: { x: 0, y: 0 }, data: { label: '1' } };
9
10
  const GraphView = ({ children, width, height }) => {
@@ -48,7 +49,7 @@ const HEATMAP_IFRAME = {
48
49
  const HEATMAP_CONFIG = {
49
50
  padding: 8,
50
51
  borderWidth: 1,
51
- borderColor: 'red',
52
+ borderColor: '#E3E3E3',
52
53
  };
53
54
  const HEATMAP_STYLE = {
54
55
  viz: {
@@ -96,25 +97,38 @@ const useHeatmapDataStore = create()((set, get) => {
96
97
  width: 1440,
97
98
  heatmapType: IHeatmapType.Click,
98
99
  },
99
- iframeHeight: 0,
100
- state: {
101
- hideSidebar: false,
102
- },
100
+ dataInfo: undefined,
103
101
  isRendering: true,
104
102
  setIsRendering: (isRendering) => set({ isRendering }),
103
+ setDataInfo: (dataInfo) => set({ dataInfo }),
105
104
  setData: (data) => set({ data }),
106
105
  setClickmap: (clickmap) => set({ clickmap }),
107
- setState: (state) => set({ state: { ...get().state, ...state } }),
108
106
  setConfig: (value) => set({ config: { ...get().config, ...value } }),
109
107
  setConfigBy: (key, value) => set({ config: { ...get().config, [key]: value } }),
110
- setIframeHeight: (iframeHeight) => set({ iframeHeight }),
108
+ };
109
+ });
110
+
111
+ const useHeatmapInteractionStore = create()((set, get) => {
112
+ return {
113
+ state: {
114
+ hideSidebar: false,
115
+ },
116
+ setState: (state) => set({ state }),
117
+ selectedElement: null,
118
+ setSelectedElement: (selectedElement) => set({ selectedElement }),
119
+ hoveredElement: null,
120
+ setHoveredElement: (hoveredElement) => set({ hoveredElement }),
111
121
  };
112
122
  });
113
123
 
114
124
  const useHeatmapVizStore = create()((set, get) => {
115
125
  return {
126
+ scale: 1,
116
127
  vizRef: undefined,
128
+ iframeHeight: 0,
129
+ setScale: (scale) => set({ scale }),
117
130
  setVizRef: (vizRef) => set({ vizRef }),
131
+ setIframeHeight: (iframeHeight) => set({ iframeHeight }),
118
132
  };
119
133
  });
120
134
 
@@ -138,18 +152,27 @@ const useRegisterControl = (control) => {
138
152
  registerControl('VizLoading', control.VizLoading);
139
153
  };
140
154
 
141
- const useRegisterData = (data) => {
155
+ const useRegisterData = (data, dataInfo) => {
142
156
  const setData = useHeatmapDataStore((state) => state.setData);
143
157
  const setIsRendering = useHeatmapDataStore((state) => state.setIsRendering);
158
+ const setDataInfo = useHeatmapDataStore((state) => state.setDataInfo);
144
159
  const handleSetData = useCallback((data) => {
145
160
  if (!data)
146
161
  return;
147
162
  setData(data);
148
163
  setIsRendering(false);
149
164
  }, [data]);
165
+ const handleSetDataInfo = useCallback((dataInfo) => {
166
+ if (!dataInfo)
167
+ return;
168
+ setDataInfo(dataInfo);
169
+ }, [setDataInfo]);
150
170
  useEffect(() => {
151
171
  handleSetData(data);
152
172
  }, [data]);
173
+ useEffect(() => {
174
+ handleSetDataInfo(dataInfo);
175
+ }, [dataInfo]);
153
176
  };
154
177
 
155
178
  const useRegisterHeatmap = (clickmap) => {
@@ -164,11 +187,13 @@ const useRegisterHeatmap = (clickmap) => {
164
187
  }, [clickmap]);
165
188
  };
166
189
 
167
- const useClickedElement = ({ selectedElement, heatmapInfo, getRect }) => {
190
+ const useClickedElement = ({ heatmapInfo, getRect }) => {
191
+ const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
168
192
  const [clickedElement, setClickedElement] = useState(null);
169
193
  const [showMissingElement, setShowMissingElement] = useState(false);
170
194
  const [shouldShowCallout, setShouldShowCallout] = useState(false);
171
195
  useEffect(() => {
196
+ console.log(`🚀 🐥 ~ useClickedElement ~ heatmapInfo?.elementMapInfo:`, heatmapInfo?.elementMapInfo);
172
197
  if (!selectedElement || !heatmapInfo?.elementMapInfo) {
173
198
  setClickedElement(null);
174
199
  setShowMissingElement(false);
@@ -212,7 +237,8 @@ const useClickedElement = ({ selectedElement, heatmapInfo, getRect }) => {
212
237
  return { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout };
213
238
  };
214
239
 
215
- const useHeatmapEffects = ({ isVisible, isElementSidebarOpen, selectedElement, setShouldShowCallout, resetAll, }) => {
240
+ const useHeatmapEffects = ({ isVisible, isElementSidebarOpen, setShouldShowCallout, resetAll, }) => {
241
+ const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
216
242
  // Reset khi ẩn
217
243
  useEffect(() => {
218
244
  if (!isVisible)
@@ -245,10 +271,6 @@ function getElementLayout(element) {
245
271
  function formatPercentage(value, decimals = 2) {
246
272
  return value.toFixed(decimals);
247
273
  }
248
- function getSimpleSelector(selector) {
249
- const parts = selector.split(' > ');
250
- return parts[parts.length - 1] || selector;
251
- }
252
274
  function calculateRankPosition(rect, widthScale) {
253
275
  const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
254
276
  const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
@@ -259,7 +281,8 @@ function calculateRankPosition(rect, widthScale) {
259
281
  };
260
282
  }
261
283
 
262
- const useHeatmapElementPosition = ({ iframeRef, parentRef, visualizer, heatmapWidth, iframeHeight, widthScale, projectId, }) => {
284
+ const useHeatmapElementPosition = ({ iframeRef, parentRef, visualizer, heatmapWidth, widthScale, projectId, }) => {
285
+ const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
263
286
  return useCallback((element) => {
264
287
  const hash = element?.hash;
265
288
  if (!iframeRef.current?.contentDocument || !hash || !visualizer)
@@ -304,8 +327,10 @@ const debounce = (fn, delay) => {
304
327
  timeout = setTimeout(() => fn(...args), delay);
305
328
  };
306
329
  };
307
- const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect, onSelect, }) => {
308
- const [hoveredElement, setHoveredElement] = useState(null);
330
+ const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect }) => {
331
+ const hoveredElement = useHeatmapInteractionStore((state) => state.hoveredElement);
332
+ const setHoveredElement = useHeatmapInteractionStore((state) => state.setHoveredElement);
333
+ const onSelect = useHeatmapInteractionStore((state) => state.setSelectedElement);
309
334
  const handleMouseMove = useCallback(debounce((event) => {
310
335
  if (!iframeRef.current?.contentDocument || !heatmapInfo?.elementMapInfo) {
311
336
  setHoveredElement(null);
@@ -314,8 +339,6 @@ const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect, onSele
314
339
  const iframe = iframeRef.current;
315
340
  const iframeRect = iframe.getBoundingClientRect();
316
341
  let x = event.clientX - iframeRect.left;
317
- console.log(`🚀 🐥 ~ useHoveredElement ~ iframeRect.left:`, iframeRect.left);
318
- console.log(`🚀 🐥 ~ useHoveredElement ~ event.clientX:`, event.clientX);
319
342
  let y = event.clientY - iframeRect.top;
320
343
  if (widthScale !== 1) {
321
344
  x /= widthScale;
@@ -326,9 +349,7 @@ const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect, onSele
326
349
  setHoveredElement(null);
327
350
  return;
328
351
  }
329
- let targetElement = null;
330
- // Best: dùng caretPositionFromPoint nếu có (Chrome, Safari)
331
- targetElement = getElementAtPoint(doc, x, y);
352
+ let targetElement = getElementAtPoint(doc, x, y) || null;
332
353
  if (!targetElement) {
333
354
  targetElement = doc.elementFromPoint(x, y);
334
355
  }
@@ -336,7 +357,6 @@ const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect, onSele
336
357
  setHoveredElement(null);
337
358
  return;
338
359
  }
339
- // Lấy hash từ nhiều attribute khả dĩ
340
360
  const hash = targetElement.getAttribute('data-clarity-hash') ||
341
361
  targetElement.getAttribute('data-clarity-hashalpha') ||
342
362
  targetElement.getAttribute('data-clarity-hashbeta');
@@ -589,23 +609,21 @@ const useContainerDimensions = (props) => {
589
609
 
590
610
  const useContentDimensions = (props) => {
591
611
  const { iframeRef, config } = props;
592
- const [contentWidth, setContentWidth] = useState(0);
593
612
  useEffect(() => {
594
- if (config?.width) {
595
- if (iframeRef.current) {
596
- iframeRef.current.width = `${config.width}px`;
597
- }
598
- setContentWidth(config.width);
599
- }
613
+ if (!config?.width)
614
+ return;
615
+ if (!iframeRef.current)
616
+ return;
617
+ iframeRef.current.width = `${config.width}px`;
600
618
  }, [config?.width, iframeRef]);
601
- return { contentWidth };
619
+ return { contentWidth: config?.width ?? 0 };
602
620
  };
603
621
 
604
622
  // Hook 3: Iframe Height Observer
605
623
  const useIframeHeight = (props) => {
606
624
  const { iframeRef, contentWidth } = props;
607
- const iframeHeight = useHeatmapDataStore((state) => state.iframeHeight);
608
- const setIframeHeight = useHeatmapDataStore((state) => state.setIframeHeight);
625
+ const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
626
+ const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
609
627
  const resizeObserverRef = useRef(null);
610
628
  const mutationObserverRef = useRef(null);
611
629
  const updateIframeHeight = useCallback(() => {
@@ -628,7 +646,6 @@ const useIframeHeight = (props) => {
628
646
  }
629
647
  }, [iframeRef, setIframeHeight]);
630
648
  useEffect(() => {
631
- console.log(`🚀 🐥 ~ useIframeHeight ~ contentWidth:`, contentWidth);
632
649
  if (contentWidth > 0) {
633
650
  const timeoutId = setTimeout(() => {
634
651
  updateIframeHeight();
@@ -695,8 +712,9 @@ const useIframeHeight = (props) => {
695
712
  };
696
713
 
697
714
  const useScaleCalculation = (props) => {
715
+ const scale = useHeatmapVizStore((state) => state.scale);
716
+ const setScale = useHeatmapVizStore((state) => state.setScale);
698
717
  const { containerWidth, contentWidth } = props;
699
- const [scale, setScale] = useState(1);
700
718
  useEffect(() => {
701
719
  if (containerWidth > 0 && contentWidth > 0) {
702
720
  const availableWidth = containerWidth - HEATMAP_CONFIG['padding'] * 2;
@@ -843,7 +861,6 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
843
861
  placement: 'top',
844
862
  });
845
863
  const percentage = formatPercentage(((element.clicks ?? 0) / totalClicks) * 100, 2);
846
- // Calculate callout position
847
864
  useEffect(() => {
848
865
  const targetElement = document.querySelector(target);
849
866
  const calloutElement = calloutRef.current;
@@ -859,30 +876,25 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
859
876
  let top = 0;
860
877
  let left = 0;
861
878
  let placement = 'top';
862
- // Try positions in order: top, bottom, right, left
863
879
  const positions = [
864
- // Top
865
880
  {
866
881
  placement: 'top',
867
882
  top: targetRect.top - calloutRect.height - padding - arrowSize,
868
883
  left: targetRect.left + targetRect.width / 2 - calloutRect.width / 2,
869
884
  valid: targetRect.top - calloutRect.height - padding - arrowSize > 0,
870
885
  },
871
- // Bottom
872
886
  {
873
887
  placement: 'bottom',
874
888
  top: targetRect.bottom + padding + arrowSize,
875
889
  left: targetRect.left + targetRect.width / 2 - calloutRect.width / 2,
876
890
  valid: targetRect.bottom + calloutRect.height + padding + arrowSize < viewportHeight,
877
891
  },
878
- // Right
879
892
  {
880
893
  placement: 'right',
881
894
  top: targetRect.top + targetRect.height / 2 - calloutRect.height / 2,
882
895
  left: targetRect.right + padding + arrowSize,
883
896
  valid: targetRect.right + calloutRect.width + padding + arrowSize < viewportWidth,
884
897
  },
885
- // Left
886
898
  {
887
899
  placement: 'left',
888
900
  top: targetRect.top + targetRect.height / 2 - calloutRect.height / 2,
@@ -900,9 +912,7 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
900
912
  top = Math.max(padding, Math.min(top, viewportHeight - calloutRect.height - padding));
901
913
  setPosition({ top, left, placement });
902
914
  };
903
- // Initial calculation
904
- // calculatePosition();
905
- // Recalculate on scroll/resize
915
+ calculatePosition();
906
916
  const handleUpdate = () => {
907
917
  requestAnimationFrame(calculatePosition);
908
918
  };
@@ -915,12 +925,12 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
915
925
  parentRef?.current?.removeEventListener('scroll', handleUpdate);
916
926
  };
917
927
  }, [target, parentRef]);
918
- (jsxs("div", { ref: calloutRef, className: `clarity-callout clarity-callout--${position.placement} ${isSecondary ? 'clarity-callout--secondary' : ''}`, style: {
928
+ const calloutContent = (jsxs("div", { ref: calloutRef, className: `clarity-callout clarity-callout--${position.placement} ${isSecondary ? 'clarity-callout--secondary' : ''}`, style: {
919
929
  position: 'fixed',
920
930
  top: position.top,
921
931
  left: position.left,
922
932
  zIndex: 2147483647,
923
- }, "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: `
933
+ }, "aria-live": "assertive", role: "tooltip", children: [jsx("div", { className: "clarity-callout__arrow" }), jsx("div", { className: "clarity-callout__content", children: 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, "%)"] })] })] }) }), jsx("style", { children: `
924
934
  .clarity-callout {
925
935
  background: white;
926
936
  border-radius: 8px;
@@ -1064,21 +1074,7 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
1064
1074
  height: 14px;
1065
1075
  }
1066
1076
  ` })] }));
1067
- const [portalContainer, setPortalContainer] = useState(null);
1068
- useEffect(() => {
1069
- const container = document.querySelector('#gx-hm-project-portal');
1070
- if (container instanceof HTMLElement) {
1071
- setPortalContainer(container);
1072
- }
1073
- else {
1074
- console.warn('Portal container #gx-hm-project-portal not found!');
1075
- }
1076
- }, []);
1077
- if (!portalContainer) {
1078
- return jsx(Fragment, {});
1079
- }
1080
- return jsx(Fragment, {});
1081
- // return createPortal(calloutContent, portalContainer);
1077
+ return createPortal(calloutContent, document.body);
1082
1078
  };
1083
1079
 
1084
1080
  const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
@@ -1139,84 +1135,64 @@ const MissingElementMessage = ({ widthScale }) => {
1139
1135
  };
1140
1136
 
1141
1137
  const HeatmapElements = (props) => {
1142
- const { iframeRef, parentRef, visualizer, heatmapInfo, widthScale, iframeHeight, iframeDimensions, selectedElement, isElementSidebarOpen, isVisible = true, selectElement, areDefaultRanksHidden, isSecondary, ...rest } = props;
1138
+ const height = useHeatmapVizStore((state) => state.iframeHeight);
1139
+ const { heatmapInfo, iframeRef, parentRef, visualizer, widthScale, iframeDimensions, isElementSidebarOpen, isVisible = true, areDefaultRanksHidden, isSecondary, ...rest } = props;
1143
1140
  const getRect = useHeatmapElementPosition({
1144
1141
  iframeRef,
1145
1142
  parentRef,
1146
1143
  visualizer,
1147
1144
  heatmapWidth: heatmapInfo?.width,
1148
- iframeHeight,
1149
1145
  widthScale,
1150
1146
  projectId: props.projectId,
1151
1147
  });
1152
1148
  const { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout } = useClickedElement({
1153
- selectedElement,
1154
1149
  heatmapInfo,
1155
1150
  getRect,
1156
1151
  });
1152
+ console.log(`🚀 🐥 ~ HeatmapElements ~ clickedElement:`, clickedElement);
1157
1153
  const { hoveredElement, handleMouseMove, handleMouseLeave, handleClick } = useHoveredElement({
1158
1154
  iframeRef,
1159
1155
  heatmapInfo,
1160
1156
  getRect,
1161
- onSelect: selectElement,
1162
1157
  widthScale,
1163
1158
  });
1164
1159
  const resetAll = () => {
1165
- // nếu cần reset thêm state ở đây
1166
1160
  // setShouldShowCallout(false);
1167
1161
  };
1168
1162
  useHeatmapEffects({
1169
1163
  isVisible,
1170
1164
  isElementSidebarOpen,
1171
- selectedElement,
1172
1165
  setShouldShowCallout,
1173
1166
  resetAll,
1174
1167
  });
1175
1168
  if (!isVisible)
1176
1169
  return null;
1177
1170
  const top10 = heatmapInfo?.sortedElements?.slice(0, 10) ?? [];
1178
- 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 }))] }));
1171
+ return (jsxs("div", { onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, className: "heatmapElements gx-hm-elements", style: { ...iframeDimensions, height }, 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?.hash !== clickedElement?.hash && 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 }))] }));
1179
1172
  };
1180
1173
 
1181
- const VizElements = ({ width, height, iframeRef, wrapperRef, widthScale, }) => {
1182
- const heatmapInfo = {
1183
- sortedElements: [
1184
- {
1185
- hash: '9ebwu6a3',
1186
- selector: 'Join our email list',
1187
- },
1188
- {
1189
- hash: '350hde5d4',
1190
- selector: 'Products',
1191
- },
1192
- ],
1193
- elementMapInfo: {
1194
- '9ebwu6a3': {
1195
- totalclicks: 4,
1196
- hash: '9ebwu6a3',
1197
- },
1198
- '350hde5d4': {
1199
- totalclicks: 4,
1200
- hash: '350hde5d4',
1201
- },
1202
- },
1203
- totalClicks: 8,
1204
- };
1174
+ const VizElements = ({ width, iframeRef, wrapperRef, widthScale, }) => {
1175
+ const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
1205
1176
  const visualizer = {
1206
1177
  get: (hash) => {
1207
1178
  const doc = iframeRef.current?.contentDocument;
1208
1179
  if (!doc)
1209
1180
  return null;
1210
- // Find element by hash attribute
1211
- return doc.querySelector(`[data-clarity-hashalpha="${hash}"]`);
1181
+ const elmHashAlpha = doc.querySelector(`[data-clarity-hashalpha="${hash}"]`);
1182
+ if (elmHashAlpha) {
1183
+ return elmHashAlpha;
1184
+ }
1185
+ const elmHash = doc.querySelector(`[data-clarity-hash="${hash}"]`);
1186
+ if (elmHash) {
1187
+ return elmHash;
1188
+ }
1189
+ return null;
1212
1190
  },
1213
1191
  };
1214
- const [selectedElement, setSelectedElement] = useState(null);
1215
1192
  if (!iframeRef.current)
1216
1193
  return null;
1217
- return (jsx(HeatmapElements, { visualizer: visualizer, iframeRef: iframeRef, parentRef: wrapperRef, iframeHeight: window.innerHeight, widthScale: widthScale, heatmapInfo: heatmapInfo, selectedElement: selectedElement, selectElement: setSelectedElement, isVisible: true, iframeDimensions: {
1194
+ return (jsx(HeatmapElements, { visualizer: visualizer, iframeRef: iframeRef, parentRef: wrapperRef, widthScale: widthScale, heatmapInfo: heatmapInfo, isVisible: true, iframeDimensions: {
1218
1195
  width,
1219
- height,
1220
1196
  position: 'absolute',
1221
1197
  top: 0,
1222
1198
  left: 0,
@@ -1255,7 +1231,7 @@ const ReplayControls = () => {
1255
1231
 
1256
1232
  const VizDomRenderer = ({ mode = 'heatmap' }) => {
1257
1233
  const config = useHeatmapDataStore((state) => state.config);
1258
- const setIframeHeight = useHeatmapDataStore((state) => state.setIframeHeight);
1234
+ const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
1259
1235
  const wrapperRef = useRef(null);
1260
1236
  const visualRef = useRef(null);
1261
1237
  const { iframeRef } = useHeatmapVizRender(mode);
@@ -1281,23 +1257,24 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
1281
1257
  position: 'relative',
1282
1258
  justifyContent: 'center',
1283
1259
  flex: 1,
1284
- paddingBottom: HEATMAP_STYLE['viz']['paddingBottom'],
1285
- background: HEATMAP_STYLE['viz']['background'],
1260
+ // paddingBottom: HEATMAP_STYLE['viz']['paddingBottom'],
1261
+ background: '#fff',
1286
1262
  }, children: [jsx("div", { className: "gx-hm-visual-unscaled", style: {
1287
1263
  width: '100%',
1288
1264
  display: 'flex',
1289
1265
  justifyContent: 'center',
1290
1266
  alignItems: 'flex-start',
1291
- height: scaledHeight > 0 ? `${scaledHeight + HEATMAP_CONFIG['padding']}px` : 'auto',
1267
+ height: scaledHeight > 0 ? `${scaledHeight + HEATMAP_CONFIG['padding'] * 2}px` : 'auto',
1292
1268
  padding: HEATMAP_STYLE['wrapper']['padding'],
1293
- borderRadius: '8px',
1269
+ paddingBottom: HEATMAP_STYLE['viz']['paddingBottom'],
1270
+ background: HEATMAP_STYLE['viz']['background'],
1294
1271
  }, children: jsxs("div", { className: "gx-hm-wrapper", ref: wrapperRef, style: {
1295
1272
  width: contentWidth,
1296
1273
  height: iframeHeight,
1297
1274
  transform: `scale(${scale})`,
1298
1275
  transformOrigin: 'top center',
1299
1276
  paddingBlockEnd: '0',
1300
- }, children: [jsx(VizElements, { width: contentWidth, height: iframeHeight, widthScale: scale, iframeRef: iframeRef, wrapperRef: wrapperRef }), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no" })] }) }), mode === 'replay' && jsx(ReplayControls, {})] }));
1277
+ }, children: [jsx(VizElements, { width: contentWidth, widthScale: scale, iframeRef: iframeRef, wrapperRef: wrapperRef }), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no" })] }) }), mode === 'replay' && jsx(ReplayControls, {})] }));
1301
1278
  };
1302
1279
 
1303
1280
  const VizDomContainer = () => {
@@ -1319,12 +1296,12 @@ const ContentMetricBar = () => {
1319
1296
 
1320
1297
  const ContentToolbar = () => {
1321
1298
  const controls = useHeatmapControlStore((state) => state.controls);
1322
- return (jsx("div", { id: "gx-hm-content-toolbar", style: { position: 'absolute', bottom: 0, left: 0, right: 0, padding: '8px' }, children: controls.Toolbar ?? null }));
1299
+ return (jsx("div", { id: "gx-hm-content-toolbar", style: { position: 'absolute', bottom: 0, left: '8px', right: '24px', padding: '8px' }, children: controls.Toolbar ?? null }));
1323
1300
  };
1324
1301
 
1325
1302
  const LeftSidebar = () => {
1326
1303
  const controls = useHeatmapControlStore((state) => state.controls);
1327
- const isHideSidebar = useHeatmapDataStore((state) => state.state.hideSidebar);
1304
+ const isHideSidebar = useHeatmapInteractionStore((state) => state.state.hideSidebar);
1328
1305
  const config = useHeatmapDataStore((state) => state.config);
1329
1306
  const sidebarWidth = config?.sidebarWidth || DEFAULT_SIDEBAR_WIDTH;
1330
1307
  if (isHideSidebar) {
@@ -1355,9 +1332,9 @@ const WrapperLayout = () => {
1355
1332
  return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(WrapperPreview, {})] }));
1356
1333
  };
1357
1334
 
1358
- const HeatmapLayout = ({ data, clickmap, controls }) => {
1335
+ const HeatmapLayout = ({ data, clickmap, controls, dataInfo, }) => {
1359
1336
  useRegisterControl(controls);
1360
- useRegisterData(data);
1337
+ useRegisterData(data, dataInfo);
1361
1338
  useRegisterHeatmap(clickmap);
1362
1339
  useRegisterConfig();
1363
1340
  return (jsx(BoxStack, { id: "gx-hm-project", flexDirection: "column", flex: "1", height: "100%", style: getVariableStyle(), children: jsx(BoxStack, { id: "gx-hm-project-content", flexDirection: "column", flex: "1", children: jsx("div", { style: {