@gemx-dev/heatmap-react 3.5.30 → 3.5.31

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 (127) hide show
  1. package/dist/esm/components/Layout/ContentMetricBar.d.ts +2 -0
  2. package/dist/esm/components/Layout/ContentMetricBar.d.ts.map +1 -0
  3. package/dist/esm/components/Layout/ContentToolbar.d.ts +2 -0
  4. package/dist/esm/components/Layout/ContentToolbar.d.ts.map +1 -0
  5. package/dist/esm/components/Layout/ContentTopBar.d.ts +2 -0
  6. package/dist/esm/components/Layout/ContentTopBar.d.ts.map +1 -0
  7. package/dist/esm/components/Layout/HeatmapLayout.d.ts +2 -3
  8. package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
  9. package/dist/esm/components/Layout/LeftSidebar.d.ts +1 -3
  10. package/dist/esm/components/Layout/LeftSidebar.d.ts.map +1 -1
  11. package/dist/esm/components/Layout/WrapperLayout.d.ts +1 -7
  12. package/dist/esm/components/Layout/WrapperLayout.d.ts.map +1 -1
  13. package/dist/esm/components/Layout/WrapperPreview.d.ts +1 -3
  14. package/dist/esm/components/Layout/WrapperPreview.d.ts.map +1 -1
  15. package/dist/esm/components/VizDom/VizDomContainer.d.ts.map +1 -1
  16. package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  17. package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
  18. package/dist/esm/configs/style.d.ts +8 -1
  19. package/dist/esm/configs/style.d.ts.map +1 -1
  20. package/dist/esm/hooks/index.d.ts +1 -0
  21. package/dist/esm/hooks/index.d.ts.map +1 -1
  22. package/dist/esm/hooks/register/index.d.ts +5 -0
  23. package/dist/esm/hooks/register/index.d.ts.map +1 -0
  24. package/dist/esm/hooks/register/useRegisterConfig.d.ts +2 -0
  25. package/dist/esm/hooks/register/useRegisterConfig.d.ts.map +1 -0
  26. package/dist/esm/hooks/register/useRegisterControl.d.ts +3 -0
  27. package/dist/esm/hooks/register/useRegisterControl.d.ts.map +1 -0
  28. package/dist/esm/hooks/register/useRegisterData.d.ts +3 -0
  29. package/dist/esm/hooks/register/useRegisterData.d.ts.map +1 -0
  30. package/dist/esm/hooks/register/useRegisterHeatmap.d.ts +3 -0
  31. package/dist/esm/hooks/register/useRegisterHeatmap.d.ts.map +1 -0
  32. package/dist/esm/hooks/viz-canvas/index.d.ts +2 -0
  33. package/dist/esm/hooks/viz-canvas/index.d.ts.map +1 -0
  34. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts +2 -0
  35. package/dist/esm/hooks/viz-canvas/useClickmap.d.ts.map +1 -0
  36. package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +5 -0
  37. package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -0
  38. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts +2 -0
  39. package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts.map +1 -0
  40. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts +0 -2
  41. package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  42. package/dist/esm/hooks/viz-render/useHeatmapVizRender.d.ts +0 -2
  43. package/dist/esm/hooks/viz-render/useHeatmapVizRender.d.ts.map +1 -1
  44. package/dist/esm/hooks/viz-render/useReplayRender.d.ts +0 -2
  45. package/dist/esm/hooks/viz-render/useReplayRender.d.ts.map +1 -1
  46. package/dist/esm/index.js +261 -184
  47. package/dist/esm/index.mjs +261 -184
  48. package/dist/esm/stores/comp.d.ts +7 -0
  49. package/dist/esm/stores/comp.d.ts.map +1 -0
  50. package/dist/esm/stores/data.d.ts +2 -0
  51. package/dist/esm/stores/data.d.ts.map +1 -1
  52. package/dist/esm/stores/index.d.ts +2 -0
  53. package/dist/esm/stores/index.d.ts.map +1 -1
  54. package/dist/esm/stores/viz.d.ts +6 -0
  55. package/dist/esm/stores/viz.d.ts.map +1 -0
  56. package/dist/esm/types/control.d.ts +8 -0
  57. package/dist/esm/types/control.d.ts.map +1 -0
  58. package/dist/esm/types/heatmap.d.ts +6 -1
  59. package/dist/esm/types/heatmap.d.ts.map +1 -1
  60. package/dist/esm/ui/BoxStack/BoxStack.d.ts +1 -1
  61. package/dist/esm/ui/BoxStack/BoxStack.d.ts.map +1 -1
  62. package/dist/style.css +31 -2
  63. package/dist/umd/components/Layout/ContentMetricBar.d.ts +2 -0
  64. package/dist/umd/components/Layout/ContentMetricBar.d.ts.map +1 -0
  65. package/dist/umd/components/Layout/ContentToolbar.d.ts +2 -0
  66. package/dist/umd/components/Layout/ContentToolbar.d.ts.map +1 -0
  67. package/dist/umd/components/Layout/ContentTopBar.d.ts +2 -0
  68. package/dist/umd/components/Layout/ContentTopBar.d.ts.map +1 -0
  69. package/dist/umd/components/Layout/HeatmapLayout.d.ts +2 -3
  70. package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
  71. package/dist/umd/components/Layout/LeftSidebar.d.ts +1 -3
  72. package/dist/umd/components/Layout/LeftSidebar.d.ts.map +1 -1
  73. package/dist/umd/components/Layout/WrapperLayout.d.ts +1 -7
  74. package/dist/umd/components/Layout/WrapperLayout.d.ts.map +1 -1
  75. package/dist/umd/components/Layout/WrapperPreview.d.ts +1 -3
  76. package/dist/umd/components/Layout/WrapperPreview.d.ts.map +1 -1
  77. package/dist/umd/components/VizDom/VizDomContainer.d.ts.map +1 -1
  78. package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
  79. package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
  80. package/dist/umd/configs/style.d.ts +8 -1
  81. package/dist/umd/configs/style.d.ts.map +1 -1
  82. package/dist/umd/hooks/index.d.ts +1 -0
  83. package/dist/umd/hooks/index.d.ts.map +1 -1
  84. package/dist/umd/hooks/register/index.d.ts +5 -0
  85. package/dist/umd/hooks/register/index.d.ts.map +1 -0
  86. package/dist/umd/hooks/register/useRegisterConfig.d.ts +2 -0
  87. package/dist/umd/hooks/register/useRegisterConfig.d.ts.map +1 -0
  88. package/dist/umd/hooks/register/useRegisterControl.d.ts +3 -0
  89. package/dist/umd/hooks/register/useRegisterControl.d.ts.map +1 -0
  90. package/dist/umd/hooks/register/useRegisterData.d.ts +3 -0
  91. package/dist/umd/hooks/register/useRegisterData.d.ts.map +1 -0
  92. package/dist/umd/hooks/register/useRegisterHeatmap.d.ts +3 -0
  93. package/dist/umd/hooks/register/useRegisterHeatmap.d.ts.map +1 -0
  94. package/dist/umd/hooks/viz-canvas/index.d.ts +2 -0
  95. package/dist/umd/hooks/viz-canvas/index.d.ts.map +1 -0
  96. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts +2 -0
  97. package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -0
  98. package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +5 -0
  99. package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -0
  100. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts +2 -0
  101. package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts.map +1 -0
  102. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts +0 -2
  103. package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
  104. package/dist/umd/hooks/viz-render/useHeatmapVizRender.d.ts +0 -2
  105. package/dist/umd/hooks/viz-render/useHeatmapVizRender.d.ts.map +1 -1
  106. package/dist/umd/hooks/viz-render/useReplayRender.d.ts +0 -2
  107. package/dist/umd/hooks/viz-render/useReplayRender.d.ts.map +1 -1
  108. package/dist/umd/index.js +1 -1
  109. package/dist/umd/stores/comp.d.ts +7 -0
  110. package/dist/umd/stores/comp.d.ts.map +1 -0
  111. package/dist/umd/stores/data.d.ts +2 -0
  112. package/dist/umd/stores/data.d.ts.map +1 -1
  113. package/dist/umd/stores/index.d.ts +2 -0
  114. package/dist/umd/stores/index.d.ts.map +1 -1
  115. package/dist/umd/stores/viz.d.ts +6 -0
  116. package/dist/umd/stores/viz.d.ts.map +1 -0
  117. package/dist/umd/types/control.d.ts +8 -0
  118. package/dist/umd/types/control.d.ts.map +1 -0
  119. package/dist/umd/types/heatmap.d.ts +6 -1
  120. package/dist/umd/types/heatmap.d.ts.map +1 -1
  121. package/dist/umd/ui/BoxStack/BoxStack.d.ts +1 -1
  122. package/dist/umd/ui/BoxStack/BoxStack.d.ts.map +1 -1
  123. package/package.json +1 -1
  124. package/dist/esm/components/Layout/ContentHeader.d.ts +0 -4
  125. package/dist/esm/components/Layout/ContentHeader.d.ts.map +0 -1
  126. package/dist/umd/components/Layout/ContentHeader.d.ts +0 -4
  127. package/dist/umd/components/Layout/ContentHeader.d.ts.map +0 -1
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { useNodesState, ReactFlow, Controls, Background } from '@xyflow/react';
4
- import { useEffect, useMemo, useState, useCallback, useRef, Fragment as Fragment$1 } from 'react';
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
7
 
