@gemx-dev/heatmap-react 3.5.48 → 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/ContentTopBar.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/VizCompare/CompareGrid.d.ts +1 -0
- package/dist/esm/components/VizCompare/CompareGrid.d.ts.map +1 -0
- package/dist/esm/components/VizCompare/CompareView.d.ts +1 -0
- package/dist/esm/components/VizCompare/CompareView.d.ts.map +1 -0
- package/dist/esm/components/VizCompare/CompareViewHeader.d.ts +1 -0
- package/dist/esm/components/VizCompare/CompareViewHeader.d.ts.map +1 -0
- package/dist/esm/components/VizCompare/CompareVizRenderer.d.ts +1 -0
- package/dist/esm/components/VizCompare/CompareVizRenderer.d.ts.map +1 -0
- package/dist/esm/components/VizCompare/VizCompareHeatmap.d.ts +1 -0
- package/dist/esm/components/VizCompare/VizCompareHeatmap.d.ts.map +1 -0
- package/dist/esm/components/VizCompare/index.d.ts +1 -0
- package/dist/esm/components/VizCompare/index.d.ts.map +1 -0
- package/dist/esm/components/VizDom/VizContainer.d.ts +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/ElementCallout.d.ts.map +1 -1
- package/dist/esm/components/VizElement/ElementMissing.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/AverageFoldLine.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/HoverZones.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/ScrollMapOverlay.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/VizScrollMap.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmapV2/useScrollmapOverlay.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/contexts/CompareViewContext.d.ts +28 -0
- package/dist/esm/contexts/CompareViewContext.d.ts.map +1 -0
- package/dist/esm/contexts/index.d.ts +2 -0
- package/dist/esm/contexts/index.d.ts.map +1 -0
- package/dist/esm/helpers/iframe-helper/style-replacer.d.ts.map +1 -1
- package/dist/esm/hooks/index.d.ts +1 -0
- package/dist/esm/hooks/index.d.ts.map +1 -1
- package/dist/esm/hooks/register/useRegisterControl.d.ts.map +1 -1
- package/dist/esm/hooks/register/useRegisterData.d.ts.map +1 -1
- package/dist/esm/hooks/register/useRegisterHeatmap.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/view-context/useHeatmapData.d.ts +16 -0
- 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/esm/hooks/view-context/useHeatmapViz.d.ts +26 -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/view-context/useViewId.d.ts +15 -0
- package/dist/esm/hooks/view-context/useViewId.d.ts.map +1 -0
- package/dist/esm/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
- 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-elements/useClickedElement.d.ts +0 -1
- package/dist/esm/hooks/viz-elements/useClickedElement.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/useHeatmapElementPosition.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-render/useReplayRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
- package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts +1 -1
- package/dist/esm/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts +1 -1
- package/dist/esm/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useScrollSync.d.ts +2 -1
- package/dist/esm/hooks/viz-scale/useScrollSync.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useWrapperRefHeight.d.ts +0 -1
- package/dist/esm/hooks/viz-scale/useWrapperRefHeight.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scrollmap/useZonePositions.d.ts.map +1 -1
- package/dist/esm/index.d.ts +4 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1457 -313
- package/dist/esm/index.mjs +1457 -313
- 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/config.d.ts +2 -0
- package/dist/esm/stores/config.d.ts.map +1 -1
- package/dist/esm/stores/data.d.ts +20 -11
- package/dist/esm/stores/data.d.ts.map +1 -1
- package/dist/esm/stores/index.d.ts +1 -0
- package/dist/esm/stores/index.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-compare.d.ts +33 -0
- package/dist/esm/stores/mode-compare.d.ts.map +1 -0
- package/dist/esm/stores/mode-live.d.ts +0 -4
- package/dist/esm/stores/mode-live.d.ts.map +1 -1
- package/dist/esm/stores/mode-single.d.ts +20 -5
- 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 +22 -11
- package/dist/esm/stores/viz.d.ts.map +1 -1
- package/dist/esm/types/compare.d.ts +68 -0
- package/dist/esm/types/compare.d.ts.map +1 -0
- package/dist/esm/types/control.d.ts +10 -2
- package/dist/esm/types/control.d.ts.map +1 -1
- package/dist/esm/types/index.d.ts +2 -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/ContentTopBar.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/VizCompare/CompareGrid.d.ts +1 -0
- package/dist/umd/components/VizCompare/CompareGrid.d.ts.map +1 -0
- package/dist/umd/components/VizCompare/CompareView.d.ts +1 -0
- package/dist/umd/components/VizCompare/CompareView.d.ts.map +1 -0
- package/dist/umd/components/VizCompare/CompareViewHeader.d.ts +1 -0
- package/dist/umd/components/VizCompare/CompareViewHeader.d.ts.map +1 -0
- package/dist/umd/components/VizCompare/CompareVizRenderer.d.ts +1 -0
- package/dist/umd/components/VizCompare/CompareVizRenderer.d.ts.map +1 -0
- package/dist/umd/components/VizCompare/VizCompareHeatmap.d.ts +1 -0
- package/dist/umd/components/VizCompare/VizCompareHeatmap.d.ts.map +1 -0
- package/dist/umd/components/VizCompare/index.d.ts +1 -0
- package/dist/umd/components/VizCompare/index.d.ts.map +1 -0
- package/dist/umd/components/VizDom/VizContainer.d.ts +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/ElementCallout.d.ts.map +1 -1
- package/dist/umd/components/VizElement/ElementMissing.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/AverageFoldLine.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/HoverZones.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/ScrollMapMinimap.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/ScrollMapOverlay.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/VizScrollMap.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmapV2/useScrollmapOverlay.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/contexts/CompareViewContext.d.ts +28 -0
- package/dist/umd/contexts/CompareViewContext.d.ts.map +1 -0
- package/dist/umd/contexts/index.d.ts +2 -0
- package/dist/umd/contexts/index.d.ts.map +1 -0
- package/dist/umd/helpers/iframe-helper/style-replacer.d.ts.map +1 -1
- package/dist/umd/hooks/index.d.ts +1 -0
- package/dist/umd/hooks/index.d.ts.map +1 -1
- package/dist/umd/hooks/register/useRegisterControl.d.ts.map +1 -1
- package/dist/umd/hooks/register/useRegisterData.d.ts.map +1 -1
- package/dist/umd/hooks/register/useRegisterHeatmap.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/view-context/useHeatmapData.d.ts +16 -0
- 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/umd/hooks/view-context/useHeatmapViz.d.ts +26 -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/view-context/useViewId.d.ts +15 -0
- package/dist/umd/hooks/view-context/useViewId.d.ts.map +1 -0
- package/dist/umd/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
- 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-elements/useClickedElement.d.ts +0 -1
- package/dist/umd/hooks/viz-elements/useClickedElement.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/useHeatmapElementPosition.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-render/useReplayRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts +1 -1
- package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts +1 -1
- package/dist/umd/hooks/viz-scale/useObserveIframeHeight.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts +1 -1
- package/dist/umd/hooks/viz-scale/useScaleCalculation.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useScrollSync.d.ts +2 -1
- package/dist/umd/hooks/viz-scale/useScrollSync.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useWrapperRefHeight.d.ts +0 -1
- package/dist/umd/hooks/viz-scale/useWrapperRefHeight.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scrollmap/useZonePositions.d.ts.map +1 -1
- package/dist/umd/index.d.ts +4 -1
- 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/config.d.ts +2 -0
- package/dist/umd/stores/config.d.ts.map +1 -1
- package/dist/umd/stores/data.d.ts +20 -11
- package/dist/umd/stores/data.d.ts.map +1 -1
- package/dist/umd/stores/index.d.ts +1 -0
- package/dist/umd/stores/index.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-compare.d.ts +33 -0
- package/dist/umd/stores/mode-compare.d.ts.map +1 -0
- package/dist/umd/stores/mode-live.d.ts +0 -4
- package/dist/umd/stores/mode-live.d.ts.map +1 -1
- package/dist/umd/stores/mode-single.d.ts +20 -5
- 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 +22 -11
- package/dist/umd/stores/viz.d.ts.map +1 -1
- package/dist/umd/types/compare.d.ts +68 -0
- package/dist/umd/types/compare.d.ts.map +1 -0
- package/dist/umd/types/control.d.ts +10 -2
- package/dist/umd/types/control.d.ts.map +1 -1
- package/dist/umd/types/index.d.ts +2 -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/umd/components/Layout/LeftSidebar.d.ts +0 -2
- package/dist/umd/components/Layout/LeftSidebar.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, 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,15 +68,50 @@ 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:
|
|
74
|
-
|
|
108
|
+
Sidebar: undefined,
|
|
109
|
+
SidebarActivator: undefined,
|
|
75
110
|
Toolbar: null,
|
|
76
111
|
MetricBar: null,
|
|
77
112
|
VizLoading: null,
|
|
78
|
-
|
|
113
|
+
TopBar: undefined,
|
|
114
|
+
ElementCallout: undefined,
|
|
79
115
|
},
|
|
80
116
|
registerControl: (key, control) => {
|
|
81
117
|
set({
|
|
@@ -114,9 +150,10 @@ 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,
|
|
156
|
+
isRendering: true,
|
|
120
157
|
setMode: (mode) => set({ mode }),
|
|
121
158
|
resetMode: () => set({ mode: 'single' }),
|
|
122
159
|
setWidth: (width) => set({ width }),
|
|
@@ -124,70 +161,236 @@ const useHeatmapConfigStore = create()((set, get) => {
|
|
|
124
161
|
setHeatmapType: (heatmapType) => set({ heatmapType }),
|
|
125
162
|
setClickType: (clickType) => set({ clickType }),
|
|
126
163
|
setScrollType: (scrollType) => set({ scrollType }),
|
|
164
|
+
setIsRendering: (isRendering) => set({ isRendering }),
|
|
127
165
|
};
|
|
128
166
|
});
|
|
129
167
|
|
|
130
|
-
const useHeatmapDataStore = create()((set, get) => {
|
|
168
|
+
const useHeatmapDataStore = create()(subscribeWithSelector((set, get) => {
|
|
131
169
|
return {
|
|
132
|
-
data: undefined,
|
|
133
|
-
clickmap: undefined,
|
|
134
|
-
dataInfo: undefined,
|
|
135
|
-
scrollmap: undefined,
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
setData: (data) => set(
|
|
140
|
-
|
|
141
|
-
|
|
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) => ({
|
|
175
|
+
dataInfo: { ...state.dataInfo, [viewId]: dataInfo },
|
|
176
|
+
})),
|
|
177
|
+
setData: (data, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
178
|
+
data: { ...state.data, [viewId]: data },
|
|
179
|
+
})),
|
|
180
|
+
setClickmap: (clickmap, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
181
|
+
clickmap: { ...state.clickmap, [viewId]: clickmap },
|
|
182
|
+
})),
|
|
183
|
+
setScrollmap: (scrollmap, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
184
|
+
scrollmap: { ...state.scrollmap, [viewId]: scrollmap },
|
|
185
|
+
})),
|
|
186
|
+
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
187
|
+
data: { ...state.data, [toViewId]: state.data[fromViewId] },
|
|
188
|
+
clickmap: { ...state.clickmap, [toViewId]: state.clickmap[fromViewId] },
|
|
189
|
+
dataInfo: { ...state.dataInfo, [toViewId]: state.dataInfo[fromViewId] },
|
|
190
|
+
scrollmap: { ...state.scrollmap, [toViewId]: state.scrollmap[fromViewId] },
|
|
191
|
+
})),
|
|
192
|
+
clearView: (viewId) => set((state) => {
|
|
193
|
+
const newData = { ...state.data };
|
|
194
|
+
const newClickmap = { ...state.clickmap };
|
|
195
|
+
const newDataInfo = { ...state.dataInfo };
|
|
196
|
+
const newScrollmap = { ...state.scrollmap };
|
|
197
|
+
delete newData[viewId];
|
|
198
|
+
delete newClickmap[viewId];
|
|
199
|
+
delete newDataInfo[viewId];
|
|
200
|
+
delete newScrollmap[viewId];
|
|
201
|
+
return {
|
|
202
|
+
data: newData,
|
|
203
|
+
clickmap: newClickmap,
|
|
204
|
+
dataInfo: newDataInfo,
|
|
205
|
+
scrollmap: newScrollmap,
|
|
206
|
+
};
|
|
207
|
+
}),
|
|
208
|
+
resetAll: () => set({
|
|
209
|
+
data: { [DEFAULT_VIEW_ID]: undefined },
|
|
210
|
+
clickmap: { [DEFAULT_VIEW_ID]: undefined },
|
|
211
|
+
dataInfo: { [DEFAULT_VIEW_ID]: undefined },
|
|
212
|
+
scrollmap: { [DEFAULT_VIEW_ID]: undefined },
|
|
213
|
+
}),
|
|
142
214
|
};
|
|
143
|
-
});
|
|
215
|
+
}));
|
|
144
216
|
|
|
145
|
-
const
|
|
217
|
+
const DEFAULT_STATE = {
|
|
218
|
+
hideSidebar: false,
|
|
219
|
+
};
|
|
220
|
+
const useHeatmapInteractionStore = create()(subscribeWithSelector((set, get) => {
|
|
146
221
|
return {
|
|
147
|
-
state: {
|
|
148
|
-
|
|
149
|
-
},
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
+
}),
|
|
157
278
|
};
|
|
158
|
-
});
|
|
279
|
+
}));
|
|
159
280
|
|
|
160
|
-
const useHeatmapVizStore = create()((set, get) => {
|
|
281
|
+
const useHeatmapVizStore = create()(subscribeWithSelector((set, get) => {
|
|
161
282
|
return {
|
|
162
|
-
isRenderViz: false,
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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) => ({
|
|
289
|
+
isRenderViz: { ...state.isRenderViz, [viewId]: isRenderViz },
|
|
290
|
+
})),
|
|
291
|
+
setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
292
|
+
zoomRatio: { ...state.zoomRatio, [viewId]: zoomRatio },
|
|
293
|
+
})),
|
|
294
|
+
setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
295
|
+
minZoomRatio: { ...state.minZoomRatio, [viewId]: minZoomRatio },
|
|
296
|
+
})),
|
|
297
|
+
setScale: (scale, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
298
|
+
scale: { ...state.scale, [viewId]: scale },
|
|
299
|
+
})),
|
|
300
|
+
setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
301
|
+
isScaledToFit: { ...state.isScaledToFit, [viewId]: isScaledToFit },
|
|
302
|
+
})),
|
|
303
|
+
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
304
|
+
isRenderViz: { ...state.isRenderViz, [toViewId]: state.isRenderViz[fromViewId] ?? false },
|
|
305
|
+
zoomRatio: { ...state.zoomRatio, [toViewId]: state.zoomRatio[fromViewId] ?? 100 },
|
|
306
|
+
minZoomRatio: { ...state.minZoomRatio, [toViewId]: state.minZoomRatio[fromViewId] ?? 10 },
|
|
307
|
+
scale: { ...state.scale, [toViewId]: state.scale[fromViewId] ?? 1 },
|
|
308
|
+
isScaledToFit: {
|
|
309
|
+
...state.isScaledToFit,
|
|
310
|
+
[toViewId]: state.isScaledToFit[fromViewId] ?? false,
|
|
311
|
+
},
|
|
312
|
+
})),
|
|
313
|
+
clearView: (viewId) => set((state) => {
|
|
314
|
+
const newIsRenderViz = { ...state.isRenderViz };
|
|
315
|
+
const newZoomRatio = { ...state.zoomRatio };
|
|
316
|
+
const newMinZoomRatio = { ...state.minZoomRatio };
|
|
317
|
+
const newScale = { ...state.scale };
|
|
318
|
+
const newIsScaledToFit = { ...state.isScaledToFit };
|
|
319
|
+
delete newIsRenderViz[viewId];
|
|
320
|
+
delete newZoomRatio[viewId];
|
|
321
|
+
delete newMinZoomRatio[viewId];
|
|
322
|
+
delete newScale[viewId];
|
|
323
|
+
delete newIsScaledToFit[viewId];
|
|
324
|
+
return {
|
|
325
|
+
isRenderViz: newIsRenderViz,
|
|
326
|
+
zoomRatio: newZoomRatio,
|
|
327
|
+
minZoomRatio: newMinZoomRatio,
|
|
328
|
+
scale: newScale,
|
|
329
|
+
isScaledToFit: newIsScaledToFit,
|
|
330
|
+
};
|
|
331
|
+
}),
|
|
332
|
+
resetAll: () => set({
|
|
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 },
|
|
338
|
+
}),
|
|
172
339
|
};
|
|
173
|
-
});
|
|
340
|
+
}));
|
|
174
341
|
|
|
175
|
-
const useHeatmapVizScrollmapStore = create()((set, get) => {
|
|
342
|
+
const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set, get) => {
|
|
176
343
|
return {
|
|
177
|
-
zones: [],
|
|
178
|
-
hoveredZone: null,
|
|
179
|
-
showMinimap: true,
|
|
180
|
-
setZones: (zones) => set(
|
|
181
|
-
|
|
182
|
-
|
|
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
|
+
}),
|
|
183
388
|
};
|
|
184
|
-
});
|
|
389
|
+
}));
|
|
185
390
|
|
|
186
391
|
const initialState = {
|
|
187
392
|
payloads: [],
|
|
188
393
|
htmlContent: '',
|
|
189
|
-
wrapperHeight: 0,
|
|
190
|
-
iframeHeight: 0,
|
|
191
394
|
};
|
|
192
395
|
const useHeatmapLiveStore = create()((set, get) => {
|
|
193
396
|
return {
|
|
@@ -196,17 +399,191 @@ const useHeatmapLiveStore = create()((set, get) => {
|
|
|
196
399
|
setPayloads: (payloads) => set({ payloads }),
|
|
197
400
|
addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
|
|
198
401
|
setHtmlContent: (htmlContent) => set({ htmlContent }),
|
|
199
|
-
setWrapperHeight: (wrapperHeight) => set({ wrapperHeight }),
|
|
200
|
-
setIframeHeight: (iframeHeight) => set({ iframeHeight }),
|
|
201
402
|
};
|
|
202
403
|
});
|
|
203
404
|
|
|
204
|
-
const useHeatmapSingleStore = create()((set, get) => {
|
|
405
|
+
const useHeatmapSingleStore = create()(subscribeWithSelector((set, get) => {
|
|
205
406
|
return {
|
|
206
|
-
vizRef: null,
|
|
207
|
-
iframeHeight: 0,
|
|
208
|
-
|
|
209
|
-
|
|
407
|
+
vizRef: { [DEFAULT_VIEW_ID]: null },
|
|
408
|
+
iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
409
|
+
wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
410
|
+
wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
|
|
411
|
+
setVizRef: (vizRef, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
412
|
+
vizRef: { ...state.vizRef, [viewId]: vizRef },
|
|
413
|
+
})),
|
|
414
|
+
setIframeHeight: (iframeHeight, viewId = DEFAULT_VIEW_ID) => {
|
|
415
|
+
set((state) => ({
|
|
416
|
+
iframeHeight: { ...state.iframeHeight, [viewId]: iframeHeight },
|
|
417
|
+
}));
|
|
418
|
+
},
|
|
419
|
+
setWrapperHeight: (wrapperHeight, viewId = DEFAULT_VIEW_ID) => {
|
|
420
|
+
set((state) => ({
|
|
421
|
+
wrapperHeight: { ...state.wrapperHeight, [viewId]: wrapperHeight },
|
|
422
|
+
}));
|
|
423
|
+
},
|
|
424
|
+
setWrapperWidth: (wrapperWidth, viewId = DEFAULT_VIEW_ID) => {
|
|
425
|
+
set((state) => ({
|
|
426
|
+
wrapperWidth: { ...state.wrapperWidth, [viewId]: wrapperWidth },
|
|
427
|
+
}));
|
|
428
|
+
},
|
|
429
|
+
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
430
|
+
// Don't copy vizRef - each view needs its own visualizer instance
|
|
431
|
+
iframeHeight: { ...state.iframeHeight, [toViewId]: state.iframeHeight[fromViewId] ?? 0 },
|
|
432
|
+
wrapperHeight: {
|
|
433
|
+
...state.wrapperHeight,
|
|
434
|
+
[toViewId]: state.wrapperHeight[fromViewId] ?? 0,
|
|
435
|
+
},
|
|
436
|
+
})),
|
|
437
|
+
clearView: (viewId) => set((state) => {
|
|
438
|
+
const newVizRef = { ...state.vizRef };
|
|
439
|
+
const newIframeHeight = { ...state.iframeHeight };
|
|
440
|
+
const newWrapperHeight = { ...state.wrapperHeight };
|
|
441
|
+
const newWrapperWidth = { ...state.wrapperWidth };
|
|
442
|
+
delete newVizRef[viewId];
|
|
443
|
+
delete newIframeHeight[viewId];
|
|
444
|
+
delete newWrapperHeight[viewId];
|
|
445
|
+
delete newWrapperWidth[viewId];
|
|
446
|
+
return {
|
|
447
|
+
vizRef: newVizRef,
|
|
448
|
+
iframeHeight: newIframeHeight,
|
|
449
|
+
wrapperHeight: newWrapperHeight,
|
|
450
|
+
wrapperWidth: newWrapperWidth,
|
|
451
|
+
};
|
|
452
|
+
}),
|
|
453
|
+
resetAll: () => set({
|
|
454
|
+
vizRef: { [DEFAULT_VIEW_ID]: null },
|
|
455
|
+
iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
456
|
+
wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
457
|
+
wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
|
|
458
|
+
}),
|
|
459
|
+
};
|
|
460
|
+
}));
|
|
461
|
+
|
|
462
|
+
const createDefaultView = (id, label, options) => ({
|
|
463
|
+
id,
|
|
464
|
+
label,
|
|
465
|
+
heatmapType: options?.heatmapType ?? IHeatmapType.Scroll,
|
|
466
|
+
clickType: options?.clickType ?? IClickType.Total,
|
|
467
|
+
scrollType: options?.scrollType ?? IScrollType.Depth,
|
|
468
|
+
data: options?.data,
|
|
469
|
+
clickmap: undefined,
|
|
470
|
+
scrollmap: undefined,
|
|
471
|
+
dataInfo: undefined,
|
|
472
|
+
vizRef: null,
|
|
473
|
+
iframeHeight: 0,
|
|
474
|
+
zoomRatio: 100,
|
|
475
|
+
scale: 1,
|
|
476
|
+
isScaledToFit: false,
|
|
477
|
+
isRendering: true,
|
|
478
|
+
isRenderViz: false,
|
|
479
|
+
});
|
|
480
|
+
const useHeatmapCompareStore = create()((set, get) => {
|
|
481
|
+
return {
|
|
482
|
+
views: new Map(),
|
|
483
|
+
viewOrder: [],
|
|
484
|
+
layout: 'grid-2',
|
|
485
|
+
syncSettings: {
|
|
486
|
+
zoomRatio: false,
|
|
487
|
+
scrolling: false,
|
|
488
|
+
heatmapType: false,
|
|
489
|
+
},
|
|
490
|
+
viewIdCounter: 0,
|
|
491
|
+
addView: (options) => {
|
|
492
|
+
const state = get();
|
|
493
|
+
const viewId = `view-${state.viewIdCounter}`;
|
|
494
|
+
const label = options?.label ?? `Version ${state.viewOrder.length + 1}`;
|
|
495
|
+
const newView = createDefaultView(viewId, label, options);
|
|
496
|
+
const newViews = new Map(state.views);
|
|
497
|
+
newViews.set(viewId, newView);
|
|
498
|
+
set({
|
|
499
|
+
views: newViews,
|
|
500
|
+
viewOrder: [...state.viewOrder, viewId],
|
|
501
|
+
viewIdCounter: state.viewIdCounter + 1,
|
|
502
|
+
});
|
|
503
|
+
return viewId;
|
|
504
|
+
},
|
|
505
|
+
removeView: (viewId) => {
|
|
506
|
+
const state = get();
|
|
507
|
+
const newViews = new Map(state.views);
|
|
508
|
+
newViews.delete(viewId);
|
|
509
|
+
set({
|
|
510
|
+
views: newViews,
|
|
511
|
+
viewOrder: state.viewOrder.filter((id) => id !== viewId),
|
|
512
|
+
});
|
|
513
|
+
},
|
|
514
|
+
updateView: (viewId, updates) => {
|
|
515
|
+
const state = get();
|
|
516
|
+
const view = state.views.get(viewId);
|
|
517
|
+
if (!view)
|
|
518
|
+
return;
|
|
519
|
+
const newViews = new Map(state.views);
|
|
520
|
+
newViews.set(viewId, { ...view, ...updates });
|
|
521
|
+
set({ views: newViews });
|
|
522
|
+
// Handle syncing
|
|
523
|
+
const { syncSettings } = state;
|
|
524
|
+
if (syncSettings.zoomRatio && 'zoomRatio' in updates && updates.zoomRatio !== undefined) {
|
|
525
|
+
get().syncProperty('zoomRatio', updates.zoomRatio);
|
|
526
|
+
}
|
|
527
|
+
if (syncSettings.heatmapType &&
|
|
528
|
+
'heatmapType' in updates &&
|
|
529
|
+
updates.heatmapType !== undefined) {
|
|
530
|
+
get().syncProperty('heatmapType', updates.heatmapType);
|
|
531
|
+
}
|
|
532
|
+
},
|
|
533
|
+
getView: (viewId) => {
|
|
534
|
+
return get().views.get(viewId);
|
|
535
|
+
},
|
|
536
|
+
setLayout: (layout) => {
|
|
537
|
+
set({ layout });
|
|
538
|
+
},
|
|
539
|
+
setSyncSettings: (settings) => {
|
|
540
|
+
set({ syncSettings: { ...get().syncSettings, ...settings } });
|
|
541
|
+
},
|
|
542
|
+
initializeCompare: (count, options) => {
|
|
543
|
+
const viewIds = [];
|
|
544
|
+
const newViews = new Map();
|
|
545
|
+
for (let i = 0; i < count; i++) {
|
|
546
|
+
const viewId = `view-${i}`;
|
|
547
|
+
const label = options?.label ?? String.fromCharCode(65 + i); // A, B, C, D
|
|
548
|
+
newViews.set(viewId, createDefaultView(viewId, `Version ${label}`, options));
|
|
549
|
+
viewIds.push(viewId);
|
|
550
|
+
}
|
|
551
|
+
const layoutMap = {
|
|
552
|
+
2: 'grid-2',
|
|
553
|
+
3: 'grid-3',
|
|
554
|
+
4: 'grid-4',
|
|
555
|
+
};
|
|
556
|
+
set({
|
|
557
|
+
views: newViews,
|
|
558
|
+
viewOrder: viewIds,
|
|
559
|
+
layout: layoutMap[count],
|
|
560
|
+
viewIdCounter: count,
|
|
561
|
+
});
|
|
562
|
+
},
|
|
563
|
+
resetCompare: () => {
|
|
564
|
+
set({
|
|
565
|
+
views: new Map(),
|
|
566
|
+
viewOrder: [],
|
|
567
|
+
layout: 'grid-2',
|
|
568
|
+
viewIdCounter: 0,
|
|
569
|
+
syncSettings: {
|
|
570
|
+
zoomRatio: false,
|
|
571
|
+
scrolling: false,
|
|
572
|
+
heatmapType: false,
|
|
573
|
+
},
|
|
574
|
+
});
|
|
575
|
+
},
|
|
576
|
+
syncProperty: (property, value) => {
|
|
577
|
+
const state = get();
|
|
578
|
+
const newViews = new Map(state.views);
|
|
579
|
+
state.viewOrder.forEach((viewId) => {
|
|
580
|
+
const view = newViews.get(viewId);
|
|
581
|
+
if (view) {
|
|
582
|
+
newViews.set(viewId, { ...view, [property]: value });
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
set({ views: newViews });
|
|
586
|
+
},
|
|
210
587
|
};
|
|
211
588
|
});
|
|
212
589
|
|
|
@@ -215,7 +592,7 @@ const useRegisterConfig = () => {
|
|
|
215
592
|
const width = useHeatmapConfigStore((state) => state.width);
|
|
216
593
|
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
217
594
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
218
|
-
const setIsRendering =
|
|
595
|
+
const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
|
|
219
596
|
useEffect(() => {
|
|
220
597
|
setIsRendering(true);
|
|
221
598
|
setTimeout(() => {
|
|
@@ -227,6 +604,7 @@ const useRegisterConfig = () => {
|
|
|
227
604
|
const useRegisterControl = (control) => {
|
|
228
605
|
const registerControl = useHeatmapControlStore((state) => state.registerControl);
|
|
229
606
|
registerControl('Sidebar', control.Sidebar);
|
|
607
|
+
registerControl('SidebarActivator', control.SidebarActivator);
|
|
230
608
|
registerControl('TopBar', control.TopBar);
|
|
231
609
|
registerControl('Toolbar', control.Toolbar);
|
|
232
610
|
registerControl('MetricBar', control.MetricBar);
|
|
@@ -234,10 +612,228 @@ const useRegisterControl = (control) => {
|
|
|
234
612
|
registerControl('ElementCallout', control.ElementCallout);
|
|
235
613
|
};
|
|
236
614
|
|
|
237
|
-
|
|
615
|
+
/**
|
|
616
|
+
* Context to provide viewId to components
|
|
617
|
+
* Used in compare mode to isolate data between views
|
|
618
|
+
*/
|
|
619
|
+
const ViewIdContext = createContext(undefined);
|
|
620
|
+
/**
|
|
621
|
+
* Hook to get current viewId
|
|
622
|
+
* Returns DEFAULT_VIEW_ID if not in a ViewIdContext (single mode)
|
|
623
|
+
*/
|
|
624
|
+
const useViewId = () => {
|
|
625
|
+
const viewId = useContext(ViewIdContext);
|
|
626
|
+
return viewId || DEFAULT_VIEW_ID;
|
|
627
|
+
};
|
|
628
|
+
/**
|
|
629
|
+
* Hook to check if currently in compare mode
|
|
630
|
+
*/
|
|
631
|
+
const useIsCompareMode = () => {
|
|
632
|
+
const viewId = useContext(ViewIdContext);
|
|
633
|
+
return viewId !== undefined && viewId !== DEFAULT_VIEW_ID;
|
|
634
|
+
};
|
|
635
|
+
|
|
636
|
+
const useHeatmapData = (props) => {
|
|
637
|
+
const viewId = props?.viewId || useViewId();
|
|
638
|
+
const data = useHeatmapDataStore((state) => state.data[viewId]);
|
|
639
|
+
const clickmap = useHeatmapDataStore((state) => state.clickmap[viewId]);
|
|
640
|
+
const scrollmap = useHeatmapDataStore((state) => state.scrollmap[viewId]);
|
|
641
|
+
const dataInfo = useHeatmapDataStore((state) => state.dataInfo[viewId]);
|
|
238
642
|
const setData = useHeatmapDataStore((state) => state.setData);
|
|
239
|
-
const
|
|
643
|
+
const setClickmap = useHeatmapDataStore((state) => state.setClickmap);
|
|
644
|
+
const setScrollmap = useHeatmapDataStore((state) => state.setScrollmap);
|
|
240
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]);
|
|
652
|
+
return {
|
|
653
|
+
data,
|
|
654
|
+
clickmap,
|
|
655
|
+
scrollmap,
|
|
656
|
+
dataInfo,
|
|
657
|
+
// Setters (auto-inject viewId)
|
|
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,
|
|
831
|
+
};
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
const useRegisterData = (data, dataInfo) => {
|
|
835
|
+
const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
|
|
836
|
+
const { setData, setDataInfo } = useHeatmapData();
|
|
241
837
|
const handleSetData = useCallback((data) => {
|
|
242
838
|
if (!data)
|
|
243
839
|
return;
|
|
@@ -258,8 +854,7 @@ const useRegisterData = (data, dataInfo) => {
|
|
|
258
854
|
};
|
|
259
855
|
|
|
260
856
|
const useRegisterHeatmap = ({ clickmap, scrollmap }) => {
|
|
261
|
-
const setClickmap =
|
|
262
|
-
const setScrollmap = useHeatmapDataStore((state) => state.setScrollmap);
|
|
857
|
+
const { setClickmap, setScrollmap } = useHeatmapData();
|
|
263
858
|
const handleSetClickmap = useCallback((clickmap) => {
|
|
264
859
|
if (!clickmap)
|
|
265
860
|
return;
|
|
@@ -493,33 +1088,29 @@ const buildElementInfo = (hash, rect, heatmapInfo) => {
|
|
|
493
1088
|
};
|
|
494
1089
|
|
|
495
1090
|
function findLastSizeOfDom(data) {
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
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];
|
|
499
1095
|
const docSize = {
|
|
500
|
-
width:
|
|
501
|
-
height:
|
|
1096
|
+
width: lastDoc?.width,
|
|
1097
|
+
height: lastDoc?.height,
|
|
502
1098
|
};
|
|
503
|
-
const
|
|
504
|
-
const
|
|
505
|
-
const lastResizeEvent = reversedData.find((item) => !!item.resize);
|
|
506
|
-
const firstEventResize = lastResizeEvent?.resize?.[0];
|
|
1099
|
+
const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
|
|
1100
|
+
const lastResizeEvent = listResizes?.[listResizes.length - 1];
|
|
507
1101
|
const resize = {
|
|
508
|
-
width:
|
|
509
|
-
height:
|
|
1102
|
+
width: lastResizeEvent?.data.width,
|
|
1103
|
+
height: lastResizeEvent?.data.height,
|
|
510
1104
|
};
|
|
511
1105
|
return {
|
|
512
1106
|
doc: docSize,
|
|
513
1107
|
resize: resize,
|
|
514
1108
|
size: {
|
|
515
|
-
width: resize.width
|
|
516
|
-
height: resize.height
|
|
1109
|
+
width: resize.width || docSize.width,
|
|
1110
|
+
height: resize.height || docSize.height,
|
|
517
1111
|
},
|
|
518
1112
|
};
|
|
519
1113
|
}
|
|
520
|
-
function sort(a, b) {
|
|
521
|
-
return a.time - b.time;
|
|
522
|
-
}
|
|
523
1114
|
function decodePayloads(payload) {
|
|
524
1115
|
try {
|
|
525
1116
|
return decode(payload);
|
|
@@ -855,7 +1446,7 @@ class IframeStyleReplacer {
|
|
|
855
1446
|
doc;
|
|
856
1447
|
win;
|
|
857
1448
|
config;
|
|
858
|
-
regex = /([-.\d]+)(vh|svh|lvh|dvh
|
|
1449
|
+
regex = /([-.\d]+)(vh|svh|lvh|dvh)/gi; //vw|svw|lvw|dvw
|
|
859
1450
|
constructor(iframe, config) {
|
|
860
1451
|
if (!iframe.contentDocument || !iframe.contentWindow) {
|
|
861
1452
|
throw new Error('Iframe document or window not accessible');
|
|
@@ -1151,11 +1742,9 @@ const scrollToElementIfNeeded = (visualRef, rect, scale) => {
|
|
|
1151
1742
|
});
|
|
1152
1743
|
};
|
|
1153
1744
|
const useClickedElement = ({ visualRef, getRect }) => {
|
|
1154
|
-
const
|
|
1155
|
-
const
|
|
1156
|
-
const
|
|
1157
|
-
const setShouldShowCallout = useHeatmapInteractionStore((state) => state.setShouldShowCallout);
|
|
1158
|
-
const scale = useHeatmapVizStore((state) => state.scale);
|
|
1745
|
+
const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapInteraction();
|
|
1746
|
+
const { widthScale } = useHeatmapViz();
|
|
1747
|
+
const { dataInfo } = useHeatmapData();
|
|
1159
1748
|
const [clickedElement, setClickedElement] = useState(null);
|
|
1160
1749
|
const [showMissingElement, setShowMissingElement] = useState(false);
|
|
1161
1750
|
const reset = () => {
|
|
@@ -1164,11 +1753,13 @@ const useClickedElement = ({ visualRef, getRect }) => {
|
|
|
1164
1753
|
setShouldShowCallout(false);
|
|
1165
1754
|
};
|
|
1166
1755
|
useEffect(() => {
|
|
1167
|
-
if (
|
|
1756
|
+
if (selectedElement === clickedElement?.hash)
|
|
1757
|
+
return;
|
|
1758
|
+
if (!selectedElement || !dataInfo?.elementMapInfo) {
|
|
1168
1759
|
reset();
|
|
1169
1760
|
return;
|
|
1170
1761
|
}
|
|
1171
|
-
const info =
|
|
1762
|
+
const info = dataInfo.elementMapInfo[selectedElement];
|
|
1172
1763
|
if (!info) {
|
|
1173
1764
|
setClickedElement(null);
|
|
1174
1765
|
return;
|
|
@@ -1176,7 +1767,7 @@ const useClickedElement = ({ visualRef, getRect }) => {
|
|
|
1176
1767
|
const hash = selectedElement;
|
|
1177
1768
|
const selector = info.selector;
|
|
1178
1769
|
const rect = getRect({ hash: selectedElement, selector });
|
|
1179
|
-
const elementInfo = buildElementInfo(hash, rect,
|
|
1770
|
+
const elementInfo = buildElementInfo(hash, rect, dataInfo);
|
|
1180
1771
|
if (!rect) {
|
|
1181
1772
|
setClickedElement(elementInfo);
|
|
1182
1773
|
setShowMissingElement(true);
|
|
@@ -1184,29 +1775,28 @@ const useClickedElement = ({ visualRef, getRect }) => {
|
|
|
1184
1775
|
return;
|
|
1185
1776
|
}
|
|
1186
1777
|
setShowMissingElement(false);
|
|
1187
|
-
scrollToElementIfNeeded(visualRef, rect,
|
|
1778
|
+
scrollToElementIfNeeded(visualRef, rect, widthScale);
|
|
1188
1779
|
setShouldShowCallout(true);
|
|
1189
1780
|
requestAnimationFrame(() => {
|
|
1190
1781
|
setClickedElement(elementInfo);
|
|
1191
1782
|
});
|
|
1192
|
-
}, [selectedElement,
|
|
1193
|
-
return { clickedElement, showMissingElement, shouldShowCallout
|
|
1783
|
+
}, [selectedElement, dataInfo, visualRef, widthScale]);
|
|
1784
|
+
return { clickedElement, showMissingElement, shouldShowCallout };
|
|
1194
1785
|
};
|
|
1195
1786
|
|
|
1196
1787
|
const useElementCalloutVisible = ({ visualRef, getRect }) => {
|
|
1197
|
-
const
|
|
1198
|
-
const
|
|
1199
|
-
const
|
|
1200
|
-
const setShouldShowCallout = useHeatmapInteractionStore((state) => state.setShouldShowCallout);
|
|
1788
|
+
const { selectedElement, setShouldShowCallout } = useHeatmapInteraction();
|
|
1789
|
+
const { widthScale } = useHeatmapViz();
|
|
1790
|
+
const { dataInfo } = useHeatmapData();
|
|
1201
1791
|
useEffect(() => {
|
|
1202
1792
|
if (!selectedElement)
|
|
1203
1793
|
return;
|
|
1204
1794
|
const elementIsInViewportFn = () => {
|
|
1205
|
-
const elementInfo =
|
|
1795
|
+
const elementInfo = dataInfo?.elementMapInfo?.[selectedElement];
|
|
1206
1796
|
if (!elementInfo)
|
|
1207
1797
|
return;
|
|
1208
1798
|
const rect = getRect({ hash: selectedElement, selector: elementInfo.selector });
|
|
1209
|
-
const isInViewport = isElementInViewport(rect, visualRef,
|
|
1799
|
+
const isInViewport = isElementInViewport(rect, visualRef, widthScale);
|
|
1210
1800
|
setShouldShowCallout(isInViewport);
|
|
1211
1801
|
};
|
|
1212
1802
|
elementIsInViewportFn();
|
|
@@ -1221,32 +1811,31 @@ const useElementCalloutVisible = ({ visualRef, getRect }) => {
|
|
|
1221
1811
|
window.removeEventListener('resize', handleUpdate);
|
|
1222
1812
|
visualRef?.current?.removeEventListener('scroll', handleUpdate);
|
|
1223
1813
|
};
|
|
1224
|
-
}, [selectedElement, visualRef,
|
|
1814
|
+
}, [selectedElement, visualRef, widthScale, dataInfo]);
|
|
1225
1815
|
return {};
|
|
1226
1816
|
};
|
|
1227
1817
|
|
|
1228
|
-
const useHeatmapEffects = ({ isVisible
|
|
1229
|
-
|
|
1818
|
+
const useHeatmapEffects = ({ isVisible }) => {
|
|
1819
|
+
useHeatmapInteraction();
|
|
1820
|
+
const resetAll = () => {
|
|
1821
|
+
// setShouldShowCallout(false);
|
|
1822
|
+
};
|
|
1230
1823
|
// Reset khi ẩn
|
|
1231
1824
|
useEffect(() => {
|
|
1232
|
-
if (!isVisible)
|
|
1233
|
-
resetAll();
|
|
1234
1825
|
}, [isVisible, resetAll]);
|
|
1235
1826
|
// Ẩn callout khi sidebar mở
|
|
1236
|
-
useEffect(() => {
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
}, [isElementSidebarOpen, selectedElement, setShouldShowCallout]);
|
|
1827
|
+
// useEffect(() => {
|
|
1828
|
+
// if (isElementSidebarOpen && selectedElement) {
|
|
1829
|
+
// setShouldShowCallout(false);
|
|
1830
|
+
// } else if (!isElementSidebarOpen && selectedElement) {
|
|
1831
|
+
// setShouldShowCallout(true);
|
|
1832
|
+
// }
|
|
1833
|
+
// }, [isElementSidebarOpen, selectedElement]);
|
|
1244
1834
|
};
|
|
1245
1835
|
|
|
1246
1836
|
const useHeatmapElementPosition = ({ iframeRef, wrapperRef, visualizer }) => {
|
|
1247
|
-
const widthScale = useHeatmapVizStore((state) => state.scale);
|
|
1248
|
-
const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
|
|
1249
1837
|
const heatmapWidth = useHeatmapConfigStore((state) => state.width);
|
|
1838
|
+
const { iframeHeight, widthScale } = useHeatmapViz();
|
|
1250
1839
|
return useCallback((element) => {
|
|
1251
1840
|
const hash = element?.hash;
|
|
1252
1841
|
if (!iframeRef.current?.contentDocument || !hash || !visualizer)
|
|
@@ -1397,11 +1986,9 @@ function HeatmapComponent() {
|
|
|
1397
1986
|
*/
|
|
1398
1987
|
|
|
1399
1988
|
const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
1400
|
-
const hoveredElement =
|
|
1401
|
-
const
|
|
1402
|
-
const
|
|
1403
|
-
const widthScale = useHeatmapVizStore((state) => state.scale);
|
|
1404
|
-
const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
|
|
1989
|
+
const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapInteraction();
|
|
1990
|
+
const { widthScale } = useHeatmapViz();
|
|
1991
|
+
const { dataInfo } = useHeatmapData();
|
|
1405
1992
|
const reset = useCallback(() => {
|
|
1406
1993
|
setHoveredElement(null);
|
|
1407
1994
|
}, [setHoveredElement]);
|
|
@@ -1409,16 +1996,24 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
|
1409
1996
|
reset();
|
|
1410
1997
|
}, [reset]);
|
|
1411
1998
|
const getHashFromEvent = useCallback((event) => {
|
|
1412
|
-
if (!
|
|
1999
|
+
if (!dataInfo || !isIframeReady(iframeRef, dataInfo)) {
|
|
1413
2000
|
reset();
|
|
1414
2001
|
return;
|
|
1415
2002
|
}
|
|
1416
2003
|
const iframe = iframeRef.current;
|
|
2004
|
+
if (!iframe) {
|
|
2005
|
+
reset();
|
|
2006
|
+
return;
|
|
2007
|
+
}
|
|
1417
2008
|
const doc = iframe.contentDocument;
|
|
2009
|
+
if (!doc) {
|
|
2010
|
+
reset();
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
1418
2013
|
const iframeRect = iframe.getBoundingClientRect();
|
|
1419
2014
|
const { x, y } = convertViewportToIframeCoords(event.clientX, event.clientY, iframeRect, widthScale);
|
|
1420
|
-
const targetElement = findTargetElement(doc, x, y,
|
|
1421
|
-
if (!targetElement || !isValidElement(targetElement,
|
|
2015
|
+
const targetElement = findTargetElement(doc, x, y, dataInfo);
|
|
2016
|
+
if (!targetElement || !isValidElement(targetElement, dataInfo)) {
|
|
1422
2017
|
reset();
|
|
1423
2018
|
return;
|
|
1424
2019
|
}
|
|
@@ -1427,9 +2022,9 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
|
1427
2022
|
return hash;
|
|
1428
2023
|
reset();
|
|
1429
2024
|
return;
|
|
1430
|
-
}, [
|
|
2025
|
+
}, [dataInfo, iframeRef, getRect, widthScale, reset]);
|
|
1431
2026
|
const handleMouseMove = useCallback(debounce((event) => {
|
|
1432
|
-
if (!
|
|
2027
|
+
if (!dataInfo) {
|
|
1433
2028
|
reset();
|
|
1434
2029
|
return;
|
|
1435
2030
|
}
|
|
@@ -1438,19 +2033,19 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
|
1438
2033
|
reset();
|
|
1439
2034
|
return;
|
|
1440
2035
|
}
|
|
1441
|
-
const selector =
|
|
2036
|
+
const selector = dataInfo?.elementMapInfo?.[hash];
|
|
1442
2037
|
const rect = getRect({ hash, selector });
|
|
1443
|
-
const elementInfo = buildElementInfo(hash, rect,
|
|
2038
|
+
const elementInfo = buildElementInfo(hash, rect, dataInfo);
|
|
1444
2039
|
if (!elementInfo) {
|
|
1445
2040
|
reset();
|
|
1446
2041
|
return;
|
|
1447
2042
|
}
|
|
1448
2043
|
setHoveredElement(elementInfo);
|
|
1449
|
-
}, 16), [
|
|
2044
|
+
}, 16), [dataInfo, getRect, reset, getHashFromEvent]);
|
|
1450
2045
|
const handleClick = useCallback(() => {
|
|
1451
2046
|
if (!hoveredElement?.hash)
|
|
1452
2047
|
return;
|
|
1453
|
-
|
|
2048
|
+
setSelectedElement(hoveredElement.hash);
|
|
1454
2049
|
}, [hoveredElement?.hash]);
|
|
1455
2050
|
return {
|
|
1456
2051
|
hoveredElement,
|
|
@@ -1562,11 +2157,9 @@ function useVizLiveIframeMsg(options = {}) {
|
|
|
1562
2157
|
}
|
|
1563
2158
|
|
|
1564
2159
|
function useVizLiveRender() {
|
|
1565
|
-
const wrapperHeight =
|
|
1566
|
-
const setIframeHeight = useHeatmapLiveStore((state) => state.setIframeHeight);
|
|
2160
|
+
const { setIframeHeight, wrapperHeight, setIsRenderViz, wrapperWidth } = useHeatmapViz();
|
|
1567
2161
|
const contentWidth = useHeatmapConfigStore((state) => state.width);
|
|
1568
2162
|
const htmlContent = useHeatmapLiveStore((state) => state.htmlContent);
|
|
1569
|
-
const setIsRenderViz = useHeatmapVizStore((state) => state.setIsRenderViz);
|
|
1570
2163
|
const { iframeRef, isReady } = useVizLiveIframeMsg();
|
|
1571
2164
|
useEffect(() => {
|
|
1572
2165
|
if (!htmlContent || !iframeRef.current)
|
|
@@ -1582,17 +2175,20 @@ function useVizLiveRender() {
|
|
|
1582
2175
|
useEffect(() => {
|
|
1583
2176
|
if (!isReady)
|
|
1584
2177
|
return;
|
|
2178
|
+
if (!wrapperHeight)
|
|
2179
|
+
return;
|
|
1585
2180
|
if (!iframeRef.current)
|
|
1586
2181
|
return;
|
|
2182
|
+
console.log(`🚀 🐥 ~ useVizLiveRender ~ wrapperHeight:`, wrapperHeight);
|
|
1587
2183
|
const iframe = iframeRef.current;
|
|
1588
2184
|
if (!iframe || !htmlContent)
|
|
1589
2185
|
return;
|
|
1590
2186
|
setIsRenderViz(false);
|
|
1591
|
-
reset(iframe, { width:
|
|
2187
|
+
reset(iframe, { width: wrapperWidth, height: wrapperHeight }, (height) => {
|
|
1592
2188
|
height && setIframeHeight(height);
|
|
1593
2189
|
setIsRenderViz(true);
|
|
1594
2190
|
});
|
|
1595
|
-
}, [isReady, contentWidth, wrapperHeight]);
|
|
2191
|
+
}, [isReady, contentWidth, wrapperHeight, wrapperWidth]);
|
|
1596
2192
|
return {
|
|
1597
2193
|
iframeRef,
|
|
1598
2194
|
};
|
|
@@ -1611,16 +2207,19 @@ function reset(iframe, rect, onSuccess) {
|
|
|
1611
2207
|
fixer.enableNavigationBlocking();
|
|
1612
2208
|
}
|
|
1613
2209
|
|
|
1614
|
-
let visualizer = new Visualizer();
|
|
1615
2210
|
const useHeatmapRender = () => {
|
|
1616
|
-
const data =
|
|
1617
|
-
const setVizRef =
|
|
1618
|
-
|
|
1619
|
-
const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
|
|
2211
|
+
const { data } = useHeatmapData();
|
|
2212
|
+
const { vizRef, setVizRef, setIsRenderViz, setIframeHeight, wrapperHeight, wrapperWidth } = useHeatmapViz();
|
|
2213
|
+
console.log(`🚀 🐥 ~ useHeatmapRender ~ wrapperHeight:`, wrapperHeight);
|
|
1620
2214
|
const iframeRef = useRef(null);
|
|
1621
2215
|
const renderHeatmap = useCallback(async (payloads) => {
|
|
1622
2216
|
if (!payloads || payloads.length === 0)
|
|
1623
2217
|
return;
|
|
2218
|
+
let visualizer = vizRef;
|
|
2219
|
+
if (!visualizer) {
|
|
2220
|
+
visualizer = new Visualizer();
|
|
2221
|
+
setVizRef(visualizer);
|
|
2222
|
+
}
|
|
1624
2223
|
setIsRenderViz(false);
|
|
1625
2224
|
const iframe = iframeRef.current;
|
|
1626
2225
|
if (!iframe?.contentWindow)
|
|
@@ -1629,7 +2228,6 @@ const useHeatmapRender = () => {
|
|
|
1629
2228
|
initIframe(iframe, payloads, (height) => {
|
|
1630
2229
|
height && setIframeHeight(height);
|
|
1631
2230
|
setIsRenderViz(true);
|
|
1632
|
-
setVizRef(visualizer);
|
|
1633
2231
|
});
|
|
1634
2232
|
}, []);
|
|
1635
2233
|
useEffect(() => {
|
|
@@ -1644,7 +2242,9 @@ const useHeatmapRender = () => {
|
|
|
1644
2242
|
iframeRef,
|
|
1645
2243
|
};
|
|
1646
2244
|
};
|
|
1647
|
-
function initIframe(iframe,
|
|
2245
|
+
function initIframe(iframe,
|
|
2246
|
+
// size: { width: number; height: number },
|
|
2247
|
+
payloads, onSuccess) {
|
|
1648
2248
|
const { size } = findLastSizeOfDom(payloads);
|
|
1649
2249
|
const docWidth = size.width ?? 0;
|
|
1650
2250
|
const docHeight = size.height ?? 0;
|
|
@@ -1671,8 +2271,8 @@ function sortEvents(a, b) {
|
|
|
1671
2271
|
}
|
|
1672
2272
|
|
|
1673
2273
|
const useReplayRender = () => {
|
|
1674
|
-
const data = useHeatmapDataStore((state) => state.data);
|
|
1675
2274
|
const setWidth = useHeatmapConfigStore((state) => state.setWidth);
|
|
2275
|
+
const { data } = useHeatmapData();
|
|
1676
2276
|
const visualizerRef = useRef(null);
|
|
1677
2277
|
const iframeRef = useRef(null);
|
|
1678
2278
|
const eventsRef = useRef([]);
|
|
@@ -1843,8 +2443,8 @@ const useContentDimensions = ({ iframeRef, }) => {
|
|
|
1843
2443
|
};
|
|
1844
2444
|
|
|
1845
2445
|
const useObserveIframeHeight = (props) => {
|
|
1846
|
-
const { iframeRef,
|
|
1847
|
-
const
|
|
2446
|
+
const { iframeRef, isRenderViz } = props;
|
|
2447
|
+
const { setIframeHeight } = useHeatmapViz();
|
|
1848
2448
|
const resizeObserverRef = useRef(null);
|
|
1849
2449
|
const mutationObserverRef = useRef(null);
|
|
1850
2450
|
const debounceTimerRef = useRef(null);
|
|
@@ -1852,7 +2452,7 @@ const useObserveIframeHeight = (props) => {
|
|
|
1852
2452
|
const animationFrameRef = useRef(null);
|
|
1853
2453
|
const updateIframeHeight = useCallback(() => {
|
|
1854
2454
|
const iframe = iframeRef.current;
|
|
1855
|
-
if (!iframe
|
|
2455
|
+
if (!iframe)
|
|
1856
2456
|
return;
|
|
1857
2457
|
try {
|
|
1858
2458
|
const iframeDocument = iframe.contentDocument;
|
|
@@ -1860,7 +2460,7 @@ const useObserveIframeHeight = (props) => {
|
|
|
1860
2460
|
const iframeDocumentElement = iframeDocument?.documentElement;
|
|
1861
2461
|
if (!iframeBody || !iframeDocumentElement)
|
|
1862
2462
|
return;
|
|
1863
|
-
iframe.style.height = 'auto';
|
|
2463
|
+
// iframe.style.height = 'auto'; // TODO: check if this is needed
|
|
1864
2464
|
requestAnimationFrame(() => {
|
|
1865
2465
|
const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
|
|
1866
2466
|
const documentHeight = Math.max(iframeDocumentElement.scrollHeight, iframeDocumentElement.offsetHeight, iframeDocumentElement.clientHeight);
|
|
@@ -1876,7 +2476,7 @@ const useObserveIframeHeight = (props) => {
|
|
|
1876
2476
|
catch (error) {
|
|
1877
2477
|
console.warn('Cannot measure iframe content:', error);
|
|
1878
2478
|
}
|
|
1879
|
-
}, [iframeRef
|
|
2479
|
+
}, [iframeRef]);
|
|
1880
2480
|
const debouncedUpdate = useCallback(() => {
|
|
1881
2481
|
// Cancel pending updates
|
|
1882
2482
|
if (debounceTimerRef.current) {
|
|
@@ -1969,13 +2569,8 @@ const useObserveIframeHeight = (props) => {
|
|
|
1969
2569
|
};
|
|
1970
2570
|
|
|
1971
2571
|
const useScaleCalculation = (props) => {
|
|
1972
|
-
const
|
|
1973
|
-
const
|
|
1974
|
-
const setScale = useHeatmapVizStore((state) => state.setScale);
|
|
1975
|
-
const isScaledToFit = useHeatmapVizStore((state) => state.isScaledToFit);
|
|
1976
|
-
const setIsScaledToFit = useHeatmapVizStore((state) => state.setIsScaledToFit);
|
|
1977
|
-
const minZoomRatio = useHeatmapVizStore((state) => state.minZoomRatio);
|
|
1978
|
-
const setMinZoomRatio = useHeatmapVizStore((state) => state.setMinZoomRatio);
|
|
2572
|
+
const { widthScale, zoomRatio, isScaledToFit, minZoomRatio } = useHeatmapViz();
|
|
2573
|
+
const { setScale, setIsScaledToFit, setMinZoomRatio } = useHeatmapViz();
|
|
1979
2574
|
const { containerWidth, containerHeight, contentWidth, contentHeight } = props;
|
|
1980
2575
|
const calculateScaleResult = useCallback(() => {
|
|
1981
2576
|
if (containerWidth > 0 && contentWidth > 0 && containerHeight > 0 && contentHeight > 0) {
|
|
@@ -1983,7 +2578,7 @@ const useScaleCalculation = (props) => {
|
|
|
1983
2578
|
const availableWidth = containerWidth - HEATMAP_CONFIG['padding'] * 2;
|
|
1984
2579
|
const widthScale = Math.min(availableWidth / contentWidth, 1);
|
|
1985
2580
|
// 2. Calculate available height
|
|
1986
|
-
const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] ;
|
|
2581
|
+
const toolbarHeight = HEATMAP_CONFIG['heightToolbar'] || 0;
|
|
1987
2582
|
const paddingTotal = HEATMAP_CONFIG['padding'] * 2;
|
|
1988
2583
|
const availableHeight = containerHeight - toolbarHeight - paddingTotal; // 10px buffer to avoid scroll bar
|
|
1989
2584
|
// 3. Calculate minZoomRatio (zoom ratio minimum to fit iframe in container)
|
|
@@ -2015,11 +2610,10 @@ const useScaleCalculation = (props) => {
|
|
|
2015
2610
|
useEffect(() => {
|
|
2016
2611
|
calculateScaleResult();
|
|
2017
2612
|
}, [calculateScaleResult]);
|
|
2018
|
-
return {
|
|
2613
|
+
return { widthScale, isScaledToFit, minZoomRatio };
|
|
2019
2614
|
};
|
|
2020
2615
|
|
|
2021
|
-
const useScrollSync = ({ iframeRef }) => {
|
|
2022
|
-
const widthScale = useHeatmapVizStore((state) => state.scale);
|
|
2616
|
+
const useScrollSync = ({ widthScale, iframeRef, }) => {
|
|
2023
2617
|
const handleScroll = useCallback((scrollTop) => {
|
|
2024
2618
|
const iframe = iframeRef.current;
|
|
2025
2619
|
if (!iframe || widthScale <= 0)
|
|
@@ -2040,24 +2634,24 @@ const useScrollSync = ({ iframeRef }) => {
|
|
|
2040
2634
|
};
|
|
2041
2635
|
|
|
2042
2636
|
const useHeatmapScale = (props) => {
|
|
2043
|
-
const { wrapperRef, iframeRef, iframeHeight,
|
|
2637
|
+
const { wrapperRef, iframeRef, iframeHeight, isRenderViz } = props;
|
|
2044
2638
|
// 1. Observe container dimensions
|
|
2045
2639
|
const { containerWidth, containerHeight } = useContainerDimensions({ wrapperRef });
|
|
2046
2640
|
// 2. Get content dimensions from config
|
|
2047
2641
|
const { contentWidth } = useContentDimensions({ iframeRef });
|
|
2048
2642
|
// 3. Observe iframe height (now reacts to width changes)
|
|
2049
|
-
useObserveIframeHeight({ iframeRef,
|
|
2643
|
+
useObserveIframeHeight({ iframeRef, isRenderViz });
|
|
2050
2644
|
// 4. Calculate scale
|
|
2051
|
-
const {
|
|
2645
|
+
const { widthScale } = useScaleCalculation({
|
|
2052
2646
|
containerWidth,
|
|
2053
2647
|
containerHeight,
|
|
2054
2648
|
contentWidth,
|
|
2055
2649
|
contentHeight: iframeHeight,
|
|
2056
2650
|
});
|
|
2057
2651
|
// 5. Setup scroll sync
|
|
2058
|
-
const { handleScroll } = useScrollSync({ iframeRef });
|
|
2059
|
-
const scaledHeight = iframeHeight *
|
|
2060
|
-
const scaledWidth = contentWidth *
|
|
2652
|
+
const { handleScroll } = useScrollSync({ widthScale, iframeRef });
|
|
2653
|
+
const scaledHeight = iframeHeight * widthScale;
|
|
2654
|
+
const scaledWidth = contentWidth * widthScale;
|
|
2061
2655
|
return {
|
|
2062
2656
|
containerWidth,
|
|
2063
2657
|
containerHeight,
|
|
@@ -2068,27 +2662,29 @@ const useHeatmapScale = (props) => {
|
|
|
2068
2662
|
};
|
|
2069
2663
|
|
|
2070
2664
|
const useWrapperRefHeight = (props) => {
|
|
2071
|
-
const { isActive, wrapperRef
|
|
2072
|
-
const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz);
|
|
2665
|
+
const { isActive, wrapperRef } = props;
|
|
2073
2666
|
const resizeObserverRef = useRef(null);
|
|
2074
2667
|
const mutationObserverRef = useRef(null);
|
|
2668
|
+
const { setWrapperHeight, setWrapperWidth } = useHeatmapViz();
|
|
2075
2669
|
const updateWrapperHeight = useCallback(() => {
|
|
2076
2670
|
const wrapper = wrapperRef.current;
|
|
2077
2671
|
if (!wrapper)
|
|
2078
2672
|
return;
|
|
2079
2673
|
try {
|
|
2080
2674
|
const wrapperHeight = wrapper.offsetHeight;
|
|
2675
|
+
const wrapperWidth = wrapper.offsetWidth;
|
|
2081
2676
|
if (wrapperHeight > 0) {
|
|
2082
|
-
setWrapperHeight
|
|
2677
|
+
setWrapperHeight(wrapperHeight);
|
|
2678
|
+
setWrapperWidth(wrapperWidth);
|
|
2083
2679
|
}
|
|
2084
2680
|
}
|
|
2085
2681
|
catch (error) {
|
|
2086
2682
|
console.warn('Cannot measure iframe content:', error);
|
|
2087
2683
|
}
|
|
2088
|
-
}, [wrapperRef
|
|
2684
|
+
}, [wrapperRef]);
|
|
2089
2685
|
useEffect(() => {
|
|
2090
2686
|
const wrapper = wrapperRef.current;
|
|
2091
|
-
if (!wrapper
|
|
2687
|
+
if (!wrapper)
|
|
2092
2688
|
return;
|
|
2093
2689
|
const setupObservers = () => {
|
|
2094
2690
|
try {
|
|
@@ -2118,6 +2714,7 @@ const useWrapperRefHeight = (props) => {
|
|
|
2118
2714
|
updateWrapperHeight();
|
|
2119
2715
|
}
|
|
2120
2716
|
catch (error) {
|
|
2717
|
+
console.log(`🚀 🐥 ~ setupObservers ~ error:`, error);
|
|
2121
2718
|
console.warn('Cannot access wrapper content:', error);
|
|
2122
2719
|
}
|
|
2123
2720
|
};
|
|
@@ -2132,12 +2729,12 @@ const useWrapperRefHeight = (props) => {
|
|
|
2132
2729
|
mutationObserverRef.current.disconnect();
|
|
2133
2730
|
}
|
|
2134
2731
|
};
|
|
2135
|
-
}, [wrapperRef,
|
|
2732
|
+
}, [wrapperRef, updateWrapperHeight, isActive]);
|
|
2136
2733
|
return {};
|
|
2137
2734
|
};
|
|
2138
2735
|
|
|
2139
2736
|
const useZonePositions = (options) => {
|
|
2140
|
-
const iframeHeight =
|
|
2737
|
+
const { iframeHeight } = useHeatmapViz();
|
|
2141
2738
|
const getZonePosition = useCallback((zone) => {
|
|
2142
2739
|
if (!iframeHeight) {
|
|
2143
2740
|
return null;
|
|
@@ -2163,12 +2760,13 @@ const useScrollmapZones = (options) => {
|
|
|
2163
2760
|
const { mode = 'basic', enabled = true, iframeRef, wrapperRef } = options;
|
|
2164
2761
|
const [isReady, setIsReady] = useState(false);
|
|
2165
2762
|
const [zones, setZones] = useState([]);
|
|
2166
|
-
const scrollmap =
|
|
2167
|
-
const
|
|
2763
|
+
const { scrollmap } = useHeatmapData();
|
|
2764
|
+
const { dataInfo } = useHeatmapData();
|
|
2168
2765
|
const { getZonePosition } = useZonePositions();
|
|
2766
|
+
const scrollMapInfo = dataInfo?.scrollMapInfo;
|
|
2169
2767
|
const maxUsers = useMemo(() => {
|
|
2170
2768
|
if (!scrollmap || scrollmap.length === 0)
|
|
2171
|
-
return
|
|
2769
|
+
return 0;
|
|
2172
2770
|
return Math.max(...scrollmap.map((d) => d.percUsers));
|
|
2173
2771
|
}, [scrollmap]);
|
|
2174
2772
|
const createZones = useCallback((data) => {
|
|
@@ -2265,6 +2863,514 @@ const getScrollGradientColor = (normalized) => {
|
|
|
2265
2863
|
return `rgb(${r}, ${g}, ${b})`;
|
|
2266
2864
|
};
|
|
2267
2865
|
|
|
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
|
+
}));
|
|
3017
|
+
return {
|
|
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
|
+
},
|
|
3036
|
+
};
|
|
3037
|
+
}
|
|
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
|
+
|
|
3052
|
+
/**
|
|
3053
|
+
* Hook to track render count of a component
|
|
3054
|
+
* @param componentName - Name of the component
|
|
3055
|
+
* @param viewId - Optional viewId for compare mode
|
|
3056
|
+
*/
|
|
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,
|
|
3074
|
+
};
|
|
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 });
|
|
3194
|
+
};
|
|
3195
|
+
WrappedComponent.displayName = `withPerformanceTracking(${componentName || Component.displayName || Component.name})`;
|
|
3196
|
+
return WrappedComponent;
|
|
3197
|
+
}
|
|
3198
|
+
|
|
3199
|
+
/**
|
|
3200
|
+
* Middleware để track store updates
|
|
3201
|
+
*/
|
|
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);
|
|
3243
|
+
};
|
|
3244
|
+
return config(wrappedSet, get, api);
|
|
3245
|
+
};
|
|
3246
|
+
}
|
|
3247
|
+
/**
|
|
3248
|
+
* Track specific store action
|
|
3249
|
+
*/
|
|
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,
|
|
3260
|
+
};
|
|
3261
|
+
performanceLogger.log(metric);
|
|
3262
|
+
}
|
|
3263
|
+
|
|
3264
|
+
/**
|
|
3265
|
+
* Get performance report as JSON string
|
|
3266
|
+
*/
|
|
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;
|
|
3304
|
+
}
|
|
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();
|
|
3343
|
+
return {
|
|
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
|
+
},
|
|
3354
|
+
};
|
|
3355
|
+
}
|
|
3356
|
+
/**
|
|
3357
|
+
* Compare performance between two viewIds
|
|
3358
|
+
*/
|
|
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 };
|
|
3363
|
+
return {
|
|
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
|
+
},
|
|
3371
|
+
};
|
|
3372
|
+
}
|
|
3373
|
+
|
|
2268
3374
|
const BoxStack = forwardRef(({ children, ...props }, ref) => {
|
|
2269
3375
|
const id = props.id;
|
|
2270
3376
|
const flexDirection = props.flexDirection;
|
|
@@ -2310,7 +3416,12 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
|
|
|
2310
3416
|
|
|
2311
3417
|
const ContentTopBar = () => {
|
|
2312
3418
|
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3419
|
+
useHeatmapConfigStore((state) => state.mode);
|
|
2313
3420
|
const TopBar = controls.TopBar;
|
|
3421
|
+
// In compare mode, hide individual top bars since we have a global header
|
|
3422
|
+
// if (mode === 'compare') {
|
|
3423
|
+
// return null;
|
|
3424
|
+
// }
|
|
2314
3425
|
return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
|
|
2315
3426
|
borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
2316
3427
|
}, children: TopBar && jsx(TopBar, {}) }));
|
|
@@ -2318,8 +3429,9 @@ const ContentTopBar = () => {
|
|
|
2318
3429
|
|
|
2319
3430
|
const ContentMetricBar = () => {
|
|
2320
3431
|
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3432
|
+
const borderBottom = `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`;
|
|
2321
3433
|
return (jsx(BoxStack, { id: "gx-hm-content-metric-bar", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
|
|
2322
|
-
borderBottom
|
|
3434
|
+
borderBottom,
|
|
2323
3435
|
}, children: controls.MetricBar ?? null }));
|
|
2324
3436
|
};
|
|
2325
3437
|
|
|
@@ -2335,31 +3447,88 @@ const ContentToolbar = () => {
|
|
|
2335
3447
|
}, children: controls.Toolbar ?? null }));
|
|
2336
3448
|
};
|
|
2337
3449
|
|
|
2338
|
-
const
|
|
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
|
+
|
|
3511
|
+
const VizContainer = ({ children, isActive = false }) => {
|
|
2339
3512
|
const wrapperRef = useRef(null);
|
|
3513
|
+
const viewId = useViewId();
|
|
2340
3514
|
useWrapperRefHeight({
|
|
2341
|
-
isActive
|
|
3515
|
+
isActive,
|
|
2342
3516
|
wrapperRef,
|
|
2343
|
-
setWrapperHeight,
|
|
2344
3517
|
});
|
|
2345
|
-
return (
|
|
2346
|
-
|
|
2347
|
-
|
|
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, {})] }));
|
|
2348
3521
|
};
|
|
2349
3522
|
|
|
2350
3523
|
const useClickmap = () => {
|
|
2351
|
-
const
|
|
2352
|
-
const clickmap =
|
|
2353
|
-
const vizRef = useHeatmapSingleStore((state) => state.vizRef);
|
|
3524
|
+
const { vizRef } = useHeatmapViz();
|
|
3525
|
+
const { clickmap } = useHeatmapData();
|
|
2354
3526
|
const start = useCallback(() => {
|
|
2355
|
-
if (isInitialized)
|
|
2356
|
-
return;
|
|
2357
3527
|
if (!vizRef || !clickmap || clickmap.length === 0)
|
|
2358
3528
|
return;
|
|
2359
3529
|
try {
|
|
2360
3530
|
vizRef?.clearmap?.();
|
|
2361
3531
|
vizRef?.clickmap?.(clickmap);
|
|
2362
|
-
setIsInitialized(true);
|
|
2363
3532
|
}
|
|
2364
3533
|
catch (error) {
|
|
2365
3534
|
console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
|
|
@@ -2369,8 +3538,8 @@ const useClickmap = () => {
|
|
|
2369
3538
|
};
|
|
2370
3539
|
|
|
2371
3540
|
const useScrollmap = () => {
|
|
2372
|
-
const vizRef =
|
|
2373
|
-
const scrollmap =
|
|
3541
|
+
const { vizRef } = useHeatmapViz();
|
|
3542
|
+
const { scrollmap } = useHeatmapData();
|
|
2374
3543
|
const start = useCallback(() => {
|
|
2375
3544
|
// if (isInitialized) return;
|
|
2376
3545
|
if (!vizRef || !scrollmap || scrollmap.length === 0)
|
|
@@ -2387,7 +3556,7 @@ const useScrollmap = () => {
|
|
|
2387
3556
|
return { start };
|
|
2388
3557
|
};
|
|
2389
3558
|
|
|
2390
|
-
const useHeatmapCanvas = (
|
|
3559
|
+
const useHeatmapCanvas = () => {
|
|
2391
3560
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
2392
3561
|
const { start: startClickmap } = useClickmap();
|
|
2393
3562
|
const { start: startScrollmap } = useScrollmap();
|
|
@@ -2403,10 +3572,28 @@ const useHeatmapCanvas = ({ iframeRef, }) => {
|
|
|
2403
3572
|
}, [heatmapType, startClickmap, startScrollmap]);
|
|
2404
3573
|
};
|
|
2405
3574
|
|
|
2406
|
-
|
|
2407
|
-
const
|
|
2408
|
-
const
|
|
2409
|
-
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
|
+
};
|
|
2410
3597
|
|
|
2411
3598
|
const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
|
|
2412
3599
|
const style = calculateRankPosition(elementRect, widthScale);
|
|
@@ -2415,9 +3602,9 @@ const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
|
|
|
2415
3602
|
|
|
2416
3603
|
const NUMBER_OF_TOP_ELEMENTS = 10;
|
|
2417
3604
|
const DefaultRankBadges = ({ getRect, hidden }) => {
|
|
2418
|
-
const
|
|
2419
|
-
const widthScale =
|
|
2420
|
-
const elements =
|
|
3605
|
+
const { dataInfo } = useHeatmapData();
|
|
3606
|
+
const { widthScale } = useHeatmapViz();
|
|
3607
|
+
const elements = dataInfo?.sortedElements?.slice(0, NUMBER_OF_TOP_ELEMENTS) ?? [];
|
|
2421
3608
|
if (hidden || elements.length === 0)
|
|
2422
3609
|
return null;
|
|
2423
3610
|
return (jsx(Fragment, { children: elements.map((element, index) => {
|
|
@@ -2436,6 +3623,7 @@ const DEFAULT_POSITION = {
|
|
|
2436
3623
|
};
|
|
2437
3624
|
const ElementCallout = (props) => {
|
|
2438
3625
|
const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
|
|
3626
|
+
const viewId = useViewId();
|
|
2439
3627
|
const { element, target, visualRef, hozOffset, alignment = 'left' } = props;
|
|
2440
3628
|
const calloutRef = useRef(null);
|
|
2441
3629
|
const [position, setPosition] = useState(DEFAULT_POSITION);
|
|
@@ -2468,15 +3656,15 @@ const ElementCallout = (props) => {
|
|
|
2468
3656
|
position: 'fixed',
|
|
2469
3657
|
top: position.top,
|
|
2470
3658
|
left: position.left,
|
|
2471
|
-
zIndex:
|
|
3659
|
+
zIndex: Z_INDEX.CALLOUT,
|
|
2472
3660
|
}, "aria-live": "assertive", role: "tooltip", children: CompElementCallout && jsx(CompElementCallout, { elementHash: element.hash }) }));
|
|
2473
|
-
return createPortal(calloutContent, document.getElementById(
|
|
3661
|
+
return createPortal(calloutContent, document.getElementById(`gx-hm-viz-container-${viewId}`));
|
|
2474
3662
|
};
|
|
2475
3663
|
|
|
2476
3664
|
const ElementMissing = ({ show = true }) => {
|
|
3665
|
+
const { widthScale } = useHeatmapViz();
|
|
2477
3666
|
if (!show)
|
|
2478
3667
|
return null;
|
|
2479
|
-
const widthScale = useHeatmapVizStore((state) => state.scale);
|
|
2480
3668
|
return (jsx("div", { className: "missingElement", style: {
|
|
2481
3669
|
position: 'fixed',
|
|
2482
3670
|
top: '50%',
|
|
@@ -2494,18 +3682,9 @@ const ElementMissing = ({ show = true }) => {
|
|
|
2494
3682
|
}, "aria-live": "assertive", children: "Element not visible on current screen" }));
|
|
2495
3683
|
};
|
|
2496
3684
|
|
|
2497
|
-
const
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
secondary: SECONDARY_HOVERED_ELEMENT_ID,
|
|
2501
|
-
},
|
|
2502
|
-
clicked: {
|
|
2503
|
-
default: CLICKED_ELEMENT_ID,
|
|
2504
|
-
secondary: SECONDARY_CLICKED_ELEMENT_ID,
|
|
2505
|
-
},
|
|
2506
|
-
};
|
|
2507
|
-
const ElementOverlay = ({ type, element, onClick, isSecondary }) => {
|
|
2508
|
-
const widthScale = useHeatmapVizStore((state) => state.scale);
|
|
3685
|
+
const ElementOverlay = ({ type, element, onClick, isSecondary, elementId, }) => {
|
|
3686
|
+
// useRenderCount('ElementOverlay');
|
|
3687
|
+
const { widthScale } = useHeatmapViz();
|
|
2509
3688
|
if (!element || (element.width === 0 && element.height === 0))
|
|
2510
3689
|
return null;
|
|
2511
3690
|
// Iframe has border, so we need to add it to the top position
|
|
@@ -2513,8 +3692,7 @@ const ElementOverlay = ({ type, element, onClick, isSecondary }) => {
|
|
|
2513
3692
|
const left = element.left + HEATMAP_CONFIG['borderWidthIframe'];
|
|
2514
3693
|
const width = element.width;
|
|
2515
3694
|
const height = element.height;
|
|
2516
|
-
|
|
2517
|
-
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: {
|
|
2518
3696
|
top,
|
|
2519
3697
|
left,
|
|
2520
3698
|
width,
|
|
@@ -2528,14 +3706,17 @@ const ELEMENT_CALLOUT = {
|
|
|
2528
3706
|
alignment: 'left',
|
|
2529
3707
|
};
|
|
2530
3708
|
const HeatmapElements = (props) => {
|
|
2531
|
-
const
|
|
2532
|
-
const {
|
|
3709
|
+
const viewId = useViewId();
|
|
3710
|
+
const { iframeHeight } = useHeatmapViz();
|
|
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;
|
|
2533
3714
|
const getRect = useHeatmapElementPosition({
|
|
2534
3715
|
iframeRef,
|
|
2535
3716
|
wrapperRef,
|
|
2536
3717
|
visualizer,
|
|
2537
3718
|
});
|
|
2538
|
-
const { clickedElement, showMissingElement, shouldShowCallout
|
|
3719
|
+
const { clickedElement, showMissingElement, shouldShowCallout } = useClickedElement({
|
|
2539
3720
|
visualRef,
|
|
2540
3721
|
getRect,
|
|
2541
3722
|
});
|
|
@@ -2543,53 +3724,29 @@ const HeatmapElements = (props) => {
|
|
|
2543
3724
|
iframeRef,
|
|
2544
3725
|
getRect,
|
|
2545
3726
|
});
|
|
2546
|
-
useElementCalloutVisible({
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
});
|
|
2550
|
-
const resetAll = () => {
|
|
2551
|
-
// setShouldShowCallout(false);
|
|
2552
|
-
};
|
|
2553
|
-
useHeatmapEffects({
|
|
2554
|
-
isVisible,
|
|
2555
|
-
isElementSidebarOpen,
|
|
2556
|
-
setShouldShowCallout,
|
|
2557
|
-
resetAll,
|
|
2558
|
-
});
|
|
3727
|
+
useElementCalloutVisible({ visualRef, getRect });
|
|
3728
|
+
useHeatmapEffects({ isVisible });
|
|
3729
|
+
useRenderCount('HeatmapElements');
|
|
2559
3730
|
if (!isVisible)
|
|
2560
3731
|
return null;
|
|
2561
|
-
return (jsxs("div", { onMouseMove: (
|
|
2562
|
-
handleMouseMove(event);
|
|
2563
|
-
// handleMouseMove2(event as any);
|
|
2564
|
-
}, 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 }))] }));
|
|
2565
3733
|
};
|
|
2566
3734
|
|
|
2567
3735
|
const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
|
|
2568
|
-
const heatmapInfo = useHeatmapDataStore((state) => state.dataInfo);
|
|
2569
3736
|
const contentWidth = useHeatmapConfigStore((state) => state.width);
|
|
2570
|
-
const
|
|
3737
|
+
const { dataInfo } = useHeatmapData();
|
|
3738
|
+
const { vizRef } = useHeatmapViz();
|
|
2571
3739
|
const visualizer = {
|
|
2572
3740
|
get: (hash) => {
|
|
2573
|
-
if (vizRef)
|
|
2574
|
-
return vizRef.get(hash);
|
|
2575
|
-
}
|
|
2576
|
-
const doc = iframeRef.current?.contentDocument;
|
|
2577
|
-
if (!doc)
|
|
3741
|
+
if (!vizRef)
|
|
2578
3742
|
return null;
|
|
2579
|
-
|
|
2580
|
-
if (elmHashAlpha) {
|
|
2581
|
-
return elmHashAlpha;
|
|
2582
|
-
}
|
|
2583
|
-
const elmHash = doc.querySelector(`[data-clarity-hash="${hash}"]`);
|
|
2584
|
-
if (elmHash) {
|
|
2585
|
-
return elmHash;
|
|
2586
|
-
}
|
|
2587
|
-
return null;
|
|
3743
|
+
return vizRef.get(hash);
|
|
2588
3744
|
},
|
|
2589
3745
|
};
|
|
3746
|
+
// useRenderCount('VizElements');
|
|
2590
3747
|
if (!iframeRef.current)
|
|
2591
3748
|
return null;
|
|
2592
|
-
return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo:
|
|
3749
|
+
return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo: dataInfo, isVisible: true, iframeDimensions: {
|
|
2593
3750
|
width: contentWidth,
|
|
2594
3751
|
position: 'absolute',
|
|
2595
3752
|
top: 0,
|
|
@@ -2599,8 +3756,9 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
|
|
|
2599
3756
|
};
|
|
2600
3757
|
|
|
2601
3758
|
const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
|
|
2602
|
-
const
|
|
3759
|
+
const { dataInfo } = useHeatmapData();
|
|
2603
3760
|
const { getZonePosition } = useZonePositions();
|
|
3761
|
+
const averageFold = dataInfo?.averageFold || 50;
|
|
2604
3762
|
const position = getZonePosition({
|
|
2605
3763
|
startY: averageFold,
|
|
2606
3764
|
endY: averageFold,
|
|
@@ -2635,8 +3793,8 @@ const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
|
|
|
2635
3793
|
};
|
|
2636
3794
|
|
|
2637
3795
|
const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
|
|
2638
|
-
const scrollmap = useHeatmapDataStore((state) => state.scrollmap);
|
|
2639
3796
|
const scrollType = useHeatmapConfigStore((state) => state.scrollType);
|
|
3797
|
+
const { scrollmap } = useHeatmapData();
|
|
2640
3798
|
const { getZonePosition } = useZonePositions();
|
|
2641
3799
|
if (!scrollmap || scrollmap.length === 0)
|
|
2642
3800
|
return null;
|
|
@@ -2710,8 +3868,8 @@ const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
|
|
|
2710
3868
|
};
|
|
2711
3869
|
|
|
2712
3870
|
const ScrollMapMinimap = ({ zones, maxUsers }) => {
|
|
2713
|
-
const showMinimap = useHeatmapVizScrollmapStore((state) => state.showMinimap);
|
|
2714
3871
|
const scrollType = useHeatmapConfigStore((state) => state.scrollType);
|
|
3872
|
+
const { showMinimap } = useHeatmapVizScrollmap();
|
|
2715
3873
|
const isScrollType = [IScrollType.Attention].includes(scrollType);
|
|
2716
3874
|
if (!showMinimap || !isScrollType)
|
|
2717
3875
|
return null;
|
|
@@ -2816,7 +3974,7 @@ const MetricsTooltipContent = ({ zone }) => {
|
|
|
2816
3974
|
const MetricRow = ({ label, value }) => (jsxs("div", { style: { display: 'flex', justifyContent: 'space-between' }, children: [jsxs("span", { style: { color: '#605E5C' }, children: [label, ":"] }), jsx("span", { style: { fontWeight: 600 }, children: value })] }));
|
|
2817
3975
|
|
|
2818
3976
|
const HoverZones = ({ iframeRef, wrapperRef, position, currentScrollPercent, }) => {
|
|
2819
|
-
const scrollmap =
|
|
3977
|
+
const { scrollmap } = useHeatmapData();
|
|
2820
3978
|
// const hoveredZone = useHeatmapVizScrollmapStore((state) => state.hoveredZone);
|
|
2821
3979
|
// const setHoveredZone = useHeatmapVizScrollmapStore((state) => state.setHoveredZone);
|
|
2822
3980
|
const { zones, isReady, maxUsers } = useScrollmapZones({
|
|
@@ -2834,8 +3992,7 @@ const ScrollMapOverlay = ({ wrapperRef, iframeRef }) => {
|
|
|
2834
3992
|
const overlayRef = useRef(null);
|
|
2835
3993
|
const [position, setPosition] = useState();
|
|
2836
3994
|
const [currentScrollPercent, setCurrentScrollPercent] = useState(0);
|
|
2837
|
-
const widthScale =
|
|
2838
|
-
const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
|
|
3995
|
+
const { widthScale, iframeHeight } = useHeatmapViz();
|
|
2839
3996
|
const handleMouseMove = (event) => {
|
|
2840
3997
|
if (!iframeRef.current || !wrapperRef.current)
|
|
2841
3998
|
return;
|
|
@@ -2870,8 +4027,9 @@ const ScrollMapOverlay = ({ wrapperRef, iframeRef }) => {
|
|
|
2870
4027
|
|
|
2871
4028
|
const SCROLL_TYPES = [IHeatmapType.Scroll];
|
|
2872
4029
|
const VizScrollMap = ({ iframeRef, wrapperRef }) => {
|
|
2873
|
-
const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
|
|
2874
4030
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
4031
|
+
const { iframeHeight } = useHeatmapViz();
|
|
4032
|
+
// useRenderCount('VizScrollMap');
|
|
2875
4033
|
const isHeatmapScroll = SCROLL_TYPES.includes(heatmapType);
|
|
2876
4034
|
if (!iframeHeight || !isHeatmapScroll)
|
|
2877
4035
|
return null;
|
|
@@ -2887,10 +4045,8 @@ const VizScrollMap = ({ iframeRef, wrapperRef }) => {
|
|
|
2887
4045
|
|
|
2888
4046
|
const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHeight, onScroll, }) => {
|
|
2889
4047
|
const contentWidth = useHeatmapConfigStore((state) => state.width);
|
|
2890
|
-
const widthScale =
|
|
2891
|
-
const contentHeight =
|
|
2892
|
-
? `${scaledHeight + HEATMAP_CONFIG['heightToolbar'] + HEATMAP_CONFIG['padding'] * 2}px`
|
|
2893
|
-
: 'auto';
|
|
4048
|
+
const { widthScale } = useHeatmapViz();
|
|
4049
|
+
const contentHeight = calcContentHeight();
|
|
2894
4050
|
return (jsx("div", { ref: visualRef, className: "gx-hm-visual", onScroll: onScroll, style: {
|
|
2895
4051
|
overflow: 'hidden scroll',
|
|
2896
4052
|
display: 'flex',
|
|
@@ -2915,29 +4071,34 @@ const WrapperVisual = ({ children, visualRef, wrapperRef, scaledHeight, iframeHe
|
|
|
2915
4071
|
transformOrigin: 'top center',
|
|
2916
4072
|
paddingBlockEnd: '0',
|
|
2917
4073
|
}, children: children }) }) }));
|
|
4074
|
+
function calcContentHeight() {
|
|
4075
|
+
return scaledHeight > 0
|
|
4076
|
+
? `${scaledHeight + HEATMAP_CONFIG['heightToolbar'] + HEATMAP_CONFIG['padding'] * 2}px`
|
|
4077
|
+
: 'auto';
|
|
4078
|
+
}
|
|
2918
4079
|
};
|
|
2919
4080
|
|
|
2920
4081
|
const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
2921
|
-
const
|
|
4082
|
+
const contentWidth = useHeatmapConfigStore((state) => state.width || 0);
|
|
2922
4083
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
2923
|
-
const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
|
|
2924
|
-
const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
|
|
2925
|
-
const setSelectedElement = useHeatmapInteractionStore((state) => state.setSelectedElement);
|
|
2926
4084
|
const wrapperRef = useRef(null);
|
|
2927
4085
|
const visualRef = useRef(null);
|
|
4086
|
+
const { setSelectedElement } = useHeatmapInteraction();
|
|
4087
|
+
const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
|
|
2928
4088
|
const { iframeRef } = useHeatmapVizRender(mode);
|
|
2929
4089
|
const { scaledHeight, handleScroll } = useHeatmapScale({
|
|
2930
4090
|
wrapperRef,
|
|
2931
4091
|
iframeRef,
|
|
2932
4092
|
visualRef,
|
|
2933
4093
|
iframeHeight,
|
|
4094
|
+
isRenderViz,
|
|
2934
4095
|
});
|
|
2935
|
-
|
|
4096
|
+
useHeatmapCanvas();
|
|
4097
|
+
useRenderCount('VizDomRenderer');
|
|
2936
4098
|
const onScroll = (e) => {
|
|
2937
4099
|
const scrollTop = e.currentTarget.scrollTop;
|
|
2938
4100
|
handleScroll(scrollTop);
|
|
2939
4101
|
};
|
|
2940
|
-
useHeatmapCanvas({ iframeRef: iframeRef });
|
|
2941
4102
|
const cleanUp = () => {
|
|
2942
4103
|
setIframeHeight(0);
|
|
2943
4104
|
setSelectedElement(null);
|
|
@@ -2953,26 +4114,20 @@ const VizLoading = () => {
|
|
|
2953
4114
|
};
|
|
2954
4115
|
|
|
2955
4116
|
const VizDomHeatmap = () => {
|
|
2956
|
-
const
|
|
2957
|
-
|
|
2958
|
-
const iframeHeight = useHeatmapSingleStore((state) => state.iframeHeight);
|
|
2959
|
-
const setIframeHeight = useHeatmapSingleStore((state) => state.setIframeHeight);
|
|
2960
|
-
const setVizRef = useHeatmapSingleStore((state) => state.setVizRef);
|
|
4117
|
+
const { iframeHeight, setIframeHeight } = useHeatmapViz();
|
|
4118
|
+
useRenderCount('VizDomHeatmap');
|
|
2961
4119
|
useEffect(() => {
|
|
2962
4120
|
return () => {
|
|
2963
|
-
|
|
4121
|
+
console.log('🚀 🐥 ~ useEffect ~ return:');
|
|
2964
4122
|
setIframeHeight(0);
|
|
2965
4123
|
};
|
|
2966
4124
|
}, []);
|
|
2967
|
-
|
|
2968
|
-
return controls.VizLoading ?? null;
|
|
2969
|
-
return (jsxs(VizContainer, { children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
|
|
4125
|
+
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
|
|
2970
4126
|
};
|
|
2971
4127
|
|
|
2972
4128
|
const VizLiveRenderer = () => {
|
|
2973
4129
|
const contentWidth = useHeatmapConfigStore((state) => state.width);
|
|
2974
|
-
const iframeHeight =
|
|
2975
|
-
const setIframeHeight = useHeatmapLiveStore((state) => state.setIframeHeight);
|
|
4130
|
+
const { isRenderViz, iframeHeight, setIframeHeight } = useHeatmapViz();
|
|
2976
4131
|
const visualRef = useRef(null);
|
|
2977
4132
|
const wrapperRef = useRef(null);
|
|
2978
4133
|
const { iframeRef } = useVizLiveRender();
|
|
@@ -2981,7 +4136,7 @@ const VizLiveRenderer = () => {
|
|
|
2981
4136
|
iframeRef,
|
|
2982
4137
|
visualRef,
|
|
2983
4138
|
iframeHeight,
|
|
2984
|
-
|
|
4139
|
+
isRenderViz,
|
|
2985
4140
|
});
|
|
2986
4141
|
const onScroll = (e) => {
|
|
2987
4142
|
const scrollTop = e.currentTarget.scrollTop;
|
|
@@ -2993,62 +4148,38 @@ const VizLiveRenderer = () => {
|
|
|
2993
4148
|
};
|
|
2994
4149
|
|
|
2995
4150
|
const VizLiveHeatmap = () => {
|
|
2996
|
-
const
|
|
2997
|
-
const isRendering = useHeatmapDataStore((state) => state.isRendering);
|
|
2998
|
-
const iframeHeight = useHeatmapLiveStore((state) => state.iframeHeight);
|
|
2999
|
-
const wrapperHeight = useHeatmapLiveStore((state) => state.wrapperHeight);
|
|
3000
|
-
const setWrapperHeight = useHeatmapLiveStore((state) => state.setWrapperHeight);
|
|
4151
|
+
const { iframeHeight, wrapperHeight } = useHeatmapViz();
|
|
3001
4152
|
const reset = useHeatmapLiveStore((state) => state.reset);
|
|
3002
4153
|
useEffect(() => {
|
|
3003
4154
|
return () => {
|
|
3004
4155
|
reset();
|
|
3005
4156
|
};
|
|
3006
|
-
}, [
|
|
3007
|
-
|
|
3008
|
-
return controls.VizLoading ?? null;
|
|
3009
|
-
return (jsxs(VizContainer, { setWrapperHeight: setWrapperHeight, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
|
|
4157
|
+
}, []);
|
|
4158
|
+
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
|
|
3010
4159
|
};
|
|
3011
4160
|
|
|
3012
4161
|
const ContentVizByMode = () => {
|
|
3013
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;
|
|
3014
4167
|
switch (mode) {
|
|
3015
4168
|
case 'live':
|
|
3016
4169
|
return jsx(VizLiveHeatmap, {});
|
|
4170
|
+
// case 'compare':
|
|
4171
|
+
// return <VizCompareHeatmap />;
|
|
3017
4172
|
default:
|
|
3018
4173
|
return jsx(VizDomHeatmap, {});
|
|
3019
4174
|
}
|
|
3020
4175
|
};
|
|
3021
4176
|
|
|
3022
|
-
const LeftSidebar = () => {
|
|
3023
|
-
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3024
|
-
const isHideSidebar = useHeatmapInteractionStore((state) => state.state.hideSidebar);
|
|
3025
|
-
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
3026
|
-
if (isHideSidebar) {
|
|
3027
|
-
return null;
|
|
3028
|
-
}
|
|
3029
|
-
return (jsx("div", { className: "gx-hm-sidebar", style: {
|
|
3030
|
-
height: '100%',
|
|
3031
|
-
display: 'flex',
|
|
3032
|
-
zIndex: 1,
|
|
3033
|
-
...(isHideSidebar
|
|
3034
|
-
? {
|
|
3035
|
-
width: '0',
|
|
3036
|
-
transform: 'translateX(-100%)',
|
|
3037
|
-
visibility: 'hidden',
|
|
3038
|
-
}
|
|
3039
|
-
: { width: `${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px` }),
|
|
3040
|
-
}, children: jsx("div", { className: "gx-hm-sidebar-wrapper", style: {
|
|
3041
|
-
height: '100%',
|
|
3042
|
-
width: `calc(${sidebarWidth}px + ${HEATMAP_CONFIG.borderWidth}px)`,
|
|
3043
|
-
borderRight: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
3044
|
-
}, children: controls.Sidebar ?? null }) }));
|
|
3045
|
-
};
|
|
3046
|
-
|
|
3047
4177
|
const WrapperPreview = () => {
|
|
3048
|
-
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, {})] })] }));
|
|
3049
4179
|
};
|
|
3050
4180
|
|
|
3051
4181
|
const WrapperLayout = () => {
|
|
4182
|
+
useRenderCount('WrapperLayout');
|
|
3052
4183
|
return (jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(WrapperPreview, {})] }));
|
|
3053
4184
|
};
|
|
3054
4185
|
|
|
@@ -3057,6 +4188,19 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
3057
4188
|
useRegisterData(data, dataInfo);
|
|
3058
4189
|
useRegisterHeatmap({ clickmap, scrollmap });
|
|
3059
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
|
+
});
|
|
3060
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: {
|
|
3061
4205
|
minHeight: '100%',
|
|
3062
4206
|
display: 'flex',
|
|
@@ -3070,4 +4214,4 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
3070
4214
|
}
|
|
3071
4215
|
};
|
|
3072
4216
|
|
|
3073
|
-
export { GraphView, HeatmapLayout, IClickType, IHeatmapType, IScrollType, useHeatmapConfigStore, useHeatmapDataStore, useHeatmapInteractionStore, useHeatmapLiveStore, useHeatmapVizStore };
|
|
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 };
|