@gemx-dev/heatmap-react 3.5.49 → 3.5.50
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.map +1 -1
- package/dist/esm/components/Layout/ContentVizByMode.d.ts.map +1 -1
- package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/esm/components/Layout/Sidebar/ContentSidebar.d.ts +2 -0
- package/dist/esm/components/Layout/Sidebar/ContentSidebar.d.ts.map +1 -0
- package/dist/esm/components/Layout/Sidebar/PopoverSidebar.d.ts +2 -0
- package/dist/esm/components/Layout/Sidebar/PopoverSidebar.d.ts.map +1 -0
- package/dist/esm/components/Layout/Sidebar/index.d.ts +3 -0
- package/dist/esm/components/Layout/Sidebar/index.d.ts.map +1 -0
- package/dist/esm/components/Layout/WrapperLayout.d.ts.map +1 -1
- package/dist/esm/components/Layout/WrapperPreview.d.ts.map +1 -1
- package/dist/esm/components/VizDom/VizContainer.d.ts.map +1 -1
- package/dist/esm/components/VizDom/VizDomHeatmap.d.ts.map +1 -1
- package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
- package/dist/esm/components/VizDom/WrapperVisual.d.ts.map +1 -1
- package/dist/esm/components/VizElement/DefaultRankBadges.d.ts.map +1 -1
- package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
- package/dist/esm/components/VizElement/ElementOverlay.d.ts +1 -0
- package/dist/esm/components/VizElement/ElementOverlay.d.ts.map +1 -1
- package/dist/esm/components/VizElement/HeatmapElements.d.ts +0 -1
- package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
- package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
- package/dist/esm/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
- package/dist/esm/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/VizScrollMap.d.ts.map +1 -1
- package/dist/esm/configs/index.d.ts +2 -0
- package/dist/esm/configs/index.d.ts.map +1 -1
- package/dist/esm/configs/viewId.d.ts +21 -0
- package/dist/esm/configs/viewId.d.ts.map +1 -0
- package/dist/esm/configs/z-index.d.ts +8 -0
- package/dist/esm/configs/z-index.d.ts.map +1 -0
- package/dist/esm/constants/index.d.ts +13 -4
- package/dist/esm/constants/index.d.ts.map +1 -1
- package/dist/esm/helpers/iframe-helper/style-replacer.d.ts.map +1 -1
- package/dist/esm/hooks/index.d.ts +1 -4
- package/dist/esm/hooks/index.d.ts.map +1 -1
- package/dist/esm/hooks/register/useRegisterControl.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/index.d.ts +7 -0
- package/dist/esm/hooks/view-context/index.d.ts.map +1 -0
- package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts +33 -0
- package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -0
- package/dist/esm/hooks/{useHeatmapData.d.ts → view-context/useHeatmapData.d.ts} +1 -1
- package/dist/esm/hooks/view-context/useHeatmapData.d.ts.map +1 -0
- package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts +16 -0
- package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts.map +1 -0
- package/dist/{umd/hooks → esm/hooks/view-context}/useHeatmapViz.d.ts +2 -0
- package/dist/esm/hooks/view-context/useHeatmapViz.d.ts.map +1 -0
- package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts +13 -0
- package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +1 -0
- package/dist/esm/hooks/{useViewId.d.ts → view-context/useViewId.d.ts} +1 -1
- package/dist/esm/hooks/view-context/useViewId.d.ts.map +1 -0
- package/dist/esm/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
- package/dist/esm/hooks/viz-canvas/useHeatmapCanvas.d.ts +1 -3
- package/dist/esm/hooks/viz-canvas/useHeatmapCanvas.d.ts.map +1 -1
- package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
- package/dist/esm/hooks/viz-elements/useClickedElement.d.ts +0 -1
- package/dist/esm/hooks/viz-elements/useClickedElement.d.ts.map +1 -1
- package/dist/esm/hooks/viz-elements/useElementCalloutVisible.d.ts.map +1 -1
- package/dist/esm/hooks/viz-elements/useHeatmapEffects.d.ts +1 -4
- package/dist/esm/hooks/viz-elements/useHeatmapEffects.d.ts.map +1 -1
- package/dist/esm/hooks/viz-elements/useHoveredElement.d.ts.map +1 -1
- package/dist/esm/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +0 -1
- package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts +0 -1
- package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useWrapperRefHeight.d.ts.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1080 -474
- package/dist/esm/index.mjs +1080 -474
- package/dist/esm/performance/PerformanceLogger.d.ts +22 -0
- package/dist/esm/performance/PerformanceLogger.d.ts.map +1 -0
- package/dist/esm/performance/hooks.d.ts +28 -0
- package/dist/esm/performance/hooks.d.ts.map +1 -0
- package/dist/esm/performance/index.d.ts +7 -0
- package/dist/esm/performance/index.d.ts.map +1 -0
- package/dist/esm/performance/storeTracker.d.ts +10 -0
- package/dist/esm/performance/storeTracker.d.ts.map +1 -0
- package/dist/esm/performance/types.d.ts +79 -0
- package/dist/esm/performance/types.d.ts.map +1 -0
- package/dist/esm/performance/utils.d.ts +42 -0
- package/dist/esm/performance/utils.d.ts.map +1 -0
- package/dist/esm/performance/withPerformanceTracking.d.ts +14 -0
- package/dist/esm/performance/withPerformanceTracking.d.ts.map +1 -0
- package/dist/esm/stores/comp.d.ts.map +1 -1
- package/dist/esm/stores/data.d.ts +9 -1
- package/dist/esm/stores/data.d.ts.map +1 -1
- package/dist/esm/stores/interaction.d.ts +20 -9
- package/dist/esm/stores/interaction.d.ts.map +1 -1
- package/dist/esm/stores/mode-single.d.ts +11 -1
- package/dist/esm/stores/mode-single.d.ts.map +1 -1
- package/dist/esm/stores/viz-scrollmap.d.ts +18 -7
- package/dist/esm/stores/viz-scrollmap.d.ts.map +1 -1
- package/dist/esm/stores/viz.d.ts +9 -1
- package/dist/esm/stores/viz.d.ts.map +1 -1
- package/dist/esm/types/control.d.ts +9 -1
- package/dist/esm/types/control.d.ts.map +1 -1
- package/dist/esm/types/index.d.ts +1 -0
- package/dist/esm/types/index.d.ts.map +1 -1
- package/dist/style.css +7 -1
- package/dist/umd/components/Layout/ContentMetricBar.d.ts.map +1 -1
- package/dist/umd/components/Layout/ContentVizByMode.d.ts.map +1 -1
- package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/umd/components/Layout/Sidebar/ContentSidebar.d.ts +2 -0
- package/dist/umd/components/Layout/Sidebar/ContentSidebar.d.ts.map +1 -0
- package/dist/umd/components/Layout/Sidebar/PopoverSidebar.d.ts +2 -0
- package/dist/umd/components/Layout/Sidebar/PopoverSidebar.d.ts.map +1 -0
- package/dist/umd/components/Layout/Sidebar/index.d.ts +3 -0
- package/dist/umd/components/Layout/Sidebar/index.d.ts.map +1 -0
- package/dist/umd/components/Layout/WrapperLayout.d.ts.map +1 -1
- package/dist/umd/components/Layout/WrapperPreview.d.ts.map +1 -1
- package/dist/umd/components/VizDom/VizContainer.d.ts.map +1 -1
- package/dist/umd/components/VizDom/VizDomHeatmap.d.ts.map +1 -1
- package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
- package/dist/umd/components/VizDom/WrapperVisual.d.ts.map +1 -1
- package/dist/umd/components/VizElement/DefaultRankBadges.d.ts.map +1 -1
- package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
- package/dist/umd/components/VizElement/ElementOverlay.d.ts +1 -0
- package/dist/umd/components/VizElement/ElementOverlay.d.ts.map +1 -1
- package/dist/umd/components/VizElement/HeatmapElements.d.ts +0 -1
- package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
- package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
- package/dist/umd/components/VizLive/VizLiveHeatmap.d.ts.map +1 -1
- package/dist/umd/components/VizLive/VizLiveRenderer.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/VizScrollMap.d.ts.map +1 -1
- package/dist/umd/configs/index.d.ts +2 -0
- package/dist/umd/configs/index.d.ts.map +1 -1
- package/dist/umd/configs/viewId.d.ts +21 -0
- package/dist/umd/configs/viewId.d.ts.map +1 -0
- package/dist/umd/configs/z-index.d.ts +8 -0
- package/dist/umd/configs/z-index.d.ts.map +1 -0
- package/dist/umd/constants/index.d.ts +13 -4
- package/dist/umd/constants/index.d.ts.map +1 -1
- package/dist/umd/helpers/iframe-helper/style-replacer.d.ts.map +1 -1
- package/dist/umd/hooks/index.d.ts +1 -4
- package/dist/umd/hooks/index.d.ts.map +1 -1
- package/dist/umd/hooks/register/useRegisterControl.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/index.d.ts +7 -0
- package/dist/umd/hooks/view-context/index.d.ts.map +1 -0
- package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts +33 -0
- package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -0
- package/dist/umd/hooks/{useHeatmapData.d.ts → view-context/useHeatmapData.d.ts} +1 -1
- package/dist/umd/hooks/view-context/useHeatmapData.d.ts.map +1 -0
- package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts +16 -0
- package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts.map +1 -0
- package/dist/{esm/hooks → umd/hooks/view-context}/useHeatmapViz.d.ts +2 -0
- package/dist/umd/hooks/view-context/useHeatmapViz.d.ts.map +1 -0
- package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts +13 -0
- package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +1 -0
- package/dist/umd/hooks/{useViewId.d.ts → view-context/useViewId.d.ts} +1 -1
- package/dist/umd/hooks/view-context/useViewId.d.ts.map +1 -0
- package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
- package/dist/umd/hooks/viz-canvas/useHeatmapCanvas.d.ts +1 -3
- package/dist/umd/hooks/viz-canvas/useHeatmapCanvas.d.ts.map +1 -1
- package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
- package/dist/umd/hooks/viz-elements/useClickedElement.d.ts +0 -1
- package/dist/umd/hooks/viz-elements/useClickedElement.d.ts.map +1 -1
- package/dist/umd/hooks/viz-elements/useElementCalloutVisible.d.ts.map +1 -1
- package/dist/umd/hooks/viz-elements/useHeatmapEffects.d.ts +1 -4
- package/dist/umd/hooks/viz-elements/useHeatmapEffects.d.ts.map +1 -1
- package/dist/umd/hooks/viz-elements/useHoveredElement.d.ts.map +1 -1
- package/dist/umd/hooks/viz-live/useVizLiveRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-render/useHeatmapRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +0 -1
- package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts +0 -1
- package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useWrapperRefHeight.d.ts.map +1 -1
- package/dist/umd/index.d.ts +3 -2
- package/dist/umd/index.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/dist/umd/performance/PerformanceLogger.d.ts +22 -0
- package/dist/umd/performance/PerformanceLogger.d.ts.map +1 -0
- package/dist/umd/performance/hooks.d.ts +28 -0
- package/dist/umd/performance/hooks.d.ts.map +1 -0
- package/dist/umd/performance/index.d.ts +7 -0
- package/dist/umd/performance/index.d.ts.map +1 -0
- package/dist/umd/performance/storeTracker.d.ts +10 -0
- package/dist/umd/performance/storeTracker.d.ts.map +1 -0
- package/dist/umd/performance/types.d.ts +79 -0
- package/dist/umd/performance/types.d.ts.map +1 -0
- package/dist/umd/performance/utils.d.ts +42 -0
- package/dist/umd/performance/utils.d.ts.map +1 -0
- package/dist/umd/performance/withPerformanceTracking.d.ts +14 -0
- package/dist/umd/performance/withPerformanceTracking.d.ts.map +1 -0
- package/dist/umd/stores/comp.d.ts.map +1 -1
- package/dist/umd/stores/data.d.ts +9 -1
- package/dist/umd/stores/data.d.ts.map +1 -1
- package/dist/umd/stores/interaction.d.ts +20 -9
- package/dist/umd/stores/interaction.d.ts.map +1 -1
- package/dist/umd/stores/mode-single.d.ts +11 -1
- package/dist/umd/stores/mode-single.d.ts.map +1 -1
- package/dist/umd/stores/viz-scrollmap.d.ts +18 -7
- package/dist/umd/stores/viz-scrollmap.d.ts.map +1 -1
- package/dist/umd/stores/viz.d.ts +9 -1
- package/dist/umd/stores/viz.d.ts.map +1 -1
- package/dist/umd/types/control.d.ts +9 -1
- package/dist/umd/types/control.d.ts.map +1 -1
- package/dist/umd/types/index.d.ts +1 -0
- package/dist/umd/types/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/dist/esm/components/Layout/LeftSidebar.d.ts +0 -2
- package/dist/esm/components/Layout/LeftSidebar.d.ts.map +0 -1
- package/dist/esm/hooks/compare/index.d.ts +0 -4
- package/dist/esm/hooks/compare/index.d.ts.map +0 -1
- package/dist/esm/hooks/compare/useCompareAwareConfig.d.ts +0 -21
- package/dist/esm/hooks/compare/useCompareAwareConfig.d.ts.map +0 -1
- package/dist/esm/hooks/compare/useCompareAwareData.d.ts +0 -28
- package/dist/esm/hooks/compare/useCompareAwareData.d.ts.map +0 -1
- package/dist/esm/hooks/compare/useCompareAwareViz.d.ts +0 -34
- package/dist/esm/hooks/compare/useCompareAwareViz.d.ts.map +0 -1
- package/dist/esm/hooks/useHeatmapData.d.ts.map +0 -1
- package/dist/esm/hooks/useHeatmapViz.d.ts.map +0 -1
- package/dist/esm/hooks/useViewId.d.ts.map +0 -1
- package/dist/umd/components/Layout/LeftSidebar.d.ts +0 -2
- package/dist/umd/components/Layout/LeftSidebar.d.ts.map +0 -1
- package/dist/umd/hooks/compare/index.d.ts +0 -4
- package/dist/umd/hooks/compare/index.d.ts.map +0 -1
- package/dist/umd/hooks/compare/useCompareAwareConfig.d.ts +0 -21
- package/dist/umd/hooks/compare/useCompareAwareConfig.d.ts.map +0 -1
- package/dist/umd/hooks/compare/useCompareAwareData.d.ts +0 -28
- package/dist/umd/hooks/compare/useCompareAwareData.d.ts.map +0 -1
- package/dist/umd/hooks/compare/useCompareAwareViz.d.ts +0 -34
- package/dist/umd/hooks/compare/useCompareAwareViz.d.ts.map +0 -1
- package/dist/umd/hooks/useHeatmapData.d.ts.map +0 -1
- package/dist/umd/hooks/useHeatmapViz.d.ts.map +0 -1
- package/dist/umd/hooks/useViewId.d.ts.map +0 -1
package/dist/esm/index.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
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, createContext, useContext, useCallback, useState, useRef,
|
|
4
|
+
import { useEffect, createContext, useContext, useMemo, useCallback, useState, useRef, forwardRef, Fragment as Fragment$1 } from 'react';
|
|
5
5
|
import { create } from 'zustand';
|
|
6
|
+
import { subscribeWithSelector } from 'zustand/middleware';
|
|
6
7
|
import { decode } from '@gemx-dev/clarity-decode';
|
|
7
8
|
import { Visualizer } from '@gemx-dev/clarity-visualize';
|
|
8
9
|
import { createPortal } from 'react-dom';
|
|
@@ -67,10 +68,45 @@ const HEATMAP_STYLE = {
|
|
|
67
68
|
};
|
|
68
69
|
const DEFAULT_SIDEBAR_WIDTH = 260;
|
|
69
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Default view ID for single mode
|
|
73
|
+
*/
|
|
74
|
+
const DEFAULT_VIEW_ID = 'default';
|
|
75
|
+
/**
|
|
76
|
+
* Generate compare view ID
|
|
77
|
+
* @param index - Zero-based index (0, 1, 2, 3)
|
|
78
|
+
* @returns View ID string like 'view-0', 'view-1', etc.
|
|
79
|
+
*/
|
|
80
|
+
const getCompareViewId = (index) => `view-${index}`;
|
|
81
|
+
/**
|
|
82
|
+
* Check if a view ID is a compare view
|
|
83
|
+
*/
|
|
84
|
+
const isCompareViewId = (viewId) => viewId.startsWith('view-');
|
|
85
|
+
/**
|
|
86
|
+
* Extract index from compare view ID
|
|
87
|
+
* @param viewId - View ID like 'view-0'
|
|
88
|
+
* @returns Index number or -1 if invalid
|
|
89
|
+
*/
|
|
90
|
+
const getCompareViewIndex = (viewId) => {
|
|
91
|
+
if (!isCompareViewId(viewId))
|
|
92
|
+
return -1;
|
|
93
|
+
const match = viewId.match(/^view-(\d+)$/);
|
|
94
|
+
return match ? parseInt(match[1], 10) : -1;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const Z_INDEX = {
|
|
98
|
+
ELEMENTS: 1000,
|
|
99
|
+
CALLOUT: 2000,
|
|
100
|
+
MINIMAP: 3000,
|
|
101
|
+
SIDEBAR: 4000,
|
|
102
|
+
SIDEBAR_POPOVER: 4001,
|
|
103
|
+
};
|
|
104
|
+
|
|
70
105
|
const useHeatmapControlStore = create()((set, get) => {
|
|
71
106
|
return {
|
|
72
107
|
controls: {
|
|
73
|
-
Sidebar:
|
|
108
|
+
Sidebar: undefined,
|
|
109
|
+
SidebarActivator: undefined,
|
|
74
110
|
Toolbar: null,
|
|
75
111
|
MetricBar: null,
|
|
76
112
|
VizLoading: null,
|
|
@@ -114,7 +150,7 @@ const useHeatmapConfigStore = create()((set, get) => {
|
|
|
114
150
|
mode: 'single',
|
|
115
151
|
width: 1440,
|
|
116
152
|
sidebarWidth: DEFAULT_SIDEBAR_WIDTH,
|
|
117
|
-
heatmapType: IHeatmapType.
|
|
153
|
+
heatmapType: IHeatmapType.Click,
|
|
118
154
|
clickType: IClickType.Total,
|
|
119
155
|
scrollType: IScrollType.Depth,
|
|
120
156
|
isRendering: true,
|
|
@@ -129,23 +165,22 @@ const useHeatmapConfigStore = create()((set, get) => {
|
|
|
129
165
|
};
|
|
130
166
|
});
|
|
131
167
|
|
|
132
|
-
const
|
|
133
|
-
const useHeatmapDataStore = create()((set, get) => {
|
|
168
|
+
const useHeatmapDataStore = create()(subscribeWithSelector((set, get) => {
|
|
134
169
|
return {
|
|
135
|
-
data: { [DEFAULT_VIEW_ID
|
|
136
|
-
clickmap: { [DEFAULT_VIEW_ID
|
|
137
|
-
dataInfo: { [DEFAULT_VIEW_ID
|
|
138
|
-
scrollmap: { [DEFAULT_VIEW_ID
|
|
139
|
-
setDataInfo: (dataInfo, viewId = DEFAULT_VIEW_ID
|
|
170
|
+
data: { [DEFAULT_VIEW_ID]: undefined },
|
|
171
|
+
clickmap: { [DEFAULT_VIEW_ID]: undefined },
|
|
172
|
+
dataInfo: { [DEFAULT_VIEW_ID]: undefined },
|
|
173
|
+
scrollmap: { [DEFAULT_VIEW_ID]: undefined },
|
|
174
|
+
setDataInfo: (dataInfo, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
140
175
|
dataInfo: { ...state.dataInfo, [viewId]: dataInfo },
|
|
141
176
|
})),
|
|
142
|
-
setData: (data, viewId = DEFAULT_VIEW_ID
|
|
177
|
+
setData: (data, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
143
178
|
data: { ...state.data, [viewId]: data },
|
|
144
179
|
})),
|
|
145
|
-
setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID
|
|
180
|
+
setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
146
181
|
clickmap: { ...state.clickmap, [viewId]: clickmap },
|
|
147
182
|
})),
|
|
148
|
-
setScrollmap: (scrollmap, viewId = DEFAULT_VIEW_ID
|
|
183
|
+
setScrollmap: (scrollmap, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
149
184
|
scrollmap: { ...state.scrollmap, [viewId]: scrollmap },
|
|
150
185
|
})),
|
|
151
186
|
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
@@ -171,50 +206,98 @@ const useHeatmapDataStore = create()((set, get) => {
|
|
|
171
206
|
};
|
|
172
207
|
}),
|
|
173
208
|
resetAll: () => set({
|
|
174
|
-
data: { [DEFAULT_VIEW_ID
|
|
175
|
-
clickmap: { [DEFAULT_VIEW_ID
|
|
176
|
-
dataInfo: { [DEFAULT_VIEW_ID
|
|
177
|
-
scrollmap: { [DEFAULT_VIEW_ID
|
|
209
|
+
data: { [DEFAULT_VIEW_ID]: undefined },
|
|
210
|
+
clickmap: { [DEFAULT_VIEW_ID]: undefined },
|
|
211
|
+
dataInfo: { [DEFAULT_VIEW_ID]: undefined },
|
|
212
|
+
scrollmap: { [DEFAULT_VIEW_ID]: undefined },
|
|
178
213
|
}),
|
|
179
214
|
};
|
|
180
|
-
});
|
|
215
|
+
}));
|
|
181
216
|
|
|
182
|
-
const
|
|
217
|
+
const DEFAULT_STATE = {
|
|
218
|
+
hideSidebar: false,
|
|
219
|
+
};
|
|
220
|
+
const useHeatmapInteractionStore = create()(subscribeWithSelector((set, get) => {
|
|
183
221
|
return {
|
|
184
|
-
state: {
|
|
185
|
-
|
|
186
|
-
},
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
222
|
+
state: { [DEFAULT_VIEW_ID]: DEFAULT_STATE },
|
|
223
|
+
selectedElement: { [DEFAULT_VIEW_ID]: null },
|
|
224
|
+
hoveredElement: { [DEFAULT_VIEW_ID]: null },
|
|
225
|
+
shouldShowCallout: { [DEFAULT_VIEW_ID]: false },
|
|
226
|
+
setState: (state, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
|
|
227
|
+
state: { ...prev.state, [viewId]: state },
|
|
228
|
+
})),
|
|
229
|
+
setSelectedElement: (selectedElement, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
|
|
230
|
+
selectedElement: { ...prev.selectedElement, [viewId]: selectedElement },
|
|
231
|
+
})),
|
|
232
|
+
setHoveredElement: (hoveredElement, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
|
|
233
|
+
hoveredElement: { ...prev.hoveredElement, [viewId]: hoveredElement },
|
|
234
|
+
})),
|
|
235
|
+
setShouldShowCallout: (shouldShowCallout, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
|
|
236
|
+
shouldShowCallout: { ...prev.shouldShowCallout, [viewId]: shouldShowCallout },
|
|
237
|
+
})),
|
|
238
|
+
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
239
|
+
state: {
|
|
240
|
+
...state.state,
|
|
241
|
+
[toViewId]: state.state[fromViewId] ?? DEFAULT_STATE,
|
|
242
|
+
},
|
|
243
|
+
selectedElement: {
|
|
244
|
+
...state.selectedElement,
|
|
245
|
+
[toViewId]: state.selectedElement[fromViewId] ?? null,
|
|
246
|
+
},
|
|
247
|
+
hoveredElement: {
|
|
248
|
+
...state.hoveredElement,
|
|
249
|
+
[toViewId]: state.hoveredElement[fromViewId] ?? null,
|
|
250
|
+
},
|
|
251
|
+
shouldShowCallout: {
|
|
252
|
+
...state.shouldShowCallout,
|
|
253
|
+
[toViewId]: state.shouldShowCallout[fromViewId] ?? false,
|
|
254
|
+
},
|
|
255
|
+
})),
|
|
256
|
+
clearView: (viewId) => set((state) => {
|
|
257
|
+
const newState = { ...state.state };
|
|
258
|
+
const newSelectedElement = { ...state.selectedElement };
|
|
259
|
+
const newHoveredElement = { ...state.hoveredElement };
|
|
260
|
+
const newShouldShowCallout = { ...state.shouldShowCallout };
|
|
261
|
+
delete newState[viewId];
|
|
262
|
+
delete newSelectedElement[viewId];
|
|
263
|
+
delete newHoveredElement[viewId];
|
|
264
|
+
delete newShouldShowCallout[viewId];
|
|
265
|
+
return {
|
|
266
|
+
state: newState,
|
|
267
|
+
selectedElement: newSelectedElement,
|
|
268
|
+
hoveredElement: newHoveredElement,
|
|
269
|
+
shouldShowCallout: newShouldShowCallout,
|
|
270
|
+
};
|
|
271
|
+
}),
|
|
272
|
+
resetAll: () => set({
|
|
273
|
+
state: { default: DEFAULT_STATE },
|
|
274
|
+
selectedElement: { default: null },
|
|
275
|
+
hoveredElement: { default: null },
|
|
276
|
+
shouldShowCallout: { default: false },
|
|
277
|
+
}),
|
|
194
278
|
};
|
|
195
|
-
});
|
|
279
|
+
}));
|
|
196
280
|
|
|
197
|
-
const
|
|
198
|
-
const useHeatmapVizStore = create()((set, get) => {
|
|
281
|
+
const useHeatmapVizStore = create()(subscribeWithSelector((set, get) => {
|
|
199
282
|
return {
|
|
200
|
-
isRenderViz: { [DEFAULT_VIEW_ID
|
|
201
|
-
zoomRatio: { [DEFAULT_VIEW_ID
|
|
202
|
-
minZoomRatio: { [DEFAULT_VIEW_ID
|
|
203
|
-
scale: { [DEFAULT_VIEW_ID
|
|
204
|
-
isScaledToFit: { [DEFAULT_VIEW_ID
|
|
205
|
-
setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID
|
|
283
|
+
isRenderViz: { [DEFAULT_VIEW_ID]: false },
|
|
284
|
+
zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
|
|
285
|
+
minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
|
|
286
|
+
scale: { [DEFAULT_VIEW_ID]: 1 },
|
|
287
|
+
isScaledToFit: { [DEFAULT_VIEW_ID]: false },
|
|
288
|
+
setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
206
289
|
isRenderViz: { ...state.isRenderViz, [viewId]: isRenderViz },
|
|
207
290
|
})),
|
|
208
|
-
setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID
|
|
291
|
+
setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
209
292
|
zoomRatio: { ...state.zoomRatio, [viewId]: zoomRatio },
|
|
210
293
|
})),
|
|
211
|
-
setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID
|
|
294
|
+
setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
212
295
|
minZoomRatio: { ...state.minZoomRatio, [viewId]: minZoomRatio },
|
|
213
296
|
})),
|
|
214
|
-
setScale: (scale, viewId = DEFAULT_VIEW_ID
|
|
297
|
+
setScale: (scale, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
215
298
|
scale: { ...state.scale, [viewId]: scale },
|
|
216
299
|
})),
|
|
217
|
-
setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID
|
|
300
|
+
setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
218
301
|
isScaledToFit: { ...state.isScaledToFit, [viewId]: isScaledToFit },
|
|
219
302
|
})),
|
|
220
303
|
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
@@ -222,7 +305,10 @@ const useHeatmapVizStore = create()((set, get) => {
|
|
|
222
305
|
zoomRatio: { ...state.zoomRatio, [toViewId]: state.zoomRatio[fromViewId] ?? 100 },
|
|
223
306
|
minZoomRatio: { ...state.minZoomRatio, [toViewId]: state.minZoomRatio[fromViewId] ?? 10 },
|
|
224
307
|
scale: { ...state.scale, [toViewId]: state.scale[fromViewId] ?? 1 },
|
|
225
|
-
isScaledToFit: {
|
|
308
|
+
isScaledToFit: {
|
|
309
|
+
...state.isScaledToFit,
|
|
310
|
+
[toViewId]: state.isScaledToFit[fromViewId] ?? false,
|
|
311
|
+
},
|
|
226
312
|
})),
|
|
227
313
|
clearView: (viewId) => set((state) => {
|
|
228
314
|
const newIsRenderViz = { ...state.isRenderViz };
|
|
@@ -244,25 +330,63 @@ const useHeatmapVizStore = create()((set, get) => {
|
|
|
244
330
|
};
|
|
245
331
|
}),
|
|
246
332
|
resetAll: () => set({
|
|
247
|
-
isRenderViz: { [DEFAULT_VIEW_ID
|
|
248
|
-
zoomRatio: { [DEFAULT_VIEW_ID
|
|
249
|
-
minZoomRatio: { [DEFAULT_VIEW_ID
|
|
250
|
-
scale: { [DEFAULT_VIEW_ID
|
|
251
|
-
isScaledToFit: { [DEFAULT_VIEW_ID
|
|
333
|
+
isRenderViz: { [DEFAULT_VIEW_ID]: false },
|
|
334
|
+
zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
|
|
335
|
+
minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
|
|
336
|
+
scale: { [DEFAULT_VIEW_ID]: 1 },
|
|
337
|
+
isScaledToFit: { [DEFAULT_VIEW_ID]: false },
|
|
252
338
|
}),
|
|
253
339
|
};
|
|
254
|
-
});
|
|
340
|
+
}));
|
|
255
341
|
|
|
256
|
-
const useHeatmapVizScrollmapStore = create()((set, get) => {
|
|
342
|
+
const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set, get) => {
|
|
257
343
|
return {
|
|
258
|
-
zones: [],
|
|
259
|
-
hoveredZone: null,
|
|
260
|
-
showMinimap: true,
|
|
261
|
-
setZones: (zones) => set(
|
|
262
|
-
|
|
263
|
-
|
|
344
|
+
zones: { [DEFAULT_VIEW_ID]: [] },
|
|
345
|
+
hoveredZone: { [DEFAULT_VIEW_ID]: null },
|
|
346
|
+
showMinimap: { [DEFAULT_VIEW_ID]: true },
|
|
347
|
+
setZones: (zones, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
|
|
348
|
+
zones: { ...prev.zones, [viewId]: zones },
|
|
349
|
+
})),
|
|
350
|
+
setHoveredZone: (hoveredZone, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
|
|
351
|
+
hoveredZone: { ...prev.hoveredZone, [viewId]: hoveredZone },
|
|
352
|
+
})),
|
|
353
|
+
setShowMinimap: (showMinimap, viewId = DEFAULT_VIEW_ID) => set((prev) => ({
|
|
354
|
+
showMinimap: { ...prev.showMinimap, [viewId]: showMinimap },
|
|
355
|
+
})),
|
|
356
|
+
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
357
|
+
zones: {
|
|
358
|
+
...state.zones,
|
|
359
|
+
[toViewId]: state.zones[fromViewId] ?? [],
|
|
360
|
+
},
|
|
361
|
+
hoveredZone: {
|
|
362
|
+
...state.hoveredZone,
|
|
363
|
+
[toViewId]: state.hoveredZone[fromViewId] ?? null,
|
|
364
|
+
},
|
|
365
|
+
showMinimap: {
|
|
366
|
+
...state.showMinimap,
|
|
367
|
+
[toViewId]: state.showMinimap[fromViewId] ?? true,
|
|
368
|
+
},
|
|
369
|
+
})),
|
|
370
|
+
clearView: (viewId) => set((state) => {
|
|
371
|
+
const newZones = { ...state.zones };
|
|
372
|
+
const newHoveredZone = { ...state.hoveredZone };
|
|
373
|
+
const newShowMinimap = { ...state.showMinimap };
|
|
374
|
+
delete newZones[viewId];
|
|
375
|
+
delete newHoveredZone[viewId];
|
|
376
|
+
delete newShowMinimap[viewId];
|
|
377
|
+
return {
|
|
378
|
+
zones: newZones,
|
|
379
|
+
hoveredZone: newHoveredZone,
|
|
380
|
+
showMinimap: newShowMinimap,
|
|
381
|
+
};
|
|
382
|
+
}),
|
|
383
|
+
resetAll: () => set({
|
|
384
|
+
zones: { default: [] },
|
|
385
|
+
hoveredZone: { default: null },
|
|
386
|
+
showMinimap: { default: true },
|
|
387
|
+
}),
|
|
264
388
|
};
|
|
265
|
-
});
|
|
389
|
+
}));
|
|
266
390
|
|
|
267
391
|
const initialState = {
|
|
268
392
|
payloads: [],
|
|
@@ -278,12 +402,12 @@ const useHeatmapLiveStore = create()((set, get) => {
|
|
|
278
402
|
};
|
|
279
403
|
});
|
|
280
404
|
|
|
281
|
-
const
|
|
282
|
-
const useHeatmapSingleStore = create()((set, get) => {
|
|
405
|
+
const useHeatmapSingleStore = create()(subscribeWithSelector((set, get) => {
|
|
283
406
|
return {
|
|
284
407
|
vizRef: { [DEFAULT_VIEW_ID]: null },
|
|
285
408
|
iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
286
409
|
wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
410
|
+
wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
|
|
287
411
|
setVizRef: (vizRef, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
288
412
|
vizRef: { ...state.vizRef, [viewId]: vizRef },
|
|
289
413
|
})),
|
|
@@ -297,31 +421,43 @@ const useHeatmapSingleStore = create()((set, get) => {
|
|
|
297
421
|
wrapperHeight: { ...state.wrapperHeight, [viewId]: wrapperHeight },
|
|
298
422
|
}));
|
|
299
423
|
},
|
|
424
|
+
setWrapperWidth: (wrapperWidth, viewId = DEFAULT_VIEW_ID) => {
|
|
425
|
+
set((state) => ({
|
|
426
|
+
wrapperWidth: { ...state.wrapperWidth, [viewId]: wrapperWidth },
|
|
427
|
+
}));
|
|
428
|
+
},
|
|
300
429
|
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
301
430
|
// Don't copy vizRef - each view needs its own visualizer instance
|
|
302
431
|
iframeHeight: { ...state.iframeHeight, [toViewId]: state.iframeHeight[fromViewId] ?? 0 },
|
|
303
|
-
wrapperHeight: {
|
|
432
|
+
wrapperHeight: {
|
|
433
|
+
...state.wrapperHeight,
|
|
434
|
+
[toViewId]: state.wrapperHeight[fromViewId] ?? 0,
|
|
435
|
+
},
|
|
304
436
|
})),
|
|
305
437
|
clearView: (viewId) => set((state) => {
|
|
306
438
|
const newVizRef = { ...state.vizRef };
|
|
307
439
|
const newIframeHeight = { ...state.iframeHeight };
|
|
308
440
|
const newWrapperHeight = { ...state.wrapperHeight };
|
|
441
|
+
const newWrapperWidth = { ...state.wrapperWidth };
|
|
309
442
|
delete newVizRef[viewId];
|
|
310
443
|
delete newIframeHeight[viewId];
|
|
311
444
|
delete newWrapperHeight[viewId];
|
|
445
|
+
delete newWrapperWidth[viewId];
|
|
312
446
|
return {
|
|
313
447
|
vizRef: newVizRef,
|
|
314
448
|
iframeHeight: newIframeHeight,
|
|
315
449
|
wrapperHeight: newWrapperHeight,
|
|
450
|
+
wrapperWidth: newWrapperWidth,
|
|
316
451
|
};
|
|
317
452
|
}),
|
|
318
453
|
resetAll: () => set({
|
|
319
454
|
vizRef: { [DEFAULT_VIEW_ID]: null },
|
|
320
455
|
iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
321
456
|
wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
457
|
+
wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
|
|
322
458
|
}),
|
|
323
459
|
};
|
|
324
|
-
});
|
|
460
|
+
}));
|
|
325
461
|
|
|
326
462
|
const createDefaultView = (id, label, options) => ({
|
|
327
463
|
id,
|
|
@@ -468,6 +604,7 @@ const useRegisterConfig = () => {
|
|
|
468
604
|
const useRegisterControl = (control) => {
|
|
469
605
|
const registerControl = useHeatmapControlStore((state) => state.registerControl);
|
|
470
606
|
registerControl('Sidebar', control.Sidebar);
|
|
607
|
+
registerControl('SidebarActivator', control.SidebarActivator);
|
|
471
608
|
registerControl('TopBar', control.TopBar);
|
|
472
609
|
registerControl('Toolbar', control.Toolbar);
|
|
473
610
|
registerControl('MetricBar', control.MetricBar);
|
|
@@ -482,18 +619,18 @@ const useRegisterControl = (control) => {
|
|
|
482
619
|
const ViewIdContext = createContext(undefined);
|
|
483
620
|
/**
|
|
484
621
|
* Hook to get current viewId
|
|
485
|
-
* Returns
|
|
622
|
+
* Returns DEFAULT_VIEW_ID if not in a ViewIdContext (single mode)
|
|
486
623
|
*/
|
|
487
624
|
const useViewId = () => {
|
|
488
625
|
const viewId = useContext(ViewIdContext);
|
|
489
|
-
return viewId ||
|
|
626
|
+
return viewId || DEFAULT_VIEW_ID;
|
|
490
627
|
};
|
|
491
628
|
/**
|
|
492
629
|
* Hook to check if currently in compare mode
|
|
493
630
|
*/
|
|
494
631
|
const useIsCompareMode = () => {
|
|
495
632
|
const viewId = useContext(ViewIdContext);
|
|
496
|
-
return viewId !== undefined && viewId !==
|
|
633
|
+
return viewId !== undefined && viewId !== DEFAULT_VIEW_ID;
|
|
497
634
|
};
|
|
498
635
|
|
|
499
636
|
const useHeatmapData = (props) => {
|
|
@@ -506,17 +643,191 @@ const useHeatmapData = (props) => {
|
|
|
506
643
|
const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
|
|
507
644
|
const setScrollmap = useHeatmapDataStore((state) => state.setScrollmap);
|
|
508
645
|
const setDataInfo = useHeatmapDataStore((state) => state.setDataInfo);
|
|
646
|
+
const memoizedSetters = useMemo(() => ({
|
|
647
|
+
setData: (newData) => setData(newData, viewId),
|
|
648
|
+
setClickmap: (newClickmap) => setClickmap(newClickmap, viewId),
|
|
649
|
+
setScrollmap: (newScrollmap) => setScrollmap(newScrollmap, viewId),
|
|
650
|
+
setDataInfo: (newDataInfo) => setDataInfo(newDataInfo, viewId),
|
|
651
|
+
}), [setData, setClickmap, setScrollmap, setDataInfo, viewId]);
|
|
509
652
|
return {
|
|
510
|
-
// Data
|
|
511
653
|
data,
|
|
512
654
|
clickmap,
|
|
513
655
|
scrollmap,
|
|
514
656
|
dataInfo,
|
|
515
657
|
// Setters (auto-inject viewId)
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
658
|
+
...memoizedSetters,
|
|
659
|
+
};
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
const useHeatmapInteraction = (props) => {
|
|
663
|
+
const viewId = props?.viewId || useViewId();
|
|
664
|
+
const state = useHeatmapInteractionStore((store) => store.state[viewId] ?? { hideSidebar: false });
|
|
665
|
+
const selectedElement = useHeatmapInteractionStore((store) => store.selectedElement[viewId] ?? null);
|
|
666
|
+
const hoveredElement = useHeatmapInteractionStore((store) => store.hoveredElement[viewId] ?? null);
|
|
667
|
+
const shouldShowCallout = useHeatmapInteractionStore((store) => store.shouldShowCallout[viewId] ?? false);
|
|
668
|
+
const setStateStore = useHeatmapInteractionStore((store) => store.setState);
|
|
669
|
+
const setSelectedElementStore = useHeatmapInteractionStore((store) => store.setSelectedElement);
|
|
670
|
+
const setHoveredElementStore = useHeatmapInteractionStore((store) => store.setHoveredElement);
|
|
671
|
+
const setShouldShowCalloutStore = useHeatmapInteractionStore((store) => store.setShouldShowCallout);
|
|
672
|
+
const memoizedSetters = useMemo(() => ({
|
|
673
|
+
setState: (newState) => setStateStore(newState, viewId),
|
|
674
|
+
setSelectedElement: (element) => setSelectedElementStore(element, viewId),
|
|
675
|
+
setHoveredElement: (element) => setHoveredElementStore(element, viewId),
|
|
676
|
+
setShouldShowCallout: (value) => setShouldShowCalloutStore(value, viewId),
|
|
677
|
+
}), [
|
|
678
|
+
setStateStore,
|
|
679
|
+
setSelectedElementStore,
|
|
680
|
+
setHoveredElementStore,
|
|
681
|
+
setShouldShowCalloutStore,
|
|
682
|
+
viewId,
|
|
683
|
+
]);
|
|
684
|
+
return {
|
|
685
|
+
state,
|
|
686
|
+
selectedElement,
|
|
687
|
+
hoveredElement,
|
|
688
|
+
shouldShowCallout,
|
|
689
|
+
// Setters (auto-inject viewId)
|
|
690
|
+
...memoizedSetters,
|
|
691
|
+
};
|
|
692
|
+
};
|
|
693
|
+
|
|
694
|
+
const useHeatmapViz = (props) => {
|
|
695
|
+
const viewId = props?.viewId || useViewId();
|
|
696
|
+
// Viz store
|
|
697
|
+
const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
|
|
698
|
+
const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
|
|
699
|
+
const minZoomRatio = useHeatmapVizStore((state) => state.minZoomRatio[viewId] ?? 10);
|
|
700
|
+
const widthScale = useHeatmapVizStore((state) => state.scale[viewId] ?? 1);
|
|
701
|
+
const isScaledToFit = useHeatmapVizStore((state) => state.isScaledToFit[viewId] ?? false);
|
|
702
|
+
const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
|
|
703
|
+
const setZoomRatio = useHeatmapVizStore((state) => state.setZoomRatio);
|
|
704
|
+
const setMinZoomRatio = useHeatmapVizStore((state) => state.setMinZoomRatio);
|
|
705
|
+
const setScale = useHeatmapVizStore((state) => state.setScale);
|
|
706
|
+
const setIsScaledToFit = useHeatmapVizStore((state) => state.setIsScaledToFit);
|
|
707
|
+
// Single store
|
|
708
|
+
const vizRef = useHeatmapSingleStore((state) => state.vizRef[viewId] ?? null);
|
|
709
|
+
const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight[viewId] ?? 0);
|
|
710
|
+
const wrapperHeight = useHeatmapSingleStore((state) => state.wrapperHeight[viewId] ?? 0);
|
|
711
|
+
const wrapperWidth = useHeatmapSingleStore((state) => state.wrapperWidth[viewId] ?? 0);
|
|
712
|
+
const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
|
|
713
|
+
const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
|
|
714
|
+
const setWrapperHeight = useHeatmapSingleStore((state) => state.setWrapperHeight);
|
|
715
|
+
const setWrapperWidth = useHeatmapSingleStore((state) => state.setWrapperWidth);
|
|
716
|
+
const memoizedSetters = useMemo(() => ({
|
|
717
|
+
setIsRenderViz: (value) => setIsRenderViz(value, viewId),
|
|
718
|
+
setZoomRatio: (value) => setZoomRatio(value, viewId),
|
|
719
|
+
setMinZoomRatio: (value) => setMinZoomRatio(value, viewId),
|
|
720
|
+
setScale: (value) => setScale(value, viewId),
|
|
721
|
+
setIsScaledToFit: (value) => setIsScaledToFit(value, viewId),
|
|
722
|
+
setVizRef: (value) => setVizRef(value, viewId),
|
|
723
|
+
setIframeHeight: (value) => setIframeHeight(value, viewId),
|
|
724
|
+
setWrapperHeight: (value) => setWrapperHeight(value, viewId),
|
|
725
|
+
setWrapperWidth: (value) => setWrapperWidth(value, viewId),
|
|
726
|
+
}), [
|
|
727
|
+
setIsRenderViz,
|
|
728
|
+
setZoomRatio,
|
|
729
|
+
setMinZoomRatio,
|
|
730
|
+
setScale,
|
|
731
|
+
setIsScaledToFit,
|
|
732
|
+
setVizRef,
|
|
733
|
+
setIframeHeight,
|
|
734
|
+
setWrapperHeight,
|
|
735
|
+
setWrapperWidth,
|
|
736
|
+
viewId,
|
|
737
|
+
]);
|
|
738
|
+
return {
|
|
739
|
+
isRenderViz,
|
|
740
|
+
zoomRatio,
|
|
741
|
+
minZoomRatio,
|
|
742
|
+
widthScale,
|
|
743
|
+
isScaledToFit,
|
|
744
|
+
vizRef,
|
|
745
|
+
iframeHeight,
|
|
746
|
+
wrapperHeight,
|
|
747
|
+
wrapperWidth,
|
|
748
|
+
// Setters (auto-inject viewId)
|
|
749
|
+
...memoizedSetters,
|
|
750
|
+
};
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
const useHeatmapVizScrollmap = (props) => {
|
|
754
|
+
const viewId = props?.viewId || useViewId();
|
|
755
|
+
const zones = useHeatmapVizScrollmapStore((store) => store.zones[viewId] ?? []);
|
|
756
|
+
const hoveredZone = useHeatmapVizScrollmapStore((store) => store.hoveredZone[viewId] ?? null);
|
|
757
|
+
const showMinimap = useHeatmapVizScrollmapStore((store) => store.showMinimap[viewId] ?? true);
|
|
758
|
+
const setZonesStore = useHeatmapVizScrollmapStore((store) => store.setZones);
|
|
759
|
+
const setHoveredZoneStore = useHeatmapVizScrollmapStore((store) => store.setHoveredZone);
|
|
760
|
+
const setShowMinimapStore = useHeatmapVizScrollmapStore((store) => store.setShowMinimap);
|
|
761
|
+
const memoizedSetters = useMemo(() => ({
|
|
762
|
+
setZones: (newZones) => setZonesStore(newZones, viewId),
|
|
763
|
+
setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
|
|
764
|
+
setShowMinimap: (value) => setShowMinimapStore(value, viewId),
|
|
765
|
+
}), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
|
|
766
|
+
return {
|
|
767
|
+
zones,
|
|
768
|
+
hoveredZone,
|
|
769
|
+
showMinimap,
|
|
770
|
+
// Setters (auto-inject viewId)
|
|
771
|
+
...memoizedSetters,
|
|
772
|
+
};
|
|
773
|
+
};
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Hook to handle copying and clearing view data across all stores
|
|
777
|
+
*/
|
|
778
|
+
const useHeatmapCopyView = () => {
|
|
779
|
+
const copyDataView = useHeatmapDataStore((state) => state.copyView);
|
|
780
|
+
const copyVizView = useHeatmapVizStore((state) => state.copyView);
|
|
781
|
+
const copySingleView = useHeatmapSingleStore((state) => state.copyView);
|
|
782
|
+
const copyInteractionView = useHeatmapInteractionStore((state) => state.copyView);
|
|
783
|
+
const copyVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.copyView);
|
|
784
|
+
const clearDataView = useHeatmapDataStore((state) => state.clearView);
|
|
785
|
+
const clearVizView = useHeatmapVizStore((state) => state.clearView);
|
|
786
|
+
const clearSingleView = useHeatmapSingleStore((state) => state.clearView);
|
|
787
|
+
const clearInteractionView = useHeatmapInteractionStore((state) => state.clearView);
|
|
788
|
+
const clearVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.clearView);
|
|
789
|
+
const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
|
|
790
|
+
const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
|
|
791
|
+
const resetSingleAll = useHeatmapSingleStore((state) => state.resetAll);
|
|
792
|
+
const resetInteractionAll = useHeatmapInteractionStore((state) => state.resetAll);
|
|
793
|
+
const resetVizScrollmapAll = useHeatmapVizScrollmapStore((state) => state.resetAll);
|
|
794
|
+
const copyView = (fromViewId, toViewId) => {
|
|
795
|
+
copyDataView(fromViewId, toViewId);
|
|
796
|
+
copyVizView(fromViewId, toViewId);
|
|
797
|
+
copySingleView(fromViewId, toViewId);
|
|
798
|
+
copyInteractionView(fromViewId, toViewId);
|
|
799
|
+
copyVizScrollmapView(fromViewId, toViewId);
|
|
800
|
+
};
|
|
801
|
+
const copyViewToMultiple = (fromViewId, toViewIds) => {
|
|
802
|
+
toViewIds.forEach((toViewId) => {
|
|
803
|
+
copyView(fromViewId, toViewId);
|
|
804
|
+
});
|
|
805
|
+
};
|
|
806
|
+
const clearView = (viewId) => {
|
|
807
|
+
clearDataView(viewId);
|
|
808
|
+
clearVizView(viewId);
|
|
809
|
+
clearSingleView(viewId);
|
|
810
|
+
clearInteractionView(viewId);
|
|
811
|
+
clearVizScrollmapView(viewId);
|
|
812
|
+
};
|
|
813
|
+
const clearMultipleViews = (viewIds) => {
|
|
814
|
+
viewIds.forEach((viewId) => {
|
|
815
|
+
clearView(viewId);
|
|
816
|
+
});
|
|
817
|
+
};
|
|
818
|
+
const resetAll = () => {
|
|
819
|
+
resetDataAll();
|
|
820
|
+
resetVizAll();
|
|
821
|
+
resetSingleAll();
|
|
822
|
+
resetInteractionAll();
|
|
823
|
+
resetVizScrollmapAll();
|
|
824
|
+
};
|
|
825
|
+
return {
|
|
826
|
+
copyView,
|
|
827
|
+
copyViewToMultiple,
|
|
828
|
+
clearView,
|
|
829
|
+
clearMultipleViews,
|
|
830
|
+
resetAll,
|
|
520
831
|
};
|
|
521
832
|
};
|
|
522
833
|
|
|
@@ -777,33 +1088,29 @@ const buildElementInfo = (hash, rect, heatmapInfo) => {
|
|
|
777
1088
|
};
|
|
778
1089
|
|
|
779
1090
|
function findLastSizeOfDom(data) {
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
1091
|
+
const listDocs = data
|
|
1092
|
+
.filter((item) => item.doc?.find((doc) => doc.data.width && doc.data.height))
|
|
1093
|
+
.flatMap((item) => item.doc?.flatMap((doc) => doc.data));
|
|
1094
|
+
const lastDoc = listDocs?.[listDocs.length - 1];
|
|
783
1095
|
const docSize = {
|
|
784
|
-
width:
|
|
785
|
-
height:
|
|
1096
|
+
width: lastDoc?.width,
|
|
1097
|
+
height: lastDoc?.height,
|
|
786
1098
|
};
|
|
787
|
-
const
|
|
788
|
-
const
|
|
789
|
-
const lastResizeEvent = reversedData.find((item) => !!item.resize);
|
|
790
|
-
const firstEventResize = lastResizeEvent?.resize?.[0];
|
|
1099
|
+
const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
|
|
1100
|
+
const lastResizeEvent = listResizes?.[listResizes.length - 1];
|
|
791
1101
|
const resize = {
|
|
792
|
-
width:
|
|
793
|
-
height:
|
|
1102
|
+
width: lastResizeEvent?.data.width,
|
|
1103
|
+
height: lastResizeEvent?.data.height,
|
|
794
1104
|
};
|
|
795
1105
|
return {
|
|
796
1106
|
doc: docSize,
|
|
797
1107
|
resize: resize,
|
|
798
1108
|
size: {
|
|
799
|
-
width: resize.width
|
|
800
|
-
height: resize.height
|
|
1109
|
+
width: resize.width || docSize.width,
|
|
1110
|
+
height: resize.height || docSize.height,
|
|
801
1111
|
},
|
|
802
1112
|
};
|
|
803
1113
|
}
|
|
804
|
-
function sort(a, b) {
|
|
805
|
-
return a.time - b.time;
|
|
806
|
-
}
|
|
807
1114
|
function decodePayloads(payload) {
|
|
808
1115
|
try {
|
|
809
1116
|
return decode(payload);
|
|
@@ -1139,7 +1446,7 @@ class IframeStyleReplacer {
|
|
|
1139
1446
|
doc;
|
|
1140
1447
|
win;
|
|
1141
1448
|
config;
|
|
1142
|
-
regex = /([-.\d]+)(vh|svh|lvh|dvh
|
|
1449
|
+
regex = /([-.\d]+)(vh|svh|lvh|dvh)/gi; //vw|svw|lvw|dvw
|
|
1143
1450
|
constructor(iframe, config) {
|
|
1144
1451
|
if (!iframe.contentDocument || !iframe.contentWindow) {
|
|
1145
1452
|
throw new Error('Iframe document or window not accessible');
|
|
@@ -1417,48 +1724,6 @@ function initIframeHelperFixer(config) {
|
|
|
1417
1724
|
return fixer;
|
|
1418
1725
|
}
|
|
1419
1726
|
|
|
1420
|
-
const useHeatmapViz = (props) => {
|
|
1421
|
-
const viewId = props?.viewId || useViewId();
|
|
1422
|
-
// Viz store
|
|
1423
|
-
const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
|
|
1424
|
-
const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
|
|
1425
|
-
const minZoomRatio = useHeatmapVizStore((state) => state.minZoomRatio[viewId] ?? 10);
|
|
1426
|
-
const widthScale = useHeatmapVizStore((state) => state.scale[viewId] ?? 1);
|
|
1427
|
-
const isScaledToFit = useHeatmapVizStore((state) => state.isScaledToFit[viewId] ?? false);
|
|
1428
|
-
const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
|
|
1429
|
-
const setZoomRatio = useHeatmapVizStore((state) => state.setZoomRatio);
|
|
1430
|
-
const setMinZoomRatio = useHeatmapVizStore((state) => state.setMinZoomRatio);
|
|
1431
|
-
const setScale = useHeatmapVizStore((state) => state.setScale);
|
|
1432
|
-
const setIsScaledToFit = useHeatmapVizStore((state) => state.setIsScaledToFit);
|
|
1433
|
-
// Single store
|
|
1434
|
-
const vizRef = useHeatmapSingleStore((state) => state.vizRef[viewId] ?? null);
|
|
1435
|
-
const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight[viewId] ?? 0);
|
|
1436
|
-
const wrapperHeight = useHeatmapSingleStore((state) => state.wrapperHeight[viewId] ?? 0);
|
|
1437
|
-
const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
|
|
1438
|
-
const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
|
|
1439
|
-
const setWrapperHeight = useHeatmapSingleStore((state) => state.setWrapperHeight);
|
|
1440
|
-
return {
|
|
1441
|
-
// State
|
|
1442
|
-
isRenderViz,
|
|
1443
|
-
zoomRatio,
|
|
1444
|
-
minZoomRatio,
|
|
1445
|
-
widthScale,
|
|
1446
|
-
isScaledToFit,
|
|
1447
|
-
vizRef,
|
|
1448
|
-
iframeHeight,
|
|
1449
|
-
wrapperHeight,
|
|
1450
|
-
// Setters (auto-inject viewId)
|
|
1451
|
-
setIsRenderViz: (value) => setIsRenderViz(value, viewId),
|
|
1452
|
-
setZoomRatio: (value) => setZoomRatio(value, viewId),
|
|
1453
|
-
setMinZoomRatio: (value) => setMinZoomRatio(value, viewId),
|
|
1454
|
-
setScale: (value) => setScale(value, viewId),
|
|
1455
|
-
setIsScaledToFit: (value) => setIsScaledToFit(value, viewId),
|
|
1456
|
-
setVizRef: (value) => setVizRef(value, viewId),
|
|
1457
|
-
setIframeHeight: (value) => setIframeHeight(value, viewId),
|
|
1458
|
-
setWrapperHeight: (value) => setWrapperHeight(value, viewId),
|
|
1459
|
-
};
|
|
1460
|
-
};
|
|
1461
|
-
|
|
1462
1727
|
const scrollToElementIfNeeded = (visualRef, rect, scale) => {
|
|
1463
1728
|
if (!visualRef.current)
|
|
1464
1729
|
return;
|
|
@@ -1477,9 +1742,7 @@ const scrollToElementIfNeeded = (visualRef, rect, scale) => {
|
|
|
1477
1742
|
});
|
|
1478
1743
|
};
|
|
1479
1744
|
const useClickedElement = ({ visualRef, getRect }) => {
|
|
1480
|
-
const selectedElement =
|
|
1481
|
-
const shouldShowCallout = useHeatmapInteractionStore((state) => state.shouldShowCallout);
|
|
1482
|
-
const setShouldShowCallout = useHeatmapInteractionStore((state) => state.setShouldShowCallout);
|
|
1745
|
+
const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapInteraction();
|
|
1483
1746
|
const { widthScale } = useHeatmapViz();
|
|
1484
1747
|
const { dataInfo } = useHeatmapData();
|
|
1485
1748
|
const [clickedElement, setClickedElement] = useState(null);
|
|
@@ -1490,6 +1753,8 @@ const useClickedElement = ({ visualRef, getRect }) => {
|
|
|
1490
1753
|
setShouldShowCallout(false);
|
|
1491
1754
|
};
|
|
1492
1755
|
useEffect(() => {
|
|
1756
|
+
if (selectedElement === clickedElement?.hash)
|
|
1757
|
+
return;
|
|
1493
1758
|
if (!selectedElement || !dataInfo?.elementMapInfo) {
|
|
1494
1759
|
reset();
|
|
1495
1760
|
return;
|
|
@@ -1515,13 +1780,12 @@ const useClickedElement = ({ visualRef, getRect }) => {
|
|
|
1515
1780
|
requestAnimationFrame(() => {
|
|
1516
1781
|
setClickedElement(elementInfo);
|
|
1517
1782
|
});
|
|
1518
|
-
}, [selectedElement, dataInfo,
|
|
1519
|
-
return { clickedElement, showMissingElement, shouldShowCallout
|
|
1783
|
+
}, [selectedElement, dataInfo, visualRef, widthScale]);
|
|
1784
|
+
return { clickedElement, showMissingElement, shouldShowCallout };
|
|
1520
1785
|
};
|
|
1521
1786
|
|
|
1522
1787
|
const useElementCalloutVisible = ({ visualRef, getRect }) => {
|
|
1523
|
-
const selectedElement =
|
|
1524
|
-
const setShouldShowCallout = useHeatmapInteractionStore((state) => state.setShouldShowCallout);
|
|
1788
|
+
const { selectedElement, setShouldShowCallout } = useHeatmapInteraction();
|
|
1525
1789
|
const { widthScale } = useHeatmapViz();
|
|
1526
1790
|
const { dataInfo } = useHeatmapData();
|
|
1527
1791
|
useEffect(() => {
|
|
@@ -1547,26 +1811,26 @@ const useElementCalloutVisible = ({ visualRef, getRect }) => {
|
|
|
1547
1811
|
window.removeEventListener('resize', handleUpdate);
|
|
1548
1812
|
visualRef?.current?.removeEventListener('scroll', handleUpdate);
|
|
1549
1813
|
};
|
|
1550
|
-
}, [selectedElement, visualRef,
|
|
1814
|
+
}, [selectedElement, visualRef, widthScale, dataInfo]);
|
|
1551
1815
|
return {};
|
|
1552
1816
|
};
|
|
1553
1817
|
|
|
1554
|
-
const useHeatmapEffects = ({ isVisible
|
|
1555
|
-
|
|
1818
|
+
const useHeatmapEffects = ({ isVisible }) => {
|
|
1819
|
+
useHeatmapInteraction();
|
|
1820
|
+
const resetAll = () => {
|
|
1821
|
+
// setShouldShowCallout(false);
|
|
1822
|
+
};
|
|
1556
1823
|
// Reset khi ẩn
|
|
1557
1824
|
useEffect(() => {
|
|
1558
|
-
if (!isVisible)
|
|
1559
|
-
resetAll();
|
|
1560
1825
|
}, [isVisible, resetAll]);
|
|
1561
1826
|
// Ẩn callout khi sidebar mở
|
|
1562
|
-
useEffect(() => {
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
}, [isElementSidebarOpen, selectedElement, setShouldShowCallout]);
|
|
1827
|
+
// useEffect(() => {
|
|
1828
|
+
// if (isElementSidebarOpen && selectedElement) {
|
|
1829
|
+
// setShouldShowCallout(false);
|
|
1830
|
+
// } else if (!isElementSidebarOpen && selectedElement) {
|
|
1831
|
+
// setShouldShowCallout(true);
|
|
1832
|
+
// }
|
|
1833
|
+
// }, [isElementSidebarOpen, selectedElement]);
|
|
1570
1834
|
};
|
|
1571
1835
|
|
|
1572
1836
|
const useHeatmapElementPosition = ({ iframeRef, wrapperRef, visualizer }) => {
|
|
@@ -1722,9 +1986,7 @@ function HeatmapComponent() {
|
|
|
1722
1986
|
*/
|
|
1723
1987
|
|
|
1724
1988
|
const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
1725
|
-
const hoveredElement =
|
|
1726
|
-
const setHoveredElement = useHeatmapInteractionStore((state) => state.setHoveredElement);
|
|
1727
|
-
const onSelect = useHeatmapInteractionStore((state) => state.setSelectedElement);
|
|
1989
|
+
const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapInteraction();
|
|
1728
1990
|
const { widthScale } = useHeatmapViz();
|
|
1729
1991
|
const { dataInfo } = useHeatmapData();
|
|
1730
1992
|
const reset = useCallback(() => {
|
|
@@ -1739,7 +2001,15 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
|
1739
2001
|
return;
|
|
1740
2002
|
}
|
|
1741
2003
|
const iframe = iframeRef.current;
|
|
2004
|
+
if (!iframe) {
|
|
2005
|
+
reset();
|
|
2006
|
+
return;
|
|
2007
|
+
}
|
|
1742
2008
|
const doc = iframe.contentDocument;
|
|
2009
|
+
if (!doc) {
|
|
2010
|
+
reset();
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
1743
2013
|
const iframeRect = iframe.getBoundingClientRect();
|
|
1744
2014
|
const { x, y } = convertViewportToIframeCoords(event.clientX, event.clientY, iframeRect, widthScale);
|
|
1745
2015
|
const targetElement = findTargetElement(doc, x, y, dataInfo);
|
|
@@ -1775,7 +2045,7 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
|
1775
2045
|
const handleClick = useCallback(() => {
|
|
1776
2046
|
if (!hoveredElement?.hash)
|
|
1777
2047
|
return;
|
|
1778
|
-
|
|
2048
|
+
setSelectedElement(hoveredElement.hash);
|
|
1779
2049
|
}, [hoveredElement?.hash]);
|
|
1780
2050
|
return {
|
|
1781
2051
|
hoveredElement,
|
|
@@ -1887,7 +2157,7 @@ function useVizLiveIframeMsg(options = {}) {
|
|
|
1887
2157
|
}
|
|
1888
2158
|
|
|
1889
2159
|
function useVizLiveRender() {
|
|
1890
|
-
const { setIframeHeight, wrapperHeight, setIsRenderViz } = useHeatmapViz();
|
|
2160
|
+
const { setIframeHeight, wrapperHeight, setIsRenderViz, wrapperWidth } = useHeatmapViz();
|
|
1891
2161
|
const contentWidth = useHeatmapConfigStore((state) => state.width);
|
|
1892
2162
|
const htmlContent = useHeatmapLiveStore((state) => state.htmlContent);
|
|
1893
2163
|
const { iframeRef, isReady } = useVizLiveIframeMsg();
|
|
@@ -1905,17 +2175,20 @@ function useVizLiveRender() {
|
|
|
1905
2175
|
useEffect(() => {
|
|
1906
2176
|
if (!isReady)
|
|
1907
2177
|
return;
|
|
2178
|
+
if (!wrapperHeight)
|
|
2179
|
+
return;
|
|
1908
2180
|
if (!iframeRef.current)
|
|
1909
2181
|
return;
|
|
2182
|
+
console.log(`🚀 🐥 ~ useVizLiveRender ~ wrapperHeight:`, wrapperHeight);
|
|
1910
2183
|
const iframe = iframeRef.current;
|
|
1911
2184
|
if (!iframe || !htmlContent)
|
|
1912
2185
|
return;
|
|
1913
2186
|
setIsRenderViz(false);
|
|
1914
|
-
reset(iframe, { width:
|
|
2187
|
+
reset(iframe, { width: wrapperWidth, height: wrapperHeight }, (height) => {
|
|
1915
2188
|
height && setIframeHeight(height);
|
|
1916
2189
|
setIsRenderViz(true);
|
|
1917
2190
|
});
|
|
1918
|
-
}, [isReady, contentWidth, wrapperHeight]);
|
|
2191
|
+
}, [isReady, contentWidth, wrapperHeight, wrapperWidth]);
|
|
1919
2192
|
return {
|
|
1920
2193
|
iframeRef,
|
|
1921
2194
|
};
|
|
@@ -1936,12 +2209,17 @@ function reset(iframe, rect, onSuccess) {
|
|
|
1936
2209
|
|
|
1937
2210
|
const useHeatmapRender = () => {
|
|
1938
2211
|
const { data } = useHeatmapData();
|
|
1939
|
-
const { vizRef, setVizRef, setIsRenderViz, setIframeHeight } = useHeatmapViz();
|
|
2212
|
+
const { vizRef, setVizRef, setIsRenderViz, setIframeHeight, wrapperHeight, wrapperWidth } = useHeatmapViz();
|
|
2213
|
+
console.log(`🚀 🐥 ~ useHeatmapRender ~ wrapperHeight:`, wrapperHeight);
|
|
1940
2214
|
const iframeRef = useRef(null);
|
|
1941
2215
|
const renderHeatmap = useCallback(async (payloads) => {
|
|
1942
2216
|
if (!payloads || payloads.length === 0)
|
|
1943
2217
|
return;
|
|
1944
|
-
|
|
2218
|
+
let visualizer = vizRef;
|
|
2219
|
+
if (!visualizer) {
|
|
2220
|
+
visualizer = new Visualizer();
|
|
2221
|
+
setVizRef(visualizer);
|
|
2222
|
+
}
|
|
1945
2223
|
setIsRenderViz(false);
|
|
1946
2224
|
const iframe = iframeRef.current;
|
|
1947
2225
|
if (!iframe?.contentWindow)
|
|
@@ -1950,7 +2228,6 @@ const useHeatmapRender = () => {
|
|
|
1950
2228
|
initIframe(iframe, payloads, (height) => {
|
|
1951
2229
|
height && setIframeHeight(height);
|
|
1952
2230
|
setIsRenderViz(true);
|
|
1953
|
-
setVizRef(visualizer);
|
|
1954
2231
|
});
|
|
1955
2232
|
}, []);
|
|
1956
2233
|
useEffect(() => {
|
|
@@ -1965,7 +2242,9 @@ const useHeatmapRender = () => {
|
|
|
1965
2242
|
iframeRef,
|
|
1966
2243
|
};
|
|
1967
2244
|
};
|
|
1968
|
-
function initIframe(iframe,
|
|
2245
|
+
function initIframe(iframe,
|
|
2246
|
+
// size: { width: number; height: number },
|
|
2247
|
+
payloads, onSuccess) {
|
|
1969
2248
|
const { size } = findLastSizeOfDom(payloads);
|
|
1970
2249
|
const docWidth = size.width ?? 0;
|
|
1971
2250
|
const docHeight = size.height ?? 0;
|
|
@@ -2164,7 +2443,8 @@ const useContentDimensions = ({ iframeRef, }) => {
|
|
|
2164
2443
|
};
|
|
2165
2444
|
|
|
2166
2445
|
const useObserveIframeHeight = (props) => {
|
|
2167
|
-
const { iframeRef,
|
|
2446
|
+
const { iframeRef, isRenderViz } = props;
|
|
2447
|
+
const { setIframeHeight } = useHeatmapViz();
|
|
2168
2448
|
const resizeObserverRef = useRef(null);
|
|
2169
2449
|
const mutationObserverRef = useRef(null);
|
|
2170
2450
|
const debounceTimerRef = useRef(null);
|
|
@@ -2172,7 +2452,7 @@ const useObserveIframeHeight = (props) => {
|
|
|
2172
2452
|
const animationFrameRef = useRef(null);
|
|
2173
2453
|
const updateIframeHeight = useCallback(() => {
|
|
2174
2454
|
const iframe = iframeRef.current;
|
|
2175
|
-
if (!iframe
|
|
2455
|
+
if (!iframe)
|
|
2176
2456
|
return;
|
|
2177
2457
|
try {
|
|
2178
2458
|
const iframeDocument = iframe.contentDocument;
|
|
@@ -2180,7 +2460,7 @@ const useObserveIframeHeight = (props) => {
|
|
|
2180
2460
|
const iframeDocumentElement = iframeDocument?.documentElement;
|
|
2181
2461
|
if (!iframeBody || !iframeDocumentElement)
|
|
2182
2462
|
return;
|
|
2183
|
-
iframe.style.height = 'auto';
|
|
2463
|
+
// iframe.style.height = 'auto'; // TODO: check if this is needed
|
|
2184
2464
|
requestAnimationFrame(() => {
|
|
2185
2465
|
const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
|
|
2186
2466
|
const documentHeight = Math.max(iframeDocumentElement.scrollHeight, iframeDocumentElement.offsetHeight, iframeDocumentElement.clientHeight);
|
|
@@ -2196,7 +2476,7 @@ const useObserveIframeHeight = (props) => {
|
|
|
2196
2476
|
catch (error) {
|
|
2197
2477
|
console.warn('Cannot measure iframe content:', error);
|
|
2198
2478
|
}
|
|
2199
|
-
}, [iframeRef
|
|
2479
|
+
}, [iframeRef]);
|
|
2200
2480
|
const debouncedUpdate = useCallback(() => {
|
|
2201
2481
|
// Cancel pending updates
|
|
2202
2482
|
if (debounceTimerRef.current) {
|
|
@@ -2298,7 +2578,7 @@ const useScaleCalculation = (props) => {
|
|
|
2298
2578
|
const availableWidth = containerWidth - HEATMAP_CONFIG['padding'] * 2;
|
|
2299
2579
|
const widthScale = Math.min(availableWidth / contentWidth, 1);
|
|
2300
2580
|
// 2. Calculate available height
|
|
2301
|
-
const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] ;
|
|
2581
|
+
const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] || 0;
|
|
2302
2582
|
const paddingTotal = HEATMAP_CONFIG['padding'] * 2;
|
|
2303
2583
|
const availableHeight = containerHeight - toolbarHeight - paddingTotal; // 10px buffer to avoid scroll bar
|
|
2304
2584
|
// 3. Calculate minZoomRatio (zoom ratio minimum to fit iframe in container)
|
|
@@ -2354,13 +2634,13 @@ const useScrollSync = ({ widthScale, iframeRef, }) => {
|
|
|
2354
2634
|
};
|
|
2355
2635
|
|
|
2356
2636
|
const useHeatmapScale = (props) => {
|
|
2357
|
-
const { wrapperRef, iframeRef, iframeHeight,
|
|
2637
|
+
const { wrapperRef, iframeRef, iframeHeight, isRenderViz } = props;
|
|
2358
2638
|
// 1. Observe container dimensions
|
|
2359
2639
|
const { containerWidth, containerHeight } = useContainerDimensions({ wrapperRef });
|
|
2360
2640
|
// 2. Get content dimensions from config
|
|
2361
2641
|
const { contentWidth } = useContentDimensions({ iframeRef });
|
|
2362
2642
|
// 3. Observe iframe height (now reacts to width changes)
|
|
2363
|
-
useObserveIframeHeight({ iframeRef,
|
|
2643
|
+
useObserveIframeHeight({ iframeRef, isRenderViz });
|
|
2364
2644
|
// 4. Calculate scale
|
|
2365
2645
|
const { widthScale } = useScaleCalculation({
|
|
2366
2646
|
containerWidth,
|
|
@@ -2385,15 +2665,17 @@ const useWrapperRefHeight = (props) => {
|
|
|
2385
2665
|
const { isActive, wrapperRef } = props;
|
|
2386
2666
|
const resizeObserverRef = useRef(null);
|
|
2387
2667
|
const mutationObserverRef = useRef(null);
|
|
2388
|
-
const {
|
|
2668
|
+
const { setWrapperHeight, setWrapperWidth } = useHeatmapViz();
|
|
2389
2669
|
const updateWrapperHeight = useCallback(() => {
|
|
2390
2670
|
const wrapper = wrapperRef.current;
|
|
2391
2671
|
if (!wrapper)
|
|
2392
2672
|
return;
|
|
2393
2673
|
try {
|
|
2394
2674
|
const wrapperHeight = wrapper.offsetHeight;
|
|
2675
|
+
const wrapperWidth = wrapper.offsetWidth;
|
|
2395
2676
|
if (wrapperHeight > 0) {
|
|
2396
2677
|
setWrapperHeight(wrapperHeight);
|
|
2678
|
+
setWrapperWidth(wrapperWidth);
|
|
2397
2679
|
}
|
|
2398
2680
|
}
|
|
2399
2681
|
catch (error) {
|
|
@@ -2402,7 +2684,7 @@ const useWrapperRefHeight = (props) => {
|
|
|
2402
2684
|
}, [wrapperRef]);
|
|
2403
2685
|
useEffect(() => {
|
|
2404
2686
|
const wrapper = wrapperRef.current;
|
|
2405
|
-
if (!wrapper
|
|
2687
|
+
if (!wrapper)
|
|
2406
2688
|
return;
|
|
2407
2689
|
const setupObservers = () => {
|
|
2408
2690
|
try {
|
|
@@ -2432,6 +2714,7 @@ const useWrapperRefHeight = (props) => {
|
|
|
2432
2714
|
updateWrapperHeight();
|
|
2433
2715
|
}
|
|
2434
2716
|
catch (error) {
|
|
2717
|
+
console.log(`🚀 🐥 ~ setupObservers ~ error:`, error);
|
|
2435
2718
|
console.warn('Cannot access wrapper content:', error);
|
|
2436
2719
|
}
|
|
2437
2720
|
};
|
|
@@ -2446,7 +2729,7 @@ const useWrapperRefHeight = (props) => {
|
|
|
2446
2729
|
mutationObserverRef.current.disconnect();
|
|
2447
2730
|
}
|
|
2448
2731
|
};
|
|
2449
|
-
}, [wrapperRef,
|
|
2732
|
+
}, [wrapperRef, updateWrapperHeight, isActive]);
|
|
2450
2733
|
return {};
|
|
2451
2734
|
};
|
|
2452
2735
|
|
|
@@ -2580,184 +2863,513 @@ const getScrollGradientColor = (normalized) => {
|
|
|
2580
2863
|
return `rgb(${r}, ${g}, ${b})`;
|
|
2581
2864
|
};
|
|
2582
2865
|
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
|
|
2592
|
-
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
2602
|
-
|
|
2603
|
-
|
|
2866
|
+
class PerformanceLogger {
|
|
2867
|
+
static instance;
|
|
2868
|
+
metrics = [];
|
|
2869
|
+
sessionId;
|
|
2870
|
+
sessionStartTime;
|
|
2871
|
+
config;
|
|
2872
|
+
constructor() {
|
|
2873
|
+
this.sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
2874
|
+
this.sessionStartTime = Date.now();
|
|
2875
|
+
this.config = {
|
|
2876
|
+
enabled: false,
|
|
2877
|
+
logToConsole: false,
|
|
2878
|
+
logLevel: 'normal',
|
|
2879
|
+
thresholds: {
|
|
2880
|
+
slowRenderMs: 16, // > 16ms = slower than 60fps
|
|
2881
|
+
slowHookMs: 5,
|
|
2882
|
+
excessiveRenderCount: 10,
|
|
2883
|
+
},
|
|
2884
|
+
};
|
|
2885
|
+
}
|
|
2886
|
+
static getInstance() {
|
|
2887
|
+
if (!PerformanceLogger.instance) {
|
|
2888
|
+
PerformanceLogger.instance = new PerformanceLogger();
|
|
2889
|
+
}
|
|
2890
|
+
return PerformanceLogger.instance;
|
|
2891
|
+
}
|
|
2892
|
+
configure(config) {
|
|
2893
|
+
this.config = { ...this.config, ...config };
|
|
2894
|
+
if (this.config.enabled && this.config.logToConsole) {
|
|
2895
|
+
console.log('[Performance Monitor] Enabled', {
|
|
2896
|
+
sessionId: this.sessionId,
|
|
2897
|
+
config: this.config,
|
|
2898
|
+
});
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
log(metric) {
|
|
2902
|
+
if (!this.config.enabled)
|
|
2903
|
+
return;
|
|
2904
|
+
this.metrics.push(metric);
|
|
2905
|
+
// Log to console based on level
|
|
2906
|
+
if (this.config.logToConsole) {
|
|
2907
|
+
this.logToConsole(metric);
|
|
2908
|
+
}
|
|
2909
|
+
// Send to external logger if configured
|
|
2910
|
+
if (this.config.externalLogger) {
|
|
2911
|
+
this.config.externalLogger(metric);
|
|
2912
|
+
}
|
|
2913
|
+
// Check thresholds and warn
|
|
2914
|
+
this.checkThresholds(metric);
|
|
2915
|
+
}
|
|
2916
|
+
logToConsole(metric) {
|
|
2917
|
+
const { logLevel } = this.config;
|
|
2918
|
+
if (logLevel === 'minimal') {
|
|
2919
|
+
// Only log warnings
|
|
2920
|
+
return;
|
|
2921
|
+
}
|
|
2922
|
+
const style = this.getConsoleStyle(metric.type);
|
|
2923
|
+
const label = `[${metric.type.toUpperCase()}] ${metric.name}`;
|
|
2924
|
+
if (logLevel === 'verbose') {
|
|
2925
|
+
console.log(`%c${label}`, style, metric);
|
|
2926
|
+
}
|
|
2927
|
+
else {
|
|
2928
|
+
// Normal: Log compact info
|
|
2929
|
+
const info = { name: metric.name };
|
|
2930
|
+
if (metric.duration)
|
|
2931
|
+
info.duration = `${metric.duration.toFixed(2)}ms`;
|
|
2932
|
+
if ('viewId' in metric && metric.viewId)
|
|
2933
|
+
info.viewId = metric.viewId;
|
|
2934
|
+
if ('renderCount' in metric)
|
|
2935
|
+
info.renderCount = metric.renderCount;
|
|
2936
|
+
console.log(`%c${label}`, style, info);
|
|
2937
|
+
}
|
|
2938
|
+
}
|
|
2939
|
+
getConsoleStyle(type) {
|
|
2940
|
+
const styles = {
|
|
2941
|
+
render: 'color: #61dafb; font-weight: bold',
|
|
2942
|
+
hook: 'color: #ffa500; font-weight: bold',
|
|
2943
|
+
store: 'color: #9c27b0; font-weight: bold',
|
|
2944
|
+
function: 'color: #4caf50; font-weight: bold',
|
|
2945
|
+
};
|
|
2946
|
+
return styles[type] || '';
|
|
2947
|
+
}
|
|
2948
|
+
checkThresholds(metric) {
|
|
2949
|
+
const { thresholds } = this.config;
|
|
2950
|
+
// Check slow render
|
|
2951
|
+
if (metric.type === 'render' && metric.duration && metric.duration > thresholds.slowRenderMs) {
|
|
2952
|
+
console.warn(`[Performance] Slow render detected: ${metric.name} took ${metric.duration.toFixed(2)}ms`, metric);
|
|
2953
|
+
}
|
|
2954
|
+
// Check slow hook
|
|
2955
|
+
if (metric.type === 'hook' && metric.duration && metric.duration > thresholds.slowHookMs) {
|
|
2956
|
+
console.warn(`[Performance] Slow hook detected: ${metric.name} took ${metric.duration.toFixed(2)}ms`, metric);
|
|
2957
|
+
}
|
|
2958
|
+
// Check excessive renders
|
|
2959
|
+
if (metric.type === 'render' && 'renderCount' in metric) {
|
|
2960
|
+
const renderMetric = metric;
|
|
2961
|
+
if (renderMetric.renderCount > thresholds.excessiveRenderCount) {
|
|
2962
|
+
console.warn(`[Performance] Excessive renders: ${metric.name} has rendered ${renderMetric.renderCount} times`, metric);
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
generateReport() {
|
|
2967
|
+
const now = Date.now();
|
|
2968
|
+
const duration = now - this.sessionStartTime;
|
|
2969
|
+
// Calculate summary
|
|
2970
|
+
const renderMetrics = this.metrics.filter((m) => m.type === 'render');
|
|
2971
|
+
const hookMetrics = this.metrics.filter((m) => m.type === 'hook');
|
|
2972
|
+
const storeMetrics = this.metrics.filter((m) => m.type === 'store');
|
|
2973
|
+
const totalRenders = renderMetrics.length;
|
|
2974
|
+
const totalHookCalls = hookMetrics.length;
|
|
2975
|
+
const totalStoreUpdates = storeMetrics.length;
|
|
2976
|
+
const renderDurations = renderMetrics
|
|
2977
|
+
.filter((m) => m.duration)
|
|
2978
|
+
.map((m) => m.duration);
|
|
2979
|
+
const averageRenderTime = renderDurations.length > 0
|
|
2980
|
+
? renderDurations.reduce((a, b) => a + b, 0) / renderDurations.length
|
|
2981
|
+
: 0;
|
|
2982
|
+
// View-specific metrics
|
|
2983
|
+
const viewMetrics = {};
|
|
2984
|
+
this.metrics.forEach((metric) => {
|
|
2985
|
+
const viewId = 'viewId' in metric && metric.viewId ? metric.viewId : DEFAULT_VIEW_ID;
|
|
2986
|
+
if (!viewMetrics[viewId]) {
|
|
2987
|
+
viewMetrics[viewId] = { renders: 0, hookCalls: 0, storeUpdates: 0 };
|
|
2988
|
+
}
|
|
2989
|
+
if (metric.type === 'render')
|
|
2990
|
+
viewMetrics[viewId].renders++;
|
|
2991
|
+
if (metric.type === 'hook')
|
|
2992
|
+
viewMetrics[viewId].hookCalls++;
|
|
2993
|
+
if (metric.type === 'store')
|
|
2994
|
+
viewMetrics[viewId].storeUpdates++;
|
|
2995
|
+
});
|
|
2996
|
+
// Find warnings
|
|
2997
|
+
const renderCounts = new Map();
|
|
2998
|
+
renderMetrics.forEach((m) => {
|
|
2999
|
+
const key = `${m.name}-${'viewId' in m ? m.viewId : DEFAULT_VIEW_ID}`;
|
|
3000
|
+
renderCounts.set(key, (renderCounts.get(key) || 0) + 1);
|
|
3001
|
+
});
|
|
3002
|
+
const excessiveRenders = Array.from(renderCounts.entries())
|
|
3003
|
+
.filter(([, count]) => count > this.config.thresholds.excessiveRenderCount)
|
|
3004
|
+
.map(([key, count]) => {
|
|
3005
|
+
const [component, viewId] = key.split('-');
|
|
3006
|
+
return { component, count, viewId: viewId !== DEFAULT_VIEW_ID ? viewId : undefined };
|
|
3007
|
+
});
|
|
3008
|
+
const slowOperations = this.metrics
|
|
3009
|
+
.filter((m) => m.duration &&
|
|
3010
|
+
((m.type === 'render' && m.duration > this.config.thresholds.slowRenderMs) ||
|
|
3011
|
+
(m.type === 'hook' && m.duration > this.config.thresholds.slowHookMs)))
|
|
3012
|
+
.map((m) => ({
|
|
3013
|
+
name: m.name,
|
|
3014
|
+
duration: m.duration,
|
|
3015
|
+
type: m.type,
|
|
3016
|
+
}));
|
|
2604
3017
|
return {
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
3018
|
+
session: {
|
|
3019
|
+
id: this.sessionId,
|
|
3020
|
+
startTime: this.sessionStartTime,
|
|
3021
|
+
endTime: now,
|
|
3022
|
+
duration,
|
|
3023
|
+
},
|
|
3024
|
+
summary: {
|
|
3025
|
+
totalRenders,
|
|
3026
|
+
totalHookCalls,
|
|
3027
|
+
totalStoreUpdates,
|
|
3028
|
+
averageRenderTime,
|
|
3029
|
+
viewMetrics,
|
|
3030
|
+
},
|
|
3031
|
+
metrics: this.metrics,
|
|
3032
|
+
warnings: {
|
|
3033
|
+
excessiveRenders,
|
|
3034
|
+
slowOperations,
|
|
3035
|
+
},
|
|
2609
3036
|
};
|
|
2610
3037
|
}
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
}
|
|
3038
|
+
clearMetrics() {
|
|
3039
|
+
this.metrics = [];
|
|
3040
|
+
this.sessionId = `session-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
3041
|
+
this.sessionStartTime = Date.now();
|
|
3042
|
+
}
|
|
3043
|
+
getMetrics() {
|
|
3044
|
+
return [...this.metrics];
|
|
3045
|
+
}
|
|
3046
|
+
isEnabled() {
|
|
3047
|
+
return this.config.enabled;
|
|
3048
|
+
}
|
|
3049
|
+
}
|
|
3050
|
+
const performanceLogger = PerformanceLogger.getInstance();
|
|
3051
|
+
|
|
2619
3052
|
/**
|
|
2620
|
-
* Hook
|
|
2621
|
-
*
|
|
2622
|
-
*
|
|
3053
|
+
* Hook to track render count of a component
|
|
3054
|
+
* @param componentName - Name of the component
|
|
3055
|
+
* @param viewId - Optional viewId for compare mode
|
|
2623
3056
|
*/
|
|
2624
|
-
|
|
2625
|
-
const
|
|
2626
|
-
const
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
3057
|
+
function useRenderCount(componentName, viewId) {
|
|
3058
|
+
const renderCount = useRef(0);
|
|
3059
|
+
const startTime = useRef(0);
|
|
3060
|
+
// Increment before render
|
|
3061
|
+
renderCount.current += 1;
|
|
3062
|
+
startTime.current = performance.now();
|
|
3063
|
+
useEffect(() => {
|
|
3064
|
+
const duration = performance.now() - startTime.current;
|
|
3065
|
+
const metric = {
|
|
3066
|
+
id: `render-${componentName}-${Date.now()}`,
|
|
3067
|
+
type: 'render',
|
|
3068
|
+
name: componentName,
|
|
3069
|
+
componentName,
|
|
3070
|
+
renderCount: renderCount.current,
|
|
3071
|
+
timestamp: Date.now(),
|
|
3072
|
+
duration,
|
|
3073
|
+
viewId,
|
|
2636
3074
|
};
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
3075
|
+
performanceLogger.log(metric);
|
|
3076
|
+
});
|
|
3077
|
+
return renderCount.current;
|
|
3078
|
+
}
|
|
3079
|
+
/**
|
|
3080
|
+
* Hook to detect why a component re-rendered (which props changed)
|
|
3081
|
+
* @param componentName - Name of the component
|
|
3082
|
+
* @param props - Props object to track
|
|
3083
|
+
* @param viewId - Optional viewId
|
|
3084
|
+
*/
|
|
3085
|
+
function useWhyDidYouUpdate(componentName, props, viewId) {
|
|
3086
|
+
const previousProps = useRef();
|
|
3087
|
+
const renderCount = useRef(0);
|
|
3088
|
+
const startTime = useRef(0);
|
|
3089
|
+
renderCount.current += 1;
|
|
3090
|
+
startTime.current = performance.now();
|
|
3091
|
+
useEffect(() => {
|
|
3092
|
+
if (previousProps.current) {
|
|
3093
|
+
const duration = performance.now() - startTime.current;
|
|
3094
|
+
const allKeys = Object.keys({ ...previousProps.current, ...props });
|
|
3095
|
+
const changedProps = [];
|
|
3096
|
+
allKeys.forEach((key) => {
|
|
3097
|
+
if (previousProps.current[key] !== props[key]) {
|
|
3098
|
+
changedProps.push(key);
|
|
3099
|
+
}
|
|
3100
|
+
});
|
|
3101
|
+
if (changedProps.length > 0) {
|
|
3102
|
+
const metric = {
|
|
3103
|
+
id: `render-${componentName}-${Date.now()}`,
|
|
3104
|
+
type: 'render',
|
|
3105
|
+
name: componentName,
|
|
3106
|
+
componentName,
|
|
3107
|
+
renderCount: renderCount.current,
|
|
3108
|
+
timestamp: Date.now(),
|
|
3109
|
+
duration,
|
|
3110
|
+
viewId,
|
|
3111
|
+
reason: 'Props changed',
|
|
3112
|
+
propsChanged: changedProps,
|
|
3113
|
+
metadata: {
|
|
3114
|
+
changes: changedProps.reduce((acc, key) => {
|
|
3115
|
+
acc[key] = {
|
|
3116
|
+
from: previousProps.current[key],
|
|
3117
|
+
to: props[key],
|
|
3118
|
+
};
|
|
3119
|
+
return acc;
|
|
3120
|
+
}, {}),
|
|
3121
|
+
},
|
|
3122
|
+
};
|
|
3123
|
+
performanceLogger.log(metric);
|
|
3124
|
+
}
|
|
3125
|
+
}
|
|
3126
|
+
previousProps.current = props;
|
|
3127
|
+
});
|
|
3128
|
+
}
|
|
3129
|
+
/**
|
|
3130
|
+
* Hook to measure execution time of a function
|
|
3131
|
+
* @param functionName - Name of the function
|
|
3132
|
+
* @param fn - Function to measure
|
|
3133
|
+
* @param viewId - Optional viewId
|
|
3134
|
+
*/
|
|
3135
|
+
function useMeasureFunction(functionName, fn, viewId) {
|
|
3136
|
+
const measuredFn = ((...args) => {
|
|
3137
|
+
const startTime = performance.now();
|
|
3138
|
+
const result = fn(...args);
|
|
3139
|
+
const duration = performance.now() - startTime;
|
|
3140
|
+
performanceLogger.log({
|
|
3141
|
+
id: `function-${functionName}-${Date.now()}`,
|
|
3142
|
+
type: 'function',
|
|
3143
|
+
name: functionName,
|
|
3144
|
+
functionName,
|
|
3145
|
+
timestamp: Date.now(),
|
|
3146
|
+
duration,
|
|
3147
|
+
viewId,
|
|
3148
|
+
metadata: {
|
|
3149
|
+
args: args.length,
|
|
3150
|
+
},
|
|
3151
|
+
});
|
|
3152
|
+
return result;
|
|
3153
|
+
});
|
|
3154
|
+
return measuredFn;
|
|
3155
|
+
}
|
|
3156
|
+
/**
|
|
3157
|
+
* Hook to track when a hook is called
|
|
3158
|
+
* @param hookName - Name of the hook
|
|
3159
|
+
* @param viewId - Optional viewId
|
|
3160
|
+
* @param storeSlice - Optional store slice being accessed
|
|
3161
|
+
*/
|
|
3162
|
+
function useTrackHookCall(hookName, viewId, storeSlice) {
|
|
3163
|
+
const startTime = useRef(0);
|
|
3164
|
+
startTime.current = performance.now();
|
|
3165
|
+
useEffect(() => {
|
|
3166
|
+
const duration = performance.now() - startTime.current;
|
|
3167
|
+
performanceLogger.log({
|
|
3168
|
+
id: `hook-${hookName}-${Date.now()}`,
|
|
3169
|
+
type: 'hook',
|
|
3170
|
+
name: hookName,
|
|
3171
|
+
hookName,
|
|
3172
|
+
timestamp: Date.now(),
|
|
3173
|
+
duration,
|
|
3174
|
+
viewId,
|
|
3175
|
+
storeSlice,
|
|
3176
|
+
});
|
|
3177
|
+
});
|
|
3178
|
+
}
|
|
3179
|
+
|
|
3180
|
+
/**
|
|
3181
|
+
* HOC to track component performance
|
|
3182
|
+
* @param Component - Component to wrap
|
|
3183
|
+
* @param options - Tracking options
|
|
3184
|
+
*/
|
|
3185
|
+
function withPerformanceTracking(Component, options = {}) {
|
|
3186
|
+
const { trackProps = true, componentName, viewIdProp = 'viewId' } = options;
|
|
3187
|
+
const WrappedComponent = (props) => {
|
|
3188
|
+
const name = componentName || Component.displayName || Component.name || 'Unknown';
|
|
3189
|
+
const viewId = viewIdProp in props ? props[viewIdProp] : undefined;
|
|
3190
|
+
if (trackProps) {
|
|
3191
|
+
useWhyDidYouUpdate(name, props, viewId);
|
|
3192
|
+
}
|
|
3193
|
+
return jsx(Component, { ...props });
|
|
2643
3194
|
};
|
|
2644
|
-
}
|
|
3195
|
+
WrappedComponent.displayName = `withPerformanceTracking(${componentName || Component.displayName || Component.name})`;
|
|
3196
|
+
return WrappedComponent;
|
|
3197
|
+
}
|
|
2645
3198
|
|
|
2646
3199
|
/**
|
|
2647
|
-
*
|
|
2648
|
-
* otherwise returns config from global store
|
|
3200
|
+
* Middleware để track store updates
|
|
2649
3201
|
*/
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
3202
|
+
function createStorePerformanceTracker(storeName) {
|
|
3203
|
+
return (config) => (set, get, api) => {
|
|
3204
|
+
const wrappedSet = (partial, replace) => {
|
|
3205
|
+
const startTime = performance.now();
|
|
3206
|
+
const prevState = get();
|
|
3207
|
+
// Call original set
|
|
3208
|
+
set(partial, replace);
|
|
3209
|
+
const duration = performance.now() - startTime;
|
|
3210
|
+
const nextState = get();
|
|
3211
|
+
// Detect which viewIds were affected
|
|
3212
|
+
const affectedViews = new Set();
|
|
3213
|
+
// Check Record<string, any> properties for viewId keys
|
|
3214
|
+
Object.keys(nextState).forEach((key) => {
|
|
3215
|
+
if (typeof prevState[key] === 'object' &&
|
|
3216
|
+
typeof nextState[key] === 'object' &&
|
|
3217
|
+
prevState[key] !== nextState[key]) {
|
|
3218
|
+
// Check if this is a Record<viewId, value> structure
|
|
3219
|
+
const prevKeys = Object.keys(prevState[key] || {});
|
|
3220
|
+
const nextKeys = Object.keys(nextState[key] || {});
|
|
3221
|
+
const allKeys = new Set([...prevKeys, ...nextKeys]);
|
|
3222
|
+
allKeys.forEach((viewId) => {
|
|
3223
|
+
if (prevState[key]?.[viewId] !== nextState[key]?.[viewId]) {
|
|
3224
|
+
affectedViews.add(viewId);
|
|
3225
|
+
}
|
|
3226
|
+
});
|
|
3227
|
+
}
|
|
3228
|
+
});
|
|
3229
|
+
const metric = {
|
|
3230
|
+
id: `store-${storeName}-${Date.now()}`,
|
|
3231
|
+
type: 'store',
|
|
3232
|
+
name: `${storeName} update`,
|
|
3233
|
+
storeName,
|
|
3234
|
+
action: typeof partial === 'function' ? 'function update' : 'direct update',
|
|
3235
|
+
timestamp: Date.now(),
|
|
3236
|
+
duration,
|
|
3237
|
+
affectedViews: Array.from(affectedViews),
|
|
3238
|
+
metadata: {
|
|
3239
|
+
stateKeys: Object.keys(nextState),
|
|
3240
|
+
},
|
|
3241
|
+
};
|
|
3242
|
+
performanceLogger.log(metric);
|
|
2662
3243
|
};
|
|
2663
|
-
|
|
2664
|
-
// Otherwise use global store
|
|
2665
|
-
return {
|
|
2666
|
-
heatmapType: globalHeatmapType,
|
|
2667
|
-
clickType: globalClickType,
|
|
2668
|
-
scrollType: globalScrollType,
|
|
3244
|
+
return config(wrappedSet, get, api);
|
|
2669
3245
|
};
|
|
2670
|
-
}
|
|
3246
|
+
}
|
|
2671
3247
|
/**
|
|
2672
|
-
*
|
|
2673
|
-
* In compare mode, updates the view context
|
|
2674
|
-
* In single/live mode, updates the global store
|
|
3248
|
+
* Track specific store action
|
|
2675
3249
|
*/
|
|
2676
|
-
|
|
2677
|
-
const
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
};
|
|
2687
|
-
}
|
|
2688
|
-
return {
|
|
2689
|
-
setHeatmapType: setHeatmapTypeGlobal,
|
|
2690
|
-
setClickType: setClickTypeGlobal,
|
|
2691
|
-
setScrollType: setScrollTypeGlobal,
|
|
3250
|
+
function trackStoreAction(storeName, action, viewId, metadata) {
|
|
3251
|
+
const metric = {
|
|
3252
|
+
id: `store-${storeName}-${action}-${Date.now()}`,
|
|
3253
|
+
type: 'store',
|
|
3254
|
+
name: `${storeName}.${action}`,
|
|
3255
|
+
storeName,
|
|
3256
|
+
action,
|
|
3257
|
+
timestamp: Date.now(),
|
|
3258
|
+
viewId,
|
|
3259
|
+
metadata,
|
|
2692
3260
|
};
|
|
2693
|
-
|
|
3261
|
+
performanceLogger.log(metric);
|
|
3262
|
+
}
|
|
2694
3263
|
|
|
2695
3264
|
/**
|
|
2696
|
-
*
|
|
2697
|
-
* otherwise returns viz state from global store
|
|
3265
|
+
* Get performance report as JSON string
|
|
2698
3266
|
*/
|
|
2699
|
-
|
|
2700
|
-
const
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
const
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
3267
|
+
function getPerformanceReportJSON() {
|
|
3268
|
+
const report = performanceLogger.generateReport();
|
|
3269
|
+
return JSON.stringify(report, null, 2);
|
|
3270
|
+
}
|
|
3271
|
+
/**
|
|
3272
|
+
* Download performance report as JSON file
|
|
3273
|
+
*/
|
|
3274
|
+
function downloadPerformanceReport(filename = 'heatmap-performance-report.json') {
|
|
3275
|
+
const report = performanceLogger.generateReport();
|
|
3276
|
+
const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
|
|
3277
|
+
const url = URL.createObjectURL(blob);
|
|
3278
|
+
const link = document.createElement('a');
|
|
3279
|
+
link.href = url;
|
|
3280
|
+
link.download = filename;
|
|
3281
|
+
link.click();
|
|
3282
|
+
URL.revokeObjectURL(url);
|
|
3283
|
+
}
|
|
3284
|
+
/**
|
|
3285
|
+
* Send performance report to external endpoint
|
|
3286
|
+
*/
|
|
3287
|
+
async function sendPerformanceReport(endpoint) {
|
|
3288
|
+
const report = performanceLogger.generateReport();
|
|
3289
|
+
try {
|
|
3290
|
+
const response = await fetch(endpoint, {
|
|
3291
|
+
method: 'POST',
|
|
3292
|
+
headers: {
|
|
3293
|
+
'Content-Type': 'application/json',
|
|
3294
|
+
},
|
|
3295
|
+
body: JSON.stringify(report),
|
|
3296
|
+
});
|
|
3297
|
+
if (!response.ok) {
|
|
3298
|
+
throw new Error(`Failed to send report: ${response.statusText}`);
|
|
3299
|
+
}
|
|
3300
|
+
}
|
|
3301
|
+
catch (error) {
|
|
3302
|
+
console.error('[Performance] Failed to send report:', error);
|
|
3303
|
+
throw error;
|
|
2718
3304
|
}
|
|
2719
|
-
|
|
3305
|
+
}
|
|
3306
|
+
/**
|
|
3307
|
+
* Print performance summary to console
|
|
3308
|
+
*/
|
|
3309
|
+
function printPerformanceSummary() {
|
|
3310
|
+
const report = performanceLogger.generateReport();
|
|
3311
|
+
console.group('📊 Performance Summary');
|
|
3312
|
+
console.log('Session:', report.session);
|
|
3313
|
+
console.log('Total Renders:', report.summary.totalRenders);
|
|
3314
|
+
console.log('Total Hook Calls:', report.summary.totalHookCalls);
|
|
3315
|
+
console.log('Total Store Updates:', report.summary.totalStoreUpdates);
|
|
3316
|
+
console.log('Average Render Time:', `${report.summary.averageRenderTime.toFixed(2)}ms`);
|
|
3317
|
+
console.log('View Metrics:', report.summary.viewMetrics);
|
|
3318
|
+
if (report.warnings.excessiveRenders.length > 0) {
|
|
3319
|
+
console.group('⚠️ Excessive Renders');
|
|
3320
|
+
report.warnings.excessiveRenders.forEach((warning) => {
|
|
3321
|
+
console.warn(`${warning.component}${warning.viewId ? ` (${warning.viewId})` : ''}: ${warning.count} renders`);
|
|
3322
|
+
});
|
|
3323
|
+
console.groupEnd();
|
|
3324
|
+
}
|
|
3325
|
+
if (report.warnings.slowOperations.length > 0) {
|
|
3326
|
+
console.group('🐌 Slow Operations');
|
|
3327
|
+
report.warnings.slowOperations.forEach((warning) => {
|
|
3328
|
+
console.warn(`${warning.name} (${warning.type}): ${warning.duration.toFixed(2)}ms`);
|
|
3329
|
+
});
|
|
3330
|
+
console.groupEnd();
|
|
3331
|
+
}
|
|
3332
|
+
console.groupEnd();
|
|
3333
|
+
}
|
|
3334
|
+
/**
|
|
3335
|
+
* Get performance metrics filtered by viewId
|
|
3336
|
+
*/
|
|
3337
|
+
function getMetricsByViewId(viewId) {
|
|
3338
|
+
const allMetrics = performanceLogger.getMetrics();
|
|
3339
|
+
const filteredMetrics = allMetrics.filter((m) => {
|
|
3340
|
+
return 'viewId' in m && m.viewId === viewId;
|
|
3341
|
+
});
|
|
3342
|
+
const report = performanceLogger.generateReport();
|
|
2720
3343
|
return {
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
3344
|
+
...report,
|
|
3345
|
+
metrics: filteredMetrics,
|
|
3346
|
+
summary: {
|
|
3347
|
+
...report.summary,
|
|
3348
|
+
totalRenders: filteredMetrics.filter((m) => m.type === 'render').length,
|
|
3349
|
+
totalHookCalls: filteredMetrics.filter((m) => m.type === 'hook').length,
|
|
3350
|
+
totalStoreUpdates: filteredMetrics.filter((m) => m.type === 'store').length,
|
|
3351
|
+
averageRenderTime: 0, // Recalculate if needed
|
|
3352
|
+
viewMetrics: { [viewId]: report.summary.viewMetrics[viewId] || { renders: 0, hookCalls: 0, storeUpdates: 0 } },
|
|
3353
|
+
},
|
|
2727
3354
|
};
|
|
2728
|
-
}
|
|
3355
|
+
}
|
|
2729
3356
|
/**
|
|
2730
|
-
*
|
|
2731
|
-
* In compare mode, updates the view context
|
|
2732
|
-
* In single/live mode, updates the global store
|
|
3357
|
+
* Compare performance between two viewIds
|
|
2733
3358
|
*/
|
|
2734
|
-
|
|
2735
|
-
const
|
|
2736
|
-
const
|
|
2737
|
-
const
|
|
2738
|
-
const setIsScaledToFitGlobal = useHeatmapVizStore((state) => state.setIsScaledToFit);
|
|
2739
|
-
const setIsRenderVizGlobal = useHeatmapVizStore((state) => state.setIsRenderViz);
|
|
2740
|
-
const setIframeHeightGlobal = useHeatmapSingleStore((state) => state.setIframeHeight);
|
|
2741
|
-
const setVizRefGlobal = useHeatmapSingleStore((state) => state.setVizRef);
|
|
2742
|
-
if (compareContext) {
|
|
2743
|
-
return {
|
|
2744
|
-
setZoomRatio: (zoomRatio) => compareContext.updateView({ zoomRatio }),
|
|
2745
|
-
setScale: (scale) => compareContext.updateView({ scale }),
|
|
2746
|
-
setIsScaledToFit: (isScaledToFit) => compareContext.updateView({ isScaledToFit }),
|
|
2747
|
-
setIsRenderViz: (isRenderViz) => compareContext.updateView({ isRenderViz }),
|
|
2748
|
-
setIframeHeight: (iframeHeight) => compareContext.updateView({ iframeHeight }),
|
|
2749
|
-
setVizRef: (vizRef) => compareContext.updateView({ vizRef }),
|
|
2750
|
-
};
|
|
2751
|
-
}
|
|
3359
|
+
function compareViewPerformance(viewId1, viewId2) {
|
|
3360
|
+
const report = performanceLogger.generateReport();
|
|
3361
|
+
const view1Metrics = report.summary.viewMetrics[viewId1] || { renders: 0, hookCalls: 0, storeUpdates: 0 };
|
|
3362
|
+
const view2Metrics = report.summary.viewMetrics[viewId2] || { renders: 0, hookCalls: 0, storeUpdates: 0 };
|
|
2752
3363
|
return {
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
3364
|
+
view1: view1Metrics,
|
|
3365
|
+
view2: view2Metrics,
|
|
3366
|
+
difference: {
|
|
3367
|
+
renders: view1Metrics.renders - view2Metrics.renders,
|
|
3368
|
+
hookCalls: view1Metrics.hookCalls - view2Metrics.hookCalls,
|
|
3369
|
+
storeUpdates: view1Metrics.storeUpdates - view2Metrics.storeUpdates,
|
|
3370
|
+
},
|
|
2759
3371
|
};
|
|
2760
|
-
}
|
|
3372
|
+
}
|
|
2761
3373
|
|
|
2762
3374
|
const BoxStack = forwardRef(({ children, ...props }, ref) => {
|
|
2763
3375
|
const id = props.id;
|
|
@@ -2817,8 +3429,9 @@ const ContentTopBar = () => {
|
|
|
2817
3429
|
|
|
2818
3430
|
const ContentMetricBar = () => {
|
|
2819
3431
|
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3432
|
+
const borderBottom = `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`;
|
|
2820
3433
|
return (jsx(BoxStack, { id: "gx-hm-content-metric-bar", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
|
|
2821
|
-
borderBottom
|
|
3434
|
+
borderBottom,
|
|
2822
3435
|
}, children: controls.MetricBar ?? null }));
|
|
2823
3436
|
};
|
|
2824
3437
|
|
|
@@ -2834,30 +3447,88 @@ const ContentToolbar = () => {
|
|
|
2834
3447
|
}, children: controls.Toolbar ?? null }));
|
|
2835
3448
|
};
|
|
2836
3449
|
|
|
3450
|
+
const ContentSidebar = () => {
|
|
3451
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3452
|
+
const { state } = useHeatmapInteraction();
|
|
3453
|
+
const isHideSidebar = state.hideSidebar;
|
|
3454
|
+
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
3455
|
+
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
3456
|
+
const SidebarComponent = controls.Sidebar ?? null;
|
|
3457
|
+
const isCompareMode = mode === 'compare';
|
|
3458
|
+
if (isCompareMode)
|
|
3459
|
+
return null;
|
|
3460
|
+
if (!SidebarComponent)
|
|
3461
|
+
return null;
|
|
3462
|
+
return (jsx("div", { className: "gx-hm-sidebar", style: {
|
|
3463
|
+
height: '100%',
|
|
3464
|
+
display: 'flex',
|
|
3465
|
+
zIndex: 1,
|
|
3466
|
+
...(isHideSidebar
|
|
3467
|
+
? {
|
|
3468
|
+
width: '0',
|
|
3469
|
+
transform: 'translateX(-100%)',
|
|
3470
|
+
visibility: 'hidden',
|
|
3471
|
+
}
|
|
3472
|
+
: { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
|
|
3473
|
+
}, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
|
|
3474
|
+
height: '100%',
|
|
3475
|
+
width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
|
|
3476
|
+
borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
3477
|
+
}, children: jsx(SidebarComponent, {}) }) }));
|
|
3478
|
+
};
|
|
3479
|
+
|
|
3480
|
+
const PopoverSidebar = () => {
|
|
3481
|
+
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
3482
|
+
const CompSidebar = useHeatmapControlStore((state) => state.controls.Sidebar);
|
|
3483
|
+
const CompSidebarActivator = useHeatmapControlStore((state) => state.controls.SidebarActivator);
|
|
3484
|
+
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
3485
|
+
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
3486
|
+
const { state } = useHeatmapInteraction();
|
|
3487
|
+
const isCompareMode = mode === 'compare';
|
|
3488
|
+
const isHideSidebar = state.hideSidebar;
|
|
3489
|
+
const stylePopover = {
|
|
3490
|
+
position: 'absolute',
|
|
3491
|
+
top: '24px',
|
|
3492
|
+
left: '24px',
|
|
3493
|
+
zIndex: Z_INDEX.SIDEBAR_POPOVER,
|
|
3494
|
+
height: `calc(100% - ${HEATMAP_CONFIG.heightToolbar}px - ${HEATMAP_CONFIG.padding}px - 24px)`,
|
|
3495
|
+
width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
|
|
3496
|
+
};
|
|
3497
|
+
if (isHideSidebar || !isCompareMode)
|
|
3498
|
+
return null;
|
|
3499
|
+
if (!CompSidebar || !CompSidebarActivator)
|
|
3500
|
+
return null;
|
|
3501
|
+
return (jsxs("div", { children: [!isPopoverOpen && (jsx("div", { style: { ...stylePopover, width: 'auto', height: 'auto' }, children: jsx(CompSidebarActivator, { onClick: () => setIsPopoverOpen(true) }) })), isPopoverOpen && (jsx(Fragment, { children: jsx("div", { className: "gx-hm-sidebar-popover", style: {
|
|
3502
|
+
...stylePopover,
|
|
3503
|
+
backgroundColor: '#fff',
|
|
3504
|
+
borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
3505
|
+
borderRadius: '8px',
|
|
3506
|
+
boxShadow: '4px 0 12px rgba(0, 0, 0, 0.15)',
|
|
3507
|
+
overflow: 'auto',
|
|
3508
|
+
}, children: jsx(CompSidebar, { closeAction: { onClick: () => setIsPopoverOpen(false) } }) }) }))] }));
|
|
3509
|
+
};
|
|
3510
|
+
|
|
2837
3511
|
const VizContainer = ({ children, isActive = false }) => {
|
|
2838
3512
|
const wrapperRef = useRef(null);
|
|
3513
|
+
const viewId = useViewId();
|
|
2839
3514
|
useWrapperRefHeight({
|
|
2840
3515
|
isActive,
|
|
2841
3516
|
wrapperRef,
|
|
2842
3517
|
});
|
|
2843
|
-
return (
|
|
2844
|
-
|
|
2845
|
-
|
|
3518
|
+
return (jsxs(BoxStack, { ref: wrapperRef, id: `gx-hm-viz-container-${viewId}`, flexDirection: "column", flex: "1 1 auto", overflow: "auto", zIndex: 1, children: [jsx(BoxStack, { id: "gx-hm-content", flexDirection: "column", flex: "1 1 auto", overflow: "hidden", style: {
|
|
3519
|
+
minWidth: '394px',
|
|
3520
|
+
}, children: children }), jsx(PopoverSidebar, {})] }));
|
|
2846
3521
|
};
|
|
2847
3522
|
|
|
2848
3523
|
const useClickmap = () => {
|
|
2849
|
-
const [isInitialized, setIsInitialized] = useState(false);
|
|
2850
|
-
const { clickmap } = useHeatmapData();
|
|
2851
3524
|
const { vizRef } = useHeatmapViz();
|
|
3525
|
+
const { clickmap } = useHeatmapData();
|
|
2852
3526
|
const start = useCallback(() => {
|
|
2853
|
-
if (isInitialized)
|
|
2854
|
-
return;
|
|
2855
3527
|
if (!vizRef || !clickmap || clickmap.length === 0)
|
|
2856
3528
|
return;
|
|
2857
3529
|
try {
|
|
2858
3530
|
vizRef?.clearmap?.();
|
|
2859
3531
|
vizRef?.clickmap?.(clickmap);
|
|
2860
|
-
setIsInitialized(true);
|
|
2861
3532
|
}
|
|
2862
3533
|
catch (error) {
|
|
2863
3534
|
console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
|
|
@@ -2885,7 +3556,7 @@ const useScrollmap = () => {
|
|
|
2885
3556
|
return { start };
|
|
2886
3557
|
};
|
|
2887
3558
|
|
|
2888
|
-
const useHeatmapCanvas = (
|
|
3559
|
+
const useHeatmapCanvas = () => {
|
|
2889
3560
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
2890
3561
|
const { start: startClickmap } = useClickmap();
|
|
2891
3562
|
const { start: startScrollmap } = useScrollmap();
|
|
@@ -2901,10 +3572,28 @@ const useHeatmapCanvas = ({ iframeRef, }) => {
|
|
|
2901
3572
|
}, [heatmapType, startClickmap, startScrollmap]);
|
|
2902
3573
|
};
|
|
2903
3574
|
|
|
2904
|
-
|
|
2905
|
-
const
|
|
2906
|
-
const
|
|
2907
|
-
const
|
|
3575
|
+
// Base IDs for elements (without viewId suffix)
|
|
3576
|
+
const CLICKED_ELEMENT_ID_BASE = 'gx-hm-clicked-element';
|
|
3577
|
+
const SECONDARY_CLICKED_ELEMENT_ID_BASE = 'gx-hm-secondary-clicked-element';
|
|
3578
|
+
const HOVERED_ELEMENT_ID_BASE = 'gx-hm-hovered-element';
|
|
3579
|
+
const SECONDARY_HOVERED_ELEMENT_ID_BASE = 'gx-hm-secondary-hovered-element';
|
|
3580
|
+
/**
|
|
3581
|
+
* Generate unique element ID for a specific view
|
|
3582
|
+
* @param baseId - Base element ID
|
|
3583
|
+
* @param viewId - View ID
|
|
3584
|
+
* @returns Unique element ID (e.g., 'gx-hm-clicked-element-view-0')
|
|
3585
|
+
*/
|
|
3586
|
+
const getElementId = (baseId, viewId) => {
|
|
3587
|
+
return `${baseId}-${viewId}`;
|
|
3588
|
+
};
|
|
3589
|
+
const getClickedElementId = (viewId, isSecondary = false) => {
|
|
3590
|
+
const baseId = isSecondary ? SECONDARY_CLICKED_ELEMENT_ID_BASE : CLICKED_ELEMENT_ID_BASE;
|
|
3591
|
+
return getElementId(baseId, viewId);
|
|
3592
|
+
};
|
|
3593
|
+
const getHoveredElementId = (viewId, isSecondary = false) => {
|
|
3594
|
+
const baseId = isSecondary ? SECONDARY_HOVERED_ELEMENT_ID_BASE : HOVERED_ELEMENT_ID_BASE;
|
|
3595
|
+
return getElementId(baseId, viewId);
|
|
3596
|
+
};
|
|
2908
3597
|
|
|
2909
3598
|
const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
|
|
2910
3599
|
const style = calculateRankPosition(elementRect, widthScale);
|
|
@@ -2934,6 +3623,7 @@ const DEFAULT_POSITION = {
|
|
|
2934
3623
|
};
|
|
2935
3624
|
const ElementCallout = (props) => {
|
|
2936
3625
|
const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
|
|
3626
|
+
const viewId = useViewId();
|
|
2937
3627
|
const { element, target, visualRef, hozOffset, alignment = 'left' } = props;
|
|
2938
3628
|
const calloutRef = useRef(null);
|
|
2939
3629
|
const [position, setPosition] = useState(DEFAULT_POSITION);
|
|
@@ -2966,9 +3656,9 @@ const ElementCallout = (props) => {
|
|
|
2966
3656
|
position: 'fixed',
|
|
2967
3657
|
top: position.top,
|
|
2968
3658
|
left: position.left,
|
|
2969
|
-
zIndex:
|
|
3659
|
+
zIndex: Z_INDEX.CALLOUT,
|
|
2970
3660
|
}, "aria-live": "assertive", role: "tooltip", children: CompElementCallout && jsx(CompElementCallout, { elementHash: element.hash }) }));
|
|
2971
|
-
return createPortal(calloutContent, document.getElementById(
|
|
3661
|
+
return createPortal(calloutContent, document.getElementById(`gx-hm-viz-container-${viewId}`));
|
|
2972
3662
|
};
|
|
2973
3663
|
|
|
2974
3664
|
const ElementMissing = ({ show = true }) => {
|
|
@@ -2992,17 +3682,8 @@ const ElementMissing = ({ show = true }) => {
|
|
|
2992
3682
|
}, "aria-live": "assertive", children: "Element not visible on current screen" }));
|
|
2993
3683
|
};
|
|
2994
3684
|
|
|
2995
|
-
const
|
|
2996
|
-
|
|
2997
|
-
default: HOVERED_ELEMENT_ID,
|
|
2998
|
-
secondary: SECONDARY_HOVERED_ELEMENT_ID,
|
|
2999
|
-
},
|
|
3000
|
-
clicked: {
|
|
3001
|
-
default: CLICKED_ELEMENT_ID,
|
|
3002
|
-
secondary: SECONDARY_CLICKED_ELEMENT_ID,
|
|
3003
|
-
},
|
|
3004
|
-
};
|
|
3005
|
-
const ElementOverlay = ({ type, element, onClick, isSecondary }) => {
|
|
3685
|
+
const ElementOverlay = ({ type, element, onClick, isSecondary, elementId, }) => {
|
|
3686
|
+
// useRenderCount('ElementOverlay');
|
|
3006
3687
|
const { widthScale } = useHeatmapViz();
|
|
3007
3688
|
if (!element || (element.width === 0 && element.height === 0))
|
|
3008
3689
|
return null;
|
|
@@ -3011,8 +3692,7 @@ const ElementOverlay = ({ type, element, onClick, isSecondary }) => {
|
|
|
3011
3692
|
const left = element.left + HEATMAP_CONFIG['borderWidthIframe'];
|
|
3012
3693
|
const width = element.width;
|
|
3013
3694
|
const height = element.height;
|
|
3014
|
-
|
|
3015
|
-
return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: "heatmapElement", id: targetId, style: {
|
|
3695
|
+
return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: "heatmapElement", id: elementId, style: {
|
|
3016
3696
|
top,
|
|
3017
3697
|
left,
|
|
3018
3698
|
width,
|
|
@@ -3026,14 +3706,17 @@ const ELEMENT_CALLOUT = {
|
|
|
3026
3706
|
alignment: 'left',
|
|
3027
3707
|
};
|
|
3028
3708
|
const HeatmapElements = (props) => {
|
|
3709
|
+
const viewId = useViewId();
|
|
3029
3710
|
const { iframeHeight } = useHeatmapViz();
|
|
3030
|
-
const
|
|
3711
|
+
const clickedElementId = getClickedElementId(viewId, props.isSecondary);
|
|
3712
|
+
const hoveredElementId = getHoveredElementId(viewId, props.isSecondary);
|
|
3713
|
+
const { iframeRef, wrapperRef, visualRef, visualizer, iframeDimensions, isVisible = true, areDefaultRanksHidden, isSecondary, } = props;
|
|
3031
3714
|
const getRect = useHeatmapElementPosition({
|
|
3032
3715
|
iframeRef,
|
|
3033
3716
|
wrapperRef,
|
|
3034
3717
|
visualizer,
|
|
3035
3718
|
});
|
|
3036
|
-
const { clickedElement, showMissingElement, shouldShowCallout
|
|
3719
|
+
const { clickedElement, showMissingElement, shouldShowCallout } = useClickedElement({
|
|
3037
3720
|
visualRef,
|
|
3038
3721
|
getRect,
|
|
3039
3722
|
});
|
|
@@ -3041,25 +3724,12 @@ const HeatmapElements = (props) => {
|
|
|
3041
3724
|
iframeRef,
|
|
3042
3725
|
getRect,
|
|
3043
3726
|
});
|
|
3044
|
-
useElementCalloutVisible({
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
});
|
|
3048
|
-
const resetAll = () => {
|
|
3049
|
-
// setShouldShowCallout(false);
|
|
3050
|
-
};
|
|
3051
|
-
useHeatmapEffects({
|
|
3052
|
-
isVisible,
|
|
3053
|
-
isElementSidebarOpen,
|
|
3054
|
-
setShouldShowCallout,
|
|
3055
|
-
resetAll,
|
|
3056
|
-
});
|
|
3727
|
+
useElementCalloutVisible({ visualRef, getRect });
|
|
3728
|
+
useHeatmapEffects({ isVisible });
|
|
3729
|
+
useRenderCount('HeatmapElements');
|
|
3057
3730
|
if (!isVisible)
|
|
3058
3731
|
return null;
|
|
3059
|
-
return (jsxs("div", { onMouseMove: (
|
|
3060
|
-
handleMouseMove(event);
|
|
3061
|
-
// handleMouseMove2(event as any);
|
|
3062
|
-
}, 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 }))] }));
|
|
3732
|
+
return (jsxs("div", { onMouseMove: handleMouseMove, 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, elementId: clickedElementId }), jsx(ElementOverlay, { type: "hovered", element: hoveredElement, isSecondary: isSecondary, onClick: handleClick, elementId: hoveredElementId }), hoveredElement?.hash !== clickedElement?.hash && hoveredElement && (jsx(ElementCallout, { element: hoveredElement, target: `#${hoveredElementId}`, visualRef: visualRef, ...ELEMENT_CALLOUT })), shouldShowCallout && clickedElement && (jsx(ElementCallout, { element: clickedElement, target: `#${clickedElementId}`, visualRef: visualRef, ...ELEMENT_CALLOUT }))] }));
|
|
3063
3733
|
};
|
|
3064
3734
|
|
|
3065
3735
|
const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
|
|
@@ -3068,23 +3738,12 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
|
|
|
3068
3738
|
const { vizRef } = useHeatmapViz();
|
|
3069
3739
|
const visualizer = {
|
|
3070
3740
|
get: (hash) => {
|
|
3071
|
-
if (vizRef)
|
|
3072
|
-
return vizRef.get(hash);
|
|
3073
|
-
}
|
|
3074
|
-
const doc = iframeRef.current?.contentDocument;
|
|
3075
|
-
if (!doc)
|
|
3741
|
+
if (!vizRef)
|
|
3076
3742
|
return null;
|
|
3077
|
-
|
|
3078
|
-
if (elmHashAlpha) {
|
|
3079
|
-
return elmHashAlpha;
|
|
3080
|
-
}
|
|
3081
|
-
const elmHash = doc.querySelector(`[data-clarity-hash="${hash}"]`);
|
|
3082
|
-
if (elmHash) {
|
|
3083
|
-
return elmHash;
|
|
3084
|
-
}
|
|
3085
|
-
return null;
|
|
3743
|
+
return vizRef.get(hash);
|
|
3086
3744
|
},
|
|
3087
3745
|
};
|
|
3746
|
+
// useRenderCount('VizElements');
|
|
3088
3747
|
if (!iframeRef.current)
|
|
3089
3748
|
return null;
|
|
3090
3749
|
return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo: dataInfo, isVisible: true, iframeDimensions: {
|
|
@@ -3209,8 +3868,8 @@ const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
|
|
|
3209
3868
|
};
|
|
3210
3869
|
|
|
3211
3870
|
const ScrollMapMinimap = ({ zones, maxUsers }) => {
|
|
3212
|
-
const showMinimap = useHeatmapVizScrollmapStore((state) => state.showMinimap);
|
|
3213
3871
|
const scrollType = useHeatmapConfigStore((state) => state.scrollType);
|
|
3872
|
+
const { showMinimap } = useHeatmapVizScrollmap();
|
|
3214
3873
|
const isScrollType = [IScrollType.Attention].includes(scrollType);
|
|
3215
3874
|
if (!showMinimap || !isScrollType)
|
|
3216
3875
|
return null;
|
|
@@ -3370,6 +4029,7 @@ const SCROLL_TYPES = [IHeatmapType.Scroll];
|
|
|
3370
4029
|
const VizScrollMap = ({ iframeRef, wrapperRef }) => {
|
|
3371
4030
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
3372
4031
|
const { iframeHeight } = useHeatmapViz();
|
|
4032
|
+
// useRenderCount('VizScrollMap');
|
|
3373
4033
|
const isHeatmapScroll = SCROLL_TYPES.includes(heatmapType);
|
|
3374
4034
|
if (!iframeHeight || !isHeatmapScroll)
|
|
3375
4035
|
return null;
|
|
@@ -3386,9 +4046,7 @@ const VizScrollMap = ({ iframeRef, wrapperRef }) => {
|
|
|
3386
4046
|
const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHeight, onScroll, }) => {
|
|
3387
4047
|
const contentWidth = useHeatmapConfigStore((state) => state.width);
|
|
3388
4048
|
const { widthScale } = useHeatmapViz();
|
|
3389
|
-
const contentHeight =
|
|
3390
|
-
? `${scaledHeight + HEATMAP_CONFIG['heightToolbar'] + HEATMAP_CONFIG['padding'] * 2}px`
|
|
3391
|
-
: 'auto';
|
|
4049
|
+
const contentHeight = calcContentHeight();
|
|
3392
4050
|
return (jsx("div", { ref: visualRef, className: "gx-hm-visual", onScroll: onScroll, style: {
|
|
3393
4051
|
overflow: 'hidden scroll',
|
|
3394
4052
|
display: 'flex',
|
|
@@ -3413,15 +4071,20 @@ const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHe
|
|
|
3413
4071
|
transformOrigin: 'top center',
|
|
3414
4072
|
paddingBlockEnd: '0',
|
|
3415
4073
|
}, children: children }) }) }));
|
|
4074
|
+
function calcContentHeight() {
|
|
4075
|
+
return scaledHeight > 0
|
|
4076
|
+
? `${scaledHeight + HEATMAP_CONFIG['heightToolbar'] + HEATMAP_CONFIG['padding'] * 2}px`
|
|
4077
|
+
: 'auto';
|
|
4078
|
+
}
|
|
3416
4079
|
};
|
|
3417
4080
|
|
|
3418
4081
|
const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
3419
|
-
const
|
|
4082
|
+
const contentWidth = useHeatmapConfigStore((state) => state.width || 0);
|
|
3420
4083
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
3421
|
-
const setSelectedElement = useHeatmapInteractionStore((state) => state.setSelectedElement);
|
|
3422
|
-
const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
|
|
3423
4084
|
const wrapperRef = useRef(null);
|
|
3424
4085
|
const visualRef = useRef(null);
|
|
4086
|
+
const { setSelectedElement } = useHeatmapInteraction();
|
|
4087
|
+
const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
|
|
3425
4088
|
const { iframeRef } = useHeatmapVizRender(mode);
|
|
3426
4089
|
const { scaledHeight, handleScroll } = useHeatmapScale({
|
|
3427
4090
|
wrapperRef,
|
|
@@ -3430,12 +4093,12 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
|
3430
4093
|
iframeHeight,
|
|
3431
4094
|
isRenderViz,
|
|
3432
4095
|
});
|
|
3433
|
-
|
|
4096
|
+
useHeatmapCanvas();
|
|
4097
|
+
useRenderCount('VizDomRenderer');
|
|
3434
4098
|
const onScroll = (e) => {
|
|
3435
4099
|
const scrollTop = e.currentTarget.scrollTop;
|
|
3436
4100
|
handleScroll(scrollTop);
|
|
3437
4101
|
};
|
|
3438
|
-
useHeatmapCanvas({ iframeRef: iframeRef });
|
|
3439
4102
|
const cleanUp = () => {
|
|
3440
4103
|
setIframeHeight(0);
|
|
3441
4104
|
setSelectedElement(null);
|
|
@@ -3451,21 +4114,15 @@ const VizLoading = () => {
|
|
|
3451
4114
|
};
|
|
3452
4115
|
|
|
3453
4116
|
const VizDomHeatmap = () => {
|
|
3454
|
-
const
|
|
3455
|
-
|
|
3456
|
-
console.log(`🚀 🐥 ~ VizDomHeatmap ~ isRendering:`, isRendering);
|
|
3457
|
-
const { iframeHeight, setIframeHeight, setVizRef } = useHeatmapViz();
|
|
3458
|
-
const viewId = useViewId();
|
|
4117
|
+
const { iframeHeight, setIframeHeight } = useHeatmapViz();
|
|
4118
|
+
useRenderCount('VizDomHeatmap');
|
|
3459
4119
|
useEffect(() => {
|
|
3460
4120
|
return () => {
|
|
3461
|
-
console.log(
|
|
3462
|
-
setVizRef(null);
|
|
4121
|
+
console.log('🚀 🐥 ~ useEffect ~ return:');
|
|
3463
4122
|
setIframeHeight(0);
|
|
3464
4123
|
};
|
|
3465
4124
|
}, []);
|
|
3466
|
-
|
|
3467
|
-
return controls.VizLoading ?? null;
|
|
3468
|
-
return (jsxs(VizContainer, { children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
|
|
4125
|
+
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
|
|
3469
4126
|
};
|
|
3470
4127
|
|
|
3471
4128
|
const VizLiveRenderer = () => {
|
|
@@ -3480,7 +4137,6 @@ const VizLiveRenderer = () => {
|
|
|
3480
4137
|
visualRef,
|
|
3481
4138
|
iframeHeight,
|
|
3482
4139
|
isRenderViz,
|
|
3483
|
-
setIframeHeight,
|
|
3484
4140
|
});
|
|
3485
4141
|
const onScroll = (e) => {
|
|
3486
4142
|
const scrollTop = e.currentTarget.scrollTop;
|
|
@@ -3492,8 +4148,6 @@ const VizLiveRenderer = () => {
|
|
|
3492
4148
|
};
|
|
3493
4149
|
|
|
3494
4150
|
const VizLiveHeatmap = () => {
|
|
3495
|
-
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3496
|
-
const isRendering = useHeatmapConfigStore((state) => state.isRendering);
|
|
3497
4151
|
const { iframeHeight, wrapperHeight } = useHeatmapViz();
|
|
3498
4152
|
const reset = useHeatmapLiveStore((state) => state.reset);
|
|
3499
4153
|
useEffect(() => {
|
|
@@ -3501,13 +4155,15 @@ const VizLiveHeatmap = () => {
|
|
|
3501
4155
|
reset();
|
|
3502
4156
|
};
|
|
3503
4157
|
}, []);
|
|
3504
|
-
if (isRendering)
|
|
3505
|
-
return controls.VizLoading ?? null;
|
|
3506
4158
|
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
|
|
3507
4159
|
};
|
|
3508
4160
|
|
|
3509
4161
|
const ContentVizByMode = () => {
|
|
3510
4162
|
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
4163
|
+
const isRendering = useHeatmapConfigStore((state) => state.isRendering);
|
|
4164
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
4165
|
+
if (isRendering)
|
|
4166
|
+
return controls.VizLoading ?? null;
|
|
3511
4167
|
switch (mode) {
|
|
3512
4168
|
case 'live':
|
|
3513
4169
|
return jsx(VizLiveHeatmap, {});
|
|
@@ -3518,75 +4174,12 @@ const ContentVizByMode = () => {
|
|
|
3518
4174
|
}
|
|
3519
4175
|
};
|
|
3520
4176
|
|
|
3521
|
-
const LeftSidebar = () => {
|
|
3522
|
-
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3523
|
-
const isHideSidebar = useHeatmapInteractionStore((state) => state.state.hideSidebar);
|
|
3524
|
-
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
3525
|
-
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
3526
|
-
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
3527
|
-
// In compare mode, render as popover
|
|
3528
|
-
if (mode === 'compare') {
|
|
3529
|
-
return (jsxs("div", { style: { position: 'relative', zIndex: 10 }, children: [jsx("button", { onClick: () => setIsPopoverOpen(!isPopoverOpen), style: {
|
|
3530
|
-
position: 'absolute',
|
|
3531
|
-
top: '8px',
|
|
3532
|
-
left: '8px',
|
|
3533
|
-
padding: '8px 16px',
|
|
3534
|
-
backgroundColor: '#fff',
|
|
3535
|
-
border: '1px solid #c9cccf',
|
|
3536
|
-
borderRadius: '8px',
|
|
3537
|
-
cursor: 'pointer',
|
|
3538
|
-
fontSize: '14px',
|
|
3539
|
-
fontWeight: 500,
|
|
3540
|
-
zIndex: 11,
|
|
3541
|
-
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.05)',
|
|
3542
|
-
}, children: "Click data" }), isPopoverOpen && (jsxs(Fragment, { children: [jsx("div", { onClick: () => setIsPopoverOpen(false), style: {
|
|
3543
|
-
position: 'fixed',
|
|
3544
|
-
top: 0,
|
|
3545
|
-
left: 0,
|
|
3546
|
-
right: 0,
|
|
3547
|
-
bottom: 0,
|
|
3548
|
-
backgroundColor: 'rgba(0, 0, 0, 0.3)',
|
|
3549
|
-
zIndex: 12,
|
|
3550
|
-
} }), jsx("div", { className: "gx-hm-sidebar-popover", style: {
|
|
3551
|
-
position: 'fixed',
|
|
3552
|
-
top: 0,
|
|
3553
|
-
left: 0,
|
|
3554
|
-
height: '100vh',
|
|
3555
|
-
width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
|
|
3556
|
-
backgroundColor: '#fff',
|
|
3557
|
-
borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
3558
|
-
zIndex: 13,
|
|
3559
|
-
boxShadow: '4px 0 12px rgba(0, 0, 0, 0.15)',
|
|
3560
|
-
overflow: 'auto',
|
|
3561
|
-
}, children: controls.Sidebar ?? null })] }))] }));
|
|
3562
|
-
}
|
|
3563
|
-
// Normal sidebar rendering for single mode
|
|
3564
|
-
if (isHideSidebar) {
|
|
3565
|
-
return null;
|
|
3566
|
-
}
|
|
3567
|
-
return (jsx("div", { className: "gx-hm-sidebar", style: {
|
|
3568
|
-
height: '100%',
|
|
3569
|
-
display: 'flex',
|
|
3570
|
-
zIndex: 1,
|
|
3571
|
-
...(isHideSidebar
|
|
3572
|
-
? {
|
|
3573
|
-
width: '0',
|
|
3574
|
-
transform: 'translateX(-100%)',
|
|
3575
|
-
visibility: 'hidden',
|
|
3576
|
-
}
|
|
3577
|
-
: { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
|
|
3578
|
-
}, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
|
|
3579
|
-
height: '100%',
|
|
3580
|
-
width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
|
|
3581
|
-
borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
3582
|
-
}, children: controls.Sidebar ?? null }) }));
|
|
3583
|
-
};
|
|
3584
|
-
|
|
3585
4177
|
const WrapperPreview = () => {
|
|
3586
|
-
return (jsxs(BoxStack, { id: "gx-hm-container", flexDirection: "row", overflow: "hidden", flex: "1", position: "relative", children: [jsx(
|
|
4178
|
+
return (jsxs(BoxStack, { id: "gx-hm-container", flexDirection: "row", overflow: "hidden", flex: "1", position: "relative", children: [jsx(ContentSidebar, {}), jsxs(BoxStack, { flexDirection: "column", flex: "1", children: [jsx(ContentMetricBar, {}), jsx(ContentVizByMode, {}), jsx(ContentToolbar, {})] })] }));
|
|
3587
4179
|
};
|
|
3588
4180
|
|
|
3589
4181
|
const WrapperLayout = () => {
|
|
4182
|
+
useRenderCount('WrapperLayout');
|
|
3590
4183
|
return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(WrapperPreview, {})] }));
|
|
3591
4184
|
};
|
|
3592
4185
|
|
|
@@ -3595,6 +4188,19 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
3595
4188
|
useRegisterData(data, dataInfo);
|
|
3596
4189
|
useRegisterHeatmap({ clickmap, scrollmap });
|
|
3597
4190
|
useRegisterConfig();
|
|
4191
|
+
performanceLogger.configure({
|
|
4192
|
+
enabled: true,
|
|
4193
|
+
logToConsole: false,
|
|
4194
|
+
logLevel: 'normal', // 'verbose' | 'normal' | 'minimal'
|
|
4195
|
+
thresholds: {
|
|
4196
|
+
slowRenderMs: 16, // Warn if render > 16ms (60fps)
|
|
4197
|
+
slowHookMs: 5, // Warn if hook > 5ms
|
|
4198
|
+
excessiveRenderCount: 10, // Warn if component renders > 10 times
|
|
4199
|
+
},
|
|
4200
|
+
externalLogger: (metric) => {
|
|
4201
|
+
if (metric.name === 'VizDomRenderer') ;
|
|
4202
|
+
},
|
|
4203
|
+
});
|
|
3598
4204
|
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: {
|
|
3599
4205
|
minHeight: '100%',
|
|
3600
4206
|
display: 'flex',
|
|
@@ -3608,4 +4214,4 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
3608
4214
|
}
|
|
3609
4215
|
};
|
|
3610
4216
|
|
|
3611
|
-
export { GraphView, HeatmapLayout, IClickType, IHeatmapType, IScrollType, ViewIdContext, convertViewportToIframeCoords,
|
|
4217
|
+
export { DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, IClickType, IHeatmapType, IScrollType, ViewIdContext, Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, downloadPerformanceReport, getCompareViewId, getCompareViewIndex, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, isCompareViewId, performanceLogger, printPerformanceSummary, sendPerformanceReport, trackStoreAction, useClickedElement, useElementCalloutVisible, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapData, useHeatmapDataStore, useHeatmapEffects, useHeatmapElementPosition, useHeatmapInteraction, useHeatmapInteractionStore, useHeatmapLiveStore, useHeatmapScale, useHeatmapSingleStore, useHeatmapViz, useHeatmapVizRender, useHeatmapVizScrollmap, useHeatmapVizScrollmapStore, useHeatmapVizStore, useHoveredElement, useIsCompareMode, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewId, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };
|