@@ -37,15 +37,71 @@ const GraphView = ({ children, width, height }) => {
37
37
  return (jsxs(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, onNodesChange: onNodesChange, debug: true, minZoom: 0.5, maxZoom: 2, fitView: true, children: [jsx(Controls, {}), jsx(Background, {})] }));
38
38
  };
39
39
 
40
+ const HEATMAP_IFRAME = {
41
+ id: 'clarity-iframe',
42
+ title: 'Clarity Session Replay',
43
+ sandbox: 'allow-same-origin allow-scripts',
44
+ scrolling: 'no',
45
+ height: '100%',
46
+ };
47
+
48
+ const HEATMAP_CONFIG = {
49
+ padding: 8,
50
+ borderWidth: 1,
51
+ borderColor: 'red',
52
+ };
53
+ const HEATMAP_STYLE = {
54
+ viz: {
55
+ background: '#f3f3f3',
56
+ paddingBottom: `${HEATMAP_CONFIG.padding}px`,
57
+ },
58
+ wrapper: {
59
+ padding: `${HEATMAP_CONFIG.padding}px 0`,
60
+ },
61
+ };
62
+ const DEFAULT_SIDEBAR_WIDTH = 260;
63
+
64
+ const useHeatmapControlStore = create()((set, get) => {
65
+ return {
66
+ controls: {
67
+ Sidebar: null,
68
+ TopBar: null,
69
+ Toolbar: null,
70
+ MetricBar: null,
71
+ VizLoading: null,
72
+ },
73
+ registerControl: (key, control) => {
74
+ set({
75
+ controls: {
76
+ ...get().controls,
77
+ [key]: control,
78
+ },
79
+ });
80
+ },
81
+ };
82
+ });
83
+
84
+ var IHeatmapType;
85
+ (function (IHeatmapType) {
86
+ IHeatmapType["Click"] = "click";
87
+ IHeatmapType["Scroll"] = "scroll";
88
+ })(IHeatmapType || (IHeatmapType = {}));
89
+
40
90
  const useHeatmapDataStore = create()((set, get) => {
41
91
  return {
42
92
  data: undefined,
43
93
  clickmap: undefined,
44
- config: undefined,
94
+ config: {
95
+ sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
96
+ width: 1440,
97
+ heatmapType: IHeatmapType.Click,
98
+ },
45
99
  iframeHeight: 0,
46
100
  state: {
47
101
  hideSidebar: false,
48
102
  },
103
+ isRendering: true,
104
+ setIsRendering: (isRendering) => set({ isRendering }),
49
105
  setData: (data) => set({ data }),
50
106
  setClickmap: (clickmap) => set({ clickmap }),
51
107
  setState: (state) => set({ state: { ...get().state, ...state } }),
@@ -55,63 +111,57 @@ const useHeatmapDataStore = create()((set, get) => {
55
111
  };
56
112
  });
57
113
 
58
- const BoxStack = ({ children, ...props }) => {
59
- const id = props.id;
60
- const flexDirection = props.flexDirection;
61
- const overflow = props.overflow || 'hidden';
62
- const position = props.position || 'relative';
63
- const flex = props.flex || 'none';
64
- const justifyContent = props.justifyContent;
65
- const alignItems = props.alignItems;
66
- const style = props.style || {};
67
- const gap = props.gap || 0;
68
- const height = props.height || 'auto';
69
- const styleGap = useMemo(() => {
70
- switch (flexDirection) {
71
- case 'row':
72
- return {
73
- columnGap: gap,
74
- };
75
- case 'column':
76
- return {
77
- rowGap: gap,
78
- };
79
- }
80
- }, [gap, flexDirection]);
81
- const styleProps = {
82
- display: 'flex',
83
- flexDirection,
84
- overflow,
85
- position,
86
- flex,
87
- justifyContent,
88
- alignItems,
89
- height,
90
- ...styleGap,
91
- ...style,
114
+ const useHeatmapVizStore = create()((set, get) => {
115
+ return {
116
+ vizRef: undefined,
117
+ setVizRef: (vizRef) => set({ vizRef }),
92
118
  };
93
- return (jsx("div", { id: id, style: styleProps, children: children }));
94
- };
119
+ });
95
120
 
96
- const ContentHeader = ({ children }) => {
97
- return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", children: children }));
121
+ const useRegisterConfig = () => {
122
+ const config = useHeatmapDataStore((state) => state.config);
123
+ const setIsRendering = useHeatmapDataStore((state) => state.setIsRendering);
124
+ useEffect(() => {
125
+ setIsRendering(true);
126
+ setTimeout(() => {
127
+ setIsRendering(false);
128
+ }, 1000);
129
+ }, [config]);
98
130
  };
99
131
 
100
- const HEATMAP_IFRAME = {
101
- id: 'clarity-iframe',
102
- title: 'Clarity Session Replay',
103
- sandbox: 'allow-same-origin allow-scripts',
104
- scrolling: 'no',
105
- height: '100%',
132
+ const useRegisterControl = (control) => {
133
+ const registerControl = useHeatmapControlStore((state) => state.registerControl);
134
+ registerControl('Sidebar', control.Sidebar);
135
+ registerControl('TopBar', control.TopBar);
136
+ registerControl('Toolbar', control.Toolbar);
137
+ registerControl('MetricBar', control.MetricBar);
138
+ registerControl('VizLoading', control.VizLoading);
106
139
  };
107
140
 
108
- const HEATMAP_CONFIG = {
109
- paddingBlock: 0,
141
+ const useRegisterData = (data) => {
142
+ const setData = useHeatmapDataStore((state) => state.setData);
143
+ const setIsRendering = useHeatmapDataStore((state) => state.setIsRendering);
144
+ const handleSetData = useCallback((data) => {
145
+ if (!data)
146
+ return;
147
+ setData(data);
148
+ setIsRendering(false);
149
+ }, [data]);
150
+ useEffect(() => {
151
+ handleSetData(data);
152
+ }, [data]);
110
153
  };
111
- const HEATMAP_STYLE = {
112
- wrapper: {
113
- padding: `${HEATMAP_CONFIG.paddingBlock}px 0`,
114
- },
154
+
155
+ const useRegisterHeatmap = (clickmap) => {
156
+ const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
157
+ const handleSetClickmap = useCallback((clickmap) => {
158
+ if (!clickmap)
159
+ return;
160
+ setClickmap(clickmap);
161
+ }, [clickmap]);
162
+ useEffect(() => {
163
+ handleSetClickmap(clickmap);
164
+ }, [clickmap]);
115
165
  };
116
166
 
117
167
  const useClickedElement = ({ selectedElement, heatmapInfo, getRect }) => {
@@ -339,107 +389,40 @@ const getElementAtPoint = (doc, x, y) => {
339
389
  return element;
340
390
  };
341
391
 
342
- const recreateIframe = (iframeRef, config) => {
343
- const container = iframeRef.current?.parentElement;
344
- if (!container)
345
- return;
346
- const oldIframe = iframeRef.current;
347
- if (!oldIframe?.contentDocument?.body.innerHTML) {
348
- return oldIframe;
349
- }
350
- if (oldIframe && oldIframe.parentElement) {
351
- oldIframe.parentElement.removeChild(oldIframe);
352
- }
353
- const newIframe = document.createElement('iframe');
354
- newIframe.id = HEATMAP_IFRAME.id;
355
- newIframe.title = HEATMAP_IFRAME.title;
356
- newIframe.sandbox = HEATMAP_IFRAME.sandbox;
357
- newIframe.scrolling = HEATMAP_IFRAME.scrolling;
358
- newIframe.width = config?.width ? `${config.width}px` : '100%';
359
- // Append to container
360
- container.appendChild(newIframe);
361
- // Update ref
362
- iframeRef.current = newIframe;
363
- return newIframe;
364
- };
365
-
366
- function isMobileDevice(userAgent) {
367
- if (!userAgent)
368
- return false;
369
- 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);
370
- }
371
-
372
392
  const useHeatmapRender = () => {
373
393
  const data = useHeatmapDataStore((state) => state.data);
374
394
  const config = useHeatmapDataStore((state) => state.config);
375
- const setConfig = useHeatmapDataStore((state) => state.setConfig);
376
- const clickmap = useHeatmapDataStore((state) => state.clickmap);
377
- const visualizerRef = useRef(null);
395
+ const setVizRef = useHeatmapVizStore((state) => state.setVizRef);
378
396
  const iframeRef = useRef(null);
379
- const initializeVisualizer = useCallback((envelope, userAgent) => {
380
- const iframe = iframeRef.current;
381
- if (!iframe?.contentWindow)
382
- return null;
383
- const visualizer = new Visualizer();
384
- const mobile = isMobileDevice(userAgent);
385
- visualizer.setup(iframe.contentWindow, {
386
- version: envelope.version,
387
- onresize: (width) => {
388
- setConfig({ width });
389
- },
390
- mobile,
391
- vNext: true,
392
- locale: 'en-us',
393
- });
394
- return visualizer;
395
- }, []);
396
- // Process and render heatmap HTML
397
397
  const renderHeatmap = useCallback(async (payloads) => {
398
398
  if (!payloads || payloads.length === 0)
399
399
  return;
400
- let visualizer = new Visualizer();
401
- const iframe = recreateIframe(iframeRef, config);
402
- // const merged = visualizer.merge(payloads);
403
- // setIframeHeight(Number(iframeRef.current?.height || 0));
404
- // for (const decoded of payloads) {
405
- // // Initialize on first sequence
406
- // if (decoded.envelope.sequence === 1) {
407
- // const userAgent = (decoded.dimension?.[0]?.data[0]?.[0] as string) || '';
408
- // visualizer = initializeVisualizer(decoded.envelope as any, userAgent);
409
- // if (!visualizer) return;
410
- // visualizerRef.current = visualizer;
411
- // }
412
- // if (!visualizer) continue;
413
- // // Merge and process DOM
414
- // const merged = visualizer.merge([decoded]);
415
- // visualizer.dom(merged.dom);
416
- // }
417
- // Render static HTML
418
- if (visualizer && iframe?.contentWindow) {
419
- await visualizer.html(payloads, iframe.contentWindow);
420
- visualizerRef.current = visualizer;
421
- }
422
- }, [initializeVisualizer]);
400
+ const visualizer = new Visualizer();
401
+ const iframe = iframeRef.current;
402
+ if (!iframe?.contentWindow)
403
+ return;
404
+ await visualizer.html(payloads, iframe.contentWindow);
405
+ setVizRef(visualizer);
406
+ }, []);
423
407
  useEffect(() => {
424
408
  if (!data || data.length === 0)
425
409
  return;
426
410
  renderHeatmap(data);
427
411
  return () => {
428
- visualizerRef.current = null;
412
+ setVizRef(null);
429
413
  };
430
- }, [config, data, renderHeatmap]);
431
- useEffect(() => {
432
- if (!visualizerRef.current || !clickmap || clickmap.length === 0)
433
- return;
434
- visualizerRef.current.clearmap();
435
- visualizerRef.current?.clickmap(clickmap);
436
- }, [clickmap]);
414
+ }, [config, data]);
437
415
  return {
438
416
  iframeRef,
439
- clarityVisualizer: visualizerRef.current,
440
417
  };
441
418
  };
442
419
 
420
+ function isMobileDevice(userAgent) {
421
+ if (!userAgent)
422
+ return false;
423
+ 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);
424
+ }
425
+
443
426
  function sortEvents(a, b) {
444
427
  return a.time - b.time;
445
428
  }
@@ -558,7 +541,6 @@ const useReplayRender = () => {
558
541
  return {
559
542
  iframeRef,
560
543
  isPlaying: isPlayingRef.current,
561
- clarityVisualizer: visualizerRef.current,
562
544
  play,
563
545
  pause,
564
546
  };
@@ -645,16 +627,15 @@ const useIframeHeight = (props) => {
645
627
  console.warn('Cannot measure iframe content:', error);
646
628
  }
647
629
  }, [iframeRef, setIframeHeight]);
648
- // Trigger height update when content width changes
649
630
  useEffect(() => {
631
+ console.log(`🚀 🐥 ~ useIframeHeight ~ contentWidth:`, contentWidth);
650
632
  if (contentWidth > 0) {
651
- // Delay to allow iframe content to reflow after width change
652
633
  const timeoutId = setTimeout(() => {
653
634
  updateIframeHeight();
654
635
  }, 100);
655
636
  return () => clearTimeout(timeoutId);
656
637
  }
657
- }, [contentWidth, updateIframeHeight]);
638
+ }, [contentWidth]);
658
639
  useEffect(() => {
659
640
  const iframe = iframeRef.current;
660
641
  if (!iframe)
@@ -718,7 +699,7 @@ const useScaleCalculation = (props) => {
718
699
  const [scale, setScale] = useState(1);
719
700
  useEffect(() => {
720
701
  if (containerWidth > 0 && contentWidth > 0) {
721
- const availableWidth = containerWidth - HEATMAP_CONFIG['paddingBlock'] * 2;
702
+ const availableWidth = containerWidth - HEATMAP_CONFIG['padding'] * 2;
722
703
  const calculatedScale = Math.min(availableWidth / contentWidth, 1);
723
704
  setScale(calculatedScale);
724
705
  }
@@ -771,6 +752,84 @@ const useHeatmapScale = (props) => {
771
752
  };
772
753
  };
773
754
 
755
+ const BoxStack = ({ children, ...props }) => {
756
+ const id = props.id;
757
+ const flexDirection = props.flexDirection;
758
+ const overflow = props.overflow || 'hidden';
759
+ const position = props.position || 'relative';
760
+ const flex = props.flex || 'none';
761
+ const justifyContent = props.justifyContent;
762
+ const alignItems = props.alignItems;
763
+ const style = props.style || {};
764
+ const gap = props.gap || 0;
765
+ const height = props.height || 'auto';
766
+ const styleGap = useMemo(() => {
767
+ switch (flexDirection) {
768
+ case 'row':
769
+ return {
770
+ columnGap: gap,
771
+ };
772
+ case 'column':
773
+ return {
774
+ rowGap: gap,
775
+ };
776
+ }
777
+ }, [gap, flexDirection]);
778
+ const styleProps = {
779
+ display: 'flex',
780
+ flexDirection,
781
+ overflow,
782
+ position,
783
+ flex,
784
+ justifyContent,
785
+ alignItems,
786
+ height,
787
+ ...styleGap,
788
+ ...style,
789
+ };
790
+ return (jsx("div", { id: id, style: styleProps, children: children }));
791
+ };
792
+
793
+ const ContentTopBar = () => {
794
+ const controls = useHeatmapControlStore((state) => state.controls);
795
+ return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", style: {
796
+ borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
797
+ }, children: controls.TopBar ?? null }));
798
+ };
799
+
800
+ const useClickmap = () => {
801
+ const [isInitialized, setIsInitialized] = useState(false);
802
+ const clickmap = useHeatmapDataStore((state) => state.clickmap);
803
+ const vizRef = useHeatmapVizStore((state) => state.vizRef);
804
+ useEffect(() => {
805
+ if (isInitialized)
806
+ return;
807
+ if (!vizRef || !clickmap || clickmap.length === 0)
808
+ return;
809
+ vizRef.clearmap();
810
+ vizRef?.clickmap(clickmap);
811
+ setIsInitialized(true);
812
+ }, [vizRef, clickmap]);
813
+ return {};
814
+ };
815
+
816
+ const useScrollmap = () => {
817
+ useHeatmapDataStore((state) => state.clickmap);
818
+ return {};
819
+ };
820
+
821
+ const useHeatmapVizCanvas = ({ type }) => {
822
+ const heatmapRender = useMemo(() => {
823
+ switch (type) {
824
+ case IHeatmapType.Click:
825
+ return useClickmap;
826
+ case IHeatmapType.Scroll:
827
+ return useScrollmap;
828
+ }
829
+ }, [type]);
830
+ return heatmapRender?.();
831
+ };
832
+
774
833
  const CLICKED_ELEMENT_ID = 'clickedElement';
