@gemx-dev/heatmap-react 3.5.30 → 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.
- package/dist/esm/components/Layout/ContentMetricBar.d.ts +2 -0
- package/dist/esm/components/Layout/ContentMetricBar.d.ts.map +1 -0
- package/dist/esm/components/Layout/ContentToolbar.d.ts +2 -0
- package/dist/esm/components/Layout/ContentToolbar.d.ts.map +1 -0
- package/dist/esm/components/Layout/ContentTopBar.d.ts +2 -0
- package/dist/esm/components/Layout/ContentTopBar.d.ts.map +1 -0
- package/dist/esm/components/Layout/HeatmapLayout.d.ts +4 -4
- package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/esm/components/Layout/LeftSidebar.d.ts +1 -3
- package/dist/esm/components/Layout/LeftSidebar.d.ts.map +1 -1
- package/dist/esm/components/Layout/WrapperLayout.d.ts +1 -7
- package/dist/esm/components/Layout/WrapperLayout.d.ts.map +1 -1
- package/dist/esm/components/Layout/WrapperPreview.d.ts +1 -3
- package/dist/esm/components/Layout/WrapperPreview.d.ts.map +1 -1
- package/dist/esm/components/VizDom/VizDomContainer.d.ts.map +1 -1
- package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
- package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
- package/dist/esm/components/VizElement/HeatmapElements.d.ts +0 -3
- package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
- package/dist/esm/components/VizElement/VizElements.d.ts +0 -1
- package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
- package/dist/esm/configs/style.d.ts +8 -1
- package/dist/esm/configs/style.d.ts.map +1 -1
- package/dist/esm/hooks/index.d.ts +1 -0
- package/dist/esm/hooks/index.d.ts.map +1 -1
- package/dist/esm/hooks/register/index.d.ts +5 -0
- package/dist/esm/hooks/register/index.d.ts.map +1 -0
- package/dist/esm/hooks/register/useRegisterConfig.d.ts +2 -0
- package/dist/esm/hooks/register/useRegisterConfig.d.ts.map +1 -0
- package/dist/esm/hooks/register/useRegisterControl.d.ts +3 -0
- package/dist/esm/hooks/register/useRegisterControl.d.ts.map +1 -0
- package/dist/esm/hooks/register/useRegisterData.d.ts +3 -0
- package/dist/esm/hooks/register/useRegisterData.d.ts.map +1 -0
- package/dist/esm/hooks/register/useRegisterHeatmap.d.ts +3 -0
- package/dist/esm/hooks/register/useRegisterHeatmap.d.ts.map +1 -0
- package/dist/esm/hooks/vix-elements/useClickedElement.d.ts +1 -2
- package/dist/esm/hooks/vix-elements/useClickedElement.d.ts.map +1 -1
- package/dist/esm/hooks/vix-elements/useHeatmapEffects.d.ts +1 -2
- package/dist/esm/hooks/vix-elements/useHeatmapEffects.d.ts.map +1 -1
- package/dist/esm/hooks/vix-elements/useHeatmapElementPosition.d.ts +1 -2
- package/dist/esm/hooks/vix-elements/useHeatmapElementPosition.d.ts.map +1 -1
- package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts +3 -4
- package/dist/esm/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
- package/dist/esm/hooks/viz-canvas/index.d.ts +2 -0
- package/dist/esm/hooks/viz-canvas/index.d.ts.map +1 -0
- package/dist/esm/hooks/viz-canvas/useClickmap.d.ts +2 -0
- package/dist/esm/hooks/viz-canvas/useClickmap.d.ts.map +1 -0
- package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +5 -0
- package/dist/esm/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -0
- package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts +2 -0
- package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts.map +1 -0
- package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts +0 -2
- package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-render/useHeatmapVizRender.d.ts +0 -2
- package/dist/esm/hooks/viz-render/useHeatmapVizRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-render/useReplayRender.d.ts +0 -2
- package/dist/esm/hooks/viz-render/useReplayRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useContentDimensions.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useIframeHeight.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -1
- package/dist/esm/index.js +329 -275
- package/dist/esm/index.mjs +329 -275
- package/dist/esm/stores/comp.d.ts +7 -0
- package/dist/esm/stores/comp.d.ts.map +1 -0
- package/dist/esm/stores/data.d.ts +5 -8
- package/dist/esm/stores/data.d.ts.map +1 -1
- package/dist/esm/stores/index.d.ts +3 -0
- package/dist/esm/stores/index.d.ts.map +1 -1
- package/dist/esm/stores/interaction.d.ts +14 -0
- package/dist/esm/stores/interaction.d.ts.map +1 -0
- package/dist/esm/stores/viz.d.ts +10 -0
- package/dist/esm/stores/viz.d.ts.map +1 -0
- package/dist/esm/types/control.d.ts +8 -0
- package/dist/esm/types/control.d.ts.map +1 -0
- package/dist/esm/types/heatmap.d.ts +6 -1
- package/dist/esm/types/heatmap.d.ts.map +1 -1
- package/dist/esm/ui/BoxStack/BoxStack.d.ts +1 -1
- package/dist/esm/ui/BoxStack/BoxStack.d.ts.map +1 -1
- package/dist/style.css +31 -2
- package/dist/umd/components/Layout/ContentMetricBar.d.ts +2 -0
- package/dist/umd/components/Layout/ContentMetricBar.d.ts.map +1 -0
- package/dist/umd/components/Layout/ContentToolbar.d.ts +2 -0
- package/dist/umd/components/Layout/ContentToolbar.d.ts.map +1 -0
- package/dist/umd/components/Layout/ContentTopBar.d.ts +2 -0
- package/dist/umd/components/Layout/ContentTopBar.d.ts.map +1 -0
- package/dist/umd/components/Layout/HeatmapLayout.d.ts +4 -4
- package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/umd/components/Layout/LeftSidebar.d.ts +1 -3
- package/dist/umd/components/Layout/LeftSidebar.d.ts.map +1 -1
- package/dist/umd/components/Layout/WrapperLayout.d.ts +1 -7
- package/dist/umd/components/Layout/WrapperLayout.d.ts.map +1 -1
- package/dist/umd/components/Layout/WrapperPreview.d.ts +1 -3
- package/dist/umd/components/Layout/WrapperPreview.d.ts.map +1 -1
- package/dist/umd/components/VizDom/VizDomContainer.d.ts.map +1 -1
- package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
- package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
- package/dist/umd/components/VizElement/HeatmapElements.d.ts +0 -3
- package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
- package/dist/umd/components/VizElement/VizElements.d.ts +0 -1
- package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
- package/dist/umd/configs/style.d.ts +8 -1
- package/dist/umd/configs/style.d.ts.map +1 -1
- package/dist/umd/hooks/index.d.ts +1 -0
- package/dist/umd/hooks/index.d.ts.map +1 -1
- package/dist/umd/hooks/register/index.d.ts +5 -0
- package/dist/umd/hooks/register/index.d.ts.map +1 -0
- package/dist/umd/hooks/register/useRegisterConfig.d.ts +2 -0
- package/dist/umd/hooks/register/useRegisterConfig.d.ts.map +1 -0
- package/dist/umd/hooks/register/useRegisterControl.d.ts +3 -0
- package/dist/umd/hooks/register/useRegisterControl.d.ts.map +1 -0
- package/dist/umd/hooks/register/useRegisterData.d.ts +3 -0
- package/dist/umd/hooks/register/useRegisterData.d.ts.map +1 -0
- package/dist/umd/hooks/register/useRegisterHeatmap.d.ts +3 -0
- package/dist/umd/hooks/register/useRegisterHeatmap.d.ts.map +1 -0
- package/dist/umd/hooks/vix-elements/useClickedElement.d.ts +1 -2
- package/dist/umd/hooks/vix-elements/useClickedElement.d.ts.map +1 -1
- package/dist/umd/hooks/vix-elements/useHeatmapEffects.d.ts +1 -2
- package/dist/umd/hooks/vix-elements/useHeatmapEffects.d.ts.map +1 -1
- package/dist/umd/hooks/vix-elements/useHeatmapElementPosition.d.ts +1 -2
- package/dist/umd/hooks/vix-elements/useHeatmapElementPosition.d.ts.map +1 -1
- package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts +3 -4
- package/dist/umd/hooks/vix-elements/useHoveredElement.d.ts.map +1 -1
- package/dist/umd/hooks/viz-canvas/index.d.ts +2 -0
- package/dist/umd/hooks/viz-canvas/index.d.ts.map +1 -0
- package/dist/umd/hooks/viz-canvas/useClickmap.d.ts +2 -0
- package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -0
- package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts +5 -0
- package/dist/umd/hooks/viz-canvas/useHeatmapVizCanvas.d.ts.map +1 -0
- package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts +2 -0
- package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts.map +1 -0
- package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts +0 -2
- package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-render/useHeatmapVizRender.d.ts +0 -2
- package/dist/umd/hooks/viz-render/useHeatmapVizRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-render/useReplayRender.d.ts +0 -2
- package/dist/umd/hooks/viz-render/useReplayRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useContentDimensions.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useIframeHeight.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/dist/umd/stores/comp.d.ts +7 -0
- package/dist/umd/stores/comp.d.ts.map +1 -0
- package/dist/umd/stores/data.d.ts +5 -8
- package/dist/umd/stores/data.d.ts.map +1 -1
- package/dist/umd/stores/index.d.ts +3 -0
- package/dist/umd/stores/index.d.ts.map +1 -1
- package/dist/umd/stores/interaction.d.ts +14 -0
- package/dist/umd/stores/interaction.d.ts.map +1 -0
- package/dist/umd/stores/viz.d.ts +10 -0
- package/dist/umd/stores/viz.d.ts.map +1 -0
- package/dist/umd/types/control.d.ts +8 -0
- package/dist/umd/types/control.d.ts.map +1 -0
- package/dist/umd/types/heatmap.d.ts +6 -1
- package/dist/umd/types/heatmap.d.ts.map +1 -1
- package/dist/umd/ui/BoxStack/BoxStack.d.ts +1 -1
- package/dist/umd/ui/BoxStack/BoxStack.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/esm/components/Layout/ContentHeader.d.ts +0 -4
- package/dist/esm/components/Layout/ContentHeader.d.ts.map +0 -1
- package/dist/umd/components/Layout/ContentHeader.d.ts +0 -4
- package/dist/umd/components/Layout/ContentHeader.d.ts.map +0 -1
package/dist/esm/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
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,
|
|
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 }) => {
|
|
@@ -37,88 +38,162 @@ const GraphView = ({ children, width, height }) => {
|
|
|
37
38
|
return (jsxs(ReactFlow, { nodes: nodes, nodeTypes: nodeTypes, onNodesChange: onNodesChange, debug: true, minZoom: 0.5, maxZoom: 2, fitView: true, children: [jsx(Controls, {}), jsx(Background, {})] }));
|
|
38
39
|
};
|
|
39
40
|
|
|
41
|
+
const HEATMAP_IFRAME = {
|
|
42
|
+
id: 'clarity-iframe',
|
|
43
|
+
title: 'Clarity Session Replay',
|
|
44
|
+
sandbox: 'allow-same-origin allow-scripts',
|
|
45
|
+
scrolling: 'no',
|
|
46
|
+
height: '100%',
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const HEATMAP_CONFIG = {
|
|
50
|
+
padding: 8,
|
|
51
|
+
borderWidth: 1,
|
|
52
|
+
borderColor: '#E3E3E3',
|
|
53
|
+
};
|
|
54
|
+
const HEATMAP_STYLE = {
|
|
55
|
+
viz: {
|
|
56
|
+
background: '#f3f3f3',
|
|
57
|
+
paddingBottom: `${HEATMAP_CONFIG.padding}px`,
|
|
58
|
+
},
|
|
59
|
+
wrapper: {
|
|
60
|
+
padding: `${HEATMAP_CONFIG.padding}px 0`,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
const DEFAULT_SIDEBAR_WIDTH = 260;
|
|
64
|
+
|
|
65
|
+
const useHeatmapControlStore = create()((set, get) => {
|
|
66
|
+
return {
|
|
67
|
+
controls: {
|
|
68
|
+
Sidebar: null,
|
|
69
|
+
TopBar: null,
|
|
70
|
+
Toolbar: null,
|
|
71
|
+
MetricBar: null,
|
|
72
|
+
VizLoading: null,
|
|
73
|
+
},
|
|
74
|
+
registerControl: (key, control) => {
|
|
75
|
+
set({
|
|
76
|
+
controls: {
|
|
77
|
+
...get().controls,
|
|
78
|
+
[key]: control,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
var IHeatmapType;
|
|
86
|
+
(function (IHeatmapType) {
|
|
87
|
+
IHeatmapType["Click"] = "click";
|
|
88
|
+
IHeatmapType["Scroll"] = "scroll";
|
|
89
|
+
})(IHeatmapType || (IHeatmapType = {}));
|
|
90
|
+
|
|
40
91
|
const useHeatmapDataStore = create()((set, get) => {
|
|
41
92
|
return {
|
|
42
93
|
data: undefined,
|
|
43
94
|
clickmap: undefined,
|
|
44
|
-
config:
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
95
|
+
config: {
|
|
96
|
+
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
|
|
97
|
+
width: 1440,
|
|
98
|
+
heatmapType: IHeatmapType.Click,
|
|
48
99
|
},
|
|
100
|
+
dataInfo: undefined,
|
|
101
|
+
isRendering: true,
|
|
102
|
+
setIsRendering: (isRendering) => set({ isRendering }),
|
|
103
|
+
setDataInfo: (dataInfo) => set({ dataInfo }),
|
|
49
104
|
setData: (data) => set({ data }),
|
|
50
105
|
setClickmap: (clickmap) => set({ clickmap }),
|
|
51
|
-
setState: (state) => set({ state: { ...get().state, ...state } }),
|
|
52
106
|
setConfig: (value) => set({ config: { ...get().config, ...value } }),
|
|
53
107
|
setConfigBy: (key, value) => set({ config: { ...get().config, [key]: value } }),
|
|
54
|
-
setIframeHeight: (iframeHeight) => set({ iframeHeight }),
|
|
55
108
|
};
|
|
56
109
|
});
|
|
57
110
|
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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,
|
|
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 }),
|
|
92
121
|
};
|
|
93
|
-
|
|
94
|
-
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const useHeatmapVizStore = create()((set, get) => {
|
|
125
|
+
return {
|
|
126
|
+
scale: 1,
|
|
127
|
+
vizRef: undefined,
|
|
128
|
+
iframeHeight: 0,
|
|
129
|
+
setScale: (scale) => set({ scale }),
|
|
130
|
+
setVizRef: (vizRef) => set({ vizRef }),
|
|
131
|
+
setIframeHeight: (iframeHeight) => set({ iframeHeight }),
|
|
132
|
+
};
|
|
133
|
+
});
|
|
95
134
|
|
|
96
|
-
const
|
|
97
|
-
|
|
135
|
+
const useRegisterConfig = () => {
|
|
136
|
+
const config = useHeatmapDataStore((state) => state.config);
|
|
137
|
+
const setIsRendering = useHeatmapDataStore((state) => state.setIsRendering);
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
setIsRendering(true);
|
|
140
|
+
setTimeout(() => {
|
|
141
|
+
setIsRendering(false);
|
|
142
|
+
}, 1000);
|
|
143
|
+
}, [config]);
|
|
98
144
|
};
|
|
99
145
|
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
146
|
+
const useRegisterControl = (control) => {
|
|
147
|
+
const registerControl = useHeatmapControlStore((state) => state.registerControl);
|
|
148
|
+
registerControl('Sidebar', control.Sidebar);
|
|
149
|
+
registerControl('TopBar', control.TopBar);
|
|
150
|
+
registerControl('Toolbar', control.Toolbar);
|
|
151
|
+
registerControl('MetricBar', control.MetricBar);
|
|
152
|
+
registerControl('VizLoading', control.VizLoading);
|
|
106
153
|
};
|
|
107
154
|
|
|
108
|
-
const
|
|
109
|
-
|
|
155
|
+
const useRegisterData = (data, dataInfo) => {
|
|
156
|
+
const setData = useHeatmapDataStore((state) => state.setData);
|
|
157
|
+
const setIsRendering = useHeatmapDataStore((state) => state.setIsRendering);
|
|
158
|
+
const setDataInfo = useHeatmapDataStore((state) => state.setDataInfo);
|
|
159
|
+
const handleSetData = useCallback((data) => {
|
|
160
|
+
if (!data)
|
|
161
|
+
return;
|
|
162
|
+
setData(data);
|
|
163
|
+
setIsRendering(false);
|
|
164
|
+
}, [data]);
|
|
165
|
+
const handleSetDataInfo = useCallback((dataInfo) => {
|
|
166
|
+
if (!dataInfo)
|
|
167
|
+
return;
|
|
168
|
+
setDataInfo(dataInfo);
|
|
169
|
+
}, [setDataInfo]);
|
|
170
|
+
useEffect(() => {
|
|
171
|
+
handleSetData(data);
|
|
172
|
+
}, [data]);
|
|
173
|
+
useEffect(() => {
|
|
174
|
+
handleSetDataInfo(dataInfo);
|
|
175
|
+
}, [dataInfo]);
|
|
110
176
|
};
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
177
|
+
|
|
178
|
+
const useRegisterHeatmap = (clickmap) => {
|
|
179
|
+
const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
|
|
180
|
+
const handleSetClickmap = useCallback((clickmap) => {
|
|
181
|
+
if (!clickmap)
|
|
182
|
+
return;
|
|
183
|
+
setClickmap(clickmap);
|
|
184
|
+
}, [clickmap]);
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
handleSetClickmap(clickmap);
|
|
187
|
+
}, [clickmap]);
|
|
115
188
|
};
|
|
116
189
|
|
|
117
|
-
const useClickedElement = ({
|
|
190
|
+
const useClickedElement = ({ heatmapInfo, getRect }) => {
|
|
191
|
+
const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
|
|
118
192
|
const [clickedElement, setClickedElement] = useState(null);
|
|
119
193
|
const [showMissingElement, setShowMissingElement] = useState(false);
|
|
120
194
|
const [shouldShowCallout, setShouldShowCallout] = useState(false);
|
|
121
195
|
useEffect(() => {
|
|
196
|
+
console.log(`🚀 🐥 ~ useClickedElement ~ heatmapInfo?.elementMapInfo:`, heatmapInfo?.elementMapInfo);
|
|
122
197
|
if (!selectedElement || !heatmapInfo?.elementMapInfo) {
|
|
123
198
|
setClickedElement(null);
|
|
124
199
|
setShowMissingElement(false);
|
|
@@ -162,7 +237,8 @@ const useClickedElement = ({ selectedElement, heatmapInfo, getRect }) => {
|
|
|
162
237
|
return { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout };
|
|
163
238
|
};
|
|
164
239
|
|
|
165
|
-
const useHeatmapEffects = ({ isVisible, isElementSidebarOpen,
|
|
240
|
+
const useHeatmapEffects = ({ isVisible, isElementSidebarOpen, setShouldShowCallout, resetAll, }) => {
|
|
241
|
+
const selectedElement = useHeatmapInteractionStore((state) => state.selectedElement);
|
|
166
242
|
// Reset khi ẩn
|
|
167
243
|
useEffect(() => {
|
|
168
244
|
if (!isVisible)
|
|
@@ -195,10 +271,6 @@ function getElementLayout(element) {
|
|
|
195
271
|
function formatPercentage(value, decimals = 2) {
|
|
196
272
|
return value.toFixed(decimals);
|
|
197
273
|
}
|
|
198
|
-
function getSimpleSelector(selector) {
|
|
199
|
-
const parts = selector.split(' > ');
|
|
200
|
-
return parts[parts.length - 1] || selector;
|
|
201
|
-
}
|
|
202
274
|
function calculateRankPosition(rect, widthScale) {
|
|
203
275
|
const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
|
|
204
276
|
const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
|
|
@@ -209,7 +281,8 @@ function calculateRankPosition(rect, widthScale) {
|
|
|
209
281
|
};
|
|
210
282
|
}
|
|
211
283
|
|
|
212
|
-
const useHeatmapElementPosition = ({ iframeRef, parentRef, visualizer, heatmapWidth,
|
|
284
|
+
const useHeatmapElementPosition = ({ iframeRef, parentRef, visualizer, heatmapWidth, widthScale, projectId, }) => {
|
|
285
|
+
const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
|
|
213
286
|
return useCallback((element) => {
|
|
214
287
|
const hash = element?.hash;
|
|
215
288
|
if (!iframeRef.current?.contentDocument || !hash || !visualizer)
|
|
@@ -254,8 +327,10 @@ const debounce = (fn, delay) => {
|
|
|
254
327
|
timeout = setTimeout(() => fn(...args), delay);
|
|
255
328
|
};
|
|
256
329
|
};
|
|
257
|
-
const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect
|
|
258
|
-
const
|
|
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);
|
|
259
334
|
const handleMouseMove = useCallback(debounce((event) => {
|
|
260
335
|
if (!iframeRef.current?.contentDocument || !heatmapInfo?.elementMapInfo) {
|
|
261
336
|
setHoveredElement(null);
|
|
@@ -264,8 +339,6 @@ const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect, onSele
|
|
|
264
339
|
const iframe = iframeRef.current;
|
|
265
340
|
const iframeRect = iframe.getBoundingClientRect();
|
|
266
341
|
let x = event.clientX - iframeRect.left;
|
|
267
|
-
console.log(`🚀 🐥 ~ useHoveredElement ~ iframeRect.left:`, iframeRect.left);
|
|
268
|
-
console.log(`🚀 🐥 ~ useHoveredElement ~ event.clientX:`, event.clientX);
|
|
269
342
|
let y = event.clientY - iframeRect.top;
|
|
270
343
|
if (widthScale !== 1) {
|
|
271
344
|
x /= widthScale;
|
|
@@ -276,9 +349,7 @@ const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect, onSele
|
|
|
276
349
|
setHoveredElement(null);
|
|
277
350
|
return;
|
|
278
351
|
}
|
|
279
|
-
let targetElement = null;
|
|
280
|
-
// Best: dùng caretPositionFromPoint nếu có (Chrome, Safari)
|
|
281
|
-
targetElement = getElementAtPoint(doc, x, y);
|
|
352
|
+
let targetElement = getElementAtPoint(doc, x, y) || null;
|
|
282
353
|
if (!targetElement) {
|
|
283
354
|
targetElement = doc.elementFromPoint(x, y);
|
|
284
355
|
}
|
|
@@ -286,7 +357,6 @@ const useHoveredElement = ({ iframeRef, heatmapInfo, widthScale, getRect, onSele
|
|
|
286
357
|
setHoveredElement(null);
|
|
287
358
|
return;
|
|
288
359
|
}
|
|
289
|
-
// Lấy hash từ nhiều attribute khả dĩ
|
|
290
360
|
const hash = targetElement.getAttribute('data-clarity-hash') ||
|
|
291
361
|
targetElement.getAttribute('data-clarity-hashalpha') ||
|
|
292
362
|
targetElement.getAttribute('data-clarity-hashbeta');
|
|
@@ -339,107 +409,40 @@ const getElementAtPoint = (doc, x, y) => {
|
|
|
339
409
|
return element;
|
|
340
410
|
};
|
|
341
411
|
|
|
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
412
|
const useHeatmapRender = () => {
|
|
373
413
|
const data = useHeatmapDataStore((state) => state.data);
|
|
374
414
|
const config = useHeatmapDataStore((state) => state.config);
|
|
375
|
-
const
|
|
376
|
-
const clickmap = useHeatmapDataStore((state) => state.clickmap);
|
|
377
|
-
const visualizerRef = useRef(null);
|
|
415
|
+
const setVizRef = useHeatmapVizStore((state) => state.setVizRef);
|
|
378
416
|
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
417
|
const renderHeatmap = useCallback(async (payloads) => {
|
|
398
418
|
if (!payloads || payloads.length === 0)
|
|
399
419
|
return;
|
|
400
|
-
|
|
401
|
-
const iframe =
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
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]);
|
|
420
|
+
const visualizer = new Visualizer();
|
|
421
|
+
const iframe = iframeRef.current;
|
|
422
|
+
if (!iframe?.contentWindow)
|
|
423
|
+
return;
|
|
424
|
+
await visualizer.html(payloads, iframe.contentWindow);
|
|
425
|
+
setVizRef(visualizer);
|
|
426
|
+
}, []);
|
|
423
427
|
useEffect(() => {
|
|
424
428
|
if (!data || data.length === 0)
|
|
425
429
|
return;
|
|
426
430
|
renderHeatmap(data);
|
|
427
431
|
return () => {
|
|
428
|
-
|
|
432
|
+
setVizRef(null);
|
|
429
433
|
};
|
|
430
|
-
}, [config, data
|
|
431
|
-
useEffect(() => {
|
|
432
|
-
if (!visualizerRef.current || !clickmap || clickmap.length === 0)
|
|
433
|
-
return;
|
|
434
|
-
visualizerRef.current.clearmap();
|
|
435
|
-
visualizerRef.current?.clickmap(clickmap);
|
|
436
|
-
}, [clickmap]);
|
|
434
|
+
}, [config, data]);
|
|
437
435
|
return {
|
|
438
436
|
iframeRef,
|
|
439
|
-
clarityVisualizer: visualizerRef.current,
|
|
440
437
|
};
|
|
441
438
|
};
|
|
442
439
|
|
|
440
|
+
function isMobileDevice(userAgent) {
|
|
441
|
+
if (!userAgent)
|
|
442
|
+
return false;
|
|
443
|
+
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);
|
|
444
|
+
}
|
|
445
|
+
|
|
443
446
|
function sortEvents(a, b) {
|
|
444
447
|
return a.time - b.time;
|
|
445
448
|
}
|
|
@@ -558,7 +561,6 @@ const useReplayRender = () => {
|
|
|
558
561
|
return {
|
|
559
562
|
iframeRef,
|
|
560
563
|
isPlaying: isPlayingRef.current,
|
|
561
|
-
clarityVisualizer: visualizerRef.current,
|
|
562
564
|
play,
|
|
563
565
|
pause,
|
|
564
566
|
};
|
|
@@ -607,23 +609,21 @@ const useContainerDimensions = (props) => {
|
|
|
607
609
|
|
|
608
610
|
const useContentDimensions = (props) => {
|
|
609
611
|
const { iframeRef, config } = props;
|
|
610
|
-
const [contentWidth, setContentWidth] = useState(0);
|
|
611
612
|
useEffect(() => {
|
|
612
|
-
if (config?.width)
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
}
|
|
613
|
+
if (!config?.width)
|
|
614
|
+
return;
|
|
615
|
+
if (!iframeRef.current)
|
|
616
|
+
return;
|
|
617
|
+
iframeRef.current.width = `${config.width}px`;
|
|
618
618
|
}, [config?.width, iframeRef]);
|
|
619
|
-
return { contentWidth };
|
|
619
|
+
return { contentWidth: config?.width ?? 0 };
|
|
620
620
|
};
|
|
621
621
|
|
|
622
622
|
// Hook 3: Iframe Height Observer
|
|
623
623
|
const useIframeHeight = (props) => {
|
|
624
624
|
const { iframeRef, contentWidth } = props;
|
|
625
|
-
const iframeHeight =
|
|
626
|
-
const setIframeHeight =
|
|
625
|
+
const iframeHeight = useHeatmapVizStore((state) => state.iframeHeight);
|
|
626
|
+
const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
|
|
627
627
|
const resizeObserverRef = useRef(null);
|
|
628
628
|
const mutationObserverRef = useRef(null);
|
|
629
629
|
const updateIframeHeight = useCallback(() => {
|
|
@@ -645,16 +645,14 @@ const useIframeHeight = (props) => {
|
|
|
645
645
|
console.warn('Cannot measure iframe content:', error);
|
|
646
646
|
}
|
|
647
647
|
}, [iframeRef, setIframeHeight]);
|
|
648
|
-
// Trigger height update when content width changes
|
|
649
648
|
useEffect(() => {
|
|
650
649
|
if (contentWidth > 0) {
|
|
651
|
-
// Delay to allow iframe content to reflow after width change
|
|
652
650
|
const timeoutId = setTimeout(() => {
|
|
653
651
|
updateIframeHeight();
|
|
654
652
|
}, 100);
|
|
655
653
|
return () => clearTimeout(timeoutId);
|
|
656
654
|
}
|
|
657
|
-
}, [contentWidth
|
|
655
|
+
}, [contentWidth]);
|
|
658
656
|
useEffect(() => {
|
|
659
657
|
const iframe = iframeRef.current;
|
|
660
658
|
if (!iframe)
|
|
@@ -714,11 +712,12 @@ const useIframeHeight = (props) => {
|
|
|
714
712
|
};
|
|
715
713
|
|
|
716
714
|
const useScaleCalculation = (props) => {
|
|
715
|
+
const scale = useHeatmapVizStore((state) => state.scale);
|
|
716
|
+
const setScale = useHeatmapVizStore((state) => state.setScale);
|
|
717
717
|
const { containerWidth, contentWidth } = props;
|
|
718
|
-
const [scale, setScale] = useState(1);
|
|
719
718
|
useEffect(() => {
|
|
720
719
|
if (containerWidth > 0 && contentWidth > 0) {
|
|
721
|
-
const availableWidth = containerWidth - HEATMAP_CONFIG['
|
|
720
|
+
const availableWidth = containerWidth - HEATMAP_CONFIG['padding'] * 2;
|
|
722
721
|
const calculatedScale = Math.min(availableWidth / contentWidth, 1);
|
|
723
722
|
setScale(calculatedScale);
|
|
724
723
|
}
|
|
@@ -771,6 +770,84 @@ const useHeatmapScale = (props) => {
|
|
|
771
770
|
};
|
|
772
771
|
};
|
|
773
772
|
|
|
773
|
+
const BoxStack = ({ children, ...props }) => {
|
|
774
|
+
const id = props.id;
|
|
775
|
+
const flexDirection = props.flexDirection;
|
|
776
|
+
const overflow = props.overflow || 'hidden';
|
|
777
|
+
const position = props.position || 'relative';
|
|
778
|
+
const flex = props.flex || 'none';
|
|
779
|
+
const justifyContent = props.justifyContent;
|
|
780
|
+
const alignItems = props.alignItems;
|
|
781
|
+
const style = props.style || {};
|
|
782
|
+
const gap = props.gap || 0;
|
|
783
|
+
const height = props.height || 'auto';
|
|
784
|
+
const styleGap = useMemo(() => {
|
|
785
|
+
switch (flexDirection) {
|
|
786
|
+
case 'row':
|
|
787
|
+
return {
|
|
788
|
+
columnGap: gap,
|
|
789
|
+
};
|
|
790
|
+
case 'column':
|
|
791
|
+
return {
|
|
792
|
+
rowGap: gap,
|
|
793
|
+
};
|
|
794
|
+
}
|
|
795
|
+
}, [gap, flexDirection]);
|
|
796
|
+
const styleProps = {
|
|
797
|
+
display: 'flex',
|
|
798
|
+
flexDirection,
|
|
799
|
+
overflow,
|
|
800
|
+
position,
|
|
801
|
+
flex,
|
|
802
|
+
justifyContent,
|
|
803
|
+
alignItems,
|
|
804
|
+
height,
|
|
805
|
+
...styleGap,
|
|
806
|
+
...style,
|
|
807
|
+
};
|
|
808
|
+
return (jsx("div", { id: id, style: styleProps, children: children }));
|
|
809
|
+
};
|
|
810
|
+
|
|
811
|
+
const ContentTopBar = () => {
|
|
812
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
813
|
+
return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", style: {
|
|
814
|
+
borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
815
|
+
}, children: controls.TopBar ?? null }));
|
|
816
|
+
};
|
|
817
|
+
|
|
818
|
+
const useClickmap = () => {
|
|
819
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
820
|
+
const clickmap = useHeatmapDataStore((state) => state.clickmap);
|
|
821
|
+
const vizRef = useHeatmapVizStore((state) => state.vizRef);
|
|
822
|
+
useEffect(() => {
|
|
823
|
+
if (isInitialized)
|
|
824
|
+
return;
|
|
825
|
+
if (!vizRef || !clickmap || clickmap.length === 0)
|
|
826
|
+
return;
|
|
827
|
+
vizRef.clearmap();
|
|
828
|
+
vizRef?.clickmap(clickmap);
|
|
829
|
+
setIsInitialized(true);
|
|
830
|
+
}, [vizRef, clickmap]);
|
|
831
|
+
return {};
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
const useScrollmap = () => {
|
|
835
|
+
useHeatmapDataStore((state) => state.clickmap);
|
|
836
|
+
return {};
|
|
837
|
+
};
|
|
838
|
+
|
|
839
|
+
const useHeatmapVizCanvas = ({ type }) => {
|
|
840
|
+
const heatmapRender = useMemo(() => {
|
|
841
|
+
switch (type) {
|
|
842
|
+
case IHeatmapType.Click:
|
|
843
|
+
return useClickmap;
|
|
844
|
+
case IHeatmapType.Scroll:
|
|
845
|
+
return useScrollmap;
|
|
846
|
+
}
|
|
847
|
+
}, [type]);
|
|
848
|
+
return heatmapRender?.();
|
|
849
|
+
};
|
|
850
|
+
|
|
774
851
|
const CLICKED_ELEMENT_ID = 'clickedElement';
|
|
775
852
|
const SECONDARY_CLICKED_ELEMENT_ID = 'secondaryClickedElementID';
|
|
776
853
|
const HOVERED_ELEMENT_ID = 'hoveredElement';
|
|
@@ -784,7 +861,6 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
|
|
|
784
861
|
placement: 'top',
|
|
785
862
|
});
|
|
786
863
|
const percentage = formatPercentage(((element.clicks ?? 0) / totalClicks) * 100, 2);
|
|
787
|
-
// Calculate callout position
|
|
788
864
|
useEffect(() => {
|
|
789
865
|
const targetElement = document.querySelector(target);
|
|
790
866
|
const calloutElement = calloutRef.current;
|
|
@@ -800,30 +876,25 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
|
|
|
800
876
|
let top = 0;
|
|
801
877
|
let left = 0;
|
|
802
878
|
let placement = 'top';
|
|
803
|
-
// Try positions in order: top, bottom, right, left
|
|
804
879
|
const positions = [
|
|
805
|
-
// Top
|
|
806
880
|
{
|
|
807
881
|
placement: 'top',
|
|
808
882
|
top: targetRect.top - calloutRect.height - padding - arrowSize,
|
|
809
883
|
left: targetRect.left + targetRect.width / 2 - calloutRect.width / 2,
|
|
810
884
|
valid: targetRect.top - calloutRect.height - padding - arrowSize > 0,
|
|
811
885
|
},
|
|
812
|
-
// Bottom
|
|
813
886
|
{
|
|
814
887
|
placement: 'bottom',
|
|
815
888
|
top: targetRect.bottom + padding + arrowSize,
|
|
816
889
|
left: targetRect.left + targetRect.width / 2 - calloutRect.width / 2,
|
|
817
890
|
valid: targetRect.bottom + calloutRect.height + padding + arrowSize < viewportHeight,
|
|
818
891
|
},
|
|
819
|
-
// Right
|
|
820
892
|
{
|
|
821
893
|
placement: 'right',
|
|
822
894
|
top: targetRect.top + targetRect.height / 2 - calloutRect.height / 2,
|
|
823
895
|
left: targetRect.right + padding + arrowSize,
|
|
824
896
|
valid: targetRect.right + calloutRect.width + padding + arrowSize < viewportWidth,
|
|
825
897
|
},
|
|
826
|
-
// Left
|
|
827
898
|
{
|
|
828
899
|
placement: 'left',
|
|
829
900
|
top: targetRect.top + targetRect.height / 2 - calloutRect.height / 2,
|
|
@@ -841,9 +912,7 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
|
|
|
841
912
|
top = Math.max(padding, Math.min(top, viewportHeight - calloutRect.height - padding));
|
|
842
913
|
setPosition({ top, left, placement });
|
|
843
914
|
};
|
|
844
|
-
// Initial calculation
|
|
845
915
|
calculatePosition();
|
|
846
|
-
// Recalculate on scroll/resize
|
|
847
916
|
const handleUpdate = () => {
|
|
848
917
|
requestAnimationFrame(calculatePosition);
|
|
849
918
|
};
|
|
@@ -856,12 +925,12 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
|
|
|
856
925
|
parentRef?.current?.removeEventListener('scroll', handleUpdate);
|
|
857
926
|
};
|
|
858
927
|
}, [target, parentRef]);
|
|
859
|
-
(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: {
|
|
860
929
|
position: 'fixed',
|
|
861
930
|
top: position.top,
|
|
862
931
|
left: position.left,
|
|
863
932
|
zIndex: 2147483647,
|
|
864
|
-
}, "aria-live": "assertive", role: "tooltip", children: [jsx("div", { className: "clarity-callout__arrow" }),
|
|
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: `
|
|
865
934
|
.clarity-callout {
|
|
866
935
|
background: white;
|
|
867
936
|
border-radius: 8px;
|
|
@@ -1005,20 +1074,7 @@ const ElementCallout = ({ element, target, totalClicks, isSecondary, isRecording
|
|
|
1005
1074
|
height: 14px;
|
|
1006
1075
|
}
|
|
1007
1076
|
` })] }));
|
|
1008
|
-
|
|
1009
|
-
useEffect(() => {
|
|
1010
|
-
const container = document.querySelector('#gx-hm-project-portal');
|
|
1011
|
-
if (container instanceof HTMLElement) {
|
|
1012
|
-
setPortalContainer(container);
|
|
1013
|
-
}
|
|
1014
|
-
else {
|
|
1015
|
-
console.warn('Portal container #gx-hm-project-portal not found!');
|
|
1016
|
-
}
|
|
1017
|
-
}, []);
|
|
1018
|
-
if (!portalContainer) {
|
|
1019
|
-
return jsx(Fragment, {});
|
|
1020
|
-
}
|
|
1021
|
-
// return createPortal(calloutContent, portalContainer);
|
|
1077
|
+
return createPortal(calloutContent, document.body);
|
|
1022
1078
|
};
|
|
1023
1079
|
|
|
1024
1080
|
const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
|
|
@@ -1079,84 +1135,64 @@ const MissingElementMessage = ({ widthScale }) => {
|
|
|
1079
1135
|
};
|
|
1080
1136
|
|
|
1081
1137
|
const HeatmapElements = (props) => {
|
|
1082
|
-
const
|
|
1138
|
+
const height = useHeatmapVizStore((state) => state.iframeHeight);
|
|
1139
|
+
const { heatmapInfo, iframeRef, parentRef, visualizer, widthScale, iframeDimensions, isElementSidebarOpen, isVisible = true, areDefaultRanksHidden, isSecondary, ...rest } = props;
|
|
1083
1140
|
const getRect = useHeatmapElementPosition({
|
|
1084
1141
|
iframeRef,
|
|
1085
1142
|
parentRef,
|
|
1086
1143
|
visualizer,
|
|
1087
1144
|
heatmapWidth: heatmapInfo?.width,
|
|
1088
|
-
iframeHeight,
|
|
1089
1145
|
widthScale,
|
|
1090
1146
|
projectId: props.projectId,
|
|
1091
1147
|
});
|
|
1092
1148
|
const { clickedElement, showMissingElement, shouldShowCallout, setShouldShowCallout } = useClickedElement({
|
|
1093
|
-
selectedElement,
|
|
1094
1149
|
heatmapInfo,
|
|
1095
1150
|
getRect,
|
|
1096
1151
|
});
|
|
1152
|
+
console.log(`🚀 🐥 ~ HeatmapElements ~ clickedElement:`, clickedElement);
|
|
1097
1153
|
const { hoveredElement, handleMouseMove, handleMouseLeave, handleClick } = useHoveredElement({
|
|
1098
1154
|
iframeRef,
|
|
1099
1155
|
heatmapInfo,
|
|
1100
1156
|
getRect,
|
|
1101
|
-
onSelect: selectElement,
|
|
1102
1157
|
widthScale,
|
|
1103
1158
|
});
|
|
1104
1159
|
const resetAll = () => {
|
|
1105
|
-
// nếu cần reset thêm state ở đây
|
|
1106
1160
|
// setShouldShowCallout(false);
|
|
1107
1161
|
};
|
|
1108
1162
|
useHeatmapEffects({
|
|
1109
1163
|
isVisible,
|
|
1110
1164
|
isElementSidebarOpen,
|
|
1111
|
-
selectedElement,
|
|
1112
1165
|
setShouldShowCallout,
|
|
1113
1166
|
resetAll,
|
|
1114
1167
|
});
|
|
1115
1168
|
if (!isVisible)
|
|
1116
1169
|
return null;
|
|
1117
1170
|
const top10 = heatmapInfo?.sortedElements?.slice(0, 10) ?? [];
|
|
1118
|
-
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 }))] }));
|
|
1119
1172
|
};
|
|
1120
1173
|
|
|
1121
|
-
const VizElements = ({ width,
|
|
1122
|
-
const heatmapInfo =
|
|
1123
|
-
sortedElements: [
|
|
1124
|
-
{
|
|
1125
|
-
hash: '9ebwu6a3',
|
|
1126
|
-
selector: 'Join our email list',
|
|
1127
|
-
},
|
|
1128
|
-
{
|
|
1129
|
-
hash: '350hde5d4',
|
|
1130
|
-
selector: 'Products',
|
|
1131
|
-
},
|
|
1132
|
-
],
|
|
1133
|
-
elementMapInfo: {
|
|
1134
|
-
'9ebwu6a3': {
|
|
1135
|
-
totalclicks: 4,
|
|
1136
|
-
hash: '9ebwu6a3',
|
|
1137
|
-
},
|
|
1138
|
-
'350hde5d4': {
|
|
1139
|
-
totalclicks: 4,
|
|
1140
|
-
hash: '350hde5d4',
|
|
1141
|
-
},
|
|
1142
|
-
},
|
|
1143
|
-
totalClicks: 8,
|
|
1144
|
-
};
|
|
1174
|
+
const VizElements = ({ width, iframeRef, wrapperRef, widthScale, }) => {
|
|
1175
|
+
const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
|
|
1145
1176
|
const visualizer = {
|
|
1146
1177
|
get: (hash) => {
|
|
1147
1178
|
const doc = iframeRef.current?.contentDocument;
|
|
1148
1179
|
if (!doc)
|
|
1149
1180
|
return null;
|
|
1150
|
-
|
|
1151
|
-
|
|
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;
|
|
1152
1190
|
},
|
|
1153
1191
|
};
|
|
1154
|
-
const [selectedElement, setSelectedElement] = useState(null);
|
|
1155
1192
|
if (!iframeRef.current)
|
|
1156
1193
|
return null;
|
|
1157
|
-
return (jsx(HeatmapElements, { visualizer: visualizer, iframeRef: iframeRef, parentRef: wrapperRef,
|
|
1194
|
+
return (jsx(HeatmapElements, { visualizer: visualizer, iframeRef: iframeRef, parentRef: wrapperRef, widthScale: widthScale, heatmapInfo: heatmapInfo, isVisible: true, iframeDimensions: {
|
|
1158
1195
|
width,
|
|
1159
|
-
height,
|
|
1160
1196
|
position: 'absolute',
|
|
1161
1197
|
top: 0,
|
|
1162
1198
|
left: 0,
|
|
@@ -1195,6 +1231,7 @@ const ReplayControls = () => {
|
|
|
1195
1231
|
|
|
1196
1232
|
const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
1197
1233
|
const config = useHeatmapDataStore((state) => state.config);
|
|
1234
|
+
const setIframeHeight = useHeatmapVizStore((state) => state.setIframeHeight);
|
|
1198
1235
|
const wrapperRef = useRef(null);
|
|
1199
1236
|
const visualRef = useRef(null);
|
|
1200
1237
|
const { iframeRef } = useHeatmapVizRender(mode);
|
|
@@ -1207,45 +1244,66 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
|
1207
1244
|
const scrollTop = e.currentTarget.scrollTop;
|
|
1208
1245
|
handleScroll(scrollTop);
|
|
1209
1246
|
};
|
|
1247
|
+
useHeatmapVizCanvas({ type: config?.heatmapType });
|
|
1248
|
+
const cleanUp = () => {
|
|
1249
|
+
setIframeHeight(0);
|
|
1250
|
+
};
|
|
1251
|
+
useEffect(() => {
|
|
1252
|
+
return cleanUp;
|
|
1253
|
+
}, []);
|
|
1210
1254
|
return (jsxs("div", { ref: visualRef, className: "gx-hm-visual", onScroll: onScroll, style: {
|
|
1211
1255
|
overflow: 'hidden auto',
|
|
1212
1256
|
display: 'flex',
|
|
1213
1257
|
position: 'relative',
|
|
1214
1258
|
justifyContent: 'center',
|
|
1215
1259
|
flex: 1,
|
|
1216
|
-
|
|
1217
|
-
|
|
1260
|
+
// paddingBottom: HEATMAP_STYLE['viz']['paddingBottom'],
|
|
1261
|
+
background: '#fff',
|
|
1218
1262
|
}, children: [jsx("div", { className: "gx-hm-visual-unscaled", style: {
|
|
1219
1263
|
width: '100%',
|
|
1220
1264
|
display: 'flex',
|
|
1221
1265
|
justifyContent: 'center',
|
|
1222
1266
|
alignItems: 'flex-start',
|
|
1223
|
-
height: scaledHeight > 0 ? `${scaledHeight + HEATMAP_CONFIG['
|
|
1267
|
+
height: scaledHeight > 0 ? `${scaledHeight + HEATMAP_CONFIG['padding'] * 2}px` : 'auto',
|
|
1224
1268
|
padding: HEATMAP_STYLE['wrapper']['padding'],
|
|
1269
|
+
paddingBottom: HEATMAP_STYLE['viz']['paddingBottom'],
|
|
1270
|
+
background: HEATMAP_STYLE['viz']['background'],
|
|
1225
1271
|
}, children: jsxs("div", { className: "gx-hm-wrapper", ref: wrapperRef, style: {
|
|
1226
1272
|
width: contentWidth,
|
|
1227
1273
|
height: iframeHeight,
|
|
1228
1274
|
transform: `scale(${scale})`,
|
|
1229
1275
|
transformOrigin: 'top center',
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, height: iframeHeight, scrolling: "no" })] }) }), mode === 'replay' && jsx(ReplayControls, {})] }));
|
|
1276
|
+
paddingBlockEnd: '0',
|
|
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, {})] }));
|
|
1233
1278
|
};
|
|
1234
1279
|
|
|
1235
1280
|
const VizDomContainer = () => {
|
|
1281
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
1282
|
+
const isRendering = useHeatmapDataStore((state) => state.isRendering);
|
|
1283
|
+
if (isRendering)
|
|
1284
|
+
return controls.VizLoading ?? null;
|
|
1236
1285
|
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
1286
|
minWidth: '394px',
|
|
1241
|
-
padding: '12px',
|
|
1242
|
-
background: '#f1f1f1',
|
|
1243
1287
|
}, children: jsx(VizDomRenderer, {}) }) }));
|
|
1244
1288
|
};
|
|
1245
1289
|
|
|
1246
|
-
const
|
|
1247
|
-
const
|
|
1248
|
-
|
|
1290
|
+
const ContentMetricBar = () => {
|
|
1291
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
1292
|
+
return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", style: {
|
|
1293
|
+
borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
1294
|
+
}, children: controls.MetricBar ?? null }));
|
|
1295
|
+
};
|
|
1296
|
+
|
|
1297
|
+
const ContentToolbar = () => {
|
|
1298
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
1299
|
+
return (jsx("div", { id: "gx-hm-content-toolbar", style: { position: 'absolute', bottom: 0, left: '8px', right: '24px', padding: '8px' }, children: controls.Toolbar ?? null }));
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
const LeftSidebar = () => {
|
|
1303
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
1304
|
+
const isHideSidebar = useHeatmapInteractionStore((state) => state.state.hideSidebar);
|
|
1305
|
+
const config = useHeatmapDataStore((state) => state.config);
|
|
1306
|
+
const sidebarWidth = config?.sidebarWidth || DEFAULT_SIDEBAR_WIDTH;
|
|
1249
1307
|
if (isHideSidebar) {
|
|
1250
1308
|
return null;
|
|
1251
1309
|
}
|
|
@@ -1258,41 +1316,37 @@ const LeftSidebar = ({ children }) => {
|
|
|
1258
1316
|
transform: 'translateX(-100%)',
|
|
1259
1317
|
visibility: 'hidden',
|
|
1260
1318
|
}
|
|
1261
|
-
: { width: `${
|
|
1262
|
-
}, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
|
|
1319
|
+
: { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
|
|
1320
|
+
}, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
|
|
1321
|
+
height: '100%',
|
|
1322
|
+
width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
|
|
1323
|
+
borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
1324
|
+
}, children: controls.Sidebar ?? null }) }));
|
|
1263
1325
|
};
|
|
1264
1326
|
|
|
1265
|
-
const WrapperPreview = (
|
|
1266
|
-
return (jsxs(
|
|
1327
|
+
const WrapperPreview = () => {
|
|
1328
|
+
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
1329
|
};
|
|
1268
1330
|
|
|
1269
|
-
const WrapperLayout = (
|
|
1270
|
-
return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(
|
|
1331
|
+
const WrapperLayout = () => {
|
|
1332
|
+
return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(WrapperPreview, {})] }));
|
|
1271
1333
|
};
|
|
1272
1334
|
|
|
1273
|
-
const HeatmapLayout = ({ data, clickmap,
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
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" })] }));
|
|
1335
|
+
const HeatmapLayout = ({ data, clickmap, controls, dataInfo, }) => {
|
|
1336
|
+
useRegisterControl(controls);
|
|
1337
|
+
useRegisterData(data, dataInfo);
|
|
1338
|
+
useRegisterHeatmap(clickmap);
|
|
1339
|
+
useRegisterConfig();
|
|
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: {
|
|
1341
|
+
minHeight: '100%',
|
|
1342
|
+
display: 'flex',
|
|
1343
|
+
}, children: jsx(WrapperLayout, {}) }) }) }));
|
|
1344
|
+
function getVariableStyle() {
|
|
1345
|
+
return {
|
|
1346
|
+
'--gx-hm-border-width': `${HEATMAP_CONFIG.borderWidth}px`,
|
|
1347
|
+
'--gx-hm-border-color': `${HEATMAP_CONFIG.borderColor}`,
|
|
1348
|
+
};
|
|
1349
|
+
}
|
|
1296
1350
|
};
|
|
1297
1351
|
|
|
1298
1352
|
var PanelContent;
|
|
@@ -1313,4 +1367,4 @@ var ErrorType;
|
|
|
1313
1367
|
ErrorType["DataError"] = "DataError";
|
|
1314
1368
|
})(ErrorType || (ErrorType = {}));
|
|
1315
1369
|
|
|
1316
|
-
export { GraphView, HeatmapLayout, useHeatmapDataStore };
|
|
1370
|
+
export { GraphView, HeatmapLayout, IHeatmapType, useHeatmapDataStore };
|