@gemx-dev/heatmap-react 3.5.41 → 3.5.43

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 (88) hide show
  1. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  2. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
  3. package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  4. package/dist/esm/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  5. package/dist/esm/configs/style.d.ts +2 -0
  6. package/dist/esm/configs/style.d.ts.map +1 -1
  7. package/dist/esm/helpers/viewport-fixer.d.ts +5 -3
  8. package/dist/esm/helpers/viewport-fixer.d.ts.map +1 -1
  9. package/dist/esm/helpers/viewport-replacer.d.ts +12 -12
  10. package/dist/esm/helpers/viewport-replacer.d.ts.map +1 -1
  11. package/dist/esm/hooks/vix-elements/useHeatmapMouseHandler.d.ts +34 -0
  12. package/dist/esm/hooks/vix-elements/useHeatmapMouseHandler.d.ts.map +1 -0
  13. package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
  14. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts +3 -1
  15. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  16. package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +1 -1
  17. package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -1
  18. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts +3 -1
  19. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
  20. package/dist/esm/hooks/viz-live/index.d.ts +1 -1
  21. package/dist/esm/hooks/viz-live/{useIframeMessage.d.ts → useVizLiveIframeMsg.d.ts} +2 -5
  22. package/dist/esm/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +1 -0
  23. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts +4 -0
  24. package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +1 -0
  25. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  26. package/dist/esm/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -1
  27. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
  28. package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  29. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts +10 -0
  30. package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -0
  31. package/dist/esm/index.js +582 -194
  32. package/dist/esm/index.mjs +582 -194
  33. package/dist/esm/stores/index.d.ts +1 -0
  34. package/dist/esm/stores/index.d.ts.map +1 -1
  35. package/dist/esm/stores/interaction.d.ts.map +1 -1
  36. package/dist/esm/stores/mode-single.d.ts +9 -0
  37. package/dist/esm/stores/mode-single.d.ts.map +1 -0
  38. package/dist/esm/stores/viz.d.ts +0 -5
  39. package/dist/esm/stores/viz.d.ts.map +1 -1
  40. package/dist/esm/types/viewport-fixer.d.ts +9 -8
  41. package/dist/esm/types/viewport-fixer.d.ts.map +1 -1
  42. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  43. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
  44. package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
  45. package/dist/umd/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
  46. package/dist/umd/configs/style.d.ts +2 -0
  47. package/dist/umd/configs/style.d.ts.map +1 -1
  48. package/dist/umd/helpers/viewport-fixer.d.ts +5 -3
  49. package/dist/umd/helpers/viewport-fixer.d.ts.map +1 -1
  50. package/dist/umd/helpers/viewport-replacer.d.ts +12 -12
  51. package/dist/umd/helpers/viewport-replacer.d.ts.map +1 -1
  52. package/dist/umd/hooks/vix-elements/useHeatmapMouseHandler.d.ts +34 -0
  53. package/dist/umd/hooks/vix-elements/useHeatmapMouseHandler.d.ts.map +1 -0
  54. package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
  55. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts +3 -1
  56. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
  57. package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +1 -1
  58. package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -1
  59. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts +3 -1
  60. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
  61. package/dist/umd/hooks/viz-live/index.d.ts +1 -1
  62. package/dist/umd/hooks/viz-live/{useIframeMessage.d.ts → useVizLiveIframeMsg.d.ts} +2 -5
  63. package/dist/umd/hooks/viz-live/useVizLiveIframeMsg.d.ts.map +1 -0
  64. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts +4 -0
  65. package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +1 -0
  66. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  67. package/dist/umd/hooks/viz-scale/useContainerDimensions.d.ts.map +1 -1
  68. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
  69. package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
  70. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts +10 -0
  71. package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -0
  72. package/dist/umd/index.js +2 -2
  73. package/dist/umd/stores/index.d.ts +1 -0
  74. package/dist/umd/stores/index.d.ts.map +1 -1
  75. package/dist/umd/stores/interaction.d.ts.map +1 -1
  76. package/dist/umd/stores/mode-single.d.ts +9 -0
  77. package/dist/umd/stores/mode-single.d.ts.map +1 -0
  78. package/dist/umd/stores/viz.d.ts +0 -5
  79. package/dist/umd/stores/viz.d.ts.map +1 -1
  80. package/dist/umd/types/viewport-fixer.d.ts +9 -8
  81. package/dist/umd/types/viewport-fixer.d.ts.map +1 -1
  82. package/package.json +1 -1
  83. package/dist/esm/hooks/viz-live/useIframeMessage.d.ts.map +0 -1
  84. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts +0 -10
  85. package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts.map +0 -1
  86. package/dist/umd/hooks/viz-live/useIframeMessage.d.ts.map +0 -1
  87. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts +0 -10
  88. package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts.map +0 -1
package/dist/esm/index.js CHANGED
@@ -61,6 +61,8 @@ const HEATMAP_STYLE = {
61
61
  },
62
62
  wrapper: {
63
63
  padding: `${HEATMAP_CONFIG.padding}px 0`,
64
+ paddingBlock: `${HEATMAP_CONFIG.padding}px`,
65
+ paddingInline: `${HEATMAP_CONFIG.padding}px`,
64
66
  },
65
67
  };
66
68
  const DEFAULT_SIDEBAR_WIDTH = 260;
@@ -139,11 +141,7 @@ const useHeatmapVizStore = create()((set, get) => {
139
141
  isRenderViz: false,
140
142
  setIsRenderViz: (isRenderViz) => set({ isRenderViz }),
141
143
  scale: 1,
142
- vizRef: null,
143
- iframeHeight: 0,
144
144
  setScale: (scale) => set({ scale }),
145
- setVizRef: (vizRef) => set({ vizRef }),
146
- setIframeHeight: (iframeHeight) => set({ iframeHeight }),
147
145
  };
148
146
  });
149
147
 
@@ -165,6 +163,15 @@ const useHeatmapLiveStore = create()((set, get) => {
165
163
  };
166
164
  });
167
165
 