775
834
  const SECONDARY_CLICKED_ELEMENT_ID = 'secondaryClickedElementID';
776
835
  const HOVERED_ELEMENT_ID = 'hoveredElement';
@@ -842,7 +901,7 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
842
901
  setPosition({ top, left, placement });
843
902
  };
844
903
  // Initial calculation
845
- calculatePosition();
904
+ // calculatePosition();
846
905
  // Recalculate on scroll/resize
847
906
  const handleUpdate = () => {
848
907
  requestAnimationFrame(calculatePosition);
@@ -1018,6 +1077,7 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
1018
1077
  if (!portalContainer) {
1019
1078
  return jsx(Fragment, {});
1020
1079
  }
1080
+ return jsx(Fragment, {});
1021
1081
  // return createPortal(calloutContent, portalContainer);
1022
1082
  };
1023
1083
 
@@ -1195,6 +1255,7 @@ const ReplayControls = () => {
1195
1255
 
1196
1256
  const VizDomRenderer = ({ mode = 'heatmap' }) => {
1197
1257
  const config = useHeatmapDataStore((state) => state.config);
1258
+ const setIframeHeight = useHeatmapDataStore((state) => state.setIframeHeight);
1198
1259
  const wrapperRef = useRef(null);
1199
1260
  const visualRef = useRef(null);
1200
1261
  const { iframeRef } = useHeatmapVizRender(mode);
@@ -1207,45 +1268,65 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
1207
1268
  const scrollTop = e.currentTarget.scrollTop;
1208
1269
  handleScroll(scrollTop);
1209
1270
  };
1271
+ useHeatmapVizCanvas({ type: config?.heatmapType });
1272
+ const cleanUp = () => {
1273
+ setIframeHeight(0);
1274
+ };
1275
+ useEffect(() => {
1276
+ return cleanUp;
1277
+ }, []);
1210
1278
  return (jsxs("div", { ref: visualRef, className: "gx-hm-visual", onScroll: onScroll, style: {
1211
1279
  overflow: 'hidden auto',
1212
1280
  display: 'flex',
1213
1281
  position: 'relative',
1214
1282
  justifyContent: 'center',
1215
1283
  flex: 1,
1216
- backgroundColor: '#fff',
1217
- // borderRadius: '8px',
1284
+ paddingBottom: HEATMAP_STYLE['viz']['paddingBottom'],
1285
+ background: HEATMAP_STYLE['viz']['background'],
1218
1286
  }, children: [jsx("div", { className: "gx-hm-visual-unscaled", style: {
1219
1287
  width: '100%',
1220
1288
  display: 'flex',
1221
1289
  justifyContent: 'center',
1222
1290
  alignItems: 'flex-start',
1223
- height: scaledHeight > 0 ? `${scaledHeight + HEATMAP_CONFIG['paddingBlock']}px` : 'auto',
1291
+ height: scaledHeight > 0 ? `${scaledHeight + HEATMAP_CONFIG['padding']}px` : 'auto',
1224
1292
  padding: HEATMAP_STYLE['wrapper']['padding'],
1293
+ borderRadius: '8px',
1225
1294
  }, children: jsxs("div", { className: "gx-hm-wrapper", ref: wrapperRef, style: {
1226
1295
  width: contentWidth,
1227
1296
  height: iframeHeight,
1228
1297
  transform: `scale(${scale})`,
1229
1298
  transformOrigin: 'top center',
1230
- }, children: [jsx(VizElements, { width: contentWidth, height: iframeHeight, widthScale: scale, iframeRef: iframeRef, wrapperRef: wrapperRef }), jsx("iframe", {
1231
- // key={iframeKey}
1232
- ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no" })] }) }), mode === 'replay' && jsx(ReplayControls, {})] }));
1299
+ 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, {})] }));
1233
1301
  };
1234
1302
 
1235
1303
  const VizDomContainer = () => {
1304
+ const controls = useHeatmapControlStore((state) => state.controls);
1305
+ const isRendering = useHeatmapDataStore((state) => state.isRendering);
1306
+ if (isRendering)
1307
+ return controls.VizLoading ?? null;
1236
1308
  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: {
1237
- // margin: '0px 16px 0px 12px',
1238
- // borderRadius: '8px 8px 0 0',
1239
- // borderRadius: '8px',
1240
1309
  minWidth: '394px',
1241
- padding: '12px',
1242
- background: '#f1f1f1',
1243
1310
  }, children: jsx(VizDomRenderer, {}) }) }));
1244
1311
  };
1245
1312
 
1246
- const SIDEBAR_WIDTH = 280;
1247
- const LeftSidebar = ({ children }) => {
1313
+ const ContentMetricBar = () => {
1314
+ const controls = useHeatmapControlStore((state) => state.controls);
1315
+ return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", style: {
1316
+ borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
1317
+ }, children: controls.MetricBar ?? null }));
1318
+ };
1319
+
1320
+ const ContentToolbar = () => {
1321
+ 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 }));
1323
+ };
1324
+
1325
+ const LeftSidebar = () => {
1326
+ const controls = useHeatmapControlStore((state) => state.controls);
1248
1327
  const isHideSidebar = useHeatmapDataStore((state) => state.state.hideSidebar);
1328
+ const config = useHeatmapDataStore((state) => state.config);
1329
+ const sidebarWidth = config?.sidebarWidth || DEFAULT_SIDEBAR_WIDTH;
1249
1330
  if (isHideSidebar) {
1250
1331
  return null;
1251
1332
  }
@@ -1258,41 +1339,37 @@ const LeftSidebar = ({ children }) => {
1258
1339
  transform: 'translateX(-100%)',
1259
1340
  visibility: 'hidden',
1260
1341
  }
1261
- : { width: `${SIDEBAR_WIDTH}px` }),
1262
- }, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: { height: '100%', width: `${SIDEBAR_WIDTH}px` }, children: children }) }));
1342
+ : { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
1343
+ }, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
1344
+ height: '100%',
1345
+ width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
1346
+ borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
1347
+ }, children: controls.Sidebar ?? null }) }));
1263
1348
  };
