@gemx-dev/heatmap-react 3.5.50 → 3.5.52
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/HeatmapLayout.d.ts +2 -2
- package/dist/esm/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/esm/components/Layout/HeatmapPreview.d.ts +2 -0
- package/dist/esm/components/Layout/HeatmapPreview.d.ts.map +1 -0
- package/dist/esm/components/Layout/MetricBar/ContentMetricBar.d.ts.map +1 -0
- package/dist/esm/components/Layout/MetricBar/index.d.ts +2 -0
- package/dist/esm/components/Layout/MetricBar/index.d.ts.map +1 -0
- package/dist/esm/components/Layout/Toolbar/ContentToolbar.d.ts.map +1 -0
- package/dist/esm/components/Layout/Toolbar/index.d.ts +2 -0
- package/dist/esm/components/Layout/Toolbar/index.d.ts.map +1 -0
- package/dist/esm/components/Layout/TopBar/ContentTopBar.d.ts.map +1 -0
- package/dist/esm/components/Layout/TopBar/index.d.ts +2 -0
- package/dist/esm/components/Layout/TopBar/index.d.ts.map +1 -0
- package/dist/esm/components/Layout/VizByMode/ContentVizByMode.d.ts.map +1 -0
- package/dist/esm/components/Layout/VizByMode/index.d.ts +2 -0
- package/dist/esm/components/Layout/VizByMode/index.d.ts.map +1 -0
- package/dist/esm/components/VizAreaClick/AreaControls.d.ts +10 -0
- package/dist/esm/components/VizAreaClick/AreaControls.d.ts.map +1 -0
- package/dist/esm/components/VizAreaClick/AreaEditHighlight.d.ts +8 -0
- package/dist/esm/components/VizAreaClick/AreaEditHighlight.d.ts.map +1 -0
- package/dist/esm/components/VizAreaClick/AreaLabel.d.ts +8 -0
- package/dist/esm/components/VizAreaClick/AreaLabel.d.ts.map +1 -0
- package/dist/esm/components/VizAreaClick/AreaOverlay.d.ts +12 -0
- package/dist/esm/components/VizAreaClick/AreaOverlay.d.ts.map +1 -0
- package/dist/esm/components/VizAreaClick/PortalAreaRenderer.d.ts +16 -0
- package/dist/esm/components/VizAreaClick/PortalAreaRenderer.d.ts.map +1 -0
- package/dist/esm/components/VizAreaClick/VizAreaClick.d.ts +17 -0
- package/dist/esm/components/VizAreaClick/VizAreaClick.d.ts.map +1 -0
- package/dist/esm/components/VizAreaClick/index.d.ts +5 -0
- package/dist/esm/components/VizAreaClick/index.d.ts.map +1 -0
- package/dist/esm/components/VizDom/VizContainer.d.ts.map +1 -1
- package/dist/esm/components/VizDom/VizDomRenderer.d.ts.map +1 -1
- package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
- package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
- package/dist/esm/components/VizElement/VizElements.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/HoverZones.d.ts +1 -1
- package/dist/esm/components/VizScrollmap/HoverZones.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/ScrollMapMinimap.d.ts +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/ScrollZoneHoverArea.d.ts +1 -2
- package/dist/esm/components/VizScrollmap/ScrollZoneHoverArea.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/ScrollZoneTooltip.d.ts +1 -1
- package/dist/esm/components/VizScrollmap/ScrollZoneTooltip.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmap/index.d.ts +1 -0
- package/dist/esm/components/VizScrollmap/index.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts +1 -2
- package/dist/esm/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts.map +1 -1
- package/dist/esm/components/VizScrollmapV2/useScrollmapOverlay.d.ts +1 -1
- package/dist/esm/components/VizScrollmapV2/useScrollmapOverlay.d.ts.map +1 -1
- package/dist/esm/configs/style.d.ts +1 -1
- package/dist/esm/configs/style.d.ts.map +1 -1
- package/dist/esm/configs/viewId.d.ts +1 -19
- package/dist/esm/configs/viewId.d.ts.map +1 -1
- package/dist/esm/constants/index.d.ts +2 -13
- package/dist/esm/constants/index.d.ts.map +1 -1
- package/dist/esm/constants/viz-area-click.d.ts +6 -0
- package/dist/esm/constants/viz-area-click.d.ts.map +1 -0
- package/dist/esm/constants/viz-elm-callout.d.ts +8 -0
- package/dist/esm/constants/viz-elm-callout.d.ts.map +1 -0
- package/dist/esm/contexts/ViewIdContext.d.ts +3 -0
- package/dist/esm/contexts/ViewIdContext.d.ts.map +1 -0
- package/dist/esm/contexts/index.d.ts +1 -1
- package/dist/esm/contexts/index.d.ts.map +1 -1
- package/dist/esm/helpers/iframe-helper/fixer.d.ts +1 -1
- package/dist/esm/helpers/iframe-helper/fixer.d.ts.map +1 -1
- package/dist/esm/helpers/iframe-helper/init.d.ts +1 -1
- package/dist/esm/helpers/iframe-helper/init.d.ts.map +1 -1
- package/dist/esm/helpers/index.d.ts +5 -4
- package/dist/esm/helpers/index.d.ts.map +1 -1
- package/dist/esm/helpers/observable.d.ts +11 -0
- package/dist/esm/helpers/observable.d.ts.map +1 -0
- package/dist/esm/helpers/viewport/element.d.ts +3 -0
- package/dist/esm/helpers/viewport/element.d.ts.map +1 -0
- package/dist/esm/helpers/viewport/index.d.ts +2 -0
- package/dist/esm/helpers/viewport/index.d.ts.map +1 -0
- package/dist/esm/helpers/viz-area-click/area-builder.d.ts +8 -0
- package/dist/esm/helpers/viz-area-click/area-builder.d.ts.map +1 -0
- package/dist/esm/helpers/viz-area-click/area-color.d.ts +13 -0
- package/dist/esm/helpers/viz-area-click/area-color.d.ts.map +1 -0
- package/dist/esm/helpers/viz-area-click/area-overlap.d.ts +44 -0
- package/dist/esm/helpers/viz-area-click/area-overlap.d.ts.map +1 -0
- package/dist/esm/helpers/viz-area-click/area-utils.d.ts +54 -0
- package/dist/esm/helpers/viz-area-click/area-utils.d.ts.map +1 -0
- package/dist/esm/helpers/viz-area-click/index.d.ts +5 -0
- package/dist/esm/helpers/viz-area-click/index.d.ts.map +1 -0
- package/dist/esm/helpers/viz-canvas/area-clustering.d.ts +1 -1
- package/dist/esm/helpers/viz-canvas/area-clustering.d.ts.map +1 -1
- package/dist/esm/helpers/viz-canvas/area-overlay-manager-v2.d.ts +2 -2
- package/dist/esm/helpers/viz-canvas/area-overlay-manager-v2.d.ts.map +1 -1
- package/dist/esm/helpers/viz-canvas/area-overlay-manager.d.ts +1 -1
- package/dist/esm/helpers/viz-canvas/area-overlay-manager.d.ts.map +1 -1
- package/dist/esm/helpers/viz-canvas/hierarchical-area-clustering.d.ts +1 -1
- package/dist/esm/helpers/viz-canvas/hierarchical-area-clustering.d.ts.map +1 -1
- package/dist/esm/helpers/{iframe.d.ts → viz-dom/decode.d.ts} +2 -2
- package/dist/esm/helpers/viz-dom/decode.d.ts.map +1 -0
- package/dist/esm/helpers/viz-dom/index.d.ts +2 -0
- package/dist/esm/helpers/viz-dom/index.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts +4 -0
- package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/element.d.ts +10 -0
- package/dist/esm/helpers/viz-elm-callout/element.d.ts.map +1 -0
- package/dist/esm/helpers/{elm-getter.d.ts → viz-elm-callout/getter.d.ts} +2 -2
- package/dist/esm/helpers/viz-elm-callout/getter.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/index.d.ts +5 -0
- package/dist/esm/helpers/viz-elm-callout/index.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/position-calculator.d.ts +14 -0
- package/dist/esm/helpers/viz-elm-callout/position-calculator.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts +5 -0
- package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts +10 -0
- package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts +4 -0
- package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/rank-calculator.d.ts +7 -0
- package/dist/esm/helpers/viz-elm-callout/rank-calculator.d.ts.map +1 -0
- package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts +12 -0
- package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -0
- package/dist/esm/hooks/common/index.d.ts +2 -0
- package/dist/esm/hooks/common/index.d.ts.map +1 -0
- package/dist/esm/hooks/common/useDebounceCallback.d.ts +17 -0
- package/dist/esm/hooks/common/useDebounceCallback.d.ts.map +1 -0
- package/dist/esm/hooks/index.d.ts +5 -2
- package/dist/esm/hooks/index.d.ts.map +1 -1
- package/dist/esm/hooks/register/useRegisterControl.d.ts +1 -1
- package/dist/esm/hooks/register/useRegisterControl.d.ts.map +1 -1
- package/dist/esm/hooks/register/useRegisterData.d.ts +1 -1
- package/dist/esm/hooks/register/useRegisterData.d.ts.map +1 -1
- package/dist/esm/hooks/register/useRegisterHeatmap.d.ts +1 -1
- package/dist/esm/hooks/register/useRegisterHeatmap.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/index.d.ts +3 -3
- package/dist/esm/hooks/view-context/index.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/useHeatmapAreaClick.d.ts +19 -0
- package/dist/esm/hooks/view-context/useHeatmapAreaClick.d.ts.map +1 -0
- package/dist/{umd/hooks/view-context/useHeatmapInteraction.d.ts → esm/hooks/view-context/useHeatmapClick.d.ts} +7 -6
- package/dist/esm/hooks/view-context/useHeatmapClick.d.ts.map +1 -0
- package/dist/esm/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
- package/dist/esm/hooks/view-context/useHeatmapData.d.ts +1 -1
- package/dist/esm/hooks/view-context/useHeatmapData.d.ts.map +1 -1
- package/dist/{umd/hooks/view-context/useHeatmapVizScrollmap.d.ts → esm/hooks/view-context/useHeatmapScroll.d.ts} +6 -5
- package/dist/esm/hooks/view-context/useHeatmapScroll.d.ts.map +1 -0
- package/dist/esm/hooks/view-context/useHeatmapViz.d.ts +1 -1
- package/dist/esm/hooks/view-context/useHeatmapViz.d.ts.map +1 -1
- package/dist/esm/hooks/viz-area/useAreaHeatmap.d.ts +1 -1
- package/dist/esm/hooks/viz-area/useAreaHeatmap.d.ts.map +1 -1
- package/dist/esm/hooks/viz-area/useAreaHeatmapManager.d.ts.map +1 -1
- package/dist/esm/hooks/viz-area-click/index.d.ts +3 -0
- package/dist/esm/hooks/viz-area-click/index.d.ts.map +1 -0
- package/dist/esm/hooks/viz-area-click/useAreaEditMode.d.ts +12 -0
- package/dist/esm/hooks/viz-area-click/useAreaEditMode.d.ts.map +1 -0
- package/dist/esm/hooks/viz-area-click/useAreaScrollSync.d.ts +8 -0
- package/dist/esm/hooks/viz-area-click/useAreaScrollSync.d.ts.map +1 -0
- package/dist/esm/hooks/viz-canvas/useAreamap.d.ts +1 -1
- package/dist/esm/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
- package/dist/esm/hooks/viz-elm/index.d.ts.map +1 -0
- package/dist/esm/hooks/viz-elm/useClickedElement.d.ts.map +1 -0
- package/dist/esm/hooks/viz-elm/useElementCalloutVisible.d.ts.map +1 -0
- package/dist/esm/hooks/viz-elm/useHeatmapEffects.d.ts.map +1 -0
- package/dist/esm/hooks/viz-elm/useHeatmapElementPosition.d.ts.map +1 -0
- package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts +1 -1
- package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts.map +1 -1
- package/dist/esm/hooks/viz-elm/useHoveredElement.d.ts.map +1 -0
- 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-scroll/index.d.ts.map +1 -0
- package/dist/{umd/hooks/viz-scrollmap → esm/hooks/viz-scroll}/useScrollmapZones.d.ts +1 -1
- package/dist/esm/hooks/viz-scroll/useScrollmapZones.d.ts.map +1 -0
- package/dist/{umd/hooks/viz-scrollmap → esm/hooks/viz-scroll}/useZonePositions.d.ts +2 -2
- package/dist/esm/hooks/viz-scroll/useZonePositions.d.ts.map +1 -0
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +1763 -746
- package/dist/esm/index.mjs +1763 -746
- package/dist/esm/performance/index.d.ts +3 -3
- package/dist/esm/performance/index.d.ts.map +1 -1
- package/dist/esm/performance/{PerformanceLogger.d.ts → performance-logger.d.ts} +1 -1
- package/dist/esm/performance/performance-logger.d.ts.map +1 -0
- package/dist/esm/performance/storeTracker.d.ts.map +1 -1
- package/dist/esm/performance/utils.d.ts +0 -18
- package/dist/esm/performance/utils.d.ts.map +1 -1
- package/dist/esm/performance/withPerformanceTracking.d.ts +1 -1
- package/dist/esm/performance/withPerformanceTracking.d.ts.map +1 -1
- package/dist/esm/stores/comp.d.ts +1 -1
- package/dist/esm/stores/comp.d.ts.map +1 -1
- package/dist/esm/stores/data.d.ts +1 -1
- package/dist/esm/stores/data.d.ts.map +1 -1
- package/dist/esm/stores/index.d.ts +3 -2
- package/dist/esm/stores/index.d.ts.map +1 -1
- package/dist/esm/stores/mode-compare.d.ts +1 -15
- package/dist/esm/stores/mode-compare.d.ts.map +1 -1
- package/dist/esm/stores/mode-live.d.ts +1 -1
- package/dist/esm/stores/mode-live.d.ts.map +1 -1
- package/dist/esm/stores/mode-single.d.ts +1 -1
- package/dist/esm/stores/mode-single.d.ts.map +1 -1
- package/dist/esm/stores/viz-area-click.d.ts +28 -0
- package/dist/esm/stores/viz-area-click.d.ts.map +1 -0
- package/dist/{umd/stores/interaction.d.ts → esm/stores/viz-click.d.ts} +6 -6
- package/dist/esm/stores/viz-click.d.ts.map +1 -0
- package/dist/esm/stores/viz-scrollmap.d.ts +1 -1
- package/dist/esm/stores/viz-scrollmap.d.ts.map +1 -1
- package/dist/esm/types/compare.d.ts +0 -50
- package/dist/esm/types/compare.d.ts.map +1 -1
- package/dist/esm/types/heatmap-info.d.ts +2 -2
- package/dist/esm/types/heatmap-info.d.ts.map +1 -1
- package/dist/esm/types/heatmap.d.ts +1 -0
- package/dist/esm/types/heatmap.d.ts.map +1 -1
- package/dist/esm/types/index.d.ts +5 -4
- package/dist/esm/types/index.d.ts.map +1 -1
- package/dist/esm/types/viz-area-click.d.ts +84 -0
- package/dist/esm/types/viz-area-click.d.ts.map +1 -0
- package/dist/esm/types/viz-elm-callout.d.ts +24 -0
- package/dist/esm/types/viz-elm-callout.d.ts.map +1 -0
- package/dist/esm/types/{viz-element.d.ts → viz-elm.d.ts} +1 -1
- package/dist/esm/types/viz-elm.d.ts.map +1 -0
- package/dist/esm/utils/sort.d.ts +1 -1
- package/dist/esm/utils/sort.d.ts.map +1 -1
- package/dist/umd/components/Layout/HeatmapLayout.d.ts +2 -2
- package/dist/umd/components/Layout/HeatmapLayout.d.ts.map +1 -1
- package/dist/umd/components/Layout/HeatmapPreview.d.ts +2 -0
- package/dist/umd/components/Layout/HeatmapPreview.d.ts.map +1 -0
- package/dist/umd/components/Layout/MetricBar/ContentMetricBar.d.ts.map +1 -0
- package/dist/umd/components/Layout/MetricBar/index.d.ts +2 -0
- package/dist/umd/components/Layout/MetricBar/index.d.ts.map +1 -0
- package/dist/umd/components/Layout/Toolbar/ContentToolbar.d.ts.map +1 -0
- package/dist/umd/components/Layout/Toolbar/index.d.ts +2 -0
- package/dist/umd/components/Layout/Toolbar/index.d.ts.map +1 -0
- package/dist/umd/components/Layout/TopBar/ContentTopBar.d.ts.map +1 -0
- package/dist/umd/components/Layout/TopBar/index.d.ts +2 -0
- package/dist/umd/components/Layout/TopBar/index.d.ts.map +1 -0
- package/dist/umd/components/Layout/VizByMode/ContentVizByMode.d.ts.map +1 -0
- package/dist/umd/components/Layout/VizByMode/index.d.ts +2 -0
- package/dist/umd/components/Layout/VizByMode/index.d.ts.map +1 -0
- package/dist/umd/components/VizAreaClick/AreaControls.d.ts +10 -0
- package/dist/umd/components/VizAreaClick/AreaControls.d.ts.map +1 -0
- package/dist/umd/components/VizAreaClick/AreaEditHighlight.d.ts +8 -0
- package/dist/umd/components/VizAreaClick/AreaEditHighlight.d.ts.map +1 -0
- package/dist/umd/components/VizAreaClick/AreaLabel.d.ts +8 -0
- package/dist/umd/components/VizAreaClick/AreaLabel.d.ts.map +1 -0
- package/dist/umd/components/VizAreaClick/AreaOverlay.d.ts +12 -0
- package/dist/umd/components/VizAreaClick/AreaOverlay.d.ts.map +1 -0
- package/dist/umd/components/VizAreaClick/PortalAreaRenderer.d.ts +16 -0
- package/dist/umd/components/VizAreaClick/PortalAreaRenderer.d.ts.map +1 -0
- package/dist/umd/components/VizAreaClick/VizAreaClick.d.ts +17 -0
- package/dist/umd/components/VizAreaClick/VizAreaClick.d.ts.map +1 -0
- package/dist/umd/components/VizAreaClick/index.d.ts +5 -0
- package/dist/umd/components/VizAreaClick/index.d.ts.map +1 -0
- package/dist/umd/components/VizDom/VizContainer.d.ts.map +1 -1
- package/dist/umd/components/VizDom/VizDomRenderer.d.ts.map +1 -1
- package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
- package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
- package/dist/umd/components/VizElement/VizElements.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/HoverZones.d.ts +1 -1
- package/dist/umd/components/VizScrollmap/HoverZones.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/ScrollMapMinimap.d.ts +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/ScrollZoneHoverArea.d.ts +1 -2
- package/dist/umd/components/VizScrollmap/ScrollZoneHoverArea.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/ScrollZoneTooltip.d.ts +1 -1
- package/dist/umd/components/VizScrollmap/ScrollZoneTooltip.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmap/index.d.ts +1 -0
- package/dist/umd/components/VizScrollmap/index.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts +1 -2
- package/dist/umd/components/VizScrollmapV2/ScrollmapOverlayV2.d.ts.map +1 -1
- package/dist/umd/components/VizScrollmapV2/useScrollmapOverlay.d.ts +1 -1
- package/dist/umd/components/VizScrollmapV2/useScrollmapOverlay.d.ts.map +1 -1
- package/dist/umd/configs/style.d.ts +1 -1
- package/dist/umd/configs/style.d.ts.map +1 -1
- package/dist/umd/configs/viewId.d.ts +1 -19
- package/dist/umd/configs/viewId.d.ts.map +1 -1
- package/dist/umd/constants/index.d.ts +2 -13
- package/dist/umd/constants/index.d.ts.map +1 -1
- package/dist/umd/constants/viz-area-click.d.ts +6 -0
- package/dist/umd/constants/viz-area-click.d.ts.map +1 -0
- package/dist/umd/constants/viz-elm-callout.d.ts +8 -0
- package/dist/umd/constants/viz-elm-callout.d.ts.map +1 -0
- package/dist/umd/contexts/ViewIdContext.d.ts +3 -0
- package/dist/umd/contexts/ViewIdContext.d.ts.map +1 -0
- package/dist/umd/contexts/index.d.ts +1 -1
- package/dist/umd/contexts/index.d.ts.map +1 -1
- package/dist/umd/helpers/iframe-helper/fixer.d.ts +1 -1
- package/dist/umd/helpers/iframe-helper/fixer.d.ts.map +1 -1
- package/dist/umd/helpers/iframe-helper/init.d.ts +1 -1
- package/dist/umd/helpers/iframe-helper/init.d.ts.map +1 -1
- package/dist/umd/helpers/index.d.ts +5 -4
- package/dist/umd/helpers/index.d.ts.map +1 -1
- package/dist/umd/helpers/observable.d.ts +11 -0
- package/dist/umd/helpers/observable.d.ts.map +1 -0
- package/dist/umd/helpers/viewport/element.d.ts +3 -0
- package/dist/umd/helpers/viewport/element.d.ts.map +1 -0
- package/dist/umd/helpers/viewport/index.d.ts +2 -0
- package/dist/umd/helpers/viewport/index.d.ts.map +1 -0
- package/dist/umd/helpers/viz-area-click/area-builder.d.ts +8 -0
- package/dist/umd/helpers/viz-area-click/area-builder.d.ts.map +1 -0
- package/dist/umd/helpers/viz-area-click/area-color.d.ts +13 -0
- package/dist/umd/helpers/viz-area-click/area-color.d.ts.map +1 -0
- package/dist/umd/helpers/viz-area-click/area-overlap.d.ts +44 -0
- package/dist/umd/helpers/viz-area-click/area-overlap.d.ts.map +1 -0
- package/dist/umd/helpers/viz-area-click/area-utils.d.ts +54 -0
- package/dist/umd/helpers/viz-area-click/area-utils.d.ts.map +1 -0
- package/dist/umd/helpers/viz-area-click/index.d.ts +5 -0
- package/dist/umd/helpers/viz-area-click/index.d.ts.map +1 -0
- package/dist/umd/helpers/viz-canvas/area-clustering.d.ts +1 -1
- package/dist/umd/helpers/viz-canvas/area-clustering.d.ts.map +1 -1
- package/dist/umd/helpers/viz-canvas/area-overlay-manager-v2.d.ts +2 -2
- package/dist/umd/helpers/viz-canvas/area-overlay-manager-v2.d.ts.map +1 -1
- package/dist/umd/helpers/viz-canvas/area-overlay-manager.d.ts +1 -1
- package/dist/umd/helpers/viz-canvas/area-overlay-manager.d.ts.map +1 -1
- package/dist/umd/helpers/viz-canvas/hierarchical-area-clustering.d.ts +1 -1
- package/dist/umd/helpers/viz-canvas/hierarchical-area-clustering.d.ts.map +1 -1
- package/dist/umd/helpers/{iframe.d.ts → viz-dom/decode.d.ts} +2 -2
- package/dist/umd/helpers/viz-dom/decode.d.ts.map +1 -0
- package/dist/umd/helpers/viz-dom/index.d.ts +2 -0
- package/dist/umd/helpers/viz-dom/index.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts +4 -0
- package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/element.d.ts +10 -0
- package/dist/umd/helpers/viz-elm-callout/element.d.ts.map +1 -0
- package/dist/umd/helpers/{elm-getter.d.ts → viz-elm-callout/getter.d.ts} +2 -2
- package/dist/umd/helpers/viz-elm-callout/getter.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/index.d.ts +5 -0
- package/dist/umd/helpers/viz-elm-callout/index.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/position-calculator.d.ts +14 -0
- package/dist/umd/helpers/viz-elm-callout/position-calculator.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts +5 -0
- package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts +10 -0
- package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts +4 -0
- package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/rank-calculator.d.ts +7 -0
- package/dist/umd/helpers/viz-elm-callout/rank-calculator.d.ts.map +1 -0
- package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts +12 -0
- package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -0
- package/dist/umd/hooks/common/index.d.ts +2 -0
- package/dist/umd/hooks/common/index.d.ts.map +1 -0
- package/dist/umd/hooks/common/useDebounceCallback.d.ts +17 -0
- package/dist/umd/hooks/common/useDebounceCallback.d.ts.map +1 -0
- package/dist/umd/hooks/index.d.ts +5 -2
- package/dist/umd/hooks/index.d.ts.map +1 -1
- package/dist/umd/hooks/register/useRegisterControl.d.ts +1 -1
- package/dist/umd/hooks/register/useRegisterControl.d.ts.map +1 -1
- package/dist/umd/hooks/register/useRegisterData.d.ts +1 -1
- package/dist/umd/hooks/register/useRegisterData.d.ts.map +1 -1
- package/dist/umd/hooks/register/useRegisterHeatmap.d.ts +1 -1
- package/dist/umd/hooks/register/useRegisterHeatmap.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/index.d.ts +3 -3
- package/dist/umd/hooks/view-context/index.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/useHeatmapAreaClick.d.ts +19 -0
- package/dist/umd/hooks/view-context/useHeatmapAreaClick.d.ts.map +1 -0
- package/dist/{esm/hooks/view-context/useHeatmapInteraction.d.ts → umd/hooks/view-context/useHeatmapClick.d.ts} +7 -6
- package/dist/umd/hooks/view-context/useHeatmapClick.d.ts.map +1 -0
- package/dist/umd/hooks/view-context/useHeatmapCopyView.d.ts.map +1 -1
- package/dist/umd/hooks/view-context/useHeatmapData.d.ts +1 -1
- package/dist/umd/hooks/view-context/useHeatmapData.d.ts.map +1 -1
- package/dist/{esm/hooks/view-context/useHeatmapVizScrollmap.d.ts → umd/hooks/view-context/useHeatmapScroll.d.ts} +6 -5
- package/dist/umd/hooks/view-context/useHeatmapScroll.d.ts.map +1 -0
- package/dist/umd/hooks/view-context/useHeatmapViz.d.ts +1 -1
- package/dist/umd/hooks/view-context/useHeatmapViz.d.ts.map +1 -1
- package/dist/umd/hooks/viz-area/useAreaHeatmap.d.ts +1 -1
- package/dist/umd/hooks/viz-area/useAreaHeatmap.d.ts.map +1 -1
- package/dist/umd/hooks/viz-area/useAreaHeatmapManager.d.ts.map +1 -1
- package/dist/umd/hooks/viz-area-click/index.d.ts +3 -0
- package/dist/umd/hooks/viz-area-click/index.d.ts.map +1 -0
- package/dist/umd/hooks/viz-area-click/useAreaEditMode.d.ts +12 -0
- package/dist/umd/hooks/viz-area-click/useAreaEditMode.d.ts.map +1 -0
- package/dist/umd/hooks/viz-area-click/useAreaScrollSync.d.ts +8 -0
- package/dist/umd/hooks/viz-area-click/useAreaScrollSync.d.ts.map +1 -0
- package/dist/umd/hooks/viz-canvas/useAreamap.d.ts +1 -1
- package/dist/umd/hooks/viz-canvas/useAreamap.d.ts.map +1 -1
- package/dist/umd/hooks/viz-elm/index.d.ts.map +1 -0
- package/dist/umd/hooks/viz-elm/useClickedElement.d.ts.map +1 -0
- package/dist/umd/hooks/viz-elm/useElementCalloutVisible.d.ts.map +1 -0
- package/dist/umd/hooks/viz-elm/useHeatmapEffects.d.ts.map +1 -0
- package/dist/umd/hooks/viz-elm/useHeatmapElementPosition.d.ts.map +1 -0
- package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts +1 -1
- package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapMouseHandler.d.ts.map +1 -1
- package/dist/umd/hooks/viz-elm/useHoveredElement.d.ts.map +1 -0
- 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-scroll/index.d.ts.map +1 -0
- package/dist/{esm/hooks/viz-scrollmap → umd/hooks/viz-scroll}/useScrollmapZones.d.ts +1 -1
- package/dist/umd/hooks/viz-scroll/useScrollmapZones.d.ts.map +1 -0
- package/dist/{esm/hooks/viz-scrollmap → umd/hooks/viz-scroll}/useZonePositions.d.ts +2 -2
- package/dist/umd/hooks/viz-scroll/useZonePositions.d.ts.map +1 -0
- package/dist/umd/index.d.ts +2 -1
- package/dist/umd/index.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/dist/umd/performance/index.d.ts +3 -3
- package/dist/umd/performance/index.d.ts.map +1 -1
- package/dist/umd/performance/{PerformanceLogger.d.ts → performance-logger.d.ts} +1 -1
- package/dist/umd/performance/performance-logger.d.ts.map +1 -0
- package/dist/umd/performance/storeTracker.d.ts.map +1 -1
- package/dist/umd/performance/utils.d.ts +0 -18
- package/dist/umd/performance/utils.d.ts.map +1 -1
- package/dist/umd/performance/withPerformanceTracking.d.ts +1 -1
- package/dist/umd/performance/withPerformanceTracking.d.ts.map +1 -1
- package/dist/umd/stores/comp.d.ts +1 -1
- package/dist/umd/stores/comp.d.ts.map +1 -1
- package/dist/umd/stores/data.d.ts +1 -1
- package/dist/umd/stores/data.d.ts.map +1 -1
- package/dist/umd/stores/index.d.ts +3 -2
- package/dist/umd/stores/index.d.ts.map +1 -1
- package/dist/umd/stores/mode-compare.d.ts +1 -15
- package/dist/umd/stores/mode-compare.d.ts.map +1 -1
- package/dist/umd/stores/mode-live.d.ts +1 -1
- package/dist/umd/stores/mode-live.d.ts.map +1 -1
- package/dist/umd/stores/mode-single.d.ts +1 -1
- package/dist/umd/stores/mode-single.d.ts.map +1 -1
- package/dist/umd/stores/viz-area-click.d.ts +28 -0
- package/dist/umd/stores/viz-area-click.d.ts.map +1 -0
- package/dist/{esm/stores/interaction.d.ts → umd/stores/viz-click.d.ts} +6 -6
- package/dist/umd/stores/viz-click.d.ts.map +1 -0
- package/dist/umd/stores/viz-scrollmap.d.ts +1 -1
- package/dist/umd/stores/viz-scrollmap.d.ts.map +1 -1
- package/dist/umd/types/compare.d.ts +0 -50
- package/dist/umd/types/compare.d.ts.map +1 -1
- package/dist/umd/types/heatmap-info.d.ts +2 -2
- package/dist/umd/types/heatmap-info.d.ts.map +1 -1
- package/dist/umd/types/heatmap.d.ts +1 -0
- package/dist/umd/types/heatmap.d.ts.map +1 -1
- package/dist/umd/types/index.d.ts +5 -4
- package/dist/umd/types/index.d.ts.map +1 -1
- package/dist/umd/types/viz-area-click.d.ts +84 -0
- package/dist/umd/types/viz-area-click.d.ts.map +1 -0
- package/dist/umd/types/viz-elm-callout.d.ts +24 -0
- package/dist/umd/types/viz-elm-callout.d.ts.map +1 -0
- package/dist/umd/types/{viz-element.d.ts → viz-elm.d.ts} +1 -1
- package/dist/umd/types/viz-elm.d.ts.map +1 -0
- package/dist/umd/utils/sort.d.ts +1 -1
- package/dist/umd/utils/sort.d.ts.map +1 -1
- package/package.json +4 -1
- package/dist/esm/components/Layout/ContentMetricBar.d.ts.map +0 -1
- package/dist/esm/components/Layout/ContentToolbar.d.ts.map +0 -1
- package/dist/esm/components/Layout/ContentTopBar.d.ts.map +0 -1
- package/dist/esm/components/Layout/ContentVizByMode.d.ts.map +0 -1
- package/dist/esm/components/Layout/WrapperLayout.d.ts +0 -2
- package/dist/esm/components/Layout/WrapperLayout.d.ts.map +0 -1
- package/dist/esm/components/Layout/WrapperPreview.d.ts +0 -2
- package/dist/esm/components/Layout/WrapperPreview.d.ts.map +0 -1
- package/dist/esm/components/VizElement/HeatmapExample.d.ts +0 -2
- package/dist/esm/components/VizElement/HeatmapExample.d.ts.map +0 -1
- package/dist/esm/contexts/CompareViewContext.d.ts +0 -28
- package/dist/esm/contexts/CompareViewContext.d.ts.map +0 -1
- package/dist/esm/helpers/elm-callout.d.ts +0 -9
- package/dist/esm/helpers/elm-callout.d.ts.map +0 -1
- package/dist/esm/helpers/elm-getter.d.ts.map +0 -1
- package/dist/esm/helpers/iframe.d.ts.map +0 -1
- package/dist/esm/helpers/viz-elements.d.ts +0 -9
- package/dist/esm/helpers/viz-elements.d.ts.map +0 -1
- package/dist/esm/hooks/view-context/useHeatmapInteraction.d.ts.map +0 -1
- package/dist/esm/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +0 -1
- package/dist/esm/hooks/view-context/useViewId.d.ts +0 -15
- package/dist/esm/hooks/view-context/useViewId.d.ts.map +0 -1
- package/dist/esm/hooks/viz-elements/index.d.ts.map +0 -1
- package/dist/esm/hooks/viz-elements/useClickedElement.d.ts.map +0 -1
- package/dist/esm/hooks/viz-elements/useElementCalloutVisible.d.ts.map +0 -1
- package/dist/esm/hooks/viz-elements/useHeatmapEffects.d.ts.map +0 -1
- package/dist/esm/hooks/viz-elements/useHeatmapElementPosition.d.ts.map +0 -1
- package/dist/esm/hooks/viz-elements/useHoveredElement.d.ts.map +0 -1
- package/dist/esm/hooks/viz-scrollmap/index.d.ts.map +0 -1
- package/dist/esm/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +0 -1
- package/dist/esm/hooks/viz-scrollmap/useZonePositions.d.ts.map +0 -1
- package/dist/esm/performance/PerformanceLogger.d.ts.map +0 -1
- package/dist/esm/stores/interaction.d.ts.map +0 -1
- package/dist/esm/types/elm-callout.d.ts +0 -9
- package/dist/esm/types/elm-callout.d.ts.map +0 -1
- package/dist/esm/types/viz-element.d.ts.map +0 -1
- package/dist/umd/components/Layout/ContentMetricBar.d.ts.map +0 -1
- package/dist/umd/components/Layout/ContentToolbar.d.ts.map +0 -1
- package/dist/umd/components/Layout/ContentTopBar.d.ts.map +0 -1
- package/dist/umd/components/Layout/ContentVizByMode.d.ts.map +0 -1
- package/dist/umd/components/Layout/WrapperLayout.d.ts +0 -2
- package/dist/umd/components/Layout/WrapperLayout.d.ts.map +0 -1
- package/dist/umd/components/Layout/WrapperPreview.d.ts +0 -2
- package/dist/umd/components/Layout/WrapperPreview.d.ts.map +0 -1
- package/dist/umd/components/VizElement/HeatmapExample.d.ts +0 -2
- package/dist/umd/components/VizElement/HeatmapExample.d.ts.map +0 -1
- package/dist/umd/contexts/CompareViewContext.d.ts +0 -28
- package/dist/umd/contexts/CompareViewContext.d.ts.map +0 -1
- package/dist/umd/helpers/elm-callout.d.ts +0 -9
- package/dist/umd/helpers/elm-callout.d.ts.map +0 -1
- package/dist/umd/helpers/elm-getter.d.ts.map +0 -1
- package/dist/umd/helpers/iframe.d.ts.map +0 -1
- package/dist/umd/helpers/viz-elements.d.ts +0 -9
- package/dist/umd/helpers/viz-elements.d.ts.map +0 -1
- package/dist/umd/hooks/view-context/useHeatmapInteraction.d.ts.map +0 -1
- package/dist/umd/hooks/view-context/useHeatmapVizScrollmap.d.ts.map +0 -1
- package/dist/umd/hooks/view-context/useViewId.d.ts +0 -15
- package/dist/umd/hooks/view-context/useViewId.d.ts.map +0 -1
- package/dist/umd/hooks/viz-elements/index.d.ts.map +0 -1
- package/dist/umd/hooks/viz-elements/useClickedElement.d.ts.map +0 -1
- package/dist/umd/hooks/viz-elements/useElementCalloutVisible.d.ts.map +0 -1
- package/dist/umd/hooks/viz-elements/useHeatmapEffects.d.ts.map +0 -1
- package/dist/umd/hooks/viz-elements/useHeatmapElementPosition.d.ts.map +0 -1
- package/dist/umd/hooks/viz-elements/useHoveredElement.d.ts.map +0 -1
- package/dist/umd/hooks/viz-scrollmap/index.d.ts.map +0 -1
- package/dist/umd/hooks/viz-scrollmap/useScrollmapZones.d.ts.map +0 -1
- package/dist/umd/hooks/viz-scrollmap/useZonePositions.d.ts.map +0 -1
- package/dist/umd/performance/PerformanceLogger.d.ts.map +0 -1
- package/dist/umd/stores/interaction.d.ts.map +0 -1
- package/dist/umd/types/elm-callout.d.ts +0 -9
- package/dist/umd/types/elm-callout.d.ts.map +0 -1
- package/dist/umd/types/viz-element.d.ts.map +0 -1
- /package/dist/esm/components/Layout/{ContentMetricBar.d.ts → MetricBar/ContentMetricBar.d.ts} +0 -0
- /package/dist/esm/components/Layout/{ContentToolbar.d.ts → Toolbar/ContentToolbar.d.ts} +0 -0
- /package/dist/esm/components/Layout/{ContentTopBar.d.ts → TopBar/ContentTopBar.d.ts} +0 -0
- /package/dist/esm/components/Layout/{ContentVizByMode.d.ts → VizByMode/ContentVizByMode.d.ts} +0 -0
- /package/dist/esm/hooks/{viz-elements → viz-elm}/index.d.ts +0 -0
- /package/dist/esm/hooks/{viz-elements → viz-elm}/useClickedElement.d.ts +0 -0
- /package/dist/esm/hooks/{viz-elements → viz-elm}/useElementCalloutVisible.d.ts +0 -0
- /package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapEffects.d.ts +0 -0
- /package/dist/esm/hooks/{viz-elements → viz-elm}/useHeatmapElementPosition.d.ts +0 -0
- /package/dist/esm/hooks/{viz-elements → viz-elm}/useHoveredElement.d.ts +0 -0
- /package/dist/esm/hooks/{viz-scrollmap → viz-scroll}/index.d.ts +0 -0
- /package/dist/umd/components/Layout/{ContentMetricBar.d.ts → MetricBar/ContentMetricBar.d.ts} +0 -0
- /package/dist/umd/components/Layout/{ContentToolbar.d.ts → Toolbar/ContentToolbar.d.ts} +0 -0
- /package/dist/umd/components/Layout/{ContentTopBar.d.ts → TopBar/ContentTopBar.d.ts} +0 -0
- /package/dist/umd/components/Layout/{ContentVizByMode.d.ts → VizByMode/ContentVizByMode.d.ts} +0 -0
- /package/dist/umd/hooks/{viz-elements → viz-elm}/index.d.ts +0 -0
- /package/dist/umd/hooks/{viz-elements → viz-elm}/useClickedElement.d.ts +0 -0
- /package/dist/umd/hooks/{viz-elements → viz-elm}/useElementCalloutVisible.d.ts +0 -0
- /package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapEffects.d.ts +0 -0
- /package/dist/umd/hooks/{viz-elements → viz-elm}/useHeatmapElementPosition.d.ts +0 -0
- /package/dist/umd/hooks/{viz-elements → viz-elm}/useHoveredElement.d.ts +0 -0
- /package/dist/umd/hooks/{viz-scrollmap → viz-scroll}/index.d.ts +0 -0
package/dist/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { useNodesState, ReactFlow, Controls, Background } from '@xyflow/react';
|
|
4
|
-
import { useEffect, createContext, useContext, useMemo,
|
|
4
|
+
import { useEffect, useRef, useCallback, createContext, useContext, useMemo, useState, forwardRef, Fragment as Fragment$1 } from 'react';
|
|
5
5
|
import { create } from 'zustand';
|
|
6
6
|
import { subscribeWithSelector } from 'zustand/middleware';
|
|
7
7
|
import { decode } from '@gemx-dev/clarity-decode';
|
|
@@ -48,6 +48,7 @@ const HEATMAP_IFRAME = {
|
|
|
48
48
|
height: '100%',
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
+
const DEFAULT_SIDEBAR_WIDTH = 260;
|
|
51
52
|
const HEATMAP_CONFIG = {
|
|
52
53
|
padding: 8,
|
|
53
54
|
borderWidth: 1,
|
|
@@ -66,32 +67,10 @@ const HEATMAP_STYLE = {
|
|
|
66
67
|
paddingInline: `${HEATMAP_CONFIG.padding}px`,
|
|
67
68
|
},
|
|
68
69
|
};
|
|
69
|
-
const DEFAULT_SIDEBAR_WIDTH = 260;
|
|
70
70
|
|
|
71
|
-
/**
|
|
72
|
-
* Default view ID for single mode
|
|
73
|
-
*/
|
|
74
71
|
const DEFAULT_VIEW_ID = 'default';
|
|
75
|
-
|
|
76
|
-
|
|
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;
|
|
72
|
+
const getCompareViewId = (viewId) => {
|
|
73
|
+
return `compare-${viewId}`;
|
|
95
74
|
};
|
|
96
75
|
|
|
97
76
|
const Z_INDEX = {
|
|
@@ -102,6 +81,36 @@ const Z_INDEX = {
|
|
|
102
81
|
SIDEBAR_POPOVER: 4001,
|
|
103
82
|
};
|
|
104
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Creates a debounced version of a callback that delays invoking until after
|
|
86
|
+
* wait milliseconds have elapsed since the last time it was invoked.
|
|
87
|
+
*
|
|
88
|
+
* @param callback - The function to debounce
|
|
89
|
+
* @param delay - The number of milliseconds to delay
|
|
90
|
+
* @returns A debounced version of the callback
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* ```tsx
|
|
94
|
+
* const handleSearch = useDebounceCallback((query: string) => {
|
|
95
|
+
* searchAPI(query);
|
|
96
|
+
* }, 300);
|
|
97
|
+
* ```
|
|
98
|
+
*/
|
|
99
|
+
function useDebounceCallback(callback, delay) {
|
|
100
|
+
const timeoutRef = useRef();
|
|
101
|
+
const callbackRef = useRef(callback);
|
|
102
|
+
// Update callback ref when callback changes
|
|
103
|
+
callbackRef.current = callback;
|
|
104
|
+
return useCallback((...args) => {
|
|
105
|
+
if (timeoutRef.current) {
|
|
106
|
+
clearTimeout(timeoutRef.current);
|
|
107
|
+
}
|
|
108
|
+
timeoutRef.current = setTimeout(() => {
|
|
109
|
+
callbackRef.current(...args);
|
|
110
|
+
}, delay);
|
|
111
|
+
}, [delay]);
|
|
112
|
+
}
|
|
113
|
+
|
|
105
114
|
const useHeatmapControlStore = create()((set, get) => {
|
|
106
115
|
return {
|
|
107
116
|
controls: {
|
|
@@ -127,6 +136,7 @@ const useHeatmapControlStore = create()((set, get) => {
|
|
|
127
136
|
var IHeatmapType;
|
|
128
137
|
(function (IHeatmapType) {
|
|
129
138
|
IHeatmapType["Click"] = "click";
|
|
139
|
+
IHeatmapType["ClickArea"] = "click-area";
|
|
130
140
|
IHeatmapType["Scroll"] = "scroll";
|
|
131
141
|
})(IHeatmapType || (IHeatmapType = {}));
|
|
132
142
|
var IClickType;
|
|
@@ -145,7 +155,7 @@ var IScrollType;
|
|
|
145
155
|
IScrollType["Revenue"] = "revenue-scroll";
|
|
146
156
|
})(IScrollType || (IScrollType = {}));
|
|
147
157
|
|
|
148
|
-
const useHeatmapConfigStore = create()((set
|
|
158
|
+
const useHeatmapConfigStore = create()((set) => {
|
|
149
159
|
return {
|
|
150
160
|
mode: 'single',
|
|
151
161
|
width: 1440,
|
|
@@ -165,7 +175,7 @@ const useHeatmapConfigStore = create()((set, get) => {
|
|
|
165
175
|
};
|
|
166
176
|
});
|
|
167
177
|
|
|
168
|
-
const useHeatmapDataStore = create()(subscribeWithSelector((set
|
|
178
|
+
const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
169
179
|
return {
|
|
170
180
|
data: { [DEFAULT_VIEW_ID]: undefined },
|
|
171
181
|
clickmap: { [DEFAULT_VIEW_ID]: undefined },
|
|
@@ -214,10 +224,173 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set, get) => {
|
|
|
214
224
|
};
|
|
215
225
|
}));
|
|
216
226
|
|
|
227
|
+
const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
228
|
+
return {
|
|
229
|
+
isRenderViz: { [DEFAULT_VIEW_ID]: false },
|
|
230
|
+
zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
|
|
231
|
+
minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
|
|
232
|
+
scale: { [DEFAULT_VIEW_ID]: 1 },
|
|
233
|
+
isScaledToFit: { [DEFAULT_VIEW_ID]: false },
|
|
234
|
+
setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
235
|
+
isRenderViz: { ...state.isRenderViz, [viewId]: isRenderViz },
|
|
236
|
+
})),
|
|
237
|
+
setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
238
|
+
zoomRatio: { ...state.zoomRatio, [viewId]: zoomRatio },
|
|
239
|
+
})),
|
|
240
|
+
setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
241
|
+
minZoomRatio: { ...state.minZoomRatio, [viewId]: minZoomRatio },
|
|
242
|
+
})),
|
|
243
|
+
setScale: (scale, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
244
|
+
scale: { ...state.scale, [viewId]: scale },
|
|
245
|
+
})),
|
|
246
|
+
setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
247
|
+
isScaledToFit: { ...state.isScaledToFit, [viewId]: isScaledToFit },
|
|
248
|
+
})),
|
|
249
|
+
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
250
|
+
isRenderViz: { ...state.isRenderViz, [toViewId]: state.isRenderViz[fromViewId] ?? false },
|
|
251
|
+
zoomRatio: { ...state.zoomRatio, [toViewId]: state.zoomRatio[fromViewId] ?? 100 },
|
|
252
|
+
minZoomRatio: { ...state.minZoomRatio, [toViewId]: state.minZoomRatio[fromViewId] ?? 10 },
|
|
253
|
+
scale: { ...state.scale, [toViewId]: state.scale[fromViewId] ?? 1 },
|
|
254
|
+
isScaledToFit: {
|
|
255
|
+
...state.isScaledToFit,
|
|
256
|
+
[toViewId]: state.isScaledToFit[fromViewId] ?? false,
|
|
257
|
+
},
|
|
258
|
+
})),
|
|
259
|
+
clearView: (viewId) => set((state) => {
|
|
260
|
+
const newIsRenderViz = { ...state.isRenderViz };
|
|
261
|
+
const newZoomRatio = { ...state.zoomRatio };
|
|
262
|
+
const newMinZoomRatio = { ...state.minZoomRatio };
|
|
263
|
+
const newScale = { ...state.scale };
|
|
264
|
+
const newIsScaledToFit = { ...state.isScaledToFit };
|
|
265
|
+
delete newIsRenderViz[viewId];
|
|
266
|
+
delete newZoomRatio[viewId];
|
|
267
|
+
delete newMinZoomRatio[viewId];
|
|
268
|
+
delete newScale[viewId];
|
|
269
|
+
delete newIsScaledToFit[viewId];
|
|
270
|
+
return {
|
|
271
|
+
isRenderViz: newIsRenderViz,
|
|
272
|
+
zoomRatio: newZoomRatio,
|
|
273
|
+
minZoomRatio: newMinZoomRatio,
|
|
274
|
+
scale: newScale,
|
|
275
|
+
isScaledToFit: newIsScaledToFit,
|
|
276
|
+
};
|
|
277
|
+
}),
|
|
278
|
+
resetAll: () => set({
|
|
279
|
+
isRenderViz: { [DEFAULT_VIEW_ID]: false },
|
|
280
|
+
zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
|
|
281
|
+
minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
|
|
282
|
+
scale: { [DEFAULT_VIEW_ID]: 1 },
|
|
283
|
+
isScaledToFit: { [DEFAULT_VIEW_ID]: false },
|
|
284
|
+
}),
|
|
285
|
+
};
|
|
286
|
+
}));
|
|
287
|
+
|
|
288
|
+
const useHeatmapAreaClickStore = create()(subscribeWithSelector((set) => ({
|
|
289
|
+
selectedArea: { [DEFAULT_VIEW_ID]: null },
|
|
290
|
+
hoveredArea: { [DEFAULT_VIEW_ID]: null },
|
|
291
|
+
areas: { [DEFAULT_VIEW_ID]: [] },
|
|
292
|
+
isEditingMode: { [DEFAULT_VIEW_ID]: false },
|
|
293
|
+
setSelectedArea: (area, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
294
|
+
selectedArea: { ...state.selectedArea, [viewId]: area },
|
|
295
|
+
})),
|
|
296
|
+
setHoveredArea: (area, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
297
|
+
hoveredArea: { ...state.hoveredArea, [viewId]: area },
|
|
298
|
+
})),
|
|
299
|
+
setAreas: (areas, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
300
|
+
areas: { ...state.areas, [viewId]: areas },
|
|
301
|
+
})),
|
|
302
|
+
setIsEditingMode: (isEditing, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
303
|
+
isEditingMode: { ...state.isEditingMode, [viewId]: isEditing },
|
|
304
|
+
})),
|
|
305
|
+
addArea: (area, viewId = DEFAULT_VIEW_ID) => set((state) => {
|
|
306
|
+
const currentAreas = state.areas[viewId] || [];
|
|
307
|
+
// Check if area already exists
|
|
308
|
+
const exists = currentAreas.some((a) => a.id === area.id);
|
|
309
|
+
if (exists)
|
|
310
|
+
return state;
|
|
311
|
+
return {
|
|
312
|
+
areas: {
|
|
313
|
+
...state.areas,
|
|
314
|
+
[viewId]: [...currentAreas, area],
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}),
|
|
318
|
+
removeArea: (areaId, viewId = DEFAULT_VIEW_ID) => set((state) => {
|
|
319
|
+
const currentAreas = state.areas[viewId] || [];
|
|
320
|
+
const filtered = currentAreas.filter((a) => a.id !== areaId);
|
|
321
|
+
// If removed area was selected/hovered, clear it
|
|
322
|
+
const selectedArea = state.selectedArea[viewId];
|
|
323
|
+
const hoveredArea = state.hoveredArea[viewId];
|
|
324
|
+
return {
|
|
325
|
+
areas: { ...state.areas, [viewId]: filtered },
|
|
326
|
+
selectedArea: {
|
|
327
|
+
...state.selectedArea,
|
|
328
|
+
[viewId]: selectedArea?.id === areaId ? null : selectedArea,
|
|
329
|
+
},
|
|
330
|
+
hoveredArea: {
|
|
331
|
+
...state.hoveredArea,
|
|
332
|
+
[viewId]: hoveredArea?.id === areaId ? null : hoveredArea,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
}),
|
|
336
|
+
updateArea: (areaId, updates, viewId = DEFAULT_VIEW_ID) => set((state) => {
|
|
337
|
+
const currentAreas = state.areas[viewId] || [];
|
|
338
|
+
const updatedAreas = currentAreas.map((area) => area.id === areaId ? { ...area, ...updates } : area);
|
|
339
|
+
return {
|
|
340
|
+
areas: { ...state.areas, [viewId]: updatedAreas },
|
|
341
|
+
};
|
|
342
|
+
}),
|
|
343
|
+
clearAreas: (viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
344
|
+
areas: { ...state.areas, [viewId]: [] },
|
|
345
|
+
selectedArea: { ...state.selectedArea, [viewId]: null },
|
|
346
|
+
hoveredArea: { ...state.hoveredArea, [viewId]: null },
|
|
347
|
+
})),
|
|
348
|
+
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
349
|
+
selectedArea: {
|
|
350
|
+
...state.selectedArea,
|
|
351
|
+
[toViewId]: state.selectedArea[fromViewId] ?? null,
|
|
352
|
+
},
|
|
353
|
+
hoveredArea: {
|
|
354
|
+
...state.hoveredArea,
|
|
355
|
+
[toViewId]: state.hoveredArea[fromViewId] ?? null,
|
|
356
|
+
},
|
|
357
|
+
areas: {
|
|
358
|
+
...state.areas,
|
|
359
|
+
[toViewId]: state.areas[fromViewId] ?? [],
|
|
360
|
+
},
|
|
361
|
+
isEditingMode: {
|
|
362
|
+
...state.isEditingMode,
|
|
363
|
+
[toViewId]: state.isEditingMode[fromViewId] ?? false,
|
|
364
|
+
},
|
|
365
|
+
})),
|
|
366
|
+
clearView: (viewId) => set((state) => {
|
|
367
|
+
const newSelectedArea = { ...state.selectedArea };
|
|
368
|
+
const newHoveredArea = { ...state.hoveredArea };
|
|
369
|
+
const newAreas = { ...state.areas };
|
|
370
|
+
const newIsEditingMode = { ...state.isEditingMode };
|
|
371
|
+
delete newSelectedArea[viewId];
|
|
372
|
+
delete newHoveredArea[viewId];
|
|
373
|
+
delete newAreas[viewId];
|
|
374
|
+
delete newIsEditingMode[viewId];
|
|
375
|
+
return {
|
|
376
|
+
selectedArea: newSelectedArea,
|
|
377
|
+
hoveredArea: newHoveredArea,
|
|
378
|
+
areas: newAreas,
|
|
379
|
+
isEditingMode: newIsEditingMode,
|
|
380
|
+
};
|
|
381
|
+
}),
|
|
382
|
+
resetAll: () => set({
|
|
383
|
+
selectedArea: { [DEFAULT_VIEW_ID]: null },
|
|
384
|
+
hoveredArea: { [DEFAULT_VIEW_ID]: null },
|
|
385
|
+
areas: { [DEFAULT_VIEW_ID]: [] },
|
|
386
|
+
isEditingMode: { [DEFAULT_VIEW_ID]: false },
|
|
387
|
+
}),
|
|
388
|
+
})));
|
|
389
|
+
|
|
217
390
|
const DEFAULT_STATE = {
|
|
218
391
|
hideSidebar: false,
|
|
219
392
|
};
|
|
220
|
-
const
|
|
393
|
+
const useHeatmapClickStore = create()(subscribeWithSelector((set) => {
|
|
221
394
|
return {
|
|
222
395
|
state: { [DEFAULT_VIEW_ID]: DEFAULT_STATE },
|
|
223
396
|
selectedElement: { [DEFAULT_VIEW_ID]: null },
|
|
@@ -278,68 +451,7 @@ const useHeatmapInteractionStore = create()(subscribeWithSelector((set, get) =>
|
|
|
278
451
|
};
|
|
279
452
|
}));
|
|
280
453
|
|
|
281
|
-
const
|
|
282
|
-
return {
|
|
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
|
-
}),
|
|
339
|
-
};
|
|
340
|
-
}));
|
|
341
|
-
|
|
342
|
-
const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set, get) => {
|
|
454
|
+
const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set) => {
|
|
343
455
|
return {
|
|
344
456
|
zones: { [DEFAULT_VIEW_ID]: [] },
|
|
345
457
|
hoveredZone: { [DEFAULT_VIEW_ID]: null },
|
|
@@ -388,94 +500,9 @@ const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set, get) =>
|
|
|
388
500
|
};
|
|
389
501
|
}));
|
|
390
502
|
|
|
391
|
-
const
|
|
392
|
-
payloads: [],
|
|
393
|
-
htmlContent: '',
|
|
394
|
-
};
|
|
395
|
-
const useHeatmapLiveStore = create()((set, get) => {
|
|
396
|
-
return {
|
|
397
|
-
...initialState,
|
|
398
|
-
reset: () => set(initialState),
|
|
399
|
-
setPayloads: (payloads) => set({ payloads }),
|
|
400
|
-
addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
|
|
401
|
-
setHtmlContent: (htmlContent) => set({ htmlContent }),
|
|
402
|
-
};
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
const useHeatmapSingleStore = create()(subscribeWithSelector((set, get) => {
|
|
406
|
-
return {
|
|
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) => ({
|
|
503
|
+
const createDefaultView = (id, label) => ({
|
|
463
504
|
id,
|
|
464
505
|
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
506
|
});
|
|
480
507
|
const useHeatmapCompareStore = create()((set, get) => {
|
|
481
508
|
return {
|
|
@@ -490,9 +517,9 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
490
517
|
viewIdCounter: 0,
|
|
491
518
|
addView: (options) => {
|
|
492
519
|
const state = get();
|
|
493
|
-
const viewId =
|
|
520
|
+
const viewId = getCompareViewId(state.viewIdCounter);
|
|
494
521
|
const label = options?.label ?? `Version ${state.viewOrder.length + 1}`;
|
|
495
|
-
const newView = createDefaultView(viewId, label
|
|
522
|
+
const newView = createDefaultView(viewId, label);
|
|
496
523
|
const newViews = new Map(state.views);
|
|
497
524
|
newViews.set(viewId, newView);
|
|
498
525
|
set({
|
|
@@ -519,16 +546,6 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
519
546
|
const newViews = new Map(state.views);
|
|
520
547
|
newViews.set(viewId, { ...view, ...updates });
|
|
521
548
|
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
549
|
},
|
|
533
550
|
getView: (viewId) => {
|
|
534
551
|
return get().views.get(viewId);
|
|
@@ -543,9 +560,9 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
543
560
|
const viewIds = [];
|
|
544
561
|
const newViews = new Map();
|
|
545
562
|
for (let i = 0; i < count; i++) {
|
|
546
|
-
const viewId =
|
|
563
|
+
const viewId = getCompareViewId(i);
|
|
547
564
|
const label = options?.label ?? String.fromCharCode(65 + i); // A, B, C, D
|
|
548
|
-
newViews.set(viewId, createDefaultView(viewId, `Version ${label}
|
|
565
|
+
newViews.set(viewId, createDefaultView(viewId, `Version ${label}`));
|
|
549
566
|
viewIds.push(viewId);
|
|
550
567
|
}
|
|
551
568
|
const layoutMap = {
|
|
@@ -587,54 +604,188 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
587
604
|
};
|
|
588
605
|
});
|
|
589
606
|
|
|
590
|
-
const
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
},
|
|
601
|
-
}
|
|
602
|
-
};
|
|
603
|
-
|
|
604
|
-
const
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
607
|
+
const initialState = {
|
|
608
|
+
payloads: [],
|
|
609
|
+
htmlContent: '',
|
|
610
|
+
};
|
|
611
|
+
const useHeatmapLiveStore = create()((set) => {
|
|
612
|
+
return {
|
|
613
|
+
...initialState,
|
|
614
|
+
reset: () => set(initialState),
|
|
615
|
+
setPayloads: (payloads) => set({ payloads }),
|
|
616
|
+
addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
|
|
617
|
+
setHtmlContent: (htmlContent) => set({ htmlContent }),
|
|
618
|
+
};
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
const useHeatmapSingleStore = create()(subscribeWithSelector((set) => {
|
|
622
|
+
return {
|
|
623
|
+
vizRef: { [DEFAULT_VIEW_ID]: null },
|
|
624
|
+
iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
625
|
+
wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
626
|
+
wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
|
|
627
|
+
setVizRef: (vizRef, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
628
|
+
vizRef: { ...state.vizRef, [viewId]: vizRef },
|
|
629
|
+
})),
|
|
630
|
+
setIframeHeight: (iframeHeight, viewId = DEFAULT_VIEW_ID) => {
|
|
631
|
+
set((state) => ({
|
|
632
|
+
iframeHeight: { ...state.iframeHeight, [viewId]: iframeHeight },
|
|
633
|
+
}));
|
|
634
|
+
},
|
|
635
|
+
setWrapperHeight: (wrapperHeight, viewId = DEFAULT_VIEW_ID) => {
|
|
636
|
+
set((state) => ({
|
|
637
|
+
wrapperHeight: { ...state.wrapperHeight, [viewId]: wrapperHeight },
|
|
638
|
+
}));
|
|
639
|
+
},
|
|
640
|
+
setWrapperWidth: (wrapperWidth, viewId = DEFAULT_VIEW_ID) => {
|
|
641
|
+
set((state) => ({
|
|
642
|
+
wrapperWidth: { ...state.wrapperWidth, [viewId]: wrapperWidth },
|
|
643
|
+
}));
|
|
644
|
+
},
|
|
645
|
+
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
646
|
+
// Don't copy vizRef - each view needs its own visualizer instance
|
|
647
|
+
iframeHeight: { ...state.iframeHeight, [toViewId]: state.iframeHeight[fromViewId] ?? 0 },
|
|
648
|
+
wrapperHeight: {
|
|
649
|
+
...state.wrapperHeight,
|
|
650
|
+
[toViewId]: state.wrapperHeight[fromViewId] ?? 0,
|
|
651
|
+
},
|
|
652
|
+
})),
|
|
653
|
+
clearView: (viewId) => set((state) => {
|
|
654
|
+
const newVizRef = { ...state.vizRef };
|
|
655
|
+
const newIframeHeight = { ...state.iframeHeight };
|
|
656
|
+
const newWrapperHeight = { ...state.wrapperHeight };
|
|
657
|
+
const newWrapperWidth = { ...state.wrapperWidth };
|
|
658
|
+
delete newVizRef[viewId];
|
|
659
|
+
delete newIframeHeight[viewId];
|
|
660
|
+
delete newWrapperHeight[viewId];
|
|
661
|
+
delete newWrapperWidth[viewId];
|
|
662
|
+
return {
|
|
663
|
+
vizRef: newVizRef,
|
|
664
|
+
iframeHeight: newIframeHeight,
|
|
665
|
+
wrapperHeight: newWrapperHeight,
|
|
666
|
+
wrapperWidth: newWrapperWidth,
|
|
667
|
+
};
|
|
668
|
+
}),
|
|
669
|
+
resetAll: () => set({
|
|
670
|
+
vizRef: { [DEFAULT_VIEW_ID]: null },
|
|
671
|
+
iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
672
|
+
wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
673
|
+
wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
|
|
674
|
+
}),
|
|
675
|
+
};
|
|
676
|
+
}));
|
|
677
|
+
|
|
678
|
+
const useRegisterConfig = () => {
|
|
679
|
+
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
680
|
+
const width = useHeatmapConfigStore((state) => state.width);
|
|
681
|
+
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
682
|
+
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
683
|
+
const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
|
|
684
|
+
useEffect(() => {
|
|
685
|
+
setIsRendering(true);
|
|
686
|
+
setTimeout(() => {
|
|
687
|
+
setIsRendering(false);
|
|
688
|
+
}, 1000);
|
|
689
|
+
}, [mode, width, sidebarWidth, heatmapType]);
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
const useRegisterControl = (control) => {
|
|
693
|
+
const registerControl = useHeatmapControlStore((state) => state.registerControl);
|
|
694
|
+
registerControl('Sidebar', control.Sidebar);
|
|
695
|
+
registerControl('SidebarActivator', control.SidebarActivator);
|
|
696
|
+
registerControl('TopBar', control.TopBar);
|
|
697
|
+
registerControl('Toolbar', control.Toolbar);
|
|
698
|
+
registerControl('MetricBar', control.MetricBar);
|
|
699
|
+
registerControl('VizLoading', control.VizLoading);
|
|
700
|
+
registerControl('ElementCallout', control.ElementCallout);
|
|
613
701
|
};
|
|
614
702
|
|
|
615
|
-
/**
|
|
616
|
-
* Context to provide viewId to components
|
|
617
|
-
* Used in compare mode to isolate data between views
|
|
618
|
-
*/
|
|
619
703
|
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 = () => {
|
|
704
|
+
const useViewIdContext = () => {
|
|
625
705
|
const viewId = useContext(ViewIdContext);
|
|
626
706
|
return viewId || DEFAULT_VIEW_ID;
|
|
627
707
|
};
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
const
|
|
633
|
-
|
|
708
|
+
|
|
709
|
+
const useHeatmapAreaClick = (props) => {
|
|
710
|
+
const viewId = props?.viewId || useViewIdContext();
|
|
711
|
+
// Optimized: Only subscribe to specific viewId slice
|
|
712
|
+
const selectedArea = useHeatmapAreaClickStore((state) => state.selectedArea[viewId] ?? null);
|
|
713
|
+
const hoveredArea = useHeatmapAreaClickStore((state) => state.hoveredArea[viewId] ?? null);
|
|
714
|
+
const areas = useHeatmapAreaClickStore((state) => state.areas[viewId] ?? []);
|
|
715
|
+
const isEditingMode = useHeatmapAreaClickStore((state) => state.isEditingMode[viewId] ?? false);
|
|
716
|
+
// Get setters from store
|
|
717
|
+
const setSelectedAreaStore = useHeatmapAreaClickStore((state) => state.setSelectedArea);
|
|
718
|
+
const setHoveredAreaStore = useHeatmapAreaClickStore((state) => state.setHoveredArea);
|
|
719
|
+
const setAreasStore = useHeatmapAreaClickStore((state) => state.setAreas);
|
|
720
|
+
const setIsEditingModeStore = useHeatmapAreaClickStore((state) => state.setIsEditingMode);
|
|
721
|
+
const addAreaStore = useHeatmapAreaClickStore((state) => state.addArea);
|
|
722
|
+
const removeAreaStore = useHeatmapAreaClickStore((state) => state.removeArea);
|
|
723
|
+
const updateAreaStore = useHeatmapAreaClickStore((state) => state.updateArea);
|
|
724
|
+
const clearAreasStore = useHeatmapAreaClickStore((state) => state.clearAreas);
|
|
725
|
+
// Memoize operations to prevent unnecessary re-renders
|
|
726
|
+
const memoizedOperations = useMemo(() => ({
|
|
727
|
+
setSelectedArea: (area) => setSelectedAreaStore(area, viewId),
|
|
728
|
+
setHoveredArea: (area) => setHoveredAreaStore(area, viewId),
|
|
729
|
+
setAreas: (areas) => setAreasStore(areas, viewId),
|
|
730
|
+
setIsEditingMode: (isEditing) => setIsEditingModeStore(isEditing, viewId),
|
|
731
|
+
addArea: (area) => addAreaStore(area, viewId),
|
|
732
|
+
removeArea: (areaId) => removeAreaStore(areaId, viewId),
|
|
733
|
+
updateArea: (areaId, updates) => updateAreaStore(areaId, updates, viewId),
|
|
734
|
+
clearAreas: () => clearAreasStore(viewId),
|
|
735
|
+
}), [
|
|
736
|
+
setSelectedAreaStore,
|
|
737
|
+
setHoveredAreaStore,
|
|
738
|
+
setAreasStore,
|
|
739
|
+
setIsEditingModeStore,
|
|
740
|
+
addAreaStore,
|
|
741
|
+
removeAreaStore,
|
|
742
|
+
updateAreaStore,
|
|
743
|
+
clearAreasStore,
|
|
744
|
+
viewId,
|
|
745
|
+
]);
|
|
746
|
+
return {
|
|
747
|
+
selectedArea,
|
|
748
|
+
hoveredArea,
|
|
749
|
+
areas,
|
|
750
|
+
isEditingMode,
|
|
751
|
+
...memoizedOperations,
|
|
752
|
+
};
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const useHeatmapClick = (props) => {
|
|
756
|
+
const viewId = props?.viewId || useViewIdContext();
|
|
757
|
+
const state = useHeatmapClickStore((store) => store.state[viewId] ?? { hideSidebar: false });
|
|
758
|
+
const selectedElement = useHeatmapClickStore((store) => store.selectedElement[viewId] ?? null);
|
|
759
|
+
const hoveredElement = useHeatmapClickStore((store) => store.hoveredElement[viewId] ?? null);
|
|
760
|
+
const shouldShowCallout = useHeatmapClickStore((store) => store.shouldShowCallout[viewId] ?? false);
|
|
761
|
+
const setStateStore = useHeatmapClickStore((store) => store.setState);
|
|
762
|
+
const setSelectedElementStore = useHeatmapClickStore((store) => store.setSelectedElement);
|
|
763
|
+
const setHoveredElementStore = useHeatmapClickStore((store) => store.setHoveredElement);
|
|
764
|
+
const setShouldShowCalloutStore = useHeatmapClickStore((store) => store.setShouldShowCallout);
|
|
765
|
+
const memoizedSetters = useMemo(() => ({
|
|
766
|
+
setState: (newState) => setStateStore(newState, viewId),
|
|
767
|
+
setSelectedElement: (element) => setSelectedElementStore(element, viewId),
|
|
768
|
+
setHoveredElement: (element) => setHoveredElementStore(element, viewId),
|
|
769
|
+
setShouldShowCallout: (value) => setShouldShowCalloutStore(value, viewId),
|
|
770
|
+
}), [
|
|
771
|
+
setStateStore,
|
|
772
|
+
setSelectedElementStore,
|
|
773
|
+
setHoveredElementStore,
|
|
774
|
+
setShouldShowCalloutStore,
|
|
775
|
+
viewId,
|
|
776
|
+
]);
|
|
777
|
+
return {
|
|
778
|
+
state,
|
|
779
|
+
selectedElement,
|
|
780
|
+
hoveredElement,
|
|
781
|
+
shouldShowCallout,
|
|
782
|
+
// Setters (auto-inject viewId)
|
|
783
|
+
...memoizedSetters,
|
|
784
|
+
};
|
|
634
785
|
};
|
|
635
786
|
|
|
636
787
|
const useHeatmapData = (props) => {
|
|
637
|
-
const viewId = props?.viewId ||
|
|
788
|
+
const viewId = props?.viewId || useViewIdContext();
|
|
638
789
|
const data = useHeatmapDataStore((state) => state.data[viewId]);
|
|
639
790
|
const clickmap = useHeatmapDataStore((state) => state.clickmap[viewId]);
|
|
640
791
|
const scrollmap = useHeatmapDataStore((state) => state.scrollmap[viewId]);
|
|
@@ -659,40 +810,30 @@ const useHeatmapData = (props) => {
|
|
|
659
810
|
};
|
|
660
811
|
};
|
|
661
812
|
|
|
662
|
-
const
|
|
663
|
-
const viewId = props?.viewId ||
|
|
664
|
-
const
|
|
665
|
-
const
|
|
666
|
-
const
|
|
667
|
-
const
|
|
668
|
-
const
|
|
669
|
-
const
|
|
670
|
-
const setHoveredElementStore = useHeatmapInteractionStore((store) => store.setHoveredElement);
|
|
671
|
-
const setShouldShowCalloutStore = useHeatmapInteractionStore((store) => store.setShouldShowCallout);
|
|
813
|
+
const useHeatmapScroll = (props) => {
|
|
814
|
+
const viewId = props?.viewId || useViewIdContext();
|
|
815
|
+
const zones = useHeatmapVizScrollmapStore((store) => store.zones[viewId] ?? []);
|
|
816
|
+
const hoveredZone = useHeatmapVizScrollmapStore((store) => store.hoveredZone[viewId] ?? null);
|
|
817
|
+
const showMinimap = useHeatmapVizScrollmapStore((store) => store.showMinimap[viewId] ?? true);
|
|
818
|
+
const setZonesStore = useHeatmapVizScrollmapStore((store) => store.setZones);
|
|
819
|
+
const setHoveredZoneStore = useHeatmapVizScrollmapStore((store) => store.setHoveredZone);
|
|
820
|
+
const setShowMinimapStore = useHeatmapVizScrollmapStore((store) => store.setShowMinimap);
|
|
672
821
|
const memoizedSetters = useMemo(() => ({
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
}), [
|
|
678
|
-
setStateStore,
|
|
679
|
-
setSelectedElementStore,
|
|
680
|
-
setHoveredElementStore,
|
|
681
|
-
setShouldShowCalloutStore,
|
|
682
|
-
viewId,
|
|
683
|
-
]);
|
|
822
|
+
setZones: (newZones) => setZonesStore(newZones, viewId),
|
|
823
|
+
setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
|
|
824
|
+
setShowMinimap: (value) => setShowMinimapStore(value, viewId),
|
|
825
|
+
}), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
|
|
684
826
|
return {
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
shouldShowCallout,
|
|
827
|
+
zones,
|
|
828
|
+
hoveredZone,
|
|
829
|
+
showMinimap,
|
|
689
830
|
// Setters (auto-inject viewId)
|
|
690
831
|
...memoizedSetters,
|
|
691
832
|
};
|
|
692
833
|
};
|
|
693
834
|
|
|
694
835
|
const useHeatmapViz = (props) => {
|
|
695
|
-
const viewId = props?.viewId ||
|
|
836
|
+
const viewId = props?.viewId || useViewIdContext();
|
|
696
837
|
// Viz store
|
|
697
838
|
const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
|
|
698
839
|
const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
|
|
@@ -750,28 +891,6 @@ const useHeatmapViz = (props) => {
|
|
|
750
891
|
};
|
|
751
892
|
};
|
|
752
893
|
|
|
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
894
|
/**
|
|
776
895
|
* Hook to handle copying and clearing view data across all stores
|
|
777
896
|
*/
|
|
@@ -779,24 +898,28 @@ const useHeatmapCopyView = () => {
|
|
|
779
898
|
const copyDataView = useHeatmapDataStore((state) => state.copyView);
|
|
780
899
|
const copyVizView = useHeatmapVizStore((state) => state.copyView);
|
|
781
900
|
const copySingleView = useHeatmapSingleStore((state) => state.copyView);
|
|
782
|
-
const copyInteractionView =
|
|
901
|
+
const copyInteractionView = useHeatmapClickStore((state) => state.copyView);
|
|
783
902
|
const copyVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.copyView);
|
|
903
|
+
const copyAreaClickView = useHeatmapAreaClickStore((state) => state.copyView);
|
|
784
904
|
const clearDataView = useHeatmapDataStore((state) => state.clearView);
|
|
785
905
|
const clearVizView = useHeatmapVizStore((state) => state.clearView);
|
|
786
906
|
const clearSingleView = useHeatmapSingleStore((state) => state.clearView);
|
|
787
|
-
const clearInteractionView =
|
|
907
|
+
const clearInteractionView = useHeatmapClickStore((state) => state.clearView);
|
|
788
908
|
const clearVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.clearView);
|
|
909
|
+
const clearAreaClickView = useHeatmapAreaClickStore((state) => state.clearView);
|
|
789
910
|
const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
|
|
790
911
|
const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
|
|
791
912
|
const resetSingleAll = useHeatmapSingleStore((state) => state.resetAll);
|
|
792
|
-
const resetInteractionAll =
|
|
913
|
+
const resetInteractionAll = useHeatmapClickStore((state) => state.resetAll);
|
|
793
914
|
const resetVizScrollmapAll = useHeatmapVizScrollmapStore((state) => state.resetAll);
|
|
915
|
+
const resetAreaClickAll = useHeatmapAreaClickStore((state) => state.resetAll);
|
|
794
916
|
const copyView = (fromViewId, toViewId) => {
|
|
795
917
|
copyDataView(fromViewId, toViewId);
|
|
796
918
|
copyVizView(fromViewId, toViewId);
|
|
797
919
|
copySingleView(fromViewId, toViewId);
|
|
798
920
|
copyInteractionView(fromViewId, toViewId);
|
|
799
921
|
copyVizScrollmapView(fromViewId, toViewId);
|
|
922
|
+
copyAreaClickView(fromViewId, toViewId);
|
|
800
923
|
};
|
|
801
924
|
const copyViewToMultiple = (fromViewId, toViewIds) => {
|
|
802
925
|
toViewIds.forEach((toViewId) => {
|
|
@@ -809,6 +932,7 @@ const useHeatmapCopyView = () => {
|
|
|
809
932
|
clearSingleView(viewId);
|
|
810
933
|
clearInteractionView(viewId);
|
|
811
934
|
clearVizScrollmapView(viewId);
|
|
935
|
+
clearAreaClickView(viewId);
|
|
812
936
|
};
|
|
813
937
|
const clearMultipleViews = (viewIds) => {
|
|
814
938
|
viewIds.forEach((viewId) => {
|
|
@@ -821,6 +945,7 @@ const useHeatmapCopyView = () => {
|
|
|
821
945
|
resetSingleAll();
|
|
822
946
|
resetInteractionAll();
|
|
823
947
|
resetVizScrollmapAll();
|
|
948
|
+
resetAreaClickAll();
|
|
824
949
|
};
|
|
825
950
|
return {
|
|
826
951
|
copyView,
|
|
@@ -873,23 +998,589 @@ const useRegisterHeatmap = ({ clickmap, scrollmap }) => {
|
|
|
873
998
|
}, [scrollmap]);
|
|
874
999
|
};
|
|
875
1000
|
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
1001
|
+
/**
|
|
1002
|
+
* Create an observable value with subscribe/unsubscribe pattern
|
|
1003
|
+
*/
|
|
1004
|
+
function createObservable(initialValue) {
|
|
1005
|
+
const subscribers = new Set();
|
|
1006
|
+
const observable = {
|
|
1007
|
+
value: initialValue,
|
|
1008
|
+
observe: (callback) => {
|
|
1009
|
+
subscribers.add(callback);
|
|
1010
|
+
// Immediately call with current value
|
|
1011
|
+
if (observable.value !== undefined) {
|
|
1012
|
+
callback(observable.value);
|
|
1013
|
+
}
|
|
1014
|
+
},
|
|
1015
|
+
unobserve: (callback) => {
|
|
1016
|
+
subscribers.delete(callback);
|
|
1017
|
+
},
|
|
1018
|
+
update: (newValue) => {
|
|
1019
|
+
observable.value = newValue;
|
|
1020
|
+
// Notify all subscribers
|
|
1021
|
+
subscribers.forEach((callback) => {
|
|
1022
|
+
callback(newValue);
|
|
1023
|
+
});
|
|
1024
|
+
},
|
|
1025
|
+
};
|
|
1026
|
+
return observable;
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
function isElementInViewport(elementRect, visualRef, scale) {
|
|
1030
|
+
if (!elementRect)
|
|
1031
|
+
return false;
|
|
1032
|
+
const visualRect = visualRef.current?.getBoundingClientRect();
|
|
1033
|
+
if (!visualRect)
|
|
1034
|
+
return false;
|
|
1035
|
+
// Element position relative to the document (or container's content)
|
|
1036
|
+
const elementTop = elementRect.top * scale;
|
|
1037
|
+
const elementBottom = (elementRect.top + elementRect.height) * scale;
|
|
1038
|
+
// Current scroll position
|
|
1039
|
+
const scrollTop = visualRef.current?.scrollTop || 0;
|
|
1040
|
+
const viewportHeight = visualRect.height;
|
|
1041
|
+
// Visible viewport range in the scrollable content
|
|
1042
|
+
const viewportTop = scrollTop;
|
|
1043
|
+
const viewportBottom = scrollTop + viewportHeight;
|
|
1044
|
+
// Check if element is within the visible viewport
|
|
1045
|
+
// Element is visible if it overlaps with the viewport
|
|
1046
|
+
return elementBottom > viewportTop && elementTop < viewportBottom;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const AREA_HOVER_BOX_SHADOW = '0 0 0 1px #0078D4, 0 0 0 1px #0078D4 inset, 0 0 0 2px white inset';
|
|
1050
|
+
const AREA_HOVER_ELEMENT_ID = 'clarity-edit-hover';
|
|
1051
|
+
const AREA_MAP_DIV_ATTRIBUTE = 'data-clarity-area-map-div';
|
|
1052
|
+
const AREA_COLOR_GRADIENT = [
|
|
1053
|
+
[0, 0, 255], // Blue
|
|
1054
|
+
[0, 255, 255], // Cyan
|
|
1055
|
+
[0, 255, 0], // Green
|
|
1056
|
+
[255, 255, 0], // Yellow
|
|
1057
|
+
[255, 0, 0], // Red
|
|
1058
|
+
];
|
|
1059
|
+
|
|
1060
|
+
const CALLOUT_PADDING = 0;
|
|
1061
|
+
const CALLOUT_ARROW_SIZE = 8;
|
|
1062
|
+
const CALLOUT_HORIZONTAL_OFFSET = 0;
|
|
1063
|
+
const CLICKED_ELEMENT_ID_BASE = 'gx-hm-clicked-element';
|
|
1064
|
+
const SECONDARY_CLICKED_ELEMENT_ID_BASE = 'gx-hm-secondary-clicked-element';
|
|
1065
|
+
const HOVERED_ELEMENT_ID_BASE = 'gx-hm-hovered-element';
|
|
1066
|
+
const SECONDARY_HOVERED_ELEMENT_ID_BASE = 'gx-hm-secondary-hovered-element';
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Get color from click distribution percentage (0-100)
|
|
1070
|
+
*/
|
|
1071
|
+
function getColorFromClickDist(clickDist) {
|
|
1072
|
+
// Ensure clickDist is in range [0, 100]
|
|
1073
|
+
const normalizedDist = Math.max(0, Math.min(100, clickDist));
|
|
1074
|
+
// Calculate gradient index
|
|
1075
|
+
const maxIndex = AREA_COLOR_GRADIENT.length - 1;
|
|
1076
|
+
const index = Math.floor((normalizedDist / 100) * maxIndex);
|
|
1077
|
+
const clampedIndex = Math.min(index, maxIndex);
|
|
1078
|
+
const [r, g, b] = AREA_COLOR_GRADIENT[clampedIndex];
|
|
1079
|
+
// Return rgba with 60% opacity
|
|
1080
|
+
return `rgba(${r}, ${g}, ${b}, 0.6)`;
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Get hover color (slightly lighter) from click distribution
|
|
1084
|
+
*/
|
|
1085
|
+
function getHoverColorFromClickDist(clickDist) {
|
|
1086
|
+
const normalizedDist = Math.max(0, Math.min(100, clickDist));
|
|
1087
|
+
const maxIndex = AREA_COLOR_GRADIENT.length - 1;
|
|
1088
|
+
const index = Math.floor((normalizedDist / 100) * maxIndex);
|
|
1089
|
+
const clampedIndex = Math.min(index, maxIndex);
|
|
1090
|
+
const [r, g, b] = AREA_COLOR_GRADIENT[clampedIndex];
|
|
1091
|
+
// Return rgba with 80% opacity for hover
|
|
1092
|
+
return `rgba(${r}, ${g}, ${b}, 0.8)`;
|
|
1093
|
+
}
|
|
1094
|
+
/**
|
|
1095
|
+
* Calculate click distribution percentage from total clicks
|
|
1096
|
+
*/
|
|
1097
|
+
function calculateClickDistribution(elementClicks, totalClicks) {
|
|
1098
|
+
if (totalClicks === 0)
|
|
1099
|
+
return 0;
|
|
1100
|
+
return (elementClicks / totalClicks) * 100;
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
/**
|
|
1104
|
+
* Get element position and dimensions for rendering inside iframe
|
|
1105
|
+
*
|
|
1106
|
+
* This function calculates position relative to the iframe's document,
|
|
1107
|
+
* accounting for scroll position. Suitable for elements rendered in
|
|
1108
|
+
* shadow DOM inside the iframe with absolute positioning.
|
|
1109
|
+
*/
|
|
1110
|
+
function getElementRect(element, _shadowRoot) {
|
|
1111
|
+
const rect = element.getBoundingClientRect();
|
|
1112
|
+
const width = rect.width;
|
|
1113
|
+
const height = rect.height;
|
|
1114
|
+
// Get the document to access scroll position
|
|
1115
|
+
const doc = element.ownerDocument || document;
|
|
1116
|
+
// Get scroll offset from documentElement or body
|
|
1117
|
+
const scrollTop = doc.documentElement?.scrollTop || doc.body?.scrollTop || 0;
|
|
1118
|
+
const scrollLeft = doc.documentElement?.scrollLeft || doc.body?.scrollLeft || 0;
|
|
1119
|
+
// Calculate position relative to document (not viewport)
|
|
1120
|
+
// getBoundingClientRect() is relative to viewport, so add scroll offset
|
|
1121
|
+
const top = rect.top + scrollTop;
|
|
1122
|
+
const left = rect.left + scrollLeft;
|
|
1123
|
+
// For absolute positioning calculations (overlap detection)
|
|
1124
|
+
const absoluteLeft = left;
|
|
1125
|
+
const absoluteTop = top;
|
|
1126
|
+
const absoluteRight = absoluteLeft + width;
|
|
1127
|
+
const absoluteBottom = absoluteTop + height;
|
|
1128
|
+
return {
|
|
1129
|
+
width,
|
|
1130
|
+
height,
|
|
1131
|
+
top,
|
|
1132
|
+
left,
|
|
1133
|
+
absoluteLeft,
|
|
1134
|
+
absoluteTop,
|
|
1135
|
+
absoluteRight,
|
|
1136
|
+
absoluteBottom,
|
|
1137
|
+
outOfBounds: false,
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
/**
|
|
1141
|
+
* Check if element has CSS position: fixed
|
|
1142
|
+
*/
|
|
1143
|
+
function isElementFixed(element) {
|
|
1144
|
+
if (getComputedStyle(element).position === 'fixed') {
|
|
1145
|
+
return true;
|
|
1146
|
+
}
|
|
1147
|
+
if (element.nodeName === 'HTML') {
|
|
1148
|
+
return false;
|
|
1149
|
+
}
|
|
1150
|
+
const parent = element.parentElement;
|
|
1151
|
+
return parent ? isElementFixed(parent) : false;
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Check if two areas overlap
|
|
1155
|
+
*/
|
|
1156
|
+
function doAreasOverlap(area1, area2) {
|
|
1157
|
+
const r1 = area1.rect.value;
|
|
1158
|
+
const r2 = area2.rect.value;
|
|
1159
|
+
if (!r1 || !r2)
|
|
1160
|
+
return false;
|
|
1161
|
+
return ((r1.absoluteBottom > r2.absoluteTop &&
|
|
1162
|
+
r1.absoluteTop < r2.absoluteBottom &&
|
|
1163
|
+
r1.absoluteRight > r2.absoluteLeft &&
|
|
1164
|
+
r1.absoluteLeft < r2.absoluteRight) ||
|
|
1165
|
+
(r2.absoluteBottom > r1.absoluteTop &&
|
|
1166
|
+
r2.absoluteTop < r1.absoluteBottom &&
|
|
1167
|
+
r2.absoluteRight > r1.absoluteLeft &&
|
|
1168
|
+
r2.absoluteLeft < r1.absoluteRight));
|
|
1169
|
+
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Check if area1 is completely inside area2
|
|
1172
|
+
*/
|
|
1173
|
+
function isAreaContainedIn(area1, area2) {
|
|
1174
|
+
const r1 = area1.rect.value;
|
|
1175
|
+
const r2 = area2.rect.value;
|
|
1176
|
+
if (!r1 || !r2)
|
|
1177
|
+
return false;
|
|
1178
|
+
return (r1.absoluteTop >= r2.absoluteTop &&
|
|
1179
|
+
r1.absoluteBottom <= r2.absoluteBottom &&
|
|
1180
|
+
r1.absoluteLeft >= r2.absoluteLeft &&
|
|
1181
|
+
r1.absoluteRight <= r2.absoluteRight);
|
|
1182
|
+
}
|
|
1183
|
+
/**
|
|
1184
|
+
* Check if element contains another element in DOM tree
|
|
1185
|
+
*/
|
|
1186
|
+
function isElementAncestorOf(ancestor, descendant, doc) {
|
|
1187
|
+
return ancestor.contains(descendant);
|
|
1188
|
+
}
|
|
1189
|
+
/**
|
|
1190
|
+
* Get all elements at a specific point, including shadow DOM
|
|
1191
|
+
*/
|
|
1192
|
+
function getElementsAtPoint$1(doc, x, y, filter, visited = new Set()) {
|
|
1193
|
+
const elements = doc.elementsFromPoint(x, y);
|
|
1194
|
+
const filtered = elements.find(filter);
|
|
1195
|
+
// Check shadow DOM
|
|
1196
|
+
if (filtered?.shadowRoot && !visited.has(filtered.shadowRoot)) {
|
|
1197
|
+
visited.add(filtered.shadowRoot);
|
|
1198
|
+
return getElementsAtPoint$1(filtered.shadowRoot, x, y, filter, visited);
|
|
1199
|
+
}
|
|
1200
|
+
return elements;
|
|
1201
|
+
}
|
|
1202
|
+
/**
|
|
1203
|
+
* Check if element should be selectable for area creation
|
|
1204
|
+
*/
|
|
1205
|
+
function isElementSelectable(element, index, elements) {
|
|
1206
|
+
// Skip if it's an area map div
|
|
1207
|
+
if (element.hasAttribute(AREA_MAP_DIV_ATTRIBUTE)) {
|
|
1208
|
+
return false;
|
|
1209
|
+
}
|
|
1210
|
+
// Skip first element if it's BODY and there are other elements
|
|
1211
|
+
if (index === 0 && elements.length > 1 && element.nodeName === 'BODY') {
|
|
1212
|
+
return false;
|
|
1213
|
+
}
|
|
1214
|
+
return true;
|
|
1215
|
+
}
|
|
1216
|
+
/**
|
|
1217
|
+
* Sort areas by click distribution (descending)
|
|
1218
|
+
*/
|
|
1219
|
+
function sortAreasByClickDist(areas) {
|
|
1220
|
+
return [...areas].sort((a, b) => {
|
|
1221
|
+
// Higher clickDist first
|
|
1222
|
+
if (a.clickDist !== b.clickDist) {
|
|
1223
|
+
return b.clickDist - a.clickDist;
|
|
1224
|
+
}
|
|
1225
|
+
// Then by total clicks
|
|
1226
|
+
return b.totalclicks - a.totalclicks;
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Create shadow root or return existing one
|
|
1231
|
+
*/
|
|
1232
|
+
function getOrCreateShadowRoot(element) {
|
|
1233
|
+
if (element.shadowRoot) {
|
|
1234
|
+
return element.shadowRoot;
|
|
1235
|
+
}
|
|
1236
|
+
return element.attachShadow({ mode: 'open' });
|
|
1237
|
+
}
|
|
1238
|
+
/**
|
|
1239
|
+
* Check if rect is too small to show label
|
|
1240
|
+
*/
|
|
1241
|
+
function isRectTooSmallForLabel(rect) {
|
|
1242
|
+
return rect.width < 67 || rect.height < 30;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
function getElementSelector(element) {
|
|
1246
|
+
if (element.id) {
|
|
1247
|
+
return `#${element.id}`;
|
|
1248
|
+
}
|
|
1249
|
+
if (element.className) {
|
|
1250
|
+
const classes = Array.from(element.classList).join('.');
|
|
1251
|
+
if (classes) {
|
|
1252
|
+
return `${element.tagName.toLowerCase()}.${classes}`;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
return element.tagName.toLowerCase();
|
|
1256
|
+
}
|
|
1257
|
+
function buildAreaNode(element, hash, elementMapInfo, totalClicks, shadowRoot) {
|
|
1258
|
+
const elementInfo = elementMapInfo[hash];
|
|
1259
|
+
const elementClicks = elementInfo?.totalclicks || 0;
|
|
1260
|
+
const clickDist = calculateClickDistribution(elementClicks, totalClicks);
|
|
1261
|
+
const rect = getElementRect(element);
|
|
1262
|
+
const color = getColorFromClickDist(clickDist);
|
|
1263
|
+
const hoverColor = getHoverColorFromClickDist(clickDist);
|
|
1264
|
+
const areaNode = {
|
|
1265
|
+
kind: 'area',
|
|
1266
|
+
id: `${hash}_${Date.now()}`,
|
|
1267
|
+
hash,
|
|
1268
|
+
selector: elementInfo?.selector || getElementSelector(element),
|
|
1269
|
+
// DOM references
|
|
1270
|
+
element,
|
|
1271
|
+
areaElement: null,
|
|
1272
|
+
shadowElement: null,
|
|
1273
|
+
shadowStyleElement: null,
|
|
1274
|
+
// Graph structure
|
|
1275
|
+
parentNode: null,
|
|
1276
|
+
childNodes: new Set(),
|
|
1277
|
+
// Position
|
|
1278
|
+
rect: createObservable(rect),
|
|
1279
|
+
isFixed: isElementFixed(element),
|
|
1280
|
+
priority: false,
|
|
1281
|
+
// Click tracking
|
|
1282
|
+
totalclicks: elementClicks,
|
|
1283
|
+
cumulativeClicks: elementClicks,
|
|
1284
|
+
cumulativeMaxClicks: totalClicks,
|
|
1285
|
+
clickDist,
|
|
1286
|
+
hasClickInfo: true,
|
|
1287
|
+
// Visual
|
|
1288
|
+
color,
|
|
1289
|
+
hoverColor,
|
|
1290
|
+
// Lifecycle
|
|
1291
|
+
changeObserver: null,
|
|
1292
|
+
};
|
|
1293
|
+
return areaNode;
|
|
1294
|
+
}
|
|
1295
|
+
function getTopElementsByClicks(elementMapInfo, topN = 10) {
|
|
1296
|
+
const elements = Object.entries(elementMapInfo)
|
|
1297
|
+
.map(([hash, info]) => ({
|
|
1298
|
+
hash,
|
|
1299
|
+
totalclicks: info.totalclicks,
|
|
1300
|
+
selector: info.selector || '',
|
|
1301
|
+
}))
|
|
1302
|
+
.sort((a, b) => b.totalclicks - a.totalclicks)
|
|
1303
|
+
.slice(0, topN);
|
|
1304
|
+
return elements;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
/**
|
|
1308
|
+
* Resolve overlapping areas by priority rules
|
|
1309
|
+
*
|
|
1310
|
+
* Priority Rules (in order):
|
|
1311
|
+
* 1. Priority flag (manually set areas win)
|
|
1312
|
+
* 2. Click distribution (higher % wins)
|
|
1313
|
+
* 3. Total clicks (more clicks wins)
|
|
1314
|
+
* 4. DOM containment (parent contains child, parent wins)
|
|
1315
|
+
* 5. Size (smaller areas win - more specific)
|
|
1316
|
+
*/
|
|
1317
|
+
function resolveOverlaps(areas, iframeDocument) {
|
|
1318
|
+
if (areas.length === 0)
|
|
1319
|
+
return [];
|
|
1320
|
+
// Group overlapping areas
|
|
1321
|
+
const overlapGroups = findOverlapGroups(areas);
|
|
1322
|
+
// Resolve each group
|
|
1323
|
+
const visibleAreas = new Set();
|
|
1324
|
+
overlapGroups.forEach((group) => {
|
|
1325
|
+
const winner = resolveOverlapGroup(group, iframeDocument);
|
|
1326
|
+
visibleAreas.add(winner);
|
|
1327
|
+
});
|
|
1328
|
+
// Add non-overlapping areas
|
|
1329
|
+
areas.forEach((area) => {
|
|
1330
|
+
const hasOverlap = overlapGroups.some((group) => group.areas.includes(area));
|
|
1331
|
+
if (!hasOverlap) {
|
|
1332
|
+
visibleAreas.add(area);
|
|
1333
|
+
}
|
|
1334
|
+
});
|
|
1335
|
+
return Array.from(visibleAreas);
|
|
1336
|
+
}
|
|
1337
|
+
/**
|
|
1338
|
+
* Find groups of overlapping areas
|
|
1339
|
+
*/
|
|
1340
|
+
function findOverlapGroups(areas) {
|
|
1341
|
+
const groups = [];
|
|
1342
|
+
const processed = new Set();
|
|
1343
|
+
areas.forEach((area) => {
|
|
1344
|
+
if (processed.has(area.id))
|
|
1345
|
+
return;
|
|
1346
|
+
// Find all areas that overlap with this one
|
|
1347
|
+
const overlapping = areas.filter((other) => other.id !== area.id && doAreasOverlap(area, other));
|
|
1348
|
+
if (overlapping.length === 0) {
|
|
1349
|
+
// No overlap, skip grouping
|
|
1350
|
+
return;
|
|
1351
|
+
}
|
|
1352
|
+
// Create group with this area and all overlapping
|
|
1353
|
+
const groupAreas = [area, ...overlapping];
|
|
1354
|
+
groupAreas.forEach((a) => processed.add(a.id));
|
|
1355
|
+
// Placeholder - will be resolved later
|
|
1356
|
+
groups.push({
|
|
1357
|
+
areas: groupAreas,
|
|
1358
|
+
winner: area,
|
|
1359
|
+
hidden: [],
|
|
1360
|
+
});
|
|
1361
|
+
});
|
|
1362
|
+
return groups;
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* Resolve a single overlap group to find the winner
|
|
1366
|
+
*/
|
|
1367
|
+
function resolveOverlapGroup(group, iframeDocument) {
|
|
1368
|
+
const { areas } = group;
|
|
1369
|
+
if (areas.length === 1)
|
|
1370
|
+
return areas[0];
|
|
1371
|
+
// Sort by priority rules
|
|
1372
|
+
const sorted = [...areas].sort((a, b) => {
|
|
1373
|
+
// Rule 1: Priority flag
|
|
1374
|
+
if (a.priority !== b.priority) {
|
|
1375
|
+
return a.priority ? -1 : 1;
|
|
1376
|
+
}
|
|
1377
|
+
// Rule 2: Click distribution
|
|
1378
|
+
if (a.clickDist !== b.clickDist) {
|
|
1379
|
+
return b.clickDist - a.clickDist;
|
|
1380
|
+
}
|
|
1381
|
+
// Rule 3: Total clicks
|
|
1382
|
+
if (a.totalclicks !== b.totalclicks) {
|
|
1383
|
+
return b.totalclicks - a.totalclicks;
|
|
1384
|
+
}
|
|
1385
|
+
// Rule 4: DOM containment - parent beats child
|
|
1386
|
+
if (iframeDocument) {
|
|
1387
|
+
const aContainsB = isElementAncestorOf(a.element, b.element);
|
|
1388
|
+
const bContainsA = isElementAncestorOf(b.element, a.element);
|
|
1389
|
+
if (aContainsB)
|
|
1390
|
+
return -1; // a is parent, a wins
|
|
1391
|
+
if (bContainsA)
|
|
1392
|
+
return 1; // b is parent, b wins
|
|
1393
|
+
}
|
|
1394
|
+
// Rule 5: Size - smaller (more specific) wins
|
|
1395
|
+
const aSize = (a.rect.value?.width || 0) * (a.rect.value?.height || 0);
|
|
1396
|
+
const bSize = (b.rect.value?.width || 0) * (b.rect.value?.height || 0);
|
|
1397
|
+
return aSize - bSize;
|
|
1398
|
+
});
|
|
1399
|
+
const winner = sorted[0];
|
|
1400
|
+
group.winner = winner;
|
|
1401
|
+
group.hidden = sorted.slice(1);
|
|
1402
|
+
return winner;
|
|
1403
|
+
}
|
|
1404
|
+
/**
|
|
1405
|
+
* Filter out areas that are completely contained within others
|
|
1406
|
+
* and have lower priority
|
|
1407
|
+
*/
|
|
1408
|
+
function filterContainedAreas(areas) {
|
|
1409
|
+
const visible = [];
|
|
1410
|
+
areas.forEach((area) => {
|
|
1411
|
+
// Check if this area is contained by a higher priority area
|
|
1412
|
+
const isContained = areas.some((other) => {
|
|
1413
|
+
if (other.id === area.id)
|
|
1414
|
+
return false;
|
|
1415
|
+
// Check containment
|
|
1416
|
+
if (!isAreaContainedIn(area, other))
|
|
1417
|
+
return false;
|
|
1418
|
+
// Check priority
|
|
1419
|
+
if (other.priority && !area.priority)
|
|
1420
|
+
return true;
|
|
1421
|
+
if (!other.priority && area.priority)
|
|
1422
|
+
return false;
|
|
1423
|
+
// Compare by click dist
|
|
1424
|
+
if (other.clickDist > area.clickDist)
|
|
1425
|
+
return true;
|
|
1426
|
+
if (other.clickDist < area.clickDist)
|
|
1427
|
+
return false;
|
|
1428
|
+
// Compare by total clicks
|
|
1429
|
+
return other.totalclicks > area.totalclicks;
|
|
1430
|
+
});
|
|
1431
|
+
if (!isContained) {
|
|
1432
|
+
visible.push(area);
|
|
1433
|
+
}
|
|
1434
|
+
});
|
|
1435
|
+
return visible;
|
|
1436
|
+
}
|
|
1437
|
+
/**
|
|
1438
|
+
* Get visible areas after resolving overlaps
|
|
1439
|
+
*/
|
|
1440
|
+
function getVisibleAreas(areas, iframeDocument) {
|
|
1441
|
+
// First pass: filter contained areas
|
|
1442
|
+
let visible = filterContainedAreas(areas);
|
|
1443
|
+
// Second pass: resolve overlaps
|
|
1444
|
+
visible = resolveOverlaps(visible, iframeDocument);
|
|
1445
|
+
// Sort by click dist for rendering order
|
|
1446
|
+
return sortAreasByClickDist(visible);
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
function findLastSizeOfDom(data) {
|
|
1450
|
+
const listDocs = data
|
|
1451
|
+
.filter((item) => item.doc?.find((doc) => doc.data.width && doc.data.height))
|
|
1452
|
+
.flatMap((item) => item.doc?.flatMap((doc) => doc.data));
|
|
1453
|
+
const lastDoc = listDocs?.[listDocs.length - 1];
|
|
1454
|
+
const docSize = {
|
|
1455
|
+
width: lastDoc?.width,
|
|
1456
|
+
height: lastDoc?.height,
|
|
1457
|
+
};
|
|
1458
|
+
const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
|
|
1459
|
+
const lastResizeEvent = listResizes?.[listResizes.length - 1];
|
|
1460
|
+
const resize = {
|
|
1461
|
+
width: lastResizeEvent?.data.width,
|
|
1462
|
+
height: lastResizeEvent?.data.height,
|
|
1463
|
+
};
|
|
1464
|
+
return {
|
|
1465
|
+
doc: docSize,
|
|
1466
|
+
resize: resize,
|
|
1467
|
+
size: {
|
|
1468
|
+
width: resize.width || docSize.width,
|
|
1469
|
+
height: resize.height || docSize.height,
|
|
1470
|
+
},
|
|
1471
|
+
};
|
|
1472
|
+
}
|
|
1473
|
+
function decodePayloads(payload) {
|
|
1474
|
+
try {
|
|
1475
|
+
return decode(payload);
|
|
1476
|
+
}
|
|
1477
|
+
catch (_error) {
|
|
1478
|
+
return null;
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
/**
|
|
1483
|
+
* Generate unique element ID for a specific view
|
|
1484
|
+
* @param baseId - Base element ID
|
|
1485
|
+
* @param viewId - View ID
|
|
1486
|
+
* @returns Unique element ID (e.g., 'gx-hm-clicked-element-view-0')
|
|
1487
|
+
*/
|
|
1488
|
+
const getElementId = (baseId, viewId) => {
|
|
1489
|
+
return `${baseId}-${viewId}`;
|
|
1490
|
+
};
|
|
1491
|
+
const getClickedElementId = (viewId, isSecondary = false) => {
|
|
1492
|
+
const baseId = isSecondary ? SECONDARY_CLICKED_ELEMENT_ID_BASE : CLICKED_ELEMENT_ID_BASE;
|
|
1493
|
+
return getElementId(baseId, viewId);
|
|
1494
|
+
};
|
|
1495
|
+
const getHoveredElementId = (viewId, isSecondary = false) => {
|
|
1496
|
+
const baseId = isSecondary ? SECONDARY_HOVERED_ELEMENT_ID_BASE : HOVERED_ELEMENT_ID_BASE;
|
|
1497
|
+
return getElementId(baseId, viewId);
|
|
1498
|
+
};
|
|
1499
|
+
|
|
1500
|
+
function getElementLayout(element) {
|
|
1501
|
+
if (!element?.getBoundingClientRect)
|
|
1502
|
+
return null;
|
|
1503
|
+
const rect = element.getBoundingClientRect();
|
|
1504
|
+
if (rect.width === 0 && rect.height === 0)
|
|
1505
|
+
return null;
|
|
1506
|
+
return {
|
|
1507
|
+
top: rect.top,
|
|
1508
|
+
left: rect.left,
|
|
1509
|
+
width: rect.width,
|
|
1510
|
+
height: rect.height,
|
|
1511
|
+
};
|
|
1512
|
+
}
|
|
1513
|
+
const getElementAtPoint = (doc, x, y) => {
|
|
1514
|
+
let el = null;
|
|
1515
|
+
if ('caretPositionFromPoint' in doc) {
|
|
1516
|
+
el = doc.caretPositionFromPoint(x, y)?.offsetNode ?? null;
|
|
1517
|
+
}
|
|
1518
|
+
el = el ?? doc.elementFromPoint(x, y);
|
|
1519
|
+
let element = el;
|
|
1520
|
+
while (element && element.nodeType === Node.TEXT_NODE) {
|
|
1521
|
+
element = element.parentElement;
|
|
1522
|
+
}
|
|
1523
|
+
return element;
|
|
1524
|
+
};
|
|
1525
|
+
function getElementHash(element) {
|
|
1526
|
+
return (element.getAttribute('data-clarity-hash') ||
|
|
1527
|
+
element.getAttribute('data-clarity-hashalpha') ||
|
|
1528
|
+
element.getAttribute('data-clarity-hashbeta'));
|
|
1529
|
+
}
|
|
1530
|
+
const getElementRank = (hash, elements) => {
|
|
1531
|
+
if (!elements)
|
|
1532
|
+
return 0;
|
|
1533
|
+
return elements.findIndex((e) => e.hash === hash) + 1;
|
|
1534
|
+
};
|
|
1535
|
+
const buildElementInfo = (hash, rect, heatmapInfo) => {
|
|
1536
|
+
if (!rect || !heatmapInfo)
|
|
1537
|
+
return null;
|
|
1538
|
+
const info = heatmapInfo.elementMapInfo?.[hash];
|
|
1539
|
+
if (!info)
|
|
1540
|
+
return null;
|
|
1541
|
+
const rank = getElementRank(hash, heatmapInfo.sortedElements);
|
|
1542
|
+
const clicks = info.totalclicks ?? 0;
|
|
1543
|
+
const selector = info.selector ?? '';
|
|
1544
|
+
const baseInfo = {
|
|
1545
|
+
hash,
|
|
1546
|
+
clicks,
|
|
1547
|
+
rank,
|
|
1548
|
+
selector,
|
|
1549
|
+
};
|
|
1550
|
+
return {
|
|
1551
|
+
...baseInfo,
|
|
1552
|
+
...rect,
|
|
1553
|
+
};
|
|
1554
|
+
};
|
|
1555
|
+
|
|
1556
|
+
function calculateRankPosition(rect, widthScale) {
|
|
1557
|
+
const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
|
|
1558
|
+
const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
|
|
1559
|
+
return {
|
|
1560
|
+
transform: `scale(${1.2 * widthScale})`,
|
|
1561
|
+
top: Number.isNaN(top) ? undefined : top,
|
|
1562
|
+
left: Number.isNaN(left) ? undefined : left,
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
const getViewportDimensions = (containerElm) => {
|
|
1567
|
+
if (containerElm) {
|
|
1568
|
+
const containerRect = containerElm.getBoundingClientRect();
|
|
1569
|
+
return {
|
|
1570
|
+
width: containerRect.width,
|
|
1571
|
+
height: containerRect.height,
|
|
1572
|
+
};
|
|
1573
|
+
}
|
|
1574
|
+
return {
|
|
1575
|
+
width: window.innerWidth,
|
|
1576
|
+
height: window.innerHeight,
|
|
1577
|
+
};
|
|
1578
|
+
};
|
|
886
1579
|
const getElementDimensions = (targetElm, calloutElm) => ({
|
|
887
1580
|
targetRect: targetElm.getBoundingClientRect(),
|
|
888
1581
|
calloutRect: calloutElm.getBoundingClientRect(),
|
|
889
1582
|
});
|
|
890
|
-
|
|
891
|
-
// Alignment Order
|
|
892
|
-
// ============================================================================
|
|
1583
|
+
|
|
893
1584
|
const getAlignmentOrder = (alignment) => {
|
|
894
1585
|
switch (alignment) {
|
|
895
1586
|
case 'center':
|
|
@@ -900,9 +1591,6 @@ const getAlignmentOrder = (alignment) => {
|
|
|
900
1591
|
return ['right', 'center', 'left'];
|
|
901
1592
|
}
|
|
902
1593
|
};
|
|
903
|
-
// ============================================================================
|
|
904
|
-
// Position Calculation
|
|
905
|
-
// ============================================================================
|
|
906
1594
|
const calculateLeftPosition = ({ targetRect, calloutRect, hozOffset, align, }) => {
|
|
907
1595
|
switch (align) {
|
|
908
1596
|
case 'left':
|
|
@@ -926,31 +1614,41 @@ const calculateHorizontalPlacementPosition = (targetRect, calloutRect, placement
|
|
|
926
1614
|
: targetRect.left - calloutRect.width - padding - arrowSize;
|
|
927
1615
|
return { top, left };
|
|
928
1616
|
};
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
1617
|
+
|
|
1618
|
+
const isLeftPositionValid = (leftPos, calloutWidth, viewportWidth, padding, containerRect) => {
|
|
1619
|
+
if (containerRect) {
|
|
1620
|
+
return (leftPos >= containerRect.left + padding &&
|
|
1621
|
+
leftPos + calloutWidth <= containerRect.right - padding);
|
|
1622
|
+
}
|
|
933
1623
|
return leftPos >= padding && leftPos + calloutWidth <= viewportWidth - padding;
|
|
934
1624
|
};
|
|
935
|
-
const isVerticalPositionValid = (targetRect, calloutRect, placement, viewportHeight, padding, arrowSize) => {
|
|
1625
|
+
const isVerticalPositionValid = (targetRect, calloutRect, placement, viewportHeight, padding, arrowSize, containerRect) => {
|
|
1626
|
+
if (containerRect) {
|
|
1627
|
+
return placement === 'top'
|
|
1628
|
+
? targetRect.top - calloutRect.height - padding - arrowSize >= containerRect.top
|
|
1629
|
+
: targetRect.bottom + calloutRect.height + padding + arrowSize <= containerRect.bottom;
|
|
1630
|
+
}
|
|
936
1631
|
return placement === 'top'
|
|
937
1632
|
? targetRect.top - calloutRect.height - padding - arrowSize > 0
|
|
938
1633
|
: targetRect.bottom + calloutRect.height + padding + arrowSize < viewportHeight;
|
|
939
1634
|
};
|
|
940
|
-
const isHorizontalPlacementValid = (targetRect, calloutRect, placement, viewportWidth, padding, arrowSize) => {
|
|
1635
|
+
const isHorizontalPlacementValid = (targetRect, calloutRect, placement, viewportWidth, padding, arrowSize, containerRect) => {
|
|
1636
|
+
if (containerRect) {
|
|
1637
|
+
return placement === 'right'
|
|
1638
|
+
? targetRect.right + calloutRect.width + padding + arrowSize <= containerRect.right
|
|
1639
|
+
: targetRect.left - calloutRect.width - padding - arrowSize >= containerRect.left;
|
|
1640
|
+
}
|
|
941
1641
|
return placement === 'right'
|
|
942
1642
|
? targetRect.right + calloutRect.width + padding + arrowSize < viewportWidth
|
|
943
1643
|
: targetRect.left - calloutRect.width - padding - arrowSize > 0;
|
|
944
1644
|
};
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
// ============================================================================
|
|
948
|
-
const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHeight, viewportWidth, alignment, hozOffset, padding, arrowSize) => {
|
|
1645
|
+
|
|
1646
|
+
const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHeight, viewportWidth, alignment, hozOffset, padding, arrowSize, containerRect) => {
|
|
949
1647
|
const candidates = [];
|
|
950
1648
|
const placements = ['top', 'bottom'];
|
|
951
1649
|
placements.forEach((placement) => {
|
|
952
1650
|
const verticalPos = calculateVerticalPosition(targetRect, calloutRect, placement, padding, arrowSize);
|
|
953
|
-
const verticalValid = isVerticalPositionValid(targetRect, calloutRect, placement, viewportHeight, padding, arrowSize);
|
|
1651
|
+
const verticalValid = isVerticalPositionValid(targetRect, calloutRect, placement, viewportHeight, padding, arrowSize, containerRect);
|
|
954
1652
|
const alignmentOrder = getAlignmentOrder(alignment);
|
|
955
1653
|
alignmentOrder.forEach((align) => {
|
|
956
1654
|
const horizontalPos = calculateLeftPosition({
|
|
@@ -965,13 +1663,13 @@ const generateVerticalPositionCandidates = (targetRect, calloutRect, viewportHei
|
|
|
965
1663
|
left: horizontalPos,
|
|
966
1664
|
horizontalAlign: align,
|
|
967
1665
|
valid: verticalValid &&
|
|
968
|
-
isLeftPositionValid(horizontalPos, calloutRect.width, viewportWidth, padding),
|
|
1666
|
+
isLeftPositionValid(horizontalPos, calloutRect.width, viewportWidth, padding, containerRect),
|
|
969
1667
|
});
|
|
970
1668
|
});
|
|
971
1669
|
});
|
|
972
1670
|
return candidates;
|
|
973
1671
|
};
|
|
974
|
-
const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportWidth, padding, arrowSize) => {
|
|
1672
|
+
const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportWidth, padding, arrowSize, containerRect) => {
|
|
975
1673
|
const placements = ['left', 'right'];
|
|
976
1674
|
return placements.map((placement) => {
|
|
977
1675
|
const { top, left } = calculateHorizontalPlacementPosition(targetRect, calloutRect, placement, padding, arrowSize);
|
|
@@ -980,46 +1678,46 @@ const generateHorizontalPositionCandidates = (targetRect, calloutRect, viewportW
|
|
|
980
1678
|
top,
|
|
981
1679
|
left,
|
|
982
1680
|
horizontalAlign: 'center',
|
|
983
|
-
valid: isHorizontalPlacementValid(targetRect, calloutRect, placement, viewportWidth, padding, arrowSize),
|
|
1681
|
+
valid: isHorizontalPlacementValid(targetRect, calloutRect, placement, viewportWidth, padding, arrowSize, containerRect),
|
|
984
1682
|
};
|
|
985
1683
|
});
|
|
986
1684
|
};
|
|
987
|
-
const generateAllPositionCandidates = (rectDimensions, viewport, alignment, hozOffset, padding, arrowSize) => {
|
|
1685
|
+
const generateAllPositionCandidates = (rectDimensions, viewport, alignment, hozOffset, padding, arrowSize, containerRect) => {
|
|
988
1686
|
const { targetRect, calloutRect } = rectDimensions;
|
|
989
|
-
const verticalCandidates = generateVerticalPositionCandidates(targetRect, calloutRect, viewport.height, viewport.width, alignment, hozOffset, padding, arrowSize);
|
|
990
|
-
const horizontalCandidates = generateHorizontalPositionCandidates(targetRect, calloutRect, viewport.width, padding, arrowSize);
|
|
1687
|
+
const verticalCandidates = generateVerticalPositionCandidates(targetRect, calloutRect, viewport.height, viewport.width, alignment, hozOffset, padding, arrowSize, containerRect);
|
|
1688
|
+
const horizontalCandidates = generateHorizontalPositionCandidates(targetRect, calloutRect, viewport.width, padding, arrowSize, containerRect);
|
|
991
1689
|
return [...verticalCandidates, ...horizontalCandidates];
|
|
992
1690
|
};
|
|
993
|
-
|
|
994
|
-
// Position Selection
|
|
995
|
-
// ============================================================================
|
|
1691
|
+
|
|
996
1692
|
const selectBestPosition = (candidates) => {
|
|
997
1693
|
return candidates.find((p) => p.valid) || candidates[0];
|
|
998
1694
|
};
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
const
|
|
1695
|
+
const constrainToViewport = (position, calloutRect, viewport, padding, containerRect) => {
|
|
1696
|
+
if (containerRect) {
|
|
1697
|
+
const left = Math.max(containerRect.left + padding, Math.min(position.left, containerRect.right - calloutRect.width - padding));
|
|
1698
|
+
const top = Math.max(containerRect.top + padding, Math.min(position.top, containerRect.bottom - calloutRect.height - padding));
|
|
1699
|
+
return { top, left };
|
|
1700
|
+
}
|
|
1003
1701
|
const left = Math.max(padding, Math.min(position.left, viewport.width - calloutRect.width - padding));
|
|
1004
1702
|
const top = Math.max(padding, Math.min(position.top, viewport.height - calloutRect.height - padding));
|
|
1005
1703
|
return { top, left };
|
|
1006
1704
|
};
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
const calcCalloutPosition = ({ targetElm, calloutElm, setPosition, hozOffset = HORIZONTAL_OFFSET, alignment = 'center', }) => {
|
|
1705
|
+
|
|
1706
|
+
const calcCalloutPosition = (options) => {
|
|
1707
|
+
const { targetElm, calloutElm, setPosition, hozOffset = CALLOUT_HORIZONTAL_OFFSET, alignment = 'center', containerElm, } = options;
|
|
1011
1708
|
return () => {
|
|
1012
1709
|
// 1. Get dimensions
|
|
1013
1710
|
const rectDimensions = getElementDimensions(targetElm, calloutElm);
|
|
1014
|
-
const viewport = getViewportDimensions();
|
|
1015
|
-
const
|
|
1016
|
-
const
|
|
1711
|
+
const viewport = getViewportDimensions(containerElm);
|
|
1712
|
+
const containerRect = containerElm?.getBoundingClientRect();
|
|
1713
|
+
const padding = CALLOUT_PADDING;
|
|
1714
|
+
const arrowSize = CALLOUT_ARROW_SIZE;
|
|
1017
1715
|
// 2. Generate all position candidates
|
|
1018
|
-
const candidates = generateAllPositionCandidates(rectDimensions, viewport, alignment, hozOffset, padding, arrowSize);
|
|
1716
|
+
const candidates = generateAllPositionCandidates(rectDimensions, viewport, alignment, hozOffset, padding, arrowSize, containerRect);
|
|
1019
1717
|
// 3. Select best position
|
|
1020
1718
|
const bestPosition = selectBestPosition(candidates);
|
|
1021
1719
|
// 4. Constrain to viewport
|
|
1022
|
-
const constrainedPosition = constrainToViewport({ top: bestPosition.top, left: bestPosition.left }, rectDimensions.calloutRect, viewport, padding);
|
|
1720
|
+
const constrainedPosition = constrainToViewport({ top: bestPosition.top, left: bestPosition.left }, rectDimensions.calloutRect, viewport, padding, containerRect);
|
|
1023
1721
|
// 5. Create final position object
|
|
1024
1722
|
const finalPosition = {
|
|
1025
1723
|
top: constrainedPosition.top,
|
|
@@ -1031,124 +1729,6 @@ const calcCalloutPosition = ({ targetElm, calloutElm, setPosition, hozOffset = H
|
|
|
1031
1729
|
};
|
|
1032
1730
|
};
|
|
1033
1731
|
|
|
1034
|
-
function getElementLayout(element) {
|
|
1035
|
-
if (!element?.getBoundingClientRect)
|
|
1036
|
-
return null;
|
|
1037
|
-
const rect = element.getBoundingClientRect();
|
|
1038
|
-
if (rect.width === 0 && rect.height === 0)
|
|
1039
|
-
return null;
|
|
1040
|
-
return {
|
|
1041
|
-
top: rect.top,
|
|
1042
|
-
left: rect.left,
|
|
1043
|
-
width: rect.width,
|
|
1044
|
-
height: rect.height,
|
|
1045
|
-
};
|
|
1046
|
-
}
|
|
1047
|
-
const getElementAtPoint = (doc, x, y) => {
|
|
1048
|
-
let el = null;
|
|
1049
|
-
if ('caretPositionFromPoint' in doc) {
|
|
1050
|
-
el = doc.caretPositionFromPoint(x, y)?.offsetNode ?? null;
|
|
1051
|
-
}
|
|
1052
|
-
el = el ?? doc.elementFromPoint(x, y);
|
|
1053
|
-
let element = el;
|
|
1054
|
-
while (element && element.nodeType === Node.TEXT_NODE) {
|
|
1055
|
-
element = element.parentElement;
|
|
1056
|
-
}
|
|
1057
|
-
return element;
|
|
1058
|
-
};
|
|
1059
|
-
function getElementHash(element) {
|
|
1060
|
-
return (element.getAttribute('data-clarity-hash') ||
|
|
1061
|
-
element.getAttribute('data-clarity-hashalpha') ||
|
|
1062
|
-
element.getAttribute('data-clarity-hashbeta'));
|
|
1063
|
-
}
|
|
1064
|
-
const getElementRank = (hash, elements) => {
|
|
1065
|
-
if (!elements)
|
|
1066
|
-
return 0;
|
|
1067
|
-
return elements.findIndex((e) => e.hash === hash) + 1;
|
|
1068
|
-
};
|
|
1069
|
-
const buildElementInfo = (hash, rect, heatmapInfo) => {
|
|
1070
|
-
if (!rect || !heatmapInfo)
|
|
1071
|
-
return null;
|
|
1072
|
-
const info = heatmapInfo.elementMapInfo?.[hash];
|
|
1073
|
-
if (!info)
|
|
1074
|
-
return null;
|
|
1075
|
-
const rank = getElementRank(hash, heatmapInfo.sortedElements);
|
|
1076
|
-
const clicks = info.totalclicks ?? 0;
|
|
1077
|
-
const selector = info.selector ?? '';
|
|
1078
|
-
const baseInfo = {
|
|
1079
|
-
hash,
|
|
1080
|
-
clicks,
|
|
1081
|
-
rank,
|
|
1082
|
-
selector,
|
|
1083
|
-
};
|
|
1084
|
-
return {
|
|
1085
|
-
...baseInfo,
|
|
1086
|
-
...rect,
|
|
1087
|
-
};
|
|
1088
|
-
};
|
|
1089
|
-
|
|
1090
|
-
function findLastSizeOfDom(data) {
|
|
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];
|
|
1095
|
-
const docSize = {
|
|
1096
|
-
width: lastDoc?.width,
|
|
1097
|
-
height: lastDoc?.height,
|
|
1098
|
-
};
|
|
1099
|
-
const listResizes = data.filter((item) => !!item.resize).flatMap((item) => item.resize);
|
|
1100
|
-
const lastResizeEvent = listResizes?.[listResizes.length - 1];
|
|
1101
|
-
const resize = {
|
|
1102
|
-
width: lastResizeEvent?.data.width,
|
|
1103
|
-
height: lastResizeEvent?.data.height,
|
|
1104
|
-
};
|
|
1105
|
-
return {
|
|
1106
|
-
doc: docSize,
|
|
1107
|
-
resize: resize,
|
|
1108
|
-
size: {
|
|
1109
|
-
width: resize.width || docSize.width,
|
|
1110
|
-
height: resize.height || docSize.height,
|
|
1111
|
-
},
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
function decodePayloads(payload) {
|
|
1115
|
-
try {
|
|
1116
|
-
return decode(payload);
|
|
1117
|
-
}
|
|
1118
|
-
catch (error) {
|
|
1119
|
-
return null;
|
|
1120
|
-
}
|
|
1121
|
-
}
|
|
1122
|
-
|
|
1123
|
-
function calculateRankPosition(rect, widthScale) {
|
|
1124
|
-
const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
|
|
1125
|
-
const left = rect.left <= 18 ? rect.left + 3 : rect.left - 18;
|
|
1126
|
-
return {
|
|
1127
|
-
transform: `scale(${1.2 * widthScale})`,
|
|
1128
|
-
top: Number.isNaN(top) ? undefined : top,
|
|
1129
|
-
left: Number.isNaN(left) ? undefined : left,
|
|
1130
|
-
};
|
|
1131
|
-
}
|
|
1132
|
-
function isElementInViewport(elementRect, visualRef, scale) {
|
|
1133
|
-
if (!elementRect)
|
|
1134
|
-
return false;
|
|
1135
|
-
const visualRect = visualRef.current?.getBoundingClientRect();
|
|
1136
|
-
if (!visualRect)
|
|
1137
|
-
return false;
|
|
1138
|
-
// Element position relative to the document (or container's content)
|
|
1139
|
-
const elementTop = elementRect.top * scale;
|
|
1140
|
-
const elementBottom = (elementRect.top + elementRect.height) * scale;
|
|
1141
|
-
// Current scroll position
|
|
1142
|
-
const scrollTop = visualRef.current?.scrollTop || 0;
|
|
1143
|
-
const viewportHeight = visualRect.height;
|
|
1144
|
-
// Visible viewport range in the scrollable content
|
|
1145
|
-
const viewportTop = scrollTop;
|
|
1146
|
-
const viewportBottom = scrollTop + viewportHeight;
|
|
1147
|
-
// Check if element is within the visible viewport
|
|
1148
|
-
// Element is visible if it overlaps with the viewport
|
|
1149
|
-
return elementBottom > viewportTop && elementTop < viewportBottom;
|
|
1150
|
-
}
|
|
1151
|
-
|
|
1152
1732
|
class IframeNavigationBlockerV2 {
|
|
1153
1733
|
doc;
|
|
1154
1734
|
win;
|
|
@@ -1673,56 +2253,224 @@ class IframeHelperFixer {
|
|
|
1673
2253
|
}));
|
|
1674
2254
|
}
|
|
1675
2255
|
catch (error) {
|
|
1676
|
-
console.error('[IframeHelper] Failed to process:', error);
|
|
1677
|
-
this.config.onError?.(error);
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
async recalculate() {
|
|
1681
|
-
console.log('[IframeHelper] Recalculating...');
|
|
1682
|
-
await this.process();
|
|
1683
|
-
}
|
|
1684
|
-
updateConfig(config) {
|
|
1685
|
-
this.config = { ...this.config, ...config };
|
|
1686
|
-
if (this.replacer) {
|
|
1687
|
-
this.replacer.updateConfig(config);
|
|
2256
|
+
console.error('[IframeHelper] Failed to process:', error);
|
|
2257
|
+
this.config.onError?.(error);
|
|
2258
|
+
}
|
|
2259
|
+
}
|
|
2260
|
+
async recalculate() {
|
|
2261
|
+
console.log('[IframeHelper] Recalculating...');
|
|
2262
|
+
await this.process();
|
|
2263
|
+
}
|
|
2264
|
+
updateConfig(config) {
|
|
2265
|
+
this.config = { ...this.config, ...config };
|
|
2266
|
+
if (this.replacer) {
|
|
2267
|
+
this.replacer.updateConfig(config);
|
|
2268
|
+
}
|
|
2269
|
+
}
|
|
2270
|
+
enableNavigationBlocking() {
|
|
2271
|
+
this.navigationBlocker?.enable();
|
|
2272
|
+
}
|
|
2273
|
+
enableNavigationBlockingMessage() {
|
|
2274
|
+
this.navigationBlocker?.enableMessage();
|
|
2275
|
+
}
|
|
2276
|
+
disableNavigationBlocking() {
|
|
2277
|
+
this.navigationBlocker?.disable();
|
|
2278
|
+
}
|
|
2279
|
+
disableNavigationBlockingMessage() {
|
|
2280
|
+
this.navigationBlocker?.disableMessage();
|
|
2281
|
+
}
|
|
2282
|
+
destroy() {
|
|
2283
|
+
this.replacer = null;
|
|
2284
|
+
this.navigationBlocker?.destroy();
|
|
2285
|
+
this.navigationBlocker = null;
|
|
2286
|
+
console.log('[IframeHelper] Destroyed');
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
|
|
2290
|
+
function initIframeHelperFixer(config) {
|
|
2291
|
+
const fixer = new IframeHelperFixer(config);
|
|
2292
|
+
window.addEventListener('iframe-dimensions-applied', ((e) => {
|
|
2293
|
+
const ev = e;
|
|
2294
|
+
console.log('[IframeHelper] Iframe dimensions finalized:', ev.detail);
|
|
2295
|
+
}));
|
|
2296
|
+
window.addEventListener('iframe-navigation-blocked', ((e) => {
|
|
2297
|
+
const ev = e;
|
|
2298
|
+
console.warn('[IframeHelper] Iframe tried to navigate to:', ev.detail.url);
|
|
2299
|
+
}));
|
|
2300
|
+
window.addEventListener('iframe-form-submit', ((e) => {
|
|
2301
|
+
const ev = e;
|
|
2302
|
+
console.log('[IframeHelper] Iframe form submitted:', ev.detail.data);
|
|
2303
|
+
}));
|
|
2304
|
+
return fixer;
|
|
2305
|
+
}
|
|
2306
|
+
|
|
2307
|
+
function useAreaEditMode({ iframeRef, onCreateArea, enabled = false, }) {
|
|
2308
|
+
const [hoveredElement, setHoveredElement] = useState(null);
|
|
2309
|
+
const [isHovering, setIsHovering] = useState(false);
|
|
2310
|
+
const { isEditingMode } = useHeatmapAreaClick();
|
|
2311
|
+
const iframeDocument = iframeRef.current?.contentDocument;
|
|
2312
|
+
const isActive = enabled && isEditingMode;
|
|
2313
|
+
const handleMouseMove = useCallback((e) => {
|
|
2314
|
+
if (!isActive || !iframeDocument)
|
|
2315
|
+
return;
|
|
2316
|
+
const elements = getElementsAtPoint$1(iframeDocument, e.clientX, e.clientY, isElementSelectable);
|
|
2317
|
+
// Find first selectable element
|
|
2318
|
+
const selectableElement = elements.find((el, index, arr) => isElementSelectable(el, index, arr));
|
|
2319
|
+
if (selectableElement && selectableElement !== hoveredElement) {
|
|
2320
|
+
setHoveredElement(selectableElement);
|
|
2321
|
+
setIsHovering(true);
|
|
2322
|
+
}
|
|
2323
|
+
else if (!selectableElement && hoveredElement) {
|
|
2324
|
+
setHoveredElement(null);
|
|
2325
|
+
setIsHovering(false);
|
|
2326
|
+
}
|
|
2327
|
+
}, [isActive, iframeDocument, hoveredElement]);
|
|
2328
|
+
const handleClick = useCallback((e) => {
|
|
2329
|
+
if (!isActive || !hoveredElement)
|
|
2330
|
+
return;
|
|
2331
|
+
e.stopPropagation();
|
|
2332
|
+
e.preventDefault();
|
|
2333
|
+
if (onCreateArea) {
|
|
2334
|
+
onCreateArea(hoveredElement);
|
|
2335
|
+
}
|
|
2336
|
+
}, [isActive, hoveredElement]);
|
|
2337
|
+
const handleMouseLeave = useCallback(() => {
|
|
2338
|
+
setHoveredElement(null);
|
|
2339
|
+
setIsHovering(false);
|
|
2340
|
+
}, []);
|
|
2341
|
+
useEffect(() => {
|
|
2342
|
+
if (!isActive || !iframeDocument) {
|
|
2343
|
+
setHoveredElement(null);
|
|
2344
|
+
setIsHovering(false);
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
// Throttle mouse move
|
|
2348
|
+
let rafId = null;
|
|
2349
|
+
const throttledMouseMove = (e) => {
|
|
2350
|
+
if (rafId)
|
|
2351
|
+
return;
|
|
2352
|
+
rafId = requestAnimationFrame(() => {
|
|
2353
|
+
handleMouseMove(e);
|
|
2354
|
+
rafId = null;
|
|
2355
|
+
});
|
|
2356
|
+
};
|
|
2357
|
+
iframeDocument.addEventListener('mousemove', throttledMouseMove);
|
|
2358
|
+
iframeDocument.addEventListener('click', handleClick);
|
|
2359
|
+
iframeDocument.addEventListener('mouseleave', handleMouseLeave);
|
|
2360
|
+
iframeDocument.addEventListener('scroll', handleMouseLeave);
|
|
2361
|
+
return () => {
|
|
2362
|
+
if (rafId) {
|
|
2363
|
+
cancelAnimationFrame(rafId);
|
|
2364
|
+
}
|
|
2365
|
+
iframeDocument.removeEventListener('mousemove', throttledMouseMove);
|
|
2366
|
+
iframeDocument.removeEventListener('click', handleClick);
|
|
2367
|
+
iframeDocument.removeEventListener('mouseleave', handleMouseLeave);
|
|
2368
|
+
iframeDocument.removeEventListener('scroll', handleMouseLeave);
|
|
2369
|
+
};
|
|
2370
|
+
}, [isActive, iframeDocument]);
|
|
2371
|
+
return {
|
|
2372
|
+
hoveredElement,
|
|
2373
|
+
isHovering,
|
|
2374
|
+
};
|
|
2375
|
+
}
|
|
2376
|
+
|
|
2377
|
+
function useAreaScrollSync(options) {
|
|
2378
|
+
const { areas, iframeRef, enabled = true } = options;
|
|
2379
|
+
const iframeDocument = iframeRef.current?.contentDocument;
|
|
2380
|
+
useEffect(() => {
|
|
2381
|
+
if (!enabled || !iframeDocument || areas.length === 0) {
|
|
2382
|
+
return;
|
|
2383
|
+
}
|
|
2384
|
+
let rafId = null;
|
|
2385
|
+
let isUpdating = false;
|
|
2386
|
+
const updateAreaPositions = () => {
|
|
2387
|
+
if (isUpdating)
|
|
2388
|
+
return;
|
|
2389
|
+
isUpdating = true;
|
|
2390
|
+
rafId = requestAnimationFrame(() => {
|
|
2391
|
+
areas.forEach((area) => {
|
|
2392
|
+
if (!area.element || !area.rect)
|
|
2393
|
+
return;
|
|
2394
|
+
try {
|
|
2395
|
+
const newRect = getElementRect(area.element);
|
|
2396
|
+
area.rect.update(newRect);
|
|
2397
|
+
}
|
|
2398
|
+
catch (error) {
|
|
2399
|
+
console.warn('[useAreaScrollSync] Failed to update area rect:', error);
|
|
2400
|
+
}
|
|
2401
|
+
});
|
|
2402
|
+
isUpdating = false;
|
|
2403
|
+
rafId = null;
|
|
2404
|
+
});
|
|
2405
|
+
};
|
|
2406
|
+
iframeDocument.addEventListener('scroll', updateAreaPositions, { passive: true });
|
|
2407
|
+
const iframeWindow = iframeDocument.defaultView;
|
|
2408
|
+
if (iframeWindow) {
|
|
2409
|
+
iframeWindow.addEventListener('resize', updateAreaPositions, { passive: true });
|
|
2410
|
+
}
|
|
2411
|
+
return () => {
|
|
2412
|
+
if (rafId !== null) {
|
|
2413
|
+
cancelAnimationFrame(rafId);
|
|
2414
|
+
}
|
|
2415
|
+
iframeDocument.removeEventListener('scroll', updateAreaPositions);
|
|
2416
|
+
if (iframeWindow) {
|
|
2417
|
+
iframeWindow.removeEventListener('resize', updateAreaPositions);
|
|
2418
|
+
}
|
|
2419
|
+
};
|
|
2420
|
+
}, [areas, iframeDocument, enabled]);
|
|
2421
|
+
}
|
|
2422
|
+
|
|
2423
|
+
const useClickmap = () => {
|
|
2424
|
+
const { vizRef } = useHeatmapViz();
|
|
2425
|
+
const { clickmap } = useHeatmapData();
|
|
2426
|
+
const start = useCallback(() => {
|
|
2427
|
+
if (!vizRef || !clickmap || clickmap.length === 0)
|
|
2428
|
+
return;
|
|
2429
|
+
try {
|
|
2430
|
+
vizRef?.clearmap?.();
|
|
2431
|
+
vizRef?.clickmap?.(clickmap);
|
|
2432
|
+
}
|
|
2433
|
+
catch (error) {
|
|
2434
|
+
console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
|
|
2435
|
+
}
|
|
2436
|
+
}, [vizRef, clickmap]);
|
|
2437
|
+
return { start };
|
|
2438
|
+
};
|
|
2439
|
+
|
|
2440
|
+
const useScrollmap = () => {
|
|
2441
|
+
const { vizRef } = useHeatmapViz();
|
|
2442
|
+
const { scrollmap } = useHeatmapData();
|
|
2443
|
+
const start = useCallback(() => {
|
|
2444
|
+
// if (isInitialized) return;
|
|
2445
|
+
if (!vizRef || !scrollmap || scrollmap.length === 0)
|
|
2446
|
+
return;
|
|
2447
|
+
try {
|
|
2448
|
+
vizRef?.clearmap?.();
|
|
2449
|
+
vizRef?.scrollmap?.(scrollmap);
|
|
2450
|
+
// setIsInitialized(true);
|
|
2451
|
+
}
|
|
2452
|
+
catch (error) {
|
|
2453
|
+
console.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
|
|
1688
2454
|
}
|
|
1689
|
-
}
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
}
|
|
1693
|
-
enableNavigationBlockingMessage() {
|
|
1694
|
-
this.navigationBlocker?.enableMessage();
|
|
1695
|
-
}
|
|
1696
|
-
disableNavigationBlocking() {
|
|
1697
|
-
this.navigationBlocker?.disable();
|
|
1698
|
-
}
|
|
1699
|
-
disableNavigationBlockingMessage() {
|
|
1700
|
-
this.navigationBlocker?.disableMessage();
|
|
1701
|
-
}
|
|
1702
|
-
destroy() {
|
|
1703
|
-
this.replacer = null;
|
|
1704
|
-
this.navigationBlocker?.destroy();
|
|
1705
|
-
this.navigationBlocker = null;
|
|
1706
|
-
console.log('[IframeHelper] Destroyed');
|
|
1707
|
-
}
|
|
1708
|
-
}
|
|
2455
|
+
}, [vizRef, scrollmap]);
|
|
2456
|
+
return { start };
|
|
2457
|
+
};
|
|
1709
2458
|
|
|
1710
|
-
|
|
1711
|
-
const
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
})
|
|
1724
|
-
|
|
1725
|
-
}
|
|
2459
|
+
const useHeatmapCanvas = () => {
|
|
2460
|
+
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
2461
|
+
const { start: startClickmap } = useClickmap();
|
|
2462
|
+
const { start: startScrollmap } = useScrollmap();
|
|
2463
|
+
useEffect(() => {
|
|
2464
|
+
switch (heatmapType) {
|
|
2465
|
+
case IHeatmapType.Click:
|
|
2466
|
+
startClickmap();
|
|
2467
|
+
break;
|
|
2468
|
+
case IHeatmapType.Scroll:
|
|
2469
|
+
startScrollmap();
|
|
2470
|
+
break;
|
|
2471
|
+
}
|
|
2472
|
+
}, [heatmapType, startClickmap, startScrollmap]);
|
|
2473
|
+
};
|
|
1726
2474
|
|
|
1727
2475
|
const scrollToElementIfNeeded = (visualRef, rect, scale) => {
|
|
1728
2476
|
if (!visualRef.current)
|
|
@@ -1742,7 +2490,7 @@ const scrollToElementIfNeeded = (visualRef, rect, scale) => {
|
|
|
1742
2490
|
});
|
|
1743
2491
|
};
|
|
1744
2492
|
const useClickedElement = ({ visualRef, getRect }) => {
|
|
1745
|
-
const { selectedElement, shouldShowCallout, setShouldShowCallout } =
|
|
2493
|
+
const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapClick();
|
|
1746
2494
|
const { widthScale } = useHeatmapViz();
|
|
1747
2495
|
const { dataInfo } = useHeatmapData();
|
|
1748
2496
|
const [clickedElement, setClickedElement] = useState(null);
|
|
@@ -1785,7 +2533,7 @@ const useClickedElement = ({ visualRef, getRect }) => {
|
|
|
1785
2533
|
};
|
|
1786
2534
|
|
|
1787
2535
|
const useElementCalloutVisible = ({ visualRef, getRect }) => {
|
|
1788
|
-
const { selectedElement, setShouldShowCallout } =
|
|
2536
|
+
const { selectedElement, setShouldShowCallout } = useHeatmapClick();
|
|
1789
2537
|
const { widthScale } = useHeatmapViz();
|
|
1790
2538
|
const { dataInfo } = useHeatmapData();
|
|
1791
2539
|
useEffect(() => {
|
|
@@ -1816,7 +2564,7 @@ const useElementCalloutVisible = ({ visualRef, getRect }) => {
|
|
|
1816
2564
|
};
|
|
1817
2565
|
|
|
1818
2566
|
const useHeatmapEffects = ({ isVisible }) => {
|
|
1819
|
-
|
|
2567
|
+
useHeatmapClick();
|
|
1820
2568
|
const resetAll = () => {
|
|
1821
2569
|
// setShouldShowCallout(false);
|
|
1822
2570
|
};
|
|
@@ -1986,7 +2734,7 @@ function HeatmapComponent() {
|
|
|
1986
2734
|
*/
|
|
1987
2735
|
|
|
1988
2736
|
const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
1989
|
-
const { hoveredElement, setHoveredElement, setSelectedElement } =
|
|
2737
|
+
const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapClick();
|
|
1990
2738
|
const { widthScale } = useHeatmapViz();
|
|
1991
2739
|
const { dataInfo } = useHeatmapData();
|
|
1992
2740
|
const reset = useCallback(() => {
|
|
@@ -2018,11 +2766,12 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
|
2018
2766
|
return;
|
|
2019
2767
|
}
|
|
2020
2768
|
const hash = getElementHash(targetElement);
|
|
2021
|
-
if (
|
|
2769
|
+
if (hash)
|
|
2022
2770
|
return hash;
|
|
2023
2771
|
reset();
|
|
2024
2772
|
return;
|
|
2025
|
-
}, [dataInfo, iframeRef,
|
|
2773
|
+
}, [dataInfo, iframeRef, widthScale, reset]);
|
|
2774
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2026
2775
|
const handleMouseMove = useCallback(debounce((event) => {
|
|
2027
2776
|
if (!dataInfo) {
|
|
2028
2777
|
reset();
|
|
@@ -2046,6 +2795,7 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
|
2046
2795
|
if (!hoveredElement?.hash)
|
|
2047
2796
|
return;
|
|
2048
2797
|
setSelectedElement(hoveredElement.hash);
|
|
2798
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
2049
2799
|
}, [hoveredElement?.hash]);
|
|
2050
2800
|
return {
|
|
2051
2801
|
hoveredElement,
|
|
@@ -2071,7 +2821,6 @@ const findTargetElement = (doc, x, y, heatmapInfo) => {
|
|
|
2071
2821
|
const element = elementsAtPoint[i];
|
|
2072
2822
|
const elementHash = element.getAttribute(HEATMAP_ELEMENT_ATTRIBUTE);
|
|
2073
2823
|
if (elementHash && heatmapInfo.elementMapInfo?.[elementHash]) {
|
|
2074
|
-
heatmapInfo.elementMapInfo[elementHash];
|
|
2075
2824
|
const boundingBox = getBoundingBox(element);
|
|
2076
2825
|
if (boundingBox) {
|
|
2077
2826
|
dataElement = element;
|
|
@@ -2079,7 +2828,7 @@ const findTargetElement = (doc, x, y, heatmapInfo) => {
|
|
|
2079
2828
|
}
|
|
2080
2829
|
}
|
|
2081
2830
|
}
|
|
2082
|
-
if (
|
|
2831
|
+
if (dataElement) {
|
|
2083
2832
|
return dataElement;
|
|
2084
2833
|
}
|
|
2085
2834
|
let targetElement = getElementAtPoint(doc, x, y);
|
|
@@ -2209,8 +2958,7 @@ function reset(iframe, rect, onSuccess) {
|
|
|
2209
2958
|
|
|
2210
2959
|
const useHeatmapRender = () => {
|
|
2211
2960
|
const { data } = useHeatmapData();
|
|
2212
|
-
const { vizRef, setVizRef, setIsRenderViz, setIframeHeight
|
|
2213
|
-
console.log(`🚀 🐥 ~ useHeatmapRender ~ wrapperHeight:`, wrapperHeight);
|
|
2961
|
+
const { vizRef, setVizRef, setIsRenderViz, setIframeHeight } = useHeatmapViz();
|
|
2214
2962
|
const iframeRef = useRef(null);
|
|
2215
2963
|
const renderHeatmap = useCallback(async (payloads) => {
|
|
2216
2964
|
if (!payloads || payloads.length === 0)
|
|
@@ -2733,7 +3481,7 @@ const useWrapperRefHeight = (props) => {
|
|
|
2733
3481
|
return {};
|
|
2734
3482
|
};
|
|
2735
3483
|
|
|
2736
|
-
const useZonePositions = (
|
|
3484
|
+
const useZonePositions = (_options) => {
|
|
2737
3485
|
const { iframeHeight } = useHeatmapViz();
|
|
2738
3486
|
const getZonePosition = useCallback((zone) => {
|
|
2739
3487
|
if (!iframeHeight) {
|
|
@@ -3188,7 +3936,7 @@ function withPerformanceTracking(Component, options = {}) {
|
|
|
3188
3936
|
const name = componentName || Component.displayName || Component.name || 'Unknown';
|
|
3189
3937
|
const viewId = viewIdProp in props ? props[viewIdProp] : undefined;
|
|
3190
3938
|
if (trackProps) {
|
|
3191
|
-
useWhyDidYouUpdate(name, props, viewId);
|
|
3939
|
+
useWhyDidYouUpdate(name, props, viewId); // eslint-disable-line react-hooks/rules-of-hooks
|
|
3192
3940
|
}
|
|
3193
3941
|
return jsx(Component, { ...props });
|
|
3194
3942
|
};
|
|
@@ -3261,16 +4009,10 @@ function trackStoreAction(storeName, action, viewId, metadata) {
|
|
|
3261
4009
|
performanceLogger.log(metric);
|
|
3262
4010
|
}
|
|
3263
4011
|
|
|
3264
|
-
/**
|
|
3265
|
-
* Get performance report as JSON string
|
|
3266
|
-
*/
|
|
3267
4012
|
function getPerformanceReportJSON() {
|
|
3268
4013
|
const report = performanceLogger.generateReport();
|
|
3269
4014
|
return JSON.stringify(report, null, 2);
|
|
3270
4015
|
}
|
|
3271
|
-
/**
|
|
3272
|
-
* Download performance report as JSON file
|
|
3273
|
-
*/
|
|
3274
4016
|
function downloadPerformanceReport(filename = 'heatmap-performance-report.json') {
|
|
3275
4017
|
const report = performanceLogger.generateReport();
|
|
3276
4018
|
const blob = new Blob([JSON.stringify(report, null, 2)], { type: 'application/json' });
|
|
@@ -3281,9 +4023,6 @@ function downloadPerformanceReport(filename = 'heatmap-performance-report.json')
|
|
|
3281
4023
|
link.click();
|
|
3282
4024
|
URL.revokeObjectURL(url);
|
|
3283
4025
|
}
|
|
3284
|
-
/**
|
|
3285
|
-
* Send performance report to external endpoint
|
|
3286
|
-
*/
|
|
3287
4026
|
async function sendPerformanceReport(endpoint) {
|
|
3288
4027
|
const report = performanceLogger.generateReport();
|
|
3289
4028
|
try {
|
|
@@ -3303,9 +4042,6 @@ async function sendPerformanceReport(endpoint) {
|
|
|
3303
4042
|
throw error;
|
|
3304
4043
|
}
|
|
3305
4044
|
}
|
|
3306
|
-
/**
|
|
3307
|
-
* Print performance summary to console
|
|
3308
|
-
*/
|
|
3309
4045
|
function printPerformanceSummary() {
|
|
3310
4046
|
const report = performanceLogger.generateReport();
|
|
3311
4047
|
console.group('📊 Performance Summary');
|
|
@@ -3331,9 +4067,6 @@ function printPerformanceSummary() {
|
|
|
3331
4067
|
}
|
|
3332
4068
|
console.groupEnd();
|
|
3333
4069
|
}
|
|
3334
|
-
/**
|
|
3335
|
-
* Get performance metrics filtered by viewId
|
|
3336
|
-
*/
|
|
3337
4070
|
function getMetricsByViewId(viewId) {
|
|
3338
4071
|
const allMetrics = performanceLogger.getMetrics();
|
|
3339
4072
|
const filteredMetrics = allMetrics.filter((m) => {
|
|
@@ -3349,17 +4082,28 @@ function getMetricsByViewId(viewId) {
|
|
|
3349
4082
|
totalHookCalls: filteredMetrics.filter((m) => m.type === 'hook').length,
|
|
3350
4083
|
totalStoreUpdates: filteredMetrics.filter((m) => m.type === 'store').length,
|
|
3351
4084
|
averageRenderTime: 0, // Recalculate if needed
|
|
3352
|
-
viewMetrics: {
|
|
4085
|
+
viewMetrics: {
|
|
4086
|
+
[viewId]: report.summary.viewMetrics[viewId] || {
|
|
4087
|
+
renders: 0,
|
|
4088
|
+
hookCalls: 0,
|
|
4089
|
+
storeUpdates: 0,
|
|
4090
|
+
},
|
|
4091
|
+
},
|
|
3353
4092
|
},
|
|
3354
4093
|
};
|
|
3355
4094
|
}
|
|
3356
|
-
/**
|
|
3357
|
-
* Compare performance between two viewIds
|
|
3358
|
-
*/
|
|
3359
4095
|
function compareViewPerformance(viewId1, viewId2) {
|
|
3360
4096
|
const report = performanceLogger.generateReport();
|
|
3361
|
-
const view1Metrics = report.summary.viewMetrics[viewId1] || {
|
|
3362
|
-
|
|
4097
|
+
const view1Metrics = report.summary.viewMetrics[viewId1] || {
|
|
4098
|
+
renders: 0,
|
|
4099
|
+
hookCalls: 0,
|
|
4100
|
+
storeUpdates: 0,
|
|
4101
|
+
};
|
|
4102
|
+
const view2Metrics = report.summary.viewMetrics[viewId2] || {
|
|
4103
|
+
renders: 0,
|
|
4104
|
+
hookCalls: 0,
|
|
4105
|
+
storeUpdates: 0,
|
|
4106
|
+
};
|
|
3363
4107
|
return {
|
|
3364
4108
|
view1: view1Metrics,
|
|
3365
4109
|
view2: view2Metrics,
|
|
@@ -3382,7 +4126,7 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
|
|
|
3382
4126
|
const style = props.style || {};
|
|
3383
4127
|
const gap = props.gap || 0;
|
|
3384
4128
|
const height = props.height || 'auto';
|
|
3385
|
-
const isZIndexDefined = typeof props.zIndex !== undefined;
|
|
4129
|
+
const isZIndexDefined = typeof props.zIndex !== 'undefined';
|
|
3386
4130
|
const zIndex = props.zIndex;
|
|
3387
4131
|
const backgroundColor = props.backgroundColor || 'transparent';
|
|
3388
4132
|
const styleGap = useMemo(() => {
|
|
@@ -3413,19 +4157,7 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
|
|
|
3413
4157
|
};
|
|
3414
4158
|
return (jsx("div", { id: id, style: styleProps, ref: ref, children: children }));
|
|
3415
4159
|
});
|
|
3416
|
-
|
|
3417
|
-
const ContentTopBar = () => {
|
|
3418
|
-
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3419
|
-
useHeatmapConfigStore((state) => state.mode);
|
|
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
|
-
// }
|
|
3425
|
-
return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
|
|
3426
|
-
borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
3427
|
-
}, children: TopBar && jsx(TopBar, {}) }));
|
|
3428
|
-
};
|
|
4160
|
+
BoxStack.displayName = 'BoxStack';
|
|
3429
4161
|
|
|
3430
4162
|
const ContentMetricBar = () => {
|
|
3431
4163
|
const controls = useHeatmapControlStore((state) => state.controls);
|
|
@@ -3434,22 +4166,11 @@ const ContentMetricBar = () => {
|
|
|
3434
4166
|
borderBottom,
|
|
3435
4167
|
}, children: controls.MetricBar ?? null }));
|
|
3436
4168
|
};
|
|
3437
|
-
|
|
3438
|
-
const ContentToolbar = () => {
|
|
3439
|
-
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3440
|
-
return (jsx("div", { id: "gx-hm-content-toolbar", style: {
|
|
3441
|
-
position: 'absolute',
|
|
3442
|
-
bottom: 0,
|
|
3443
|
-
left: '8px',
|
|
3444
|
-
right: '24px',
|
|
3445
|
-
padding: '8px',
|
|
3446
|
-
paddingBlock: '16px',
|
|
3447
|
-
}, children: controls.Toolbar ?? null }));
|
|
3448
|
-
};
|
|
4169
|
+
ContentMetricBar.displayName = 'ContentMetricBar';
|
|
3449
4170
|
|
|
3450
4171
|
const ContentSidebar = () => {
|
|
3451
4172
|
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3452
|
-
const { state } =
|
|
4173
|
+
const { state } = useHeatmapClick();
|
|
3453
4174
|
const isHideSidebar = state.hideSidebar;
|
|
3454
4175
|
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
3455
4176
|
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
@@ -3483,7 +4204,7 @@ const PopoverSidebar = () => {
|
|
|
3483
4204
|
const CompSidebarActivator = useHeatmapControlStore((state) => state.controls.SidebarActivator);
|
|
3484
4205
|
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
3485
4206
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
3486
|
-
const { state } =
|
|
4207
|
+
const { state } = useHeatmapClick();
|
|
3487
4208
|
const isCompareMode = mode === 'compare';
|
|
3488
4209
|
const isHideSidebar = state.hideSidebar;
|
|
3489
4210
|
const stylePopover = {
|
|
@@ -3508,9 +4229,21 @@ const PopoverSidebar = () => {
|
|
|
3508
4229
|
}, children: jsx(CompSidebar, { closeAction: { onClick: () => setIsPopoverOpen(false) } }) }) }))] }));
|
|
3509
4230
|
};
|
|
3510
4231
|
|
|
4232
|
+
const ContentToolbar = () => {
|
|
4233
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
4234
|
+
return (jsx("div", { id: "gx-hm-content-toolbar", style: {
|
|
4235
|
+
position: 'absolute',
|
|
4236
|
+
bottom: 0,
|
|
4237
|
+
left: '8px',
|
|
4238
|
+
right: '24px',
|
|
4239
|
+
padding: '8px',
|
|
4240
|
+
paddingBlock: '16px',
|
|
4241
|
+
}, children: controls.Toolbar ?? null }));
|
|
4242
|
+
};
|
|
4243
|
+
|
|
3511
4244
|
const VizContainer = ({ children, isActive = false }) => {
|
|
3512
4245
|
const wrapperRef = useRef(null);
|
|
3513
|
-
const viewId =
|
|
4246
|
+
const viewId = useViewIdContext();
|
|
3514
4247
|
useWrapperRefHeight({
|
|
3515
4248
|
isActive,
|
|
3516
4249
|
wrapperRef,
|
|
@@ -3520,80 +4253,350 @@ const VizContainer = ({ children, isActive = false }) => {
|
|
|
3520
4253
|
}, children: children }), jsx(PopoverSidebar, {})] }));
|
|
3521
4254
|
};
|
|
3522
4255
|
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
4256
|
+
/**
|
|
4257
|
+
* Controls for area click feature - toggle edit mode, clear areas, etc.
|
|
4258
|
+
*/
|
|
4259
|
+
const AreaControls = ({ className, style }) => {
|
|
4260
|
+
const { isEditingMode, setIsEditingMode, areas, clearAreas } = useHeatmapAreaClick();
|
|
4261
|
+
return (jsxs("div", { className: className, style: {
|
|
4262
|
+
display: 'flex',
|
|
4263
|
+
gap: '8px',
|
|
4264
|
+
alignItems: 'center',
|
|
4265
|
+
...style,
|
|
4266
|
+
}, children: [jsx("button", { onClick: () => setIsEditingMode(!isEditingMode), style: {
|
|
4267
|
+
padding: '8px 16px',
|
|
4268
|
+
borderRadius: '4px',
|
|
4269
|
+
border: '1px solid #ccc',
|
|
4270
|
+
backgroundColor: isEditingMode ? '#0078D4' : 'white',
|
|
4271
|
+
color: isEditingMode ? 'white' : '#161514',
|
|
4272
|
+
cursor: 'pointer',
|
|
4273
|
+
fontWeight: 500,
|
|
4274
|
+
transition: 'all 0.2s',
|
|
4275
|
+
}, children: isEditingMode ? 'Exit Edit Mode' : 'Edit Areas' }), areas.length > 0 && (jsxs(Fragment, { children: [jsxs("span", { style: { color: '#605E5C', fontSize: '14px' }, children: [areas.length, " area", areas.length !== 1 ? 's' : ''] }), jsx("button", { onClick: () => {
|
|
4276
|
+
if (confirm(`Clear all ${areas.length} areas?`)) {
|
|
4277
|
+
clearAreas();
|
|
4278
|
+
}
|
|
4279
|
+
}, style: {
|
|
4280
|
+
padding: '8px 16px',
|
|
4281
|
+
borderRadius: '4px',
|
|
4282
|
+
border: '1px solid #ccc',
|
|
4283
|
+
backgroundColor: 'white',
|
|
4284
|
+
color: '#A4262C',
|
|
4285
|
+
cursor: 'pointer',
|
|
4286
|
+
fontWeight: 500,
|
|
4287
|
+
transition: 'all 0.2s',
|
|
4288
|
+
}, children: "Clear All" })] }))] }));
|
|
4289
|
+
};
|
|
4290
|
+
AreaControls.displayName = 'AreaControls';
|
|
4291
|
+
|
|
4292
|
+
const AreaEditHighlight = ({ element, shadowRoot, onClick, }) => {
|
|
4293
|
+
const [rect, setRect] = useState(null);
|
|
4294
|
+
const highlightRef = useRef(null);
|
|
4295
|
+
useEffect(() => {
|
|
4296
|
+
if (!element) {
|
|
4297
|
+
setRect(null);
|
|
3528
4298
|
return;
|
|
3529
|
-
try {
|
|
3530
|
-
vizRef?.clearmap?.();
|
|
3531
|
-
vizRef?.clickmap?.(clickmap);
|
|
3532
4299
|
}
|
|
3533
|
-
|
|
3534
|
-
|
|
4300
|
+
// Calculate element position
|
|
4301
|
+
const elementRect = getElementRect(element);
|
|
4302
|
+
setRect(elementRect);
|
|
4303
|
+
}, [element, shadowRoot]);
|
|
4304
|
+
if (!rect) {
|
|
4305
|
+
return null;
|
|
4306
|
+
}
|
|
4307
|
+
const handleClick = (e) => {
|
|
4308
|
+
if (element && onClick) {
|
|
4309
|
+
e.stopPropagation();
|
|
4310
|
+
e.preventDefault();
|
|
4311
|
+
onClick(element);
|
|
3535
4312
|
}
|
|
3536
|
-
}
|
|
3537
|
-
return {
|
|
4313
|
+
};
|
|
4314
|
+
return (jsx("div", { ref: highlightRef, id: AREA_HOVER_ELEMENT_ID, [AREA_MAP_DIV_ATTRIBUTE]: '1', onClick: handleClick, style: {
|
|
4315
|
+
position: 'absolute',
|
|
4316
|
+
top: `${rect.absoluteTop}px`,
|
|
4317
|
+
left: `${rect.absoluteLeft}px`,
|
|
4318
|
+
width: `${rect.width}px`,
|
|
4319
|
+
height: `${rect.height}px`,
|
|
4320
|
+
zIndex: Number.MAX_SAFE_INTEGER,
|
|
4321
|
+
boxShadow: AREA_HOVER_BOX_SHADOW,
|
|
4322
|
+
backgroundColor: 'rgba(128, 128, 128, 0.4)',
|
|
4323
|
+
backgroundImage: 'repeating-linear-gradient(135deg, transparent, transparent 35px, rgba(255,255,255,.5) 35px, rgba(255,255,255,.5) 70px)',
|
|
4324
|
+
pointerEvents: 'auto',
|
|
4325
|
+
cursor: 'pointer',
|
|
4326
|
+
boxSizing: 'border-box',
|
|
4327
|
+
} }));
|
|
3538
4328
|
};
|
|
4329
|
+
AreaEditHighlight.displayName = 'AreaEditHighlight';
|
|
3539
4330
|
|
|
3540
|
-
const
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
4331
|
+
const AreaLabel = ({ clickDist, totalClicks, kind }) => {
|
|
4332
|
+
if (kind === 'money') {
|
|
4333
|
+
return null;
|
|
4334
|
+
}
|
|
4335
|
+
return (jsxs("div", { style: {
|
|
4336
|
+
color: '#161514',
|
|
4337
|
+
backgroundColor: 'rgba(255, 255, 255, 0.86)',
|
|
4338
|
+
display: 'flex',
|
|
4339
|
+
flexDirection: 'column',
|
|
4340
|
+
alignItems: 'center',
|
|
4341
|
+
padding: '8px',
|
|
4342
|
+
borderRadius: '4px',
|
|
4343
|
+
fontSize: '16px',
|
|
4344
|
+
lineHeight: '20px',
|
|
4345
|
+
minWidth: '56px',
|
|
4346
|
+
fontWeight: 600,
|
|
4347
|
+
fontFamily: '"Segoe UI", "Segoe UI Web (West European)", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, "Helvetica Neue", sans-serif',
|
|
4348
|
+
pointerEvents: 'none',
|
|
4349
|
+
}, children: [jsxs("span", { children: [clickDist.toFixed(2), "%"] }), jsxs("span", { style: { fontSize: '12px', fontWeight: 400, opacity: 0.8 }, children: [totalClicks, " clicks"] })] }));
|
|
4350
|
+
};
|
|
4351
|
+
AreaLabel.displayName = 'AreaLabel';
|
|
4352
|
+
|
|
4353
|
+
const AreaOverlay = ({ area, onClick, onMouseEnter, onMouseLeave, isSelected, isHovered, }) => {
|
|
4354
|
+
const [rect, setRect] = useState(area.rect.value);
|
|
4355
|
+
useEffect(() => {
|
|
4356
|
+
const handleRectChange = (newRect) => {
|
|
4357
|
+
if (newRect) {
|
|
4358
|
+
setRect(newRect);
|
|
4359
|
+
}
|
|
4360
|
+
};
|
|
4361
|
+
area.rect.observe(handleRectChange);
|
|
4362
|
+
return () => {
|
|
4363
|
+
area.rect.unobserve(handleRectChange);
|
|
4364
|
+
};
|
|
4365
|
+
}, [area.rect]);
|
|
4366
|
+
if (!rect)
|
|
4367
|
+
return null;
|
|
4368
|
+
const position = area.isFixed ? 'fixed' : 'absolute';
|
|
4369
|
+
const showLabel = !isRectTooSmallForLabel(rect);
|
|
4370
|
+
const backgroundColor = isHovered ? area.hoverColor : area.color;
|
|
4371
|
+
const boxShadow = isSelected
|
|
4372
|
+
? '0 0 0 3px #0078d4 inset'
|
|
4373
|
+
: isHovered
|
|
4374
|
+
? AREA_HOVER_BOX_SHADOW
|
|
4375
|
+
: '0 0 0 2px white inset';
|
|
4376
|
+
return (jsx("div", { id: `area-${area.id}`, "data-area-id": area.id, [AREA_MAP_DIV_ATTRIBUTE]: '1', onClick: () => onClick?.(area), onMouseEnter: () => onMouseEnter?.(area), onMouseLeave: () => onMouseLeave?.(area), style: {
|
|
4377
|
+
position,
|
|
4378
|
+
top: `${rect.top}px`,
|
|
4379
|
+
left: `${rect.left}px`,
|
|
4380
|
+
width: `${rect.width}px`,
|
|
4381
|
+
height: `${rect.height}px`,
|
|
4382
|
+
backgroundColor,
|
|
4383
|
+
boxShadow,
|
|
4384
|
+
boxSizing: 'border-box',
|
|
4385
|
+
display: 'flex',
|
|
4386
|
+
alignItems: 'center',
|
|
4387
|
+
justifyContent: 'center',
|
|
4388
|
+
cursor: 'pointer',
|
|
4389
|
+
transition: 'background-color 0.2s, box-shadow 0.2s',
|
|
4390
|
+
pointerEvents: 'auto',
|
|
4391
|
+
}, children: showLabel && (jsx(AreaLabel, { clickDist: area.clickDist, totalClicks: area.totalclicks, kind: area.kind })) }));
|
|
4392
|
+
};
|
|
4393
|
+
|
|
4394
|
+
function useAreaRenderer(options) {
|
|
4395
|
+
const { iframeRef, shadowRoot: customShadowRoot, onAreaCreated, onAreaClick } = options;
|
|
4396
|
+
const iframeDocument = iframeRef.current?.contentDocument;
|
|
4397
|
+
// Get heatmap data for building areas
|
|
4398
|
+
const { dataInfo } = useHeatmapData();
|
|
4399
|
+
const { areas, selectedArea, hoveredArea, isEditingMode, setSelectedArea, setHoveredArea, addArea, } = useHeatmapAreaClick();
|
|
4400
|
+
const [shadowContainer, setShadowContainer] = useState(null);
|
|
4401
|
+
const [isReady, setIsReady] = useState(false);
|
|
4402
|
+
const containerRef = useRef(null);
|
|
4403
|
+
useEffect(() => {
|
|
4404
|
+
if (!iframeDocument) {
|
|
4405
|
+
setIsReady(false);
|
|
4406
|
+
return;
|
|
4407
|
+
}
|
|
4408
|
+
let container = iframeDocument.querySelector(`[${AREA_MAP_DIV_ATTRIBUTE}]`);
|
|
4409
|
+
if (!container) {
|
|
4410
|
+
container = iframeDocument.createElement('div');
|
|
4411
|
+
container.setAttribute(AREA_MAP_DIV_ATTRIBUTE, 'true');
|
|
4412
|
+
container.style.cssText = `
|
|
4413
|
+
position: absolute;
|
|
4414
|
+
top: 0;
|
|
4415
|
+
left: 0;
|
|
4416
|
+
width: 100%;
|
|
4417
|
+
height: 100%;
|
|
4418
|
+
pointer-events: none;
|
|
4419
|
+
z-index: 999999;
|
|
4420
|
+
`;
|
|
4421
|
+
// Append to custom shadow root or body
|
|
4422
|
+
const targetRoot = customShadowRoot || iframeDocument.body;
|
|
4423
|
+
if (targetRoot) {
|
|
4424
|
+
targetRoot.appendChild(container);
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
// Create shadow root if needed
|
|
4428
|
+
let shadowRoot;
|
|
4429
|
+
if (!container.shadowRoot) {
|
|
4430
|
+
shadowRoot = getOrCreateShadowRoot(container);
|
|
4431
|
+
}
|
|
4432
|
+
else {
|
|
4433
|
+
shadowRoot = container.shadowRoot;
|
|
4434
|
+
}
|
|
4435
|
+
// Create inner container for React portal
|
|
4436
|
+
let innerContainer = shadowRoot.querySelector('.heatmap-area-container');
|
|
4437
|
+
if (!innerContainer) {
|
|
4438
|
+
innerContainer = iframeDocument.createElement('div');
|
|
4439
|
+
innerContainer.className = 'heatmap-area-container';
|
|
4440
|
+
innerContainer.style.cssText = `
|
|
4441
|
+
position: relative;
|
|
4442
|
+
width: 100%;
|
|
4443
|
+
height: 100%;
|
|
4444
|
+
`;
|
|
4445
|
+
shadowRoot.appendChild(innerContainer);
|
|
4446
|
+
}
|
|
4447
|
+
containerRef.current = innerContainer;
|
|
4448
|
+
setShadowContainer(innerContainer);
|
|
4449
|
+
setIsReady(true);
|
|
4450
|
+
return () => {
|
|
4451
|
+
if (container && container.parentNode) {
|
|
4452
|
+
container.parentNode.removeChild(container);
|
|
4453
|
+
}
|
|
4454
|
+
containerRef.current = null;
|
|
4455
|
+
setShadowContainer(null);
|
|
4456
|
+
setIsReady(false);
|
|
4457
|
+
};
|
|
4458
|
+
}, [iframeDocument, customShadowRoot]);
|
|
4459
|
+
const handleCreateAreaFromElement = useCallback((element) => {
|
|
4460
|
+
if (!dataInfo?.elementMapInfo || !dataInfo?.totalClicks) {
|
|
4461
|
+
console.warn('[useAreaRenderer] Cannot create area: missing heatmap data');
|
|
4462
|
+
return;
|
|
4463
|
+
}
|
|
4464
|
+
const hash = getElementHash(element);
|
|
4465
|
+
if (!hash) {
|
|
4466
|
+
console.warn('[useAreaRenderer] Cannot create area: missing hash');
|
|
4467
|
+
return;
|
|
4468
|
+
}
|
|
4469
|
+
const alreadyExists = areas.some((area) => area.hash === hash);
|
|
4470
|
+
if (alreadyExists) {
|
|
4471
|
+
console.warn(`[useAreaRenderer] Area already exists for element: ${hash}`);
|
|
3546
4472
|
return;
|
|
4473
|
+
}
|
|
3547
4474
|
try {
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
4475
|
+
const area = buildAreaNode(element, hash, dataInfo.elementMapInfo, dataInfo.totalClicks, customShadowRoot);
|
|
4476
|
+
addArea(area);
|
|
4477
|
+
if (onAreaCreated) {
|
|
4478
|
+
onAreaCreated(area);
|
|
4479
|
+
}
|
|
4480
|
+
console.log('[useAreaRenderer] Area created:', {
|
|
4481
|
+
hash,
|
|
4482
|
+
selector: area.selector,
|
|
4483
|
+
clicks: area.totalclicks,
|
|
4484
|
+
clickDist: area.clickDist,
|
|
4485
|
+
});
|
|
3551
4486
|
}
|
|
3552
4487
|
catch (error) {
|
|
3553
|
-
console.error(
|
|
4488
|
+
console.error('[useAreaRenderer] Failed to create area:', error);
|
|
3554
4489
|
}
|
|
3555
|
-
}, [
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
4490
|
+
}, [dataInfo, areas, customShadowRoot, addArea]);
|
|
4491
|
+
const { hoveredElement } = useAreaEditMode({
|
|
4492
|
+
iframeRef,
|
|
4493
|
+
enabled: isEditingMode,
|
|
4494
|
+
onCreateArea: handleCreateAreaFromElement,
|
|
4495
|
+
});
|
|
4496
|
+
useAreaScrollSync({
|
|
4497
|
+
areas,
|
|
4498
|
+
iframeRef,
|
|
4499
|
+
enabled: isReady,
|
|
4500
|
+
});
|
|
4501
|
+
const handleAreaClick = (area) => {
|
|
4502
|
+
if (isEditingMode)
|
|
4503
|
+
return;
|
|
4504
|
+
setSelectedArea(selectedArea?.id === area.id ? null : area);
|
|
4505
|
+
if (onAreaClick) {
|
|
4506
|
+
onAreaClick(area);
|
|
3571
4507
|
}
|
|
3572
|
-
}
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
const
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
3589
|
-
|
|
3590
|
-
|
|
3591
|
-
return
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
4508
|
+
};
|
|
4509
|
+
const handleAreaMouseEnter = (area) => {
|
|
4510
|
+
if (isEditingMode)
|
|
4511
|
+
return;
|
|
4512
|
+
setHoveredArea(area);
|
|
4513
|
+
};
|
|
4514
|
+
const handleAreaMouseLeave = (area) => {
|
|
4515
|
+
if (isEditingMode)
|
|
4516
|
+
return;
|
|
4517
|
+
if (hoveredArea?.id === area.id) {
|
|
4518
|
+
setHoveredArea(null);
|
|
4519
|
+
}
|
|
4520
|
+
};
|
|
4521
|
+
const areasPortal = shadowContainer && isReady
|
|
4522
|
+
? createPortal(jsx(Fragment, { children: areas.map((area) => (jsx(AreaOverlay, { area: area, onClick: handleAreaClick, onMouseEnter: handleAreaMouseEnter, onMouseLeave: handleAreaMouseLeave, isSelected: selectedArea?.id === area.id, isHovered: hoveredArea?.id === area.id }, area.id))) }), shadowContainer)
|
|
4523
|
+
: null;
|
|
4524
|
+
const editHighlightPortal = shadowContainer && isReady && isEditingMode && hoveredElement
|
|
4525
|
+
? createPortal(jsx(AreaEditHighlight, { element: hoveredElement, shadowRoot: customShadowRoot, onClick: handleCreateAreaFromElement }), shadowContainer)
|
|
4526
|
+
: null;
|
|
4527
|
+
return {
|
|
4528
|
+
areasPortal: areasPortal,
|
|
4529
|
+
editHighlightPortal: editHighlightPortal,
|
|
4530
|
+
shadowContainer,
|
|
4531
|
+
isReady,
|
|
4532
|
+
};
|
|
4533
|
+
}
|
|
4534
|
+
|
|
4535
|
+
const VizAreaClick = ({ iframeRef, shadowRoot, autoCreateTopN = 10, enableOverlapResolution = true, renderControls, onAreaClick, }) => {
|
|
4536
|
+
const iframeDocument = iframeRef.current?.contentDocument;
|
|
4537
|
+
const { dataInfo } = useHeatmapData();
|
|
4538
|
+
const { areas, isEditingMode, setIsEditingMode, addArea, clearAreas, setAreas } = useHeatmapAreaClick();
|
|
4539
|
+
const { areasPortal, editHighlightPortal, isReady } = useAreaRenderer({
|
|
4540
|
+
iframeRef,
|
|
4541
|
+
shadowRoot,
|
|
4542
|
+
onAreaClick,
|
|
4543
|
+
});
|
|
4544
|
+
// Auto-create areas from top elements
|
|
4545
|
+
useEffect(() => {
|
|
4546
|
+
if (!dataInfo?.elementMapInfo || !dataInfo?.totalClicks)
|
|
4547
|
+
return;
|
|
4548
|
+
if (autoCreateTopN <= 0)
|
|
4549
|
+
return;
|
|
4550
|
+
if (areas.length > 0)
|
|
4551
|
+
return; // Already have areas
|
|
4552
|
+
// Get top elements by clicks
|
|
4553
|
+
const topElements = getTopElementsByClicks(dataInfo.elementMapInfo, autoCreateTopN);
|
|
4554
|
+
// Build area nodes
|
|
4555
|
+
const newAreas = [];
|
|
4556
|
+
topElements.forEach(({ hash, selector }) => {
|
|
4557
|
+
// Find element in DOM
|
|
4558
|
+
const element = iframeDocument?.querySelector(selector);
|
|
4559
|
+
if (!element || !(element instanceof HTMLElement))
|
|
4560
|
+
return;
|
|
4561
|
+
const area = buildAreaNode(element, hash, dataInfo.elementMapInfo, dataInfo.totalClicks);
|
|
4562
|
+
newAreas.push(area);
|
|
4563
|
+
});
|
|
4564
|
+
// Add all areas
|
|
4565
|
+
newAreas.forEach((area) => addArea(area));
|
|
4566
|
+
}, [dataInfo, autoCreateTopN, areas.length, iframeDocument, shadowRoot]);
|
|
4567
|
+
// Apply overlap resolution
|
|
4568
|
+
const visibleAreas = useMemo(() => {
|
|
4569
|
+
if (!enableOverlapResolution)
|
|
4570
|
+
return areas;
|
|
4571
|
+
if (!iframeDocument)
|
|
4572
|
+
return areas;
|
|
4573
|
+
return getVisibleAreas(areas, iframeDocument);
|
|
4574
|
+
}, [areas, iframeDocument]);
|
|
4575
|
+
// Update visible areas in store when resolution changes
|
|
4576
|
+
useEffect(() => {
|
|
4577
|
+
if (enableOverlapResolution && visibleAreas.length !== areas.length) {
|
|
4578
|
+
setAreas(visibleAreas);
|
|
4579
|
+
}
|
|
4580
|
+
}, [visibleAreas, areas.length]);
|
|
4581
|
+
const handleToggleEdit = useCallback(() => {
|
|
4582
|
+
setIsEditingMode(!isEditingMode);
|
|
4583
|
+
}, [isEditingMode]);
|
|
4584
|
+
const handleClearAll = useCallback(() => {
|
|
4585
|
+
if (window.confirm(`Clear all ${areas.length} areas?`)) {
|
|
4586
|
+
clearAreas();
|
|
4587
|
+
}
|
|
4588
|
+
}, [areas.length]);
|
|
4589
|
+
const controlsElement = renderControls ? (renderControls({
|
|
4590
|
+
isEditingMode,
|
|
4591
|
+
areasCount: areas.length,
|
|
4592
|
+
onToggleEdit: handleToggleEdit,
|
|
4593
|
+
onClearAll: handleClearAll,
|
|
4594
|
+
})) : (jsx(AreaControls, {}));
|
|
4595
|
+
if (!isReady)
|
|
4596
|
+
return null;
|
|
4597
|
+
return (jsxs(Fragment, { children: [areasPortal, editHighlightPortal, controlsElement] }));
|
|
3596
4598
|
};
|
|
4599
|
+
VizAreaClick.displayName = 'VizAreaClick';
|
|
3597
4600
|
|
|
3598
4601
|
const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
|
|
3599
4602
|
const style = calculateRankPosition(elementRect, widthScale);
|
|
@@ -3623,7 +4626,7 @@ const DEFAULT_POSITION = {
|
|
|
3623
4626
|
};
|
|
3624
4627
|
const ElementCallout = (props) => {
|
|
3625
4628
|
const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
|
|
3626
|
-
const viewId =
|
|
4629
|
+
const viewId = useViewIdContext();
|
|
3627
4630
|
const { element, target, visualRef, hozOffset, alignment = 'left' } = props;
|
|
3628
4631
|
const calloutRef = useRef(null);
|
|
3629
4632
|
const [position, setPosition] = useState(DEFAULT_POSITION);
|
|
@@ -3638,6 +4641,7 @@ const ElementCallout = (props) => {
|
|
|
3638
4641
|
setPosition,
|
|
3639
4642
|
hozOffset,
|
|
3640
4643
|
alignment,
|
|
4644
|
+
containerElm: visualRef?.current,
|
|
3641
4645
|
});
|
|
3642
4646
|
positionFn();
|
|
3643
4647
|
const handleUpdate = () => {
|
|
@@ -3706,7 +4710,7 @@ const ELEMENT_CALLOUT = {
|
|
|
3706
4710
|
alignment: 'left',
|
|
3707
4711
|
};
|
|
3708
4712
|
const HeatmapElements = (props) => {
|
|
3709
|
-
const viewId =
|
|
4713
|
+
const viewId = useViewIdContext();
|
|
3710
4714
|
const { iframeHeight } = useHeatmapViz();
|
|
3711
4715
|
const clickedElementId = getClickedElementId(viewId, props.isSecondary);
|
|
3712
4716
|
const hoveredElementId = getHoveredElementId(viewId, props.isSecondary);
|
|
@@ -3755,121 +4759,9 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
|
|
|
3755
4759
|
} }));
|
|
3756
4760
|
};
|
|
3757
4761
|
|
|
3758
|
-
const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
|
|
3759
|
-
const { dataInfo } = useHeatmapData();
|
|
3760
|
-
const { getZonePosition } = useZonePositions();
|
|
3761
|
-
const averageFold = dataInfo?.averageFold || 50;
|
|
3762
|
-
const position = getZonePosition({
|
|
3763
|
-
startY: averageFold,
|
|
3764
|
-
endY: averageFold,
|
|
3765
|
-
});
|
|
3766
|
-
if (!position)
|
|
3767
|
-
return null;
|
|
3768
|
-
return (jsx("div", { style: {
|
|
3769
|
-
position: 'absolute',
|
|
3770
|
-
top: `${position.top}px`,
|
|
3771
|
-
left: 0,
|
|
3772
|
-
width: '100%',
|
|
3773
|
-
height: '2px',
|
|
3774
|
-
backgroundColor: '#0078D4',
|
|
3775
|
-
pointerEvents: 'none',
|
|
3776
|
-
zIndex: 2,
|
|
3777
|
-
boxShadow: '0 0 4px rgba(0,120,212,0.5)',
|
|
3778
|
-
display: 'flex',
|
|
3779
|
-
alignItems: 'center',
|
|
3780
|
-
}, children: jsxs("div", { style: {
|
|
3781
|
-
position: 'absolute',
|
|
3782
|
-
padding: '8px',
|
|
3783
|
-
backgroundColor: 'rgba(0, 120, 212, 0.9)',
|
|
3784
|
-
color: 'white',
|
|
3785
|
-
fontSize: '16px',
|
|
3786
|
-
fontWeight: 600,
|
|
3787
|
-
borderRadius: '4px',
|
|
3788
|
-
whiteSpace: 'nowrap',
|
|
3789
|
-
left: '12px',
|
|
3790
|
-
minWidth: '120px',
|
|
3791
|
-
textAlign: 'center',
|
|
3792
|
-
}, children: ["Average fold - ", averageFold.toFixed(0), "%"] }) }));
|
|
3793
|
-
};
|
|
3794
|
-
|
|
3795
|
-
const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
|
|
3796
|
-
const scrollType = useHeatmapConfigStore((state) => state.scrollType);
|
|
3797
|
-
const { scrollmap } = useHeatmapData();
|
|
3798
|
-
const { getZonePosition } = useZonePositions();
|
|
3799
|
-
if (!scrollmap || scrollmap.length === 0)
|
|
3800
|
-
return null;
|
|
3801
|
-
const findScrollPositionForUserPercent = (targetPercent) => {
|
|
3802
|
-
for (let i = 0; i < scrollmap.length; i++) {
|
|
3803
|
-
if (scrollmap[i].percUsers <= targetPercent) {
|
|
3804
|
-
if (i > 0) {
|
|
3805
|
-
return scrollmap[i - 1].scrollReachY;
|
|
3806
|
-
}
|
|
3807
|
-
return scrollmap[i].scrollReachY;
|
|
3808
|
-
}
|
|
3809
|
-
}
|
|
3810
|
-
return scrollmap[scrollmap.length - 1]?.scrollReachY || null;
|
|
3811
|
-
};
|
|
3812
|
-
const boundaries = [
|
|
3813
|
-
{ percent: 75, label: '75%', color: '#10B981' },
|
|
3814
|
-
{ percent: 50, label: '50%', color: '#F59E0B' },
|
|
3815
|
-
{ percent: 25, label: '25%', color: '#EF4444' },
|
|
3816
|
-
{ percent: 5, label: '5%', color: '#8B5CF6' },
|
|
3817
|
-
];
|
|
3818
|
-
const isScrollDepth = scrollType === IScrollType.Depth;
|
|
3819
|
-
if (!isScrollDepth)
|
|
3820
|
-
return null;
|
|
3821
|
-
return (jsx(Fragment, { children: boundaries.map((boundary) => {
|
|
3822
|
-
const scrollY = findScrollPositionForUserPercent(boundary.percent);
|
|
3823
|
-
if (scrollY === null)
|
|
3824
|
-
return null;
|
|
3825
|
-
const position = getZonePosition({
|
|
3826
|
-
startY: scrollY,
|
|
3827
|
-
endY: scrollY,
|
|
3828
|
-
});
|
|
3829
|
-
if (!position)
|
|
3830
|
-
return null;
|
|
3831
|
-
return (jsx("div", { className: `marker-boundary-line-${boundary.percent}`, style: {
|
|
3832
|
-
position: 'absolute',
|
|
3833
|
-
top: `${position.top}px`,
|
|
3834
|
-
left: 0,
|
|
3835
|
-
transformOrigin: 'left center',
|
|
3836
|
-
width: '100%',
|
|
3837
|
-
height: '0px',
|
|
3838
|
-
// borderBottom: `2px dashed #323130`,
|
|
3839
|
-
borderBottom: `2px solid ${boundary.color}`,
|
|
3840
|
-
// background: 'repeating-linear-gradient(90deg, #323130, transparent 2px 3px)',
|
|
3841
|
-
zIndex: 1,
|
|
3842
|
-
display: 'flex',
|
|
3843
|
-
alignItems: 'center',
|
|
3844
|
-
}, children: jsx("div", { style: {
|
|
3845
|
-
position: 'absolute',
|
|
3846
|
-
padding: '8px',
|
|
3847
|
-
backgroundColor: boundary.color,
|
|
3848
|
-
color: 'white',
|
|
3849
|
-
fontSize: '16px',
|
|
3850
|
-
fontWeight: 600,
|
|
3851
|
-
borderRadius: '4px',
|
|
3852
|
-
whiteSpace: 'nowrap',
|
|
3853
|
-
left: '12px',
|
|
3854
|
-
minWidth: '120px',
|
|
3855
|
-
textAlign: 'center',
|
|
3856
|
-
// textAlign: 'center',
|
|
3857
|
-
// padding: '8px',
|
|
3858
|
-
// paddingInline: '8px',
|
|
3859
|
-
// fontSize: '16px',
|
|
3860
|
-
// background: '#fff',
|
|
3861
|
-
// width: 'auto',
|
|
3862
|
-
// borderRadius: '4px',
|
|
3863
|
-
// position: 'absolute',
|
|
3864
|
-
// left: '12px',
|
|
3865
|
-
// minWidth: '120px',
|
|
3866
|
-
}, children: boundary.label }) }, boundary.label));
|
|
3867
|
-
}) }));
|
|
3868
|
-
};
|
|
3869
|
-
|
|
3870
4762
|
const ScrollMapMinimap = ({ zones, maxUsers }) => {
|
|
3871
4763
|
const scrollType = useHeatmapConfigStore((state) => state.scrollType);
|
|
3872
|
-
const { showMinimap } =
|
|
4764
|
+
const { showMinimap } = useHeatmapScroll();
|
|
3873
4765
|
const isScrollType = [IScrollType.Attention].includes(scrollType);
|
|
3874
4766
|
if (!showMinimap || !isScrollType)
|
|
3875
4767
|
return null;
|
|
@@ -4025,6 +4917,118 @@ const ScrollMapOverlay = ({ wrapperRef, iframeRef }) => {
|
|
|
4025
4917
|
}, children: jsx(HoverZones, { position: position, currentScrollPercent: currentScrollPercent, iframeRef: iframeRef, wrapperRef: wrapperRef }) }));
|
|
4026
4918
|
};
|
|
4027
4919
|
|
|
4920
|
+
const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
|
|
4921
|
+
const { dataInfo } = useHeatmapData();
|
|
4922
|
+
const { getZonePosition } = useZonePositions();
|
|
4923
|
+
const averageFold = dataInfo?.averageFold || 50;
|
|
4924
|
+
const position = getZonePosition({
|
|
4925
|
+
startY: averageFold,
|
|
4926
|
+
endY: averageFold,
|
|
4927
|
+
});
|
|
4928
|
+
if (!position)
|
|
4929
|
+
return null;
|
|
4930
|
+
return (jsx("div", { style: {
|
|
4931
|
+
position: 'absolute',
|
|
4932
|
+
top: `${position.top}px`,
|
|
4933
|
+
left: 0,
|
|
4934
|
+
width: '100%',
|
|
4935
|
+
height: '2px',
|
|
4936
|
+
backgroundColor: '#0078D4',
|
|
4937
|
+
pointerEvents: 'none',
|
|
4938
|
+
zIndex: 2,
|
|
4939
|
+
boxShadow: '0 0 4px rgba(0,120,212,0.5)',
|
|
4940
|
+
display: 'flex',
|
|
4941
|
+
alignItems: 'center',
|
|
4942
|
+
}, children: jsxs("div", { style: {
|
|
4943
|
+
position: 'absolute',
|
|
4944
|
+
padding: '8px',
|
|
4945
|
+
backgroundColor: 'rgba(0, 120, 212, 0.9)',
|
|
4946
|
+
color: 'white',
|
|
4947
|
+
fontSize: '16px',
|
|
4948
|
+
fontWeight: 600,
|
|
4949
|
+
borderRadius: '4px',
|
|
4950
|
+
whiteSpace: 'nowrap',
|
|
4951
|
+
left: '12px',
|
|
4952
|
+
minWidth: '120px',
|
|
4953
|
+
textAlign: 'center',
|
|
4954
|
+
}, children: ["Average fold - ", averageFold.toFixed(0), "%"] }) }));
|
|
4955
|
+
};
|
|
4956
|
+
|
|
4957
|
+
const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
|
|
4958
|
+
const scrollType = useHeatmapConfigStore((state) => state.scrollType);
|
|
4959
|
+
const { scrollmap } = useHeatmapData();
|
|
4960
|
+
const { getZonePosition } = useZonePositions();
|
|
4961
|
+
if (!scrollmap || scrollmap.length === 0)
|
|
4962
|
+
return null;
|
|
4963
|
+
const findScrollPositionForUserPercent = (targetPercent) => {
|
|
4964
|
+
for (let i = 0; i < scrollmap.length; i++) {
|
|
4965
|
+
if (scrollmap[i].percUsers <= targetPercent) {
|
|
4966
|
+
if (i > 0) {
|
|
4967
|
+
return scrollmap[i - 1].scrollReachY;
|
|
4968
|
+
}
|
|
4969
|
+
return scrollmap[i].scrollReachY;
|
|
4970
|
+
}
|
|
4971
|
+
}
|
|
4972
|
+
return scrollmap[scrollmap.length - 1]?.scrollReachY || null;
|
|
4973
|
+
};
|
|
4974
|
+
const boundaries = [
|
|
4975
|
+
{ percent: 75, label: '75%', color: '#10B981' },
|
|
4976
|
+
{ percent: 50, label: '50%', color: '#F59E0B' },
|
|
4977
|
+
{ percent: 25, label: '25%', color: '#EF4444' },
|
|
4978
|
+
{ percent: 5, label: '5%', color: '#8B5CF6' },
|
|
4979
|
+
];
|
|
4980
|
+
const isScrollDepth = scrollType === IScrollType.Depth;
|
|
4981
|
+
if (!isScrollDepth)
|
|
4982
|
+
return null;
|
|
4983
|
+
return (jsx(Fragment, { children: boundaries.map((boundary) => {
|
|
4984
|
+
const scrollY = findScrollPositionForUserPercent(boundary.percent);
|
|
4985
|
+
if (scrollY === null)
|
|
4986
|
+
return null;
|
|
4987
|
+
const position = getZonePosition({
|
|
4988
|
+
startY: scrollY,
|
|
4989
|
+
endY: scrollY,
|
|
4990
|
+
});
|
|
4991
|
+
if (!position)
|
|
4992
|
+
return null;
|
|
4993
|
+
return (jsx("div", { className: `marker-boundary-line-${boundary.percent}`, style: {
|
|
4994
|
+
position: 'absolute',
|
|
4995
|
+
top: `${position.top}px`,
|
|
4996
|
+
left: 0,
|
|
4997
|
+
transformOrigin: 'left center',
|
|
4998
|
+
width: '100%',
|
|
4999
|
+
height: '0px',
|
|
5000
|
+
// borderBottom: `2px dashed #323130`,
|
|
5001
|
+
borderBottom: `2px solid ${boundary.color}`,
|
|
5002
|
+
// background: 'repeating-linear-gradient(90deg, #323130, transparent 2px 3px)',
|
|
5003
|
+
zIndex: 1,
|
|
5004
|
+
display: 'flex',
|
|
5005
|
+
alignItems: 'center',
|
|
5006
|
+
}, children: jsx("div", { style: {
|
|
5007
|
+
position: 'absolute',
|
|
5008
|
+
padding: '8px',
|
|
5009
|
+
backgroundColor: boundary.color,
|
|
5010
|
+
color: 'white',
|
|
5011
|
+
fontSize: '16px',
|
|
5012
|
+
fontWeight: 600,
|
|
5013
|
+
borderRadius: '4px',
|
|
5014
|
+
whiteSpace: 'nowrap',
|
|
5015
|
+
left: '12px',
|
|
5016
|
+
minWidth: '120px',
|
|
5017
|
+
textAlign: 'center',
|
|
5018
|
+
// textAlign: 'center',
|
|
5019
|
+
// padding: '8px',
|
|
5020
|
+
// paddingInline: '8px',
|
|
5021
|
+
// fontSize: '16px',
|
|
5022
|
+
// background: '#fff',
|
|
5023
|
+
// width: 'auto',
|
|
5024
|
+
// borderRadius: '4px',
|
|
5025
|
+
// position: 'absolute',
|
|
5026
|
+
// left: '12px',
|
|
5027
|
+
// minWidth: '120px',
|
|
5028
|
+
}, children: boundary.label }) }, boundary.label));
|
|
5029
|
+
}) }));
|
|
5030
|
+
};
|
|
5031
|
+
|
|
4028
5032
|
const SCROLL_TYPES = [IHeatmapType.Scroll];
|
|
4029
5033
|
const VizScrollMap = ({ iframeRef, wrapperRef }) => {
|
|
4030
5034
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
@@ -4083,7 +5087,7 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
|
4083
5087
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
4084
5088
|
const wrapperRef = useRef(null);
|
|
4085
5089
|
const visualRef = useRef(null);
|
|
4086
|
-
const { setSelectedElement } =
|
|
5090
|
+
const { setSelectedElement } = useHeatmapClick();
|
|
4087
5091
|
const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
|
|
4088
5092
|
const { iframeRef } = useHeatmapVizRender(mode);
|
|
4089
5093
|
const { scaledHeight, handleScroll } = useHeatmapScale({
|
|
@@ -4106,7 +5110,9 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
|
4106
5110
|
useEffect(() => {
|
|
4107
5111
|
return cleanUp;
|
|
4108
5112
|
}, []);
|
|
4109
|
-
return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [heatmapType === IHeatmapType.Click && (jsx(VizElements, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef })),
|
|
5113
|
+
return (jsxs(WrapperVisual, { visualRef: visualRef, wrapperRef: wrapperRef, scaledHeight: scaledHeight, onScroll: onScroll, iframeHeight: iframeHeight, children: [heatmapType === IHeatmapType.Click && (jsx(VizElements, { iframeRef: iframeRef, visualRef: visualRef, wrapperRef: wrapperRef })), heatmapType === IHeatmapType.ClickArea && (jsx(VizAreaClick, { iframeRef: iframeRef, onAreaClick: (area) => {
|
|
5114
|
+
console.log('area clicked', area);
|
|
5115
|
+
} })), jsx("iframe", { ref: iframeRef, ...HEATMAP_IFRAME, width: contentWidth, scrolling: "no" }), jsx(VizScrollMap, { iframeRef: iframeRef, wrapperRef: visualRef })] }));
|
|
4110
5116
|
};
|
|
4111
5117
|
|
|
4112
5118
|
const VizLoading = () => {
|
|
@@ -4124,6 +5130,7 @@ const VizDomHeatmap = () => {
|
|
|
4124
5130
|
}, []);
|
|
4125
5131
|
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
|
|
4126
5132
|
};
|
|
5133
|
+
VizDomHeatmap.displayName = 'VizDomHeatmap';
|
|
4127
5134
|
|
|
4128
5135
|
const VizLiveRenderer = () => {
|
|
4129
5136
|
const contentWidth = useHeatmapConfigStore((state) => state.width);
|
|
@@ -4157,6 +5164,7 @@ const VizLiveHeatmap = () => {
|
|
|
4157
5164
|
}, []);
|
|
4158
5165
|
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
|
|
4159
5166
|
};
|
|
5167
|
+
VizLiveHeatmap.displayName = 'VizLiveHeatmap';
|
|
4160
5168
|
|
|
4161
5169
|
const ContentVizByMode = () => {
|
|
4162
5170
|
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
@@ -4173,14 +5181,23 @@ const ContentVizByMode = () => {
|
|
|
4173
5181
|
return jsx(VizDomHeatmap, {});
|
|
4174
5182
|
}
|
|
4175
5183
|
};
|
|
5184
|
+
ContentVizByMode.displayName = 'ContentVizByMode';
|
|
4176
5185
|
|
|
4177
|
-
const
|
|
5186
|
+
const HeatmapPreview = () => {
|
|
4178
5187
|
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, {})] })] }));
|
|
4179
5188
|
};
|
|
4180
5189
|
|
|
4181
|
-
const
|
|
4182
|
-
|
|
4183
|
-
|
|
5190
|
+
const ContentTopBar = () => {
|
|
5191
|
+
const controls = useHeatmapControlStore((state) => state.controls);
|
|
5192
|
+
useHeatmapConfigStore((state) => state.mode);
|
|
5193
|
+
const TopBar = controls.TopBar;
|
|
5194
|
+
// In compare mode, hide individual top bars since we have a global header
|
|
5195
|
+
// if (mode === 'compare') {
|
|
5196
|
+
// return null;
|
|
5197
|
+
// }
|
|
5198
|
+
return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
|
|
5199
|
+
borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
5200
|
+
}, children: TopBar && jsx(TopBar, {}) }));
|
|
4184
5201
|
};
|
|
4185
5202
|
|
|
4186
5203
|
const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
@@ -4191,11 +5208,11 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
4191
5208
|
performanceLogger.configure({
|
|
4192
5209
|
enabled: true,
|
|
4193
5210
|
logToConsole: false,
|
|
4194
|
-
logLevel: 'normal',
|
|
5211
|
+
logLevel: 'normal',
|
|
4195
5212
|
thresholds: {
|
|
4196
|
-
slowRenderMs: 16,
|
|
4197
|
-
slowHookMs: 5,
|
|
4198
|
-
excessiveRenderCount: 10,
|
|
5213
|
+
slowRenderMs: 16,
|
|
5214
|
+
slowHookMs: 5,
|
|
5215
|
+
excessiveRenderCount: 10,
|
|
4199
5216
|
},
|
|
4200
5217
|
externalLogger: (metric) => {
|
|
4201
5218
|
if (metric.name === 'VizDomRenderer') ;
|
|
@@ -4204,7 +5221,7 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
4204
5221
|
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: {
|
|
4205
5222
|
minHeight: '100%',
|
|
4206
5223
|
display: 'flex',
|
|
4207
|
-
}, children: jsx(
|
|
5224
|
+
}, children: jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(HeatmapPreview, {})] }) }) }) }));
|
|
4208
5225
|
function getVariableStyle() {
|
|
4209
5226
|
return {
|
|
4210
5227
|
'--gx-hm-border-width': `${HEATMAP_CONFIG.borderWidth}px`,
|
|
@@ -4214,4 +5231,4 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
4214
5231
|
}
|
|
4215
5232
|
};
|
|
4216
5233
|
|
|
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,
|
|
5234
|
+
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, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, performanceLogger, printPerformanceSummary, sendPerformanceReport, trackStoreAction, useAreaEditMode, useAreaScrollSync, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapAreaClick, useHeatmapCanvas, useHeatmapClick, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapData, useHeatmapEffects, useHeatmapElementPosition, useHeatmapLiveStore, useHeatmapScale, useHeatmapScroll, useHeatmapViz, useHeatmapVizRender, useHoveredElement, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };
|