166
+ const useHeatmapSingleStore = create()((set, get) => {
167
+ return {
168
+ vizRef: null,
169
+ iframeHeight: 0,
170
+ setVizRef: (vizRef) => set({ vizRef }),
171
+ setIframeHeight: (iframeHeight) => set({ iframeHeight }),
172
+ };
173
+ });
174
+
168
175
  const useRegisterConfig = () => {
169
176
  const mode = useHeatmapConfigStore((state) => state.mode);
170
177
  const width = useHeatmapConfigStore((state) => state.width);
@@ -504,85 +511,18 @@ function isElementInViewport(elementRect, visualRef, scale) {
504
511
  return elementBottom > viewportTop && elementTop < viewportBottom;
505
512
  }
506
513
 
507
- const getScriptInjectCode = async () => {
508
- const moduleResult = (await Promise.resolve().then(function () { return viewportReplacer; }));
509
- const ActualClass = moduleResult.default;
510
- const classCode = ActualClass.toString();
511
- const classInstantiateCode = ActualClass.name;
512
- const scriptCode = `
513
- (function() {
514
- 'use strict';
515
- ${classCode}
516
- const replacer = new ${classInstantiateCode}();
517
- replacer.init();
518
- })();
519
- `;
520
- return scriptCode;
521
- };
522
- class ViewportUnitsFixer {
523
- iframe = null;
514
+ class ViewportUnitsReplacer {
515
+ doc;
516
+ win;
524
517
  config;
525
- constructor(config) {
526
- this.config = config;
527
- this.iframe = config.iframe;
528
- this.init();
529
- }
530
- async init() {
531
- if (!this.iframe) {
532
- console.error('[Parent] Required elements not found');
533
- return;
534
- }
535
- // this.injectScriptContent = await generateIframeInjectScript();
536
- window.addEventListener('message', this.handleMessage.bind(this));
537
- if (this.iframe.contentDocument?.readyState === 'complete') {
538
- await this.injectScript();
539
- }
540
- else {
541
- this.iframe.addEventListener('load', () => this.injectScript());
542
- }
543
- }
544
- async injectScript() {
545
- if (!this.iframe?.contentWindow || !this.iframe.contentDocument)
546
- return;
547
- const doc = this.iframe.contentDocument;
548
- const win = this.iframe.contentWindow;
549
- try {
550
- win.__viewportConfig = this.config;
551
- const script = doc.createElement('script');
552
- const scriptCode = await getScriptInjectCode();
553
- script.textContent = scriptCode;
554
- doc.head.appendChild(script);
555
- }
556
- catch (error) {
557
- console.error('[Parent] Failed to inject script', error);
518
+ regex = /([-.\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
519
+ constructor(iframe, config) {
520
+ if (!iframe.contentDocument || !iframe.contentWindow) {
521
+ throw new Error('Iframe document or window not accessible');
558
522
  }
559
- }
560
- handleMessage(event) {
561
- const data = event.data;
562
- if (!data || data.type !== 'IFRAME_HEIGHT_CALCULATED')
563
- return;
564
- this.config.onSuccess?.(data);
565
- }
566
- recalculate() {
567
- this.injectScript();
568
- }
569
- }
570
- function initViewportFixer(config) {
571
- const fixer = new ViewportUnitsFixer(config);
572
- window.viewportFixer = fixer;
573
- window.addEventListener('iframe-dimensions-applied', ((e) => {
574
- const ev = e;
575
- console.log('Iframe dimensions finalized:', ev.detail);
576
- }));
577
- return fixer;
578
- }
579
-
580
- class ViewportUnitsReplacer {
581
- regex() {
582
- return /([-.\d]+)(vh|svh|lvh|dvh|vw|svw|lvw|dvw)/gi;
583
- }
584
- config() {
585
- return window.__viewportConfig;
523
+ this.doc = iframe.contentDocument;
524
+ this.win = iframe.contentWindow;
525
+ this.config = config;
586
526
  }
587
527
  px(value) {
588
528
  return `${value.toFixed(2)}px`;
@@ -592,42 +532,44 @@ class ViewportUnitsReplacer {
592
532
  if (isNaN(num))
593
533
  return value;
594
534
  const map = {
595
- vh: this.config().targetHeight,
596
- svh: this.config().targetHeight,
597
- lvh: this.config().targetHeight,
598
- dvh: this.config().targetHeight,
599
- vw: this.config().targetWidth,
600
- svw: this.config().targetWidth,
601
- lvw: this.config().targetWidth,
602
- dvw: this.config().targetWidth,
535
+ vh: this.config.targetHeight,
536
+ svh: this.config.targetHeight,
537
+ lvh: this.config.targetHeight,
538
+ dvh: this.config.targetHeight,
539
+ vw: this.config.targetWidth,
540
+ svw: this.config.targetWidth,
541
+ lvw: this.config.targetWidth,
542
+ dvw: this.config.targetWidth,
603
543
  };
604
544
  return this.px((num / 100) * (map[unit.toLowerCase()] || 0));
605
545
  }
606
546
  replaceInText(cssText) {
607
- return cssText.replace(this.regex(), (_, value, unit) => this.convert(value, unit));
547
+ return cssText.replace(this.regex, (_, value, unit) => this.convert(value, unit));
608
548
  }
609
549
  processInlineStyles() {
610
550
  let count = 0;
611
- document.querySelectorAll('[style]').forEach((el) => {
551
+ this.doc.querySelectorAll('[style]').forEach((el) => {
612
552
  const style = el.getAttribute('style');
613
- if (style && this.regex().test(style)) {
553
+ if (style && this.regex.test(style)) {
554
+ this.regex.lastIndex = 0;
614
555
  el.setAttribute('style', this.replaceInText(style));
615
556
  count++;
616
557
  }
617
558
  });
618
- console.log(`[Iframe] Replaced ${count} inline style elements`);
559
+ console.log(`[ViewportReplacer] Replaced ${count} inline style elements`);
619
560
  return count;
620
561
  }
621
562
  processStyleTags() {
622
563
  let count = 0;
623
- document.querySelectorAll('style').forEach((tag) => {
564
+ this.doc.querySelectorAll('style').forEach((tag) => {
624
565
  const css = tag.textContent || '';
625
- if (this.regex().test(css)) {
566
+ if (this.regex.test(css)) {
567
+ this.regex.lastIndex = 0;
626
568
  tag.textContent = this.replaceInText(css);
627
569
  count++;
628
570
  }
629
571
  });
630
- console.log(`[Iframe] Replaced ${count} <style> tags`);
572
+ console.log(`[ViewportReplacer] Replaced ${count} <style> tags`);
631
573
  return count;
632
574
  }
633
575
  processRule(rule) {
@@ -637,7 +579,8 @@ class ViewportUnitsReplacer {
637
579
  for (let i = 0; i < style.length; i++) {
638
580
  const prop = style[i];
639
581
  const value = style.getPropertyValue(prop);
640
- if (value && this.regex().test(value)) {
582
+ if (value && this.regex.test(value)) {
583
+ this.regex.lastIndex = 0;
641
584
  style.setProperty(prop, this.replaceInText(value), style.getPropertyPriority(prop));
642
585
  count++;
643
586
  }
@@ -653,11 +596,11 @@ class ViewportUnitsReplacer {
653
596
  }
654
597
  processStylesheets() {
655
598
  let total = 0;
656
- Array.from(document.styleSheets).forEach((sheet) => {
599
+ Array.from(this.doc.styleSheets).forEach((sheet) => {
657
600
  try {
658
601
  // Bỏ qua external CSS (cross-origin)
659
- if (sheet.href && !sheet.href.startsWith(location.origin)) {
660
- console.log('[Iframe] Skipping external CSS:', sheet.href);
602
+ if (sheet.href && !sheet.href.startsWith(this.win.location.origin)) {
603
+ console.log('[ViewportReplacer] Skipping external CSS:', sheet.href);
661
604
  return;
662
605
  }
663
606
  const rules = sheet.cssRules || sheet.rules;
@@ -668,26 +611,27 @@ class ViewportUnitsReplacer {
668
611
  }
669
612
  }
670
613
  catch (e) {
671
- console.warn('[Iframe] Cannot read stylesheet (CORS?):', e.message);
614
+ console.warn('[ViewportReplacer] Cannot read stylesheet (CORS?):', e.message);
672
615
  }
673
616
  });
674
- console.log(`[Iframe] Replaced ${total} rules in stylesheets`);
617
+ console.log(`[ViewportReplacer] Replaced ${total} rules in stylesheets`);
675
618
  return total;
676
619
  }
677
620
  async processLinkedStylesheets() {
678
- const links = document.querySelectorAll('link[rel="stylesheet"]');
621
+ const links = this.doc.querySelectorAll('link[rel="stylesheet"]');
679
622
  let count = 0;
680
623
  for (const link of Array.from(links)) {
681
- if (!link.href.startsWith(location.origin)) {
682
- console.log('[Iframe] Skipping external CSS:', link.href);
624
+ if (!link.href.startsWith(this.win.location.origin)) {
625
+ console.log('[ViewportReplacer] Skipping external CSS:', link.href);
683
626
  continue;
684
627
  }
685
628
  try {
686
629
  const res = await fetch(link.href);
687
630
  let css = await res.text();
688
- if (this.regex().test(css)) {
631
+ if (this.regex.test(css)) {
632
+ this.regex.lastIndex = 0;
689
633
  css = this.replaceInText(css);
690
- const style = document.createElement('style');
634
+ const style = this.doc.createElement('style');
691
635
  style.textContent = css;
692
636
  style.dataset.originalHref = link.href;
693
637
  link.parentNode?.insertBefore(style, link);
@@ -696,30 +640,25 @@ class ViewportUnitsReplacer {
696
640
  }
697
641
  }
698
642
  catch (e) {
699
- console.warn('[Iframe] Cannot load CSS:', link.href, e);
643
+ console.warn('[ViewportReplacer] Cannot load CSS:', link.href, e);
700
644
  }
701
645
  }
702
- console.log(`[Iframe] Replaced ${count} linked CSS files`);
646
+ console.log(`[ViewportReplacer] Replaced ${count} linked CSS files`);
703
647
  return count;
704
648
  }
705
649
  getFinalHeight() {
706
650
  // Trigger reflow
707
- void document.body.offsetHeight;
708
- return Math.max(document.body.scrollHeight, document.body.offsetHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight, document.documentElement.clientHeight);
651
+ void this.doc.body.offsetHeight;
652
+ return Math.max(this.doc.body.scrollHeight, this.doc.body.offsetHeight, this.doc.documentElement.scrollHeight, this.doc.documentElement.offsetHeight, this.doc.documentElement.clientHeight);
709
653
  }
710
- notifyParent(height) {
711
- window.parent.postMessage({
712
- type: 'IFRAME_HEIGHT_CALCULATED',
713
- height,
714
- width: document.body.scrollWidth,
715
- }, '*');
716
- console.log('[Iframe] Sent height to parent:', height);
654
+ getFinalWidth() {
655
+ return Math.max(this.doc.body.scrollWidth, this.doc.body.offsetWidth, this.doc.documentElement.scrollWidth, this.doc.documentElement.offsetWidth, this.doc.documentElement.clientWidth);
717
656
  }
718
657
  async waitForResources() {
719
- if ('fonts' in document) {
720
- await document.fonts.ready;
658
+ if ('fonts' in this.doc) {
659
+ await this.doc.fonts.ready;
721
660
  }
722
- const images = Array.from(document.images).filter((img) => !img.complete);
661
+ const images = Array.from(this.doc.images).filter((img) => !img.complete);
723
662
  if (images.length > 0) {
724
663
  await Promise.all(images.map((img) => new Promise((resolve) => {
725
664
  img.onload = img.onerror = resolve;
@@ -728,37 +667,104 @@ class ViewportUnitsReplacer {
728
667
  }
729
668
  async run() {
730
669
  try {
670
+ console.log('[ViewportReplacer] Starting viewport units replacement...');
731
671
  this.processInlineStyles();
732
672
  this.processStyleTags();
733
673
  this.processStylesheets();
734
674
  await this.processLinkedStylesheets();
735
675
  // await this.waitForResources();
736
- requestAnimationFrame(() => {
737
- const height = this.getFinalHeight();
738
- this.notifyParent(height);
676
+ return await new Promise((resolve) => {
677
+ requestAnimationFrame(() => {
678
+ const height = this.getFinalHeight();
679
+ const width = this.getFinalWidth();
680
+ console.log('[ViewportReplacer] Calculated dimensions:', { height, width });
681
+ resolve({ height, width });
682
+ });
739
683
  });
740
684
  }
741
685
  catch (err) {
742
- console.error('[Iframe] Critical error:', err);
743
- this.notifyParent(document.body.scrollHeight || 1000);
686
+ console.error('[ViewportReplacer] Critical error:', err);
687
+ return {
688
+ height: this.doc.body.scrollHeight || 1000,
689
+ width: this.doc.body.scrollWidth || 1000,
690
+ };
744
691
  }
745
692
  }
746
- init() {
747
- if (!window.__viewportConfig)
693
+ updateConfig(config) {
694
+ this.config = { ...this.config, ...config };
695
+ }
696
+ }
697
+
698
+ class ViewportUnitsFixer {
699
+ iframe;
700
+ config;
701
+ replacer = null;
702
+ constructor(config) {
703
+ this.config = config;
704
+ this.iframe = config.iframe;
705
+ this.init();
706
+ }
707
+ async init() {
708
+ if (!this.iframe) {
709
+ console.error('[ViewportFixer] iframe not found');
710
+ this.config.onError?.(new Error('iframe not found'));
748
711
  return;
749
- if (document.readyState === 'loading') {
750
- document.addEventListener('DOMContentLoaded', () => this.run());
712
+ }
713
+ if (this.iframe.contentDocument?.readyState === 'complete') {
714
+ await this.process();
751
715
  }
752
716
  else {
753
- this.run();
717
+ this.iframe.addEventListener('load', () => this.process());
718
+ }
719
+ }
720
+ async process() {
721
+ if (!this.iframe.contentDocument || !this.iframe.contentWindow) {
722
+ console.error('[ViewportFixer] Cannot access iframe document');
723
+ this.config.onError?.(new Error('Cannot access iframe document'));
724
+ return;
725
+ }
726
+ try {
727
+ console.log('[ViewportFixer] Processing viewport units...');
728
+ // Tạo replacer instance với iframe và config
729
+ this.replacer = new ViewportUnitsReplacer(this.iframe, this.config);
730
+ // Chạy replacement
731
+ const result = await this.replacer.run();
732
+ console.log('[ViewportFixer] Process completed:', result);
733
+ // Trigger success callback
734
+ this.config.onSuccess?.(result);
735
+ // Dispatch custom event
736
+ window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', {
737
+ detail: result,
738
+ }));
739
+ }
740
+ catch (error) {
741
+ console.error('[ViewportFixer] Failed to process:', error);
742
+ this.config.onError?.(error);
754
743
  }
755
744
  }
745
+ async recalculate() {
746
+ console.log('[ViewportFixer] Recalculating...');
747
+ await this.process();
748
+ }
749
+ updateConfig(config) {
750
+ this.config = { ...this.config, ...config };
751
+ if (this.replacer) {
752
+ this.replacer.updateConfig(config);
753
+ }
754
+ }
755
+ destroy() {
756
+ this.replacer = null;
757
+ console.log('[ViewportFixer] Destroyed');
758
+ }
759
+ }
760
+ function initViewportFixer(config) {
761
+ const fixer = new ViewportUnitsFixer(config);
762
+ window.addEventListener('iframe-dimensions-applied', ((e) => {
763
+ const ev = e;
764
+ console.log('[ViewportFixer] Iframe dimensions finalized:', ev.detail);
765
+ }));
766
+ return fixer;
756
767
  }
757
-
758
- var viewportReplacer = /*#__PURE__*/Object.freeze({
759
- __proto__: null,
760
- default: ViewportUnitsReplacer
761
- });
762
768
 
763
769
  const scrollToElementIfNeeded = (visualRef, rect, scale) => {
764
770
  if (!visualRef.current)
@@ -872,7 +878,7 @@ const useHeatmapEffects = ({ isVisible, isElementSidebarOpen, setShouldShowCallo
872
878
 
873
879
  const useHeatmapElementPosition = ({ iframeRef, wrapperRef, visualizer }) => {
874
880
  const widthScale = useHeatmapVizStore((state) => state.scale);
875
- const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
881
+ const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
876
882
  const heatmapWidth = useHeatmapConfigStore((state) => state.width);
877
883
  return useCallback((element) => {
878
884
  const hash = element?.hash;
@@ -920,6 +926,179 @@ const debounce = (fn, delay) => {
920
926
  };
921
927
  };
922
928
 
929
+ // ===================== CONSTANTS =====================
930
+ const HEATMAP_ELEMENT_ATTRIBUTE = 'data-clarity-hashalpha'; // Hoặc attribute bạn đang dùng
931
+ // ===================== UTILITY FUNCTIONS =====================
932
+ /**
933
+ * Lấy bounding box tuyệt đối của element (relative to document)
934
+ */
935
+ function getBoundingBox(element) {
936
+ if (typeof element.getBoundingClientRect !== 'function') {
937
+ return null;
938
+ }
939
+ const rect = element.getBoundingClientRect();
940
+ // Lấy scroll offset (hỗ trợ cả cách cũ và mới)
941
+ const scrollLeft = 'pageXOffset' in window ? window.pageXOffset : document.documentElement.scrollLeft;
942
+ const scrollTop = 'pageYOffset' in window ? window.pageYOffset : document.documentElement.scrollTop;
943
+ // Kiểm tra element có kích thước hợp lệ
944
+ if (!rect || (rect.height === 0 && rect.width === 0)) {
945
+ return null;
946
+ }
947
+ // Trả về vị trí tuyệt đối
948
+ return {
949
+ left: Math.floor(rect.left + scrollLeft),
950
+ top: Math.floor(rect.top + scrollTop),
951
+ width: Math.floor(rect.width),
952
+ height: Math.floor(rect.height),
953
+ };
954
+ }
955
+ /**
956
+ * Lấy tất cả elements tại tọa độ (x, y), hỗ trợ Shadow DOM
957
+ */
958
+ function getElementsAtPoint(documentOrShadowRoot, x, y, filterFunction, visitedShadowRoots = new Set()) {
959
+ // Lấy tất cả elements tại vị trí
960
+ const elementsAtPoint = documentOrShadowRoot.elementsFromPoint(x, y);
961
+ if (!filterFunction) {
962
+ return elementsAtPoint;
963
+ }
964
+ // Tìm element đầu tiên match với filter
965
+ const matchedElement = elementsAtPoint.find(filterFunction);
966
+ // Nếu element có Shadow DOM và chưa visit -> đệ quy vào
967
+ if (matchedElement?.shadowRoot && !visitedShadowRoots.has(matchedElement.shadowRoot)) {
968
+ visitedShadowRoots.add(matchedElement.shadowRoot);
969
+ return getElementsAtPoint(matchedElement.shadowRoot, x, y, filterFunction, visitedShadowRoots);
970
+ }
971
+ return elementsAtPoint;
972
+ }
973
+ // ===================== MAIN HOOK =====================
974
+ function useHeatmapMouseHandler(props) {
975
+ const { heatmapWrapperRef, iframeRef, parentRef, heatmapInfo, scaleRatio, onElementHover } = props;
976
+ const handleMouseMove = useCallback((event) => {
977
+ // Kiểm tra tất cả refs và data cần thiết
978
+ if (!heatmapWrapperRef?.current ||
979
+ !iframeRef?.current ||
980
+ !iframeRef.current.contentDocument ||
981
+ !heatmapInfo?.elementMapInfo ||
982
+ !parentRef?.current) {
983
+ return;
984
+ }
985
+ try {
986
+ // Tính toán scroll position (đã scale)
987
+ const scrollTop = parentRef.current.scrollTop / scaleRatio;
988
+ console.log(`🚀 🐥 ~ useHeatmapMouseHandler ~ scrollTop:`, scrollTop);
989
+ // Lấy vị trí của heatmap wrapper
990
+ const wrapperRect = heatmapWrapperRef.current.getBoundingClientRect();
991
+ // Tính toán tọa độ chuột trong iframe (đã scale)
992
+ const mouseX = (event.clientX - wrapperRect.left) / scaleRatio;
993
+ const mouseY = (event.clientY - wrapperRect.top) / scaleRatio - scrollTop;
994
+ // Tìm elements tại vị trí chuột
995
+ const elementsAtPoint = getElementsAtPoint(iframeRef.current.contentDocument, Math.round(mouseX), Math.round(mouseY),
996
+ // Filter: chỉ lấy elements có heatmap attribute
997
+ (element) => element.hasAttribute(HEATMAP_ELEMENT_ATTRIBUTE));
998
+ if (!elementsAtPoint || elementsAtPoint.length === 0) {
999
+ return;
1000
+ }
1001
+ // Duyệt qua các elements tìm được
1002
+ for (let i = 0; i < elementsAtPoint.length; i++) {
1003
+ const element = elementsAtPoint[i];
1004
+ // Lấy hash/id của element
1005
+ const elementHash = element.getAttribute(HEATMAP_ELEMENT_ATTRIBUTE);
1006
+ // Kiểm tra element có data trong heatmapInfo không
1007
+ if (elementHash && heatmapInfo.elementMapInfo[elementHash]) {
1008
+ const elementData = heatmapInfo.elementMapInfo[elementHash];
1009
+ // Lấy bounding box của element
1010
+ const boundingBox = getBoundingBox(element);
1011
+ if (boundingBox) {
1012
+ // Tính rank của element
1013
+ const rank = Array.isArray(heatmapInfo.sortedElements) && elementData
1014
+ ? heatmapInfo.sortedElements.indexOf(elementData) + 1
1015
+ : NaN;
1016
+ // Callback với thông tin element
1017
+ onElementHover({
1018
+ ...boundingBox,
1019
+ // Giới hạn width không vượt quá width của heatmap
1020
+ width: Math.min(boundingBox.width, heatmapInfo.width || 0),
1021
+ // Adjust top position với scroll
1022
+ top: boundingBox.top + scrollTop,
1023
+ // Metadata
1024
+ hash: elementHash,
1025
+ clicks: elementData.totalclicks,
1026
+ rank: rank,
1027
+ selector: elementData.selector || '',
1028
+ });
1029
+ // Dừng loop khi tìm thấy element hợp lệ đầu tiên
1030
+ break;
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ catch (error) {
1036
+ console.warn('Error handling mouse move on heatmap:', error);
1037
+ }
1038
+ }, [heatmapWrapperRef, iframeRef, parentRef, heatmapInfo, scaleRatio, onElementHover]);
1039
+ return { handleMouseMove };
1040
+ }
1041
+ // ===================== EXAMPLE USAGE =====================
1042
+ /*
1043
+ import { useRef, useState } from 'react';
1044
+
1045
+ function HeatmapComponent() {
1046
+ const heatmapWrapperRef = useRef<HTMLDivElement>(null);
1047
+ const iframeRef = useRef<HTMLIFrameElement>(null);
1048
+ const parentRef = useRef<HTMLDivElement>(null);
1049
+
1050
+ const [hoveredElement, setHoveredElement] = useState<HoveredElementInfo | null>(null);
1051
+
1052
+ const heatmapInfo = {
1053
+ width: 1920,
1054
+ elementMapInfo: {
1055
+ 'hash123': {
1056
+ totalclicks: 45,
1057
+ selector: 'button.submit'
1058
+ }
1059
+ },
1060
+ sortedElements: [...]
1061
+ };
1062
+
1063
+ const { handleMouseMove } = useHeatmapMouseHandler({
1064
+ heatmapWrapperRef,
1065
+ iframeRef,
1066
+ parentRef,
1067
+ heatmapInfo,
1068
+ scaleRatio: 0.8, // 80% zoom
1069
+ onElementHover: (info) => {
1070
+ setHoveredElement(info);
1071
+ console.log('Hovered element:', info);
1072
+ }
1073
+ });
1074
+
1075
+ return (
1076
+ <div ref={parentRef}>
1077
+ <div
1078
+ ref={heatmapWrapperRef}
1079
+ onMouseMove={handleMouseMove}
1080
+ >
1081
+ <iframe ref={iframeRef} />
1082
+
1083
+ {hoveredElement && (
1084
+ <div className="tooltip" style={{
1085
+ position: 'absolute',
1086
+ left: hoveredElement.left,
1087
+ top: hoveredElement.top
1088
+ }}>
1089
+ Clicks: {hoveredElement.clicks}
1090
+ <br />
1091
+ Rank: #{hoveredElement.rank}
1092
+ <br />
1093
+ Selector: {hoveredElement.selector}
1094
+ </div>
1095
+ )}
1096
+ </div>
1097
+ </div>
1098
+ );
1099
+ }
1100
+ */
1101
+
923
1102
  const useHoveredElement = ({ iframeRef, getRect }) => {
924
1103
  const hoveredElement = useHeatmapInteractionStore((state) => state.hoveredElement);
925
1104
  const setHoveredElement = useHeatmapInteractionStore((state) => state.setHoveredElement);
@@ -941,7 +1120,7 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
941
1120
  const doc = iframe.contentDocument;
942
1121
  const iframeRect = iframe.getBoundingClientRect();
943
1122
  const { x, y } = convertViewportToIframeCoords(event.clientX, event.clientY, iframeRect, widthScale);
944
- const targetElement = findTargetElement(doc, x, y);
1123
+ const targetElement = findTargetElement(doc, x, y, heatmapInfo);
945
1124
  if (!targetElement || !isValidElement(targetElement, heatmapInfo)) {
946
1125
  reset();
947
1126
  return;
@@ -992,7 +1171,25 @@ const convertViewportToIframeCoords = (clientX, clientY, iframeRect, scale) => {
992
1171
  }
993
1172
  return { x, y };
994
1173
  };
995
- const findTargetElement = (doc, x, y) => {
1174
+ const findTargetElement = (doc, x, y, heatmapInfo) => {
1175
+ const HEATMAP_ELEMENT_ATTRIBUTE = 'data-clarity-hashalpha';
1176
+ const elementsAtPoint = getElementsAtPoint(doc, Math.round(x), Math.round(y), (element) => element.hasAttribute(HEATMAP_ELEMENT_ATTRIBUTE));
1177
+ let dataElement = null;
1178
+ for (let i = 0; i < elementsAtPoint.length; i++) {
1179
+ const element = elementsAtPoint[i];
1180
+ const elementHash = element.getAttribute(HEATMAP_ELEMENT_ATTRIBUTE);
1181
+ if (elementHash && heatmapInfo.elementMapInfo?.[elementHash]) {
1182
+ heatmapInfo.elementMapInfo[elementHash];
1183
+ const boundingBox = getBoundingBox(element);
1184
+ if (boundingBox) {
1185
+ dataElement = element;
1186
+ break;
1187
+ }
1188
+ }
1189
+ }
1190
+ if (!!dataElement) {
1191
+ return dataElement;
1192
+ }
996
1193
  let targetElement = getElementAtPoint(doc, x, y);
997
1194
  if (!targetElement) {
998
1195
  targetElement = doc.elementFromPoint(x, y);
@@ -1016,7 +1213,7 @@ var MessageType;
1016
1213
  MessageType["GX_DOM_TRACKING_PAYLOAD"] = "GX_DOM_TRACKING_PAYLOAD";
1017
1214
  MessageType["CLARITY_READY"] = "CLARITY_READY";
1018
1215
  })(MessageType || (MessageType = {}));
1019
- function useIframeMessage(options = {}) {
1216
+ function useVizLiveIframeMsg(options = {}) {
1020
1217
  const { trustedOrigins = [], onMessage } = options;
1021
1218
  const addPayload = useHeatmapLiveStore((state) => state.addPayload);
1022
1219
  const [isReady, setIsReady] = useState(false);
@@ -1066,13 +1263,14 @@ function useIframeMessage(options = {}) {
1066
1263
  isReady,
1067
1264
  };
1068
1265
  }
1069
- function useIframeRender() {
1266
+
1267
+ function useVizLiveRender() {
1070
1268
  const wrapperHeight = useHeatmapLiveStore((state) => state.wrapperHeight);
1269
+ const setIframeHeight = useHeatmapLiveStore((state) => state.setIframeHeight);
1071
1270
  const contentWidth = useHeatmapConfigStore((state) => state.width);
1072
- const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
1073
1271
  const htmlContent = useHeatmapLiveStore((state) => state.htmlContent);
1074
1272
  const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
1075
- const { iframeRef, isReady } = useIframeMessage();
1273
+ const { iframeRef, isReady } = useVizLiveIframeMsg();
1076
1274
  useEffect(() => {
1077
1275
  if (!htmlContent || !iframeRef.current)
1078
1276
  return;
@@ -1119,9 +1317,9 @@ function reset$1(iframe, rect, onSuccess) {
1119
1317
  let visualizer = new Visualizer();
1120
1318
  const useHeatmapRender = () => {
1121
1319
  const data = useHeatmapDataStore((state) => state.data);
1122
- const setVizRef = useHeatmapVizStore((state) => state.setVizRef);
1320
+ const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
1123
1321
  const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
1124
- const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
1322
+ const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
1125
1323
  const iframeRef = useRef(null);
1126
1324
  const renderHeatmap = useCallback(async (payloads) => {
1127
1325
  if (!payloads || payloads.length === 0)
@@ -1131,8 +1329,6 @@ const useHeatmapRender = () => {
1131
1329
  if (!iframe?.contentWindow)
1132
1330
  return;
1133
1331
  await visualizer.html(payloads, iframe.contentWindow);
1134
- const element = visualizer.get('3w0uppvjw');
1135
- console.log(`🚀 🐥 ~ useHeatmapRender ~ element:`, element);
1136
1332
  reset(iframe, payloads, (height) => {
1137
1333
  height && setIframeHeight(height);
1138
1334
  setIsRenderViz(true);
@@ -1160,8 +1356,8 @@ function reset(iframe, payloads, onSuccess) {
1160
1356
  targetHeight: docHeight,
1161
1357
  iframe: iframe,
1162
1358
  onSuccess: (data) => {
1163
- onSuccess(data.height);
1164
1359
  iframe.height = `${data.height}px`;
1360
+ onSuccess(data.height);
1165
1361
  },
1166
1362
  });
1167
1363
  viewportFixer.recalculate();
@@ -1350,30 +1546,64 @@ const useContentDimensions = ({ iframeRef, }) => {
1350
1546
  return { contentWidth };
1351
1547
  };
1352
1548
 
1353
- const useIframeHeight = (props) => {
1549
+ const useObserveIframeHeight = (props) => {
1354
1550
  const { iframeRef, setIframeHeight } = props;
1355
1551
  const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz);
1356
1552
  const resizeObserverRef = useRef(null);
1357
1553
  const mutationObserverRef = useRef(null);
1554
+ const debounceTimerRef = useRef(null);
1555
+ const lastHeightRef = useRef(0);
1556
+ const animationFrameRef = useRef(null);
1358
1557
  const updateIframeHeight = useCallback(() => {
1359
1558
  const iframe = iframeRef.current;
1360
- if (!iframe)
1559
+ if (!iframe || !setIframeHeight)
1361
1560
  return;
1362
1561
  try {
1363
1562
  const iframeDocument = iframe.contentDocument;
1364
1563
  const iframeBody = iframeDocument?.body;
1365
- if (!iframeBody)
1564
+ const iframeDocumentElement = iframeDocument?.documentElement;
1565
+ if (!iframeBody || !iframeDocumentElement)
1366
1566
  return;
1367
- const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
1368
- if (bodyHeight > 0) {
1369
- iframe.height = `${bodyHeight}px`;
1370
- setIframeHeight(bodyHeight);
1371
- }
1567
+ iframe.style.height = 'auto';
1568
+ requestAnimationFrame(() => {
1569
+ const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
1570
+ const documentHeight = Math.max(iframeDocumentElement.scrollHeight, iframeDocumentElement.offsetHeight, iframeDocumentElement.clientHeight);
1571
+ const actualHeight = Math.max(bodyHeight, documentHeight);
1572
+ if (actualHeight > 0) {
1573
+ lastHeightRef.current = actualHeight;
1574
+ iframe.height = `${actualHeight}px`;
1575
+ iframe.style.height = `${actualHeight}px`;
1576
+ setIframeHeight(actualHeight);
1577
+ }
1578
+ });
1372
1579
  }
1373
1580
  catch (error) {
1374
1581
  console.warn('Cannot measure iframe content:', error);
1375
1582
  }
1376
1583
  }, [iframeRef, setIframeHeight]);
1584
+ const debouncedUpdate = useCallback(() => {
1585
+ // Cancel pending updates
1586
+ if (debounceTimerRef.current) {
1587
+ clearTimeout(debounceTimerRef.current);
1588
+ }
1589
+ if (animationFrameRef.current) {
1590
+ cancelAnimationFrame(animationFrameRef.current);
1591
+ }
1592
+ debounceTimerRef.current = setTimeout(() => {
1593
+ animationFrameRef.current = requestAnimationFrame(() => {
1594
+ updateIframeHeight();
1595
+ });
1596
+ }, 50);
1597
+ }, [updateIframeHeight]);
1598
+ // Immediate update không debounce (cho ResizeObserver)
1599
+ const immediateUpdate = useCallback(() => {
1600
+ if (animationFrameRef.current) {
1601
+ cancelAnimationFrame(animationFrameRef.current);
1602
+ }
1603
+ animationFrameRef.current = requestAnimationFrame(() => {
1604
+ updateIframeHeight();
1605
+ });
1606
+ }, [updateIframeHeight]);
1377
1607
  useEffect(() => {
1378
1608
  const iframe = iframeRef.current;
1379
1609
  if (!iframe || !isRenderViz)
@@ -1391,22 +1621,24 @@ const useIframeHeight = (props) => {
1391
1621
  if (mutationObserverRef.current) {
1392
1622
  mutationObserverRef.current.disconnect();
1393
1623
  }
1394
- // ResizeObserver for size changes
1395
1624
  if (typeof window.ResizeObserver !== 'undefined') {
1396
- resizeObserverRef.current = new ResizeObserver(updateIframeHeight);
1625
+ resizeObserverRef.current = new ResizeObserver(immediateUpdate);
1397
1626
  resizeObserverRef.current.observe(iframeBody);
1627
+ const iframeDocumentElement = iframeDocument?.documentElement;
1628
+ if (iframeDocumentElement) {
1629
+ resizeObserverRef.current.observe(iframeDocumentElement);
1630
+ }
1398
1631
  }
1399
- // MutationObserver for DOM changes
1400
1632
  if (typeof window.MutationObserver !== 'undefined') {
1401
- mutationObserverRef.current = new MutationObserver(updateIframeHeight);
1633
+ mutationObserverRef.current = new MutationObserver(immediateUpdate);
1402
1634
  mutationObserverRef.current.observe(iframeBody, {
1403
1635
  childList: true,
1404
1636
  subtree: true,
1405
1637
  attributes: true,
1406
- characterData: true,
1638
+ attributeFilter: ['style', 'class'],
1639
+ characterData: false,
1407
1640
  });
1408
1641
  }
1409
- // Initial measurement
1410
1642
  updateIframeHeight();
1411
1643
  }
1412
1644
  catch (error) {
@@ -1420,15 +1652,23 @@ const useIframeHeight = (props) => {
1420
1652
  iframe.addEventListener('load', setupObservers, { once: true });
1421
1653
  }
1422
1654
  return () => {
1655
+ // Cleanup observers
1423
1656
  if (resizeObserverRef.current) {
1424
1657
  resizeObserverRef.current.disconnect();
1425
1658
  }
1426
1659
  if (mutationObserverRef.current) {
1427
1660
  mutationObserverRef.current.disconnect();
1428
1661
  }
1662
+ // Cleanup timers
1663
+ if (debounceTimerRef.current) {
1664
+ clearTimeout(debounceTimerRef.current);
1665
+ }
1666
+ if (animationFrameRef.current) {
1667
+ cancelAnimationFrame(animationFrameRef.current);
1668
+ }
1429
1669
  iframe.removeEventListener('load', setupObservers);
1430
1670
  };
1431
- }, [iframeRef, isRenderViz, updateIframeHeight]);
1671
+ }, [iframeRef, isRenderViz, updateIframeHeight, debouncedUpdate, immediateUpdate]);
1432
1672
  return {};
1433
1673
  };
1434
1674
 
@@ -1474,7 +1714,7 @@ const useHeatmapScale = (props) => {
1474
1714
  // 2. Get content dimensions from config
1475
1715
  const { contentWidth } = useContentDimensions({ iframeRef });
1476
1716
  // 3. Observe iframe height (now reacts to width changes)
1477
- useIframeHeight({ iframeRef, setIframeHeight });
1717
+ useObserveIframeHeight({ iframeRef, setIframeHeight });
1478
1718
  // 4. Calculate scale
1479
1719
  const { scale } = useScaleCalculation({ containerWidth, contentWidth });
1480
1720
  // 5. Setup scroll sync
@@ -1645,37 +1885,159 @@ const VizContainer = ({ children, setWrapperHeight }) => {
1645
1885
  const useClickmap = () => {
1646
1886
  const [isInitialized, setIsInitialized] = useState(false);
1647
1887
  const clickmap = useHeatmapDataStore((state) => state.clickmap);
1648
- const vizRef = useHeatmapVizStore((state) => state.vizRef);
1649
- useEffect(() => {
1888
+ const vizRef = useHeatmapSingleStore((state) => state.vizRef);
1889
+ const start = useCallback(() => {
1650
1890
  if (isInitialized)
1651
1891
  return;
1652
1892
  if (!vizRef || !clickmap || clickmap.length === 0)
1653
1893
  return;
1654
- vizRef.clearmap();
1655
- const element = vizRef.get('3w0uppvjw');
1656
- console.log(`🚀 🐥 ~ useClickmap ~ element:`, element);
1657
- vizRef?.clickmap(clickmap);
1658
- setIsInitialized(true);
1894
+ try {
1895
+ vizRef?.clearmap?.();
1896
+ vizRef?.clickmap?.(clickmap);
1897
+ setIsInitialized(true);
1898
+ }
1899
+ catch (error) {
1900
+ console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
1901
+ }
1659
1902
  }, [vizRef, clickmap]);
1660
- return {};
1903
+ return { start };
1661
1904
  };
1662
1905
 
1906
+ const DATA_SCROLLMAP = [
1907
+ {
1908
+ scrollReachY: 5,
1909
+ cumulativeSum: 0,
1910
+ percUsers: 0,
1911
+ },
1912
+ {
1913
+ scrollReachY: 10,
1914
+ cumulativeSum: 0,
1915
+ percUsers: 0,
1916
+ },
1917
+ {
1918
+ scrollReachY: 15,
1919
+ cumulativeSum: 0,
1920
+ percUsers: 0,
1921
+ },
1922
+ {
1923
+ scrollReachY: 20,
1924
+ cumulativeSum: 0,
1925
+ percUsers: 0,
1926
+ },
1927
+ {
1928
+ scrollReachY: 25,
1929
+ cumulativeSum: 0,
1930
+ percUsers: 0,
1931
+ },
1932
+ {
1933
+ scrollReachY: 30,
1934
+ cumulativeSum: 0,
1935
+ percUsers: 0,
1936
+ },
1937
+ {
1938
+ scrollReachY: 35,
1939
+ cumulativeSum: 0,
1940
+ percUsers: 0,
1941
+ },
1942
+ {
1943
+ scrollReachY: 40,
1944
+ cumulativeSum: 0,
1945
+ percUsers: 0,
1946
+ },
1947
+ {
1948
+ scrollReachY: 45,
1949
+ cumulativeSum: 0,
1950
+ percUsers: 0,
1951
+ },
1952
+ {
1953
+ scrollReachY: 50,
1954
+ cumulativeSum: 0,
1955
+ percUsers: 0,
1956
+ },
1957
+ {
1958
+ scrollReachY: 55,
1959
+ cumulativeSum: 0,
1960
+ percUsers: 0,
1961
+ },
1962
+ {
1963
+ scrollReachY: 60,
1964
+ cumulativeSum: 0,
1965
+ percUsers: 0,
1966
+ },
1967
+ {
1968
+ scrollReachY: 65,
1969
+ cumulativeSum: 0,
1970
+ percUsers: 0,
1971
+ },
1972
+ {
1973
+ scrollReachY: 70,
1974
+ cumulativeSum: 0,
1975
+ percUsers: 0,
1976
+ },
1977
+ {
1978
+ scrollReachY: 75,
1979
+ cumulativeSum: 0,
1980
+ percUsers: 0,
1981
+ },
1982
+ {
1983
+ scrollReachY: 80,
1984
+ cumulativeSum: 0,
1985
+ percUsers: 0,
1986
+ },
1987
+ {
1988
+ scrollReachY: 85,
1989
+ cumulativeSum: 0,
1990
+ percUsers: 0,
1991
+ },
1992
+ {
1993
+ scrollReachY: 90,
1994
+ cumulativeSum: 0,
1995
+ percUsers: 0,
1996
+ },
1997
+ {
1998
+ scrollReachY: 95,
1999
+ cumulativeSum: 0,
2000
+ percUsers: 0,
2001
+ },
2002
+ {
2003
+ scrollReachY: 100,
2004
+ cumulativeSum: 0,
2005
+ percUsers: 0,
2006
+ },
2007
+ ];
1663
2008
  const useScrollmap = () => {
1664
- useHeatmapDataStore((state) => state.clickmap);
1665
- return {};
2009
+ const vizRef = useHeatmapSingleStore((state) => state.vizRef);
2010
+ const start = useCallback(() => {
2011
+ // if (isInitialized) return;
2012
+ const scrollmap = DATA_SCROLLMAP;
2013
+ if (!vizRef || !scrollmap || scrollmap.length === 0)
2014
+ return;
2015
+ try {
2016
+ vizRef?.clearmap?.();
2017
+ vizRef?.scrollmap?.(scrollmap);
2018
+ // setIsInitialized(true);
2019
+ }
2020
+ catch (error) {
2021
+ console.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
2022
+ }
2023
+ }, [vizRef]);
2024
+ return { start };
1666
2025
  };
1667
2026
 
1668
2027
  const useHeatmapVizCanvas = () => {
1669
2028
  const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
1670
- const heatmapRender = useMemo(() => {
2029
+ const { start: startClickmap } = useClickmap();
2030
+ const { start: startScrollmap } = useScrollmap();
2031
+ useEffect(() => {
1671
2032
  switch (heatmapType) {
1672
2033
  case IHeatmapType.Click:
1673
- return useClickmap;
2034
+ startClickmap();
2035
+ break;
1674
2036
  case IHeatmapType.Scroll:
1675
- return useScrollmap;
2037
+ startClickmap();
2038
+ break;
1676
2039
  }
1677
- }, [heatmapType]);
1678
- return heatmapRender?.();
2040
+ }, [heatmapType, startClickmap, startScrollmap]);
1679
2041
  };
1680
2042
 
1681
2043
  const CLICKED_ELEMENT_ID = 'gx-hm-clicked-element';
@@ -1738,7 +2100,7 @@ const ElementCallout = (props) => {
1738
2100
  window.removeEventListener('resize', handleUpdate);
1739
2101
  visualRef?.current?.removeEventListener('scroll', handleUpdate);
1740
2102
  };
1741
- }, [target, visualRef, hozOffset, alignment]);
2103
+ }, [element, target, visualRef, hozOffset, alignment]);
1742
2104
  const calloutContent = (jsx("div", { ref: calloutRef, className: `clarity-callout clarity-callout--${position.placement} clarity-callout--align-${position.horizontalAlign}`, style: {
1743
2105
  position: 'fixed',
1744
2106
  top: position.top,
@@ -1803,7 +2165,8 @@ const ELEMENT_CALLOUT = {
1803
2165
  alignment: 'left',
1804
2166
  };
1805
2167
  const HeatmapElements = (props) => {
1806
- const height = useHeatmapVizStore((state) => state.iframeHeight);
2168
+ const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2169
+ const setHoveredElement = useHeatmapInteractionStore((state) => state.setHoveredElement);
1807
2170
  const { iframeRef, wrapperRef, visualRef, visualizer, iframeDimensions, isElementSidebarOpen, isVisible = true, areDefaultRanksHidden, isSecondary, ...rest } = props;
1808
2171
  const getRect = useHeatmapElementPosition({
1809
2172
  iframeRef,
@@ -1818,6 +2181,27 @@ const HeatmapElements = (props) => {
1818
2181
  iframeRef,
1819
2182
  getRect,
1820
2183
  });
2184
+ const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
2185
+ useHeatmapMouseHandler({
2186
+ heatmapWrapperRef: wrapperRef,
2187
+ iframeRef,
2188
+ parentRef: visualRef,
2189
+ heatmapInfo: heatmapInfo || {},
2190
+ scaleRatio: 0.8, // 80% zoom
2191
+ onElementHover: (info) => {
2192
+ setHoveredElement({
2193
+ hash: info.hash,
2194
+ clicks: info.clicks,
2195
+ rank: info.rank,
2196
+ selector: info.selector,
2197
+ top: info.top,
2198
+ left: info.left,
2199
+ width: info.width,
2200
+ height: info.height,
2201
+ });
2202
+ console.log(`🚀 🐥 ~ HeatmapElements ~ info:`, info);
2203
+ },
2204
+ });
1821
2205
  useElementCalloutVisible({
1822
2206
  visualRef,
1823
2207
  getRect,
@@ -1833,13 +2217,16 @@ const HeatmapElements = (props) => {
1833
2217
  });
1834
2218
  if (!isVisible)
1835
2219
  return null;
1836
- return (jsxs("div", { onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, className: "gx-hm-elements", style: { ...iframeDimensions, height }, children: [jsx(ElementMissing, { show: showMissingElement }), jsx(DefaultRankBadges, { getRect: getRect, hidden: areDefaultRanksHidden }), jsx(ElementOverlay, { type: "clicked", element: clickedElement, isSecondary: isSecondary }), jsx(ElementOverlay, { type: "hovered", element: hoveredElement, isSecondary: isSecondary, onClick: handleClick }), hoveredElement?.hash !== clickedElement?.hash && hoveredElement && (jsx(ElementCallout, { element: hoveredElement, target: `#${HOVERED_ELEMENT_ID}`, visualRef: visualRef, ...ELEMENT_CALLOUT })), shouldShowCallout && clickedElement && (jsx(ElementCallout, { element: clickedElement, target: `#${CLICKED_ELEMENT_ID}`, visualRef: visualRef, ...ELEMENT_CALLOUT }))] }));
2220
+ return (jsxs("div", { onMouseMove: (event) => {
2221
+ handleMouseMove(event);
2222
+ // handleMouseMove2(event as any);
2223
+ }, onMouseLeave: handleMouseLeave, className: "gx-hm-elements", style: { ...iframeDimensions, height: `${iframeHeight}px` }, children: [jsx(ElementMissing, { show: showMissingElement }), jsx(DefaultRankBadges, { getRect: getRect, hidden: areDefaultRanksHidden }), jsx(ElementOverlay, { type: "clicked", element: clickedElement, isSecondary: isSecondary }), jsx(ElementOverlay, { type: "hovered", element: hoveredElement, isSecondary: isSecondary, onClick: handleClick }), hoveredElement?.hash !== clickedElement?.hash && hoveredElement && (jsx(ElementCallout, { element: hoveredElement, target: `#${HOVERED_ELEMENT_ID}`, visualRef: visualRef, ...ELEMENT_CALLOUT })), shouldShowCallout && clickedElement && (jsx(ElementCallout, { element: clickedElement, target: `#${CLICKED_ELEMENT_ID}`, visualRef: visualRef, ...ELEMENT_CALLOUT }))] }));
1837
2224
  };
1838
2225
 
1839
2226
  const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
1840
2227
  const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
1841
2228
  const contentWidth = useHeatmapConfigStore((state) => state.width);
1842
- const vizRef = useHeatmapVizStore((state) => state.vizRef);
2229
+ const vizRef = useHeatmapSingleStore((state) => state.vizRef);
1843
2230
  const visualizer = {
1844
2231
  get: (hash) => {
1845
2232
  if (vizRef) {
@@ -1904,8 +2291,8 @@ const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHe
1904
2291
 
1905
2292
  const VizDomRenderer = ({ mode = 'heatmap' }) => {
1906
2293
  const width = useHeatmapConfigStore((state) => state.width);
1907
- const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
1908
- const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
2294
+ const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2295
+ const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
1909
2296
  const setSelectedElement = useHeatmapInteractionStore((state) => state.setSelectedElement);
1910
2297
  const wrapperRef = useRef(null);
1911
2298
  const visualRef = useRef(null);
@@ -1915,7 +2302,6 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
1915
2302
  iframeRef,
1916
2303
  visualRef,
1917
2304
  iframeHeight,
1918
- setIframeHeight,
1919
2305
  });
1920
2306
  const contentWidth = width ?? 0;
1921
2307
  const onScroll = (e) => {
@@ -1930,7 +2316,7 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
1930
2316
  useEffect(() => {
1931
2317
  return cleanUp;
1932
2318
  }, []);
1933
- return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [jsx(VizElements, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef }), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no" })] }));
2319
+ return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [jsx(VizElements, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef }), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, scrolling: "no" })] }));
1934
2320
  };
1935
2321
 
1936
2322
  const VizLoading = () => {
@@ -1940,12 +2326,12 @@ const VizLoading = () => {
1940
2326
  const VizDomHeatmap = () => {
1941
2327
  const controls = useHeatmapControlStore((state) => state.controls);
1942
2328
  const isRendering = useHeatmapDataStore((state) => state.isRendering);
1943
- const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
1944
- const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
1945
- const setVizRef = useHeatmapVizStore((state) => state.setVizRef);
2329
+ const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
2330
+ const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
2331
+ const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
1946
2332
  useEffect(() => {
1947
2333
  return () => {
1948
- setVizRef(undefined);
2334
+ setVizRef(null);
1949
2335
  setIframeHeight(0);
1950
2336
  };
1951
2337
  }, []);
@@ -1960,7 +2346,7 @@ const VizLiveRenderer = () => {
1960
2346
  const setIframeHeight = useHeatmapLiveStore((state) => state.setIframeHeight);
1961
2347
  const visualRef = useRef(null);
1962
2348
  const wrapperRef = useRef(null);
1963
- const { iframeRef } = useIframeRender();
2349
+ const { iframeRef } = useVizLiveRender();
1964
2350
  const { scaledHeight, handleScroll } = useHeatmapScale({
1965
2351
  wrapperRef,
1966
2352
  iframeRef,
@@ -1972,13 +2358,15 @@ const VizLiveRenderer = () => {
1972
2358
  const scrollTop = e.currentTarget.scrollTop;
1973
2359
  handleScroll(scrollTop);
1974
2360
  };
1975
- return (jsx(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, iframeHeight: iframeHeight, onScroll: onScroll, children: jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no", sandbox: "allow-scripts allow-same-origin" }) }));
2361
+ return (jsx(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, iframeHeight: iframeHeight, onScroll: onScroll, children: jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth,
2362
+ // height={iframeHeight}
2363
+ scrolling: "no", sandbox: "allow-scripts allow-same-origin" }) }));
1976
2364
  };
1977
2365
 
1978
2366
  const VizLiveHeatmap = () => {
1979
2367
  const controls = useHeatmapControlStore((state) => state.controls);
1980
2368
  const isRendering = useHeatmapDataStore((state) => state.isRendering);
1981
- const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
2369
+ const iframeHeight = useHeatmapLiveStore((state) => state.iframeHeight);
1982
2370
  const wrapperHeight = useHeatmapLiveStore((state) => state.wrapperHeight);
1983
2371
  const setWrapperHeight = useHeatmapLiveStore((state) => state.setWrapperHeight);
1984
2372
  const reset = useHeatmapLiveStore((state) => state.reset);