1264
1349
 
1265
- const WrapperPreview = ({ children }) => {
1266
- return (jsxs("div", { className: "gx-hm-container", style: { display: 'flex', overflowY: 'hidden', flex: '1', position: 'relative' }, children: [jsx(LeftSidebar, { children: children }), jsx(VizDomContainer, {})] }));
1350
+ const WrapperPreview = () => {
1351
+ return (jsxs(BoxStack, { id: "gx-hm-container", flexDirection: "row", overflow: "hidden", flex: "1", position: "relative", children: [jsx(LeftSidebar, {}), jsxs(BoxStack, { flexDirection: "column", flex: "1", children: [jsx(ContentMetricBar, {}), jsx(VizDomContainer, {}), jsx(ContentToolbar, {})] })] }));
1267
1352
  };
1268
1353
 
1269
- const WrapperLayout = ({ header, toolbar, sidebar }) => {
1270
- return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentHeader, { children: header }), jsx(ContentHeader, { children: toolbar }), jsx(WrapperPreview, { children: sidebar })] }));
1354
+ const WrapperLayout = () => {
1355
+ return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(WrapperPreview, {})] }));
1271
1356
  };
1272
1357
 
1273
- const HeatmapLayout = ({ data, clickmap, header, toolbar, sidebar, }) => {
1274
- const setData = useHeatmapDataStore((state) => state.setData);
1275
- const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
1276
- const handleSetClickmap = useCallback((clickmap) => {
1277
- if (!clickmap)
1278
- return;
1279
- setClickmap(clickmap);
1280
- }, [clickmap]);
1281
- const handleSetData = useCallback((data) => {
1282
- if (!data)
1283
- return;
1284
- setData(data);
1285
- }, [data]);
1286
- useEffect(() => {
1287
- handleSetData(data);
1288
- }, [data]);
1289
- useEffect(() => {
1290
- handleSetClickmap(clickmap);
1291
- }, [clickmap]);
1292
- return (jsxs(Fragment, { children: [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: {
1293
- minHeight: '100%',
1294
- display: 'flex',
1295
- }, children: jsx(WrapperLayout, { header: header, toolbar: toolbar, sidebar: sidebar }) }) }) }), jsx("div", { id: "gx-hm-project-portal" })] }));
1358
+ const HeatmapLayout = ({ data, clickmap, controls }) => {
1359
+ useRegisterControl(controls);
1360
+ useRegisterData(data);
1361
+ useRegisterHeatmap(clickmap);
1362
+ useRegisterConfig();
1363
+ 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: {
1364
+ minHeight: '100%',
1365
+ display: 'flex',
1366
+ }, children: jsx(WrapperLayout, {}) }) }) }));
1367
+ function getVariableStyle() {
1368
+ return {
1369
+ '--gx-hm-border-width': `${HEATMAP_CONFIG.borderWidth}px`,
1370
+ '--gx-hm-border-color': `${HEATMAP_CONFIG.borderColor}`,
1371
+ };
1372
+ }
1296
1373
  };
1297
1374
 
1298
1375
  var PanelContent;
@@ -1313,4 +1390,4 @@ var ErrorType;
1313
1390
  ErrorType["DataError"] = "DataError";
1314
1391
  })(ErrorType || (ErrorType = {}));
1315
1392
 
1316
- export { GraphView, HeatmapLayout, useHeatmapDataStore };
1393
+ export { GraphView, HeatmapLayout, IHeatmapType, useHeatmapDataStore };
@@ -0,0 +1,7 @@
1
+ import { IHeatmapControl } from '../types/control';
2
+ export interface IHeatmapControlStore {
3
+ controls: IHeatmapControl;
4
+ registerControl: (key: keyof IHeatmapControl, control: IHeatmapControl[keyof IHeatmapControl]) => void;
5
+ }
6
+ export declare const useHeatmapControlStore: import("zustand").UseBoundStore<import("zustand").StoreApi<IHeatmapControlStore>>;
7
+ //# sourceMappingURL=comp.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"comp.d.ts","sourceRoot":"","sources":["../../src/stores/comp.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,eAAe,CAAC;IAC1B,eAAe,EAAE,CACf,GAAG,EAAE,MAAM,eAAe,EAC1B,OAAO,EAAE,eAAe,CAAC,MAAM,eAAe,CAAC,KAC5C,IAAI,CAAC;CACX;AAED,eAAO,MAAM,sBAAsB,mFAkBjC,CAAC"}
@@ -8,6 +8,8 @@ export interface IHeatmapDataStore {
8
8
  config?: IHeatmapConfig;
9
9
  iframeHeight: number;
10
10
  state: IHeatmapState;
11
+ isRendering: boolean;
12
+ setIsRendering: (isRendering: boolean) => void;
11
13
  setState: (state: IHeatmapState) => void;
12
14
  setData: (data: DecodedPayload[]) => void;
13
15
  setClickmap: (clickmap: ClickMapPoint[]) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/stores/data.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEzE,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC1C,WAAW,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IACjD,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3C,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,cAAc,EAAE,KAAK,EAAE,cAAc,CAAC,MAAM,cAAc,CAAC,KAAK,IAAI,CAAC;IAC9F,eAAe,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,mBAAmB,gFAgB9B,CAAC"}
1
+ {"version":3,"file":"data.d.ts","sourceRoot":"","sources":["../../src/stores/data.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,cAAc,EAAgB,MAAM,UAAU,CAAC;AACvF,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,CAAC,EAAE,cAAc,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,OAAO,CAAC;IACrB,cAAc,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IAC/C,QAAQ,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IACzC,OAAO,EAAE,CAAC,IAAI,EAAE,cAAc,EAAE,KAAK,IAAI,CAAC;IAC1C,WAAW,EAAE,CAAC,QAAQ,EAAE,aAAa,EAAE,KAAK,IAAI,CAAC;IACjD,SAAS,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAC3C,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,cAAc,EAAE,KAAK,EAAE,cAAc,CAAC,MAAM,cAAc,CAAC,KAAK,IAAI,CAAC;IAC9F,eAAe,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED,eAAO,MAAM,mBAAmB,gFAsB9B,CAAC"}
@@ -1,2 +1,4 @@
1
+ export * from './comp';
1
2
  export * from './data';
3
+ export * from './viz';
2
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/stores/index.ts"],"names":[],"mappings":"AAAA,cAAc,QAAQ,CAAC;AACvB,cAAc,QAAQ,CAAC;AACvB,cAAc,OAAO,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface IHeatmapVizStore {
2
+ vizRef: any;
3
+ setVizRef: (vizRef: any) => void;
4
+ }
5
+ export declare const useHeatmapVizStore: import("zustand").UseBoundStore<import("zustand").StoreApi<IHeatmapVizStore>>;
6
+ //# sourceMappingURL=viz.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"viz.d.ts","sourceRoot":"","sources":["../../src/stores/viz.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,GAAG,CAAC;IACZ,SAAS,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;CAClC;AAED,eAAO,MAAM,kBAAkB,+EAK7B,CAAC"}
@@ -0,0 +1,8 @@
1
+ export interface IHeatmapControl {
2
+ Sidebar: React.ReactNode;
3
+ Toolbar: React.ReactNode;
4
+ TopBar: React.ReactNode;
5
+ MetricBar: React.ReactNode;
6
+ VizLoading: React.ReactNode;
7
+ }
8
+ //# sourceMappingURL=control.d.ts.map