@gemx-dev/heatmap-react 3.5.51 → 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.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/VizScrollmap/index.d.ts +1 -0
- package/dist/esm/components/VizScrollmap/index.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/index.d.ts +4 -2
- 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/{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 +3 -3
- package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts.map +1 -1
- 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/viz-elm-callout/index.d.ts +2 -0
- package/dist/esm/helpers/viz-elm-callout/index.d.ts.map +1 -1
- package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts +4 -5
- package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -1
- package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts +3 -3
- package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts.map +1 -1
- package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts.map +1 -1
- 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 +4 -2
- package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -1
- package/dist/esm/hooks/index.d.ts +3 -1
- package/dist/esm/hooks/index.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} +5 -4
- 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/{umd/hooks/view-context/useHeatmapVizScrollmap.d.ts → esm/hooks/view-context/useHeatmapScroll.d.ts} +5 -4
- package/dist/esm/hooks/view-context/useHeatmapScroll.d.ts.map +1 -0
- 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-render/useHeatmapRender.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scroll/index.d.ts.map +1 -0
- package/dist/esm/hooks/viz-scroll/useScrollmapZones.d.ts.map +1 -0
- 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 +1616 -638
- package/dist/esm/index.mjs +1616 -638
- 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 +0 -14
- package/dist/esm/stores/mode-compare.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} +5 -5
- package/dist/esm/stores/viz-click.d.ts.map +1 -0
- package/dist/esm/types/compare.d.ts +0 -50
- package/dist/esm/types/compare.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 +15 -0
- package/dist/esm/types/viz-elm-callout.d.ts.map +1 -1
- 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/VizScrollmap/index.d.ts +1 -0
- package/dist/umd/components/VizScrollmap/index.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/index.d.ts +4 -2
- 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/{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 +3 -3
- package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts.map +1 -1
- 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/viz-elm-callout/index.d.ts +2 -0
- package/dist/umd/helpers/viz-elm-callout/index.d.ts.map +1 -1
- package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts +4 -5
- package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -1
- package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts +3 -3
- package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts.map +1 -1
- package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts.map +1 -1
- 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 +4 -2
- package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -1
- package/dist/umd/hooks/index.d.ts +3 -1
- package/dist/umd/hooks/index.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} +5 -4
- 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/{esm/hooks/view-context/useHeatmapVizScrollmap.d.ts → umd/hooks/view-context/useHeatmapScroll.d.ts} +5 -4
- package/dist/umd/hooks/view-context/useHeatmapScroll.d.ts.map +1 -0
- 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-render/useHeatmapRender.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scroll/index.d.ts.map +1 -0
- package/dist/umd/hooks/viz-scroll/useScrollmapZones.d.ts.map +1 -0
- 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/stores/index.d.ts +3 -2
- package/dist/umd/stores/index.d.ts.map +1 -1
- package/dist/umd/stores/mode-compare.d.ts +0 -14
- package/dist/umd/stores/mode-compare.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} +5 -5
- package/dist/umd/stores/viz-click.d.ts.map +1 -0
- package/dist/umd/types/compare.d.ts +0 -50
- package/dist/umd/types/compare.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 +15 -0
- package/dist/umd/types/viz-elm-callout.d.ts.map +1 -1
- package/package.json +1 -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/contexts/CompareViewContext.d.ts +0 -28
- package/dist/esm/contexts/CompareViewContext.d.ts.map +0 -1
- package/dist/esm/helpers/iframe.d.ts.map +0 -1
- package/dist/esm/helpers/viz-elm-callout/constants.d.ts +0 -4
- package/dist/esm/helpers/viz-elm-callout/constants.d.ts.map +0 -1
- package/dist/esm/helpers/viz-elm-callout/types.d.ts +0 -17
- package/dist/esm/helpers/viz-elm-callout/types.d.ts.map +0 -1
- package/dist/esm/helpers/viz-elm.d.ts +0 -9
- package/dist/esm/helpers/viz-elm.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-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/stores/interaction.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/contexts/CompareViewContext.d.ts +0 -28
- package/dist/umd/contexts/CompareViewContext.d.ts.map +0 -1
- package/dist/umd/helpers/iframe.d.ts.map +0 -1
- package/dist/umd/helpers/viz-elm-callout/constants.d.ts +0 -4
- package/dist/umd/helpers/viz-elm-callout/constants.d.ts.map +0 -1
- package/dist/umd/helpers/viz-elm-callout/types.d.ts +0 -17
- package/dist/umd/helpers/viz-elm-callout/types.d.ts.map +0 -1
- package/dist/umd/helpers/viz-elm.d.ts +0 -9
- package/dist/umd/helpers/viz-elm.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-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/stores/interaction.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-scrollmap → viz-scroll}/index.d.ts +0 -0
- /package/dist/esm/hooks/{viz-scrollmap → viz-scroll}/useScrollmapZones.d.ts +0 -0
- /package/dist/esm/hooks/{viz-scrollmap → viz-scroll}/useZonePositions.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-scrollmap → viz-scroll}/index.d.ts +0 -0
- /package/dist/umd/hooks/{viz-scrollmap → viz-scroll}/useScrollmapZones.d.ts +0 -0
- /package/dist/umd/hooks/{viz-scrollmap → viz-scroll}/useZonePositions.d.ts +0 -0
package/dist/esm/index.mjs
CHANGED
|
@@ -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 = {
|
|
@@ -157,6 +136,7 @@ const useHeatmapControlStore = create()((set, get) => {
|
|
|
157
136
|
var IHeatmapType;
|
|
158
137
|
(function (IHeatmapType) {
|
|
159
138
|
IHeatmapType["Click"] = "click";
|
|
139
|
+
IHeatmapType["ClickArea"] = "click-area";
|
|
160
140
|
IHeatmapType["Scroll"] = "scroll";
|
|
161
141
|
})(IHeatmapType || (IHeatmapType = {}));
|
|
162
142
|
var IClickType;
|
|
@@ -244,10 +224,173 @@ const useHeatmapDataStore = create()(subscribeWithSelector((set) => {
|
|
|
244
224
|
};
|
|
245
225
|
}));
|
|
246
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
|
+
|
|
247
390
|
const DEFAULT_STATE = {
|
|
248
391
|
hideSidebar: false,
|
|
249
392
|
};
|
|
250
|
-
const
|
|
393
|
+
const useHeatmapClickStore = create()(subscribeWithSelector((set) => {
|
|
251
394
|
return {
|
|
252
395
|
state: { [DEFAULT_VIEW_ID]: DEFAULT_STATE },
|
|
253
396
|
selectedElement: { [DEFAULT_VIEW_ID]: null },
|
|
@@ -308,67 +451,6 @@ const useHeatmapInteractionStore = create()(subscribeWithSelector((set) => {
|
|
|
308
451
|
};
|
|
309
452
|
}));
|
|
310
453
|
|
|
311
|
-
const useHeatmapVizStore = create()(subscribeWithSelector((set) => {
|
|
312
|
-
return {
|
|
313
|
-
isRenderViz: { [DEFAULT_VIEW_ID]: false },
|
|
314
|
-
zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
|
|
315
|
-
minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
|
|
316
|
-
scale: { [DEFAULT_VIEW_ID]: 1 },
|
|
317
|
-
isScaledToFit: { [DEFAULT_VIEW_ID]: false },
|
|
318
|
-
setIsRenderViz: (isRenderViz, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
319
|
-
isRenderViz: { ...state.isRenderViz, [viewId]: isRenderViz },
|
|
320
|
-
})),
|
|
321
|
-
setZoomRatio: (zoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
322
|
-
zoomRatio: { ...state.zoomRatio, [viewId]: zoomRatio },
|
|
323
|
-
})),
|
|
324
|
-
setMinZoomRatio: (minZoomRatio, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
325
|
-
minZoomRatio: { ...state.minZoomRatio, [viewId]: minZoomRatio },
|
|
326
|
-
})),
|
|
327
|
-
setScale: (scale, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
328
|
-
scale: { ...state.scale, [viewId]: scale },
|
|
329
|
-
})),
|
|
330
|
-
setIsScaledToFit: (isScaledToFit, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
331
|
-
isScaledToFit: { ...state.isScaledToFit, [viewId]: isScaledToFit },
|
|
332
|
-
})),
|
|
333
|
-
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
334
|
-
isRenderViz: { ...state.isRenderViz, [toViewId]: state.isRenderViz[fromViewId] ?? false },
|
|
335
|
-
zoomRatio: { ...state.zoomRatio, [toViewId]: state.zoomRatio[fromViewId] ?? 100 },
|
|
336
|
-
minZoomRatio: { ...state.minZoomRatio, [toViewId]: state.minZoomRatio[fromViewId] ?? 10 },
|
|
337
|
-
scale: { ...state.scale, [toViewId]: state.scale[fromViewId] ?? 1 },
|
|
338
|
-
isScaledToFit: {
|
|
339
|
-
...state.isScaledToFit,
|
|
340
|
-
[toViewId]: state.isScaledToFit[fromViewId] ?? false,
|
|
341
|
-
},
|
|
342
|
-
})),
|
|
343
|
-
clearView: (viewId) => set((state) => {
|
|
344
|
-
const newIsRenderViz = { ...state.isRenderViz };
|
|
345
|
-
const newZoomRatio = { ...state.zoomRatio };
|
|
346
|
-
const newMinZoomRatio = { ...state.minZoomRatio };
|
|
347
|
-
const newScale = { ...state.scale };
|
|
348
|
-
const newIsScaledToFit = { ...state.isScaledToFit };
|
|
349
|
-
delete newIsRenderViz[viewId];
|
|
350
|
-
delete newZoomRatio[viewId];
|
|
351
|
-
delete newMinZoomRatio[viewId];
|
|
352
|
-
delete newScale[viewId];
|
|
353
|
-
delete newIsScaledToFit[viewId];
|
|
354
|
-
return {
|
|
355
|
-
isRenderViz: newIsRenderViz,
|
|
356
|
-
zoomRatio: newZoomRatio,
|
|
357
|
-
minZoomRatio: newMinZoomRatio,
|
|
358
|
-
scale: newScale,
|
|
359
|
-
isScaledToFit: newIsScaledToFit,
|
|
360
|
-
};
|
|
361
|
-
}),
|
|
362
|
-
resetAll: () => set({
|
|
363
|
-
isRenderViz: { [DEFAULT_VIEW_ID]: false },
|
|
364
|
-
zoomRatio: { [DEFAULT_VIEW_ID]: 100 },
|
|
365
|
-
minZoomRatio: { [DEFAULT_VIEW_ID]: 10 },
|
|
366
|
-
scale: { [DEFAULT_VIEW_ID]: 1 },
|
|
367
|
-
isScaledToFit: { [DEFAULT_VIEW_ID]: false },
|
|
368
|
-
}),
|
|
369
|
-
};
|
|
370
|
-
}));
|
|
371
|
-
|
|
372
454
|
const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set) => {
|
|
373
455
|
return {
|
|
374
456
|
zones: { [DEFAULT_VIEW_ID]: [] },
|
|
@@ -418,94 +500,9 @@ const useHeatmapVizScrollmapStore = create()(subscribeWithSelector((set) => {
|
|
|
418
500
|
};
|
|
419
501
|
}));
|
|
420
502
|
|
|
421
|
-
const
|
|
422
|
-
payloads: [],
|
|
423
|
-
htmlContent: '',
|
|
424
|
-
};
|
|
425
|
-
const useHeatmapLiveStore = create()((set) => {
|
|
426
|
-
return {
|
|
427
|
-
...initialState,
|
|
428
|
-
reset: () => set(initialState),
|
|
429
|
-
setPayloads: (payloads) => set({ payloads }),
|
|
430
|
-
addPayload: (payload) => set((state) => ({ payloads: [...state.payloads, payload] })),
|
|
431
|
-
setHtmlContent: (htmlContent) => set({ htmlContent }),
|
|
432
|
-
};
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
const useHeatmapSingleStore = create()(subscribeWithSelector((set) => {
|
|
436
|
-
return {
|
|
437
|
-
vizRef: { [DEFAULT_VIEW_ID]: null },
|
|
438
|
-
iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
439
|
-
wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
440
|
-
wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
|
|
441
|
-
setVizRef: (vizRef, viewId = DEFAULT_VIEW_ID) => set((state) => ({
|
|
442
|
-
vizRef: { ...state.vizRef, [viewId]: vizRef },
|
|
443
|
-
})),
|
|
444
|
-
setIframeHeight: (iframeHeight, viewId = DEFAULT_VIEW_ID) => {
|
|
445
|
-
set((state) => ({
|
|
446
|
-
iframeHeight: { ...state.iframeHeight, [viewId]: iframeHeight },
|
|
447
|
-
}));
|
|
448
|
-
},
|
|
449
|
-
setWrapperHeight: (wrapperHeight, viewId = DEFAULT_VIEW_ID) => {
|
|
450
|
-
set((state) => ({
|
|
451
|
-
wrapperHeight: { ...state.wrapperHeight, [viewId]: wrapperHeight },
|
|
452
|
-
}));
|
|
453
|
-
},
|
|
454
|
-
setWrapperWidth: (wrapperWidth, viewId = DEFAULT_VIEW_ID) => {
|
|
455
|
-
set((state) => ({
|
|
456
|
-
wrapperWidth: { ...state.wrapperWidth, [viewId]: wrapperWidth },
|
|
457
|
-
}));
|
|
458
|
-
},
|
|
459
|
-
copyView: (fromViewId, toViewId) => set((state) => ({
|
|
460
|
-
// Don't copy vizRef - each view needs its own visualizer instance
|
|
461
|
-
iframeHeight: { ...state.iframeHeight, [toViewId]: state.iframeHeight[fromViewId] ?? 0 },
|
|
462
|
-
wrapperHeight: {
|
|
463
|
-
...state.wrapperHeight,
|
|
464
|
-
[toViewId]: state.wrapperHeight[fromViewId] ?? 0,
|
|
465
|
-
},
|
|
466
|
-
})),
|
|
467
|
-
clearView: (viewId) => set((state) => {
|
|
468
|
-
const newVizRef = { ...state.vizRef };
|
|
469
|
-
const newIframeHeight = { ...state.iframeHeight };
|
|
470
|
-
const newWrapperHeight = { ...state.wrapperHeight };
|
|
471
|
-
const newWrapperWidth = { ...state.wrapperWidth };
|
|
472
|
-
delete newVizRef[viewId];
|
|
473
|
-
delete newIframeHeight[viewId];
|
|
474
|
-
delete newWrapperHeight[viewId];
|
|
475
|
-
delete newWrapperWidth[viewId];
|
|
476
|
-
return {
|
|
477
|
-
vizRef: newVizRef,
|
|
478
|
-
iframeHeight: newIframeHeight,
|
|
479
|
-
wrapperHeight: newWrapperHeight,
|
|
480
|
-
wrapperWidth: newWrapperWidth,
|
|
481
|
-
};
|
|
482
|
-
}),
|
|
483
|
-
resetAll: () => set({
|
|
484
|
-
vizRef: { [DEFAULT_VIEW_ID]: null },
|
|
485
|
-
iframeHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
486
|
-
wrapperHeight: { [DEFAULT_VIEW_ID]: 0 },
|
|
487
|
-
wrapperWidth: { [DEFAULT_VIEW_ID]: 0 },
|
|
488
|
-
}),
|
|
489
|
-
};
|
|
490
|
-
}));
|
|
491
|
-
|
|
492
|
-
const createDefaultView = (id, label, options) => ({
|
|
503
|
+
const createDefaultView = (id, label) => ({
|
|
493
504
|
id,
|
|
494
505
|
label,
|
|
495
|
-
heatmapType: options?.heatmapType ?? IHeatmapType.Scroll,
|
|
496
|
-
clickType: options?.clickType ?? IClickType.Total,
|
|
497
|
-
scrollType: options?.scrollType ?? IScrollType.Depth,
|
|
498
|
-
data: options?.data,
|
|
499
|
-
clickmap: undefined,
|
|
500
|
-
scrollmap: undefined,
|
|
501
|
-
dataInfo: undefined,
|
|
502
|
-
vizRef: null,
|
|
503
|
-
iframeHeight: 0,
|
|
504
|
-
zoomRatio: 100,
|
|
505
|
-
scale: 1,
|
|
506
|
-
isScaledToFit: false,
|
|
507
|
-
isRendering: true,
|
|
508
|
-
isRenderViz: false,
|
|
509
506
|
});
|
|
510
507
|
const useHeatmapCompareStore = create()((set, get) => {
|
|
511
508
|
return {
|
|
@@ -520,9 +517,9 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
520
517
|
viewIdCounter: 0,
|
|
521
518
|
addView: (options) => {
|
|
522
519
|
const state = get();
|
|
523
|
-
const viewId =
|
|
520
|
+
const viewId = getCompareViewId(state.viewIdCounter);
|
|
524
521
|
const label = options?.label ?? `Version ${state.viewOrder.length + 1}`;
|
|
525
|
-
const newView = createDefaultView(viewId, label
|
|
522
|
+
const newView = createDefaultView(viewId, label);
|
|
526
523
|
const newViews = new Map(state.views);
|
|
527
524
|
newViews.set(viewId, newView);
|
|
528
525
|
set({
|
|
@@ -549,16 +546,6 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
549
546
|
const newViews = new Map(state.views);
|
|
550
547
|
newViews.set(viewId, { ...view, ...updates });
|
|
551
548
|
set({ views: newViews });
|
|
552
|
-
// Handle syncing
|
|
553
|
-
const { syncSettings } = state;
|
|
554
|
-
if (syncSettings.zoomRatio && 'zoomRatio' in updates && updates.zoomRatio !== undefined) {
|
|
555
|
-
get().syncProperty('zoomRatio', updates.zoomRatio);
|
|
556
|
-
}
|
|
557
|
-
if (syncSettings.heatmapType &&
|
|
558
|
-
'heatmapType' in updates &&
|
|
559
|
-
updates.heatmapType !== undefined) {
|
|
560
|
-
get().syncProperty('heatmapType', updates.heatmapType);
|
|
561
|
-
}
|
|
562
549
|
},
|
|
563
550
|
getView: (viewId) => {
|
|
564
551
|
return get().views.get(viewId);
|
|
@@ -573,9 +560,9 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
573
560
|
const viewIds = [];
|
|
574
561
|
const newViews = new Map();
|
|
575
562
|
for (let i = 0; i < count; i++) {
|
|
576
|
-
const viewId =
|
|
563
|
+
const viewId = getCompareViewId(i);
|
|
577
564
|
const label = options?.label ?? String.fromCharCode(65 + i); // A, B, C, D
|
|
578
|
-
newViews.set(viewId, createDefaultView(viewId, `Version ${label}
|
|
565
|
+
newViews.set(viewId, createDefaultView(viewId, `Version ${label}`));
|
|
579
566
|
viewIds.push(viewId);
|
|
580
567
|
}
|
|
581
568
|
const layoutMap = {
|
|
@@ -617,54 +604,188 @@ const useHeatmapCompareStore = create()((set, get) => {
|
|
|
617
604
|
};
|
|
618
605
|
});
|
|
619
606
|
|
|
620
|
-
const
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
},
|
|
631
|
-
}
|
|
632
|
-
};
|
|
633
|
-
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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);
|
|
643
701
|
};
|
|
644
702
|
|
|
645
|
-
/**
|
|
646
|
-
* Context to provide viewId to components
|
|
647
|
-
* Used in compare mode to isolate data between views
|
|
648
|
-
*/
|
|
649
703
|
const ViewIdContext = createContext(undefined);
|
|
650
|
-
|
|
651
|
-
* Hook to get current viewId
|
|
652
|
-
* Returns DEFAULT_VIEW_ID if not in a ViewIdContext (single mode)
|
|
653
|
-
*/
|
|
654
|
-
const useViewId = () => {
|
|
704
|
+
const useViewIdContext = () => {
|
|
655
705
|
const viewId = useContext(ViewIdContext);
|
|
656
706
|
return viewId || DEFAULT_VIEW_ID;
|
|
657
707
|
};
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
const
|
|
663
|
-
|
|
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
|
+
};
|
|
664
785
|
};
|
|
665
786
|
|
|
666
787
|
const useHeatmapData = (props) => {
|
|
667
|
-
const viewId = props?.viewId ||
|
|
788
|
+
const viewId = props?.viewId || useViewIdContext();
|
|
668
789
|
const data = useHeatmapDataStore((state) => state.data[viewId]);
|
|
669
790
|
const clickmap = useHeatmapDataStore((state) => state.clickmap[viewId]);
|
|
670
791
|
const scrollmap = useHeatmapDataStore((state) => state.scrollmap[viewId]);
|
|
@@ -689,40 +810,30 @@ const useHeatmapData = (props) => {
|
|
|
689
810
|
};
|
|
690
811
|
};
|
|
691
812
|
|
|
692
|
-
const
|
|
693
|
-
const viewId = props?.viewId ||
|
|
694
|
-
const
|
|
695
|
-
const
|
|
696
|
-
const
|
|
697
|
-
const
|
|
698
|
-
const
|
|
699
|
-
const
|
|
700
|
-
const setHoveredElementStore = useHeatmapInteractionStore((store) => store.setHoveredElement);
|
|
701
|
-
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);
|
|
702
821
|
const memoizedSetters = useMemo(() => ({
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
}), [
|
|
708
|
-
setStateStore,
|
|
709
|
-
setSelectedElementStore,
|
|
710
|
-
setHoveredElementStore,
|
|
711
|
-
setShouldShowCalloutStore,
|
|
712
|
-
viewId,
|
|
713
|
-
]);
|
|
822
|
+
setZones: (newZones) => setZonesStore(newZones, viewId),
|
|
823
|
+
setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
|
|
824
|
+
setShowMinimap: (value) => setShowMinimapStore(value, viewId),
|
|
825
|
+
}), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
|
|
714
826
|
return {
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
shouldShowCallout,
|
|
827
|
+
zones,
|
|
828
|
+
hoveredZone,
|
|
829
|
+
showMinimap,
|
|
719
830
|
// Setters (auto-inject viewId)
|
|
720
831
|
...memoizedSetters,
|
|
721
832
|
};
|
|
722
833
|
};
|
|
723
834
|
|
|
724
835
|
const useHeatmapViz = (props) => {
|
|
725
|
-
const viewId = props?.viewId ||
|
|
836
|
+
const viewId = props?.viewId || useViewIdContext();
|
|
726
837
|
// Viz store
|
|
727
838
|
const isRenderViz = useHeatmapVizStore((state) => state.isRenderViz[viewId] ?? false);
|
|
728
839
|
const zoomRatio = useHeatmapVizStore((state) => state.zoomRatio[viewId] ?? 100);
|
|
@@ -780,28 +891,6 @@ const useHeatmapViz = (props) => {
|
|
|
780
891
|
};
|
|
781
892
|
};
|
|
782
893
|
|
|
783
|
-
const useHeatmapVizScrollmap = (props) => {
|
|
784
|
-
const viewId = props?.viewId || useViewId();
|
|
785
|
-
const zones = useHeatmapVizScrollmapStore((store) => store.zones[viewId] ?? []);
|
|
786
|
-
const hoveredZone = useHeatmapVizScrollmapStore((store) => store.hoveredZone[viewId] ?? null);
|
|
787
|
-
const showMinimap = useHeatmapVizScrollmapStore((store) => store.showMinimap[viewId] ?? true);
|
|
788
|
-
const setZonesStore = useHeatmapVizScrollmapStore((store) => store.setZones);
|
|
789
|
-
const setHoveredZoneStore = useHeatmapVizScrollmapStore((store) => store.setHoveredZone);
|
|
790
|
-
const setShowMinimapStore = useHeatmapVizScrollmapStore((store) => store.setShowMinimap);
|
|
791
|
-
const memoizedSetters = useMemo(() => ({
|
|
792
|
-
setZones: (newZones) => setZonesStore(newZones, viewId),
|
|
793
|
-
setHoveredZone: (zone) => setHoveredZoneStore(zone, viewId),
|
|
794
|
-
setShowMinimap: (value) => setShowMinimapStore(value, viewId),
|
|
795
|
-
}), [setZonesStore, setHoveredZoneStore, setShowMinimapStore, viewId]);
|
|
796
|
-
return {
|
|
797
|
-
zones,
|
|
798
|
-
hoveredZone,
|
|
799
|
-
showMinimap,
|
|
800
|
-
// Setters (auto-inject viewId)
|
|
801
|
-
...memoizedSetters,
|
|
802
|
-
};
|
|
803
|
-
};
|
|
804
|
-
|
|
805
894
|
/**
|
|
806
895
|
* Hook to handle copying and clearing view data across all stores
|
|
807
896
|
*/
|
|
@@ -809,24 +898,28 @@ const useHeatmapCopyView = () => {
|
|
|
809
898
|
const copyDataView = useHeatmapDataStore((state) => state.copyView);
|
|
810
899
|
const copyVizView = useHeatmapVizStore((state) => state.copyView);
|
|
811
900
|
const copySingleView = useHeatmapSingleStore((state) => state.copyView);
|
|
812
|
-
const copyInteractionView =
|
|
901
|
+
const copyInteractionView = useHeatmapClickStore((state) => state.copyView);
|
|
813
902
|
const copyVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.copyView);
|
|
903
|
+
const copyAreaClickView = useHeatmapAreaClickStore((state) => state.copyView);
|
|
814
904
|
const clearDataView = useHeatmapDataStore((state) => state.clearView);
|
|
815
905
|
const clearVizView = useHeatmapVizStore((state) => state.clearView);
|
|
816
906
|
const clearSingleView = useHeatmapSingleStore((state) => state.clearView);
|
|
817
|
-
const clearInteractionView =
|
|
907
|
+
const clearInteractionView = useHeatmapClickStore((state) => state.clearView);
|
|
818
908
|
const clearVizScrollmapView = useHeatmapVizScrollmapStore((state) => state.clearView);
|
|
909
|
+
const clearAreaClickView = useHeatmapAreaClickStore((state) => state.clearView);
|
|
819
910
|
const resetDataAll = useHeatmapDataStore((state) => state.resetAll);
|
|
820
911
|
const resetVizAll = useHeatmapVizStore((state) => state.resetAll);
|
|
821
912
|
const resetSingleAll = useHeatmapSingleStore((state) => state.resetAll);
|
|
822
|
-
const resetInteractionAll =
|
|
913
|
+
const resetInteractionAll = useHeatmapClickStore((state) => state.resetAll);
|
|
823
914
|
const resetVizScrollmapAll = useHeatmapVizScrollmapStore((state) => state.resetAll);
|
|
915
|
+
const resetAreaClickAll = useHeatmapAreaClickStore((state) => state.resetAll);
|
|
824
916
|
const copyView = (fromViewId, toViewId) => {
|
|
825
917
|
copyDataView(fromViewId, toViewId);
|
|
826
918
|
copyVizView(fromViewId, toViewId);
|
|
827
919
|
copySingleView(fromViewId, toViewId);
|
|
828
920
|
copyInteractionView(fromViewId, toViewId);
|
|
829
921
|
copyVizScrollmapView(fromViewId, toViewId);
|
|
922
|
+
copyAreaClickView(fromViewId, toViewId);
|
|
830
923
|
};
|
|
831
924
|
const copyViewToMultiple = (fromViewId, toViewIds) => {
|
|
832
925
|
toViewIds.forEach((toViewId) => {
|
|
@@ -839,6 +932,7 @@ const useHeatmapCopyView = () => {
|
|
|
839
932
|
clearSingleView(viewId);
|
|
840
933
|
clearInteractionView(viewId);
|
|
841
934
|
clearVizScrollmapView(viewId);
|
|
935
|
+
clearAreaClickView(viewId);
|
|
842
936
|
};
|
|
843
937
|
const clearMultipleViews = (viewIds) => {
|
|
844
938
|
viewIds.forEach((viewId) => {
|
|
@@ -851,6 +945,7 @@ const useHeatmapCopyView = () => {
|
|
|
851
945
|
resetSingleAll();
|
|
852
946
|
resetInteractionAll();
|
|
853
947
|
resetVizScrollmapAll();
|
|
948
|
+
resetAreaClickAll();
|
|
854
949
|
};
|
|
855
950
|
return {
|
|
856
951
|
copyView,
|
|
@@ -859,49 +954,497 @@ const useHeatmapCopyView = () => {
|
|
|
859
954
|
clearMultipleViews,
|
|
860
955
|
resetAll,
|
|
861
956
|
};
|
|
862
|
-
};
|
|
863
|
-
|
|
864
|
-
const useRegisterData = (data, dataInfo) => {
|
|
865
|
-
const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
|
|
866
|
-
const { setData, setDataInfo } = useHeatmapData();
|
|
867
|
-
const handleSetData = useCallback((data) => {
|
|
868
|
-
if (!data)
|
|
869
|
-
return;
|
|
870
|
-
setData(data);
|
|
871
|
-
setIsRendering(false);
|
|
872
|
-
}, [data]);
|
|
873
|
-
const handleSetDataInfo = useCallback((dataInfo) => {
|
|
874
|
-
if (!dataInfo)
|
|
875
|
-
return;
|
|
876
|
-
setDataInfo(dataInfo);
|
|
877
|
-
}, [setDataInfo]);
|
|
878
|
-
useEffect(() => {
|
|
879
|
-
handleSetData(data);
|
|
880
|
-
}, [data]);
|
|
881
|
-
useEffect(() => {
|
|
882
|
-
handleSetDataInfo(dataInfo);
|
|
883
|
-
}, [dataInfo]);
|
|
884
|
-
};
|
|
957
|
+
};
|
|
958
|
+
|
|
959
|
+
const useRegisterData = (data, dataInfo) => {
|
|
960
|
+
const setIsRendering = useHeatmapConfigStore((state) => state.setIsRendering);
|
|
961
|
+
const { setData, setDataInfo } = useHeatmapData();
|
|
962
|
+
const handleSetData = useCallback((data) => {
|
|
963
|
+
if (!data)
|
|
964
|
+
return;
|
|
965
|
+
setData(data);
|
|
966
|
+
setIsRendering(false);
|
|
967
|
+
}, [data]);
|
|
968
|
+
const handleSetDataInfo = useCallback((dataInfo) => {
|
|
969
|
+
if (!dataInfo)
|
|
970
|
+
return;
|
|
971
|
+
setDataInfo(dataInfo);
|
|
972
|
+
}, [setDataInfo]);
|
|
973
|
+
useEffect(() => {
|
|
974
|
+
handleSetData(data);
|
|
975
|
+
}, [data]);
|
|
976
|
+
useEffect(() => {
|
|
977
|
+
handleSetDataInfo(dataInfo);
|
|
978
|
+
}, [dataInfo]);
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
const useRegisterHeatmap = ({ clickmap, scrollmap }) => {
|
|
982
|
+
const { setClickmap, setScrollmap } = useHeatmapData();
|
|
983
|
+
const handleSetClickmap = useCallback((clickmap) => {
|
|
984
|
+
if (!clickmap)
|
|
985
|
+
return;
|
|
986
|
+
setClickmap(clickmap);
|
|
987
|
+
}, [clickmap]);
|
|
988
|
+
const handleSetScrollmap = useCallback((scrollmap) => {
|
|
989
|
+
if (!scrollmap)
|
|
990
|
+
return;
|
|
991
|
+
setScrollmap(scrollmap);
|
|
992
|
+
}, [scrollmap]);
|
|
993
|
+
useEffect(() => {
|
|
994
|
+
handleSetClickmap(clickmap);
|
|
995
|
+
}, [clickmap]);
|
|
996
|
+
useEffect(() => {
|
|
997
|
+
handleSetScrollmap(scrollmap);
|
|
998
|
+
}, [scrollmap]);
|
|
999
|
+
};
|
|
1000
|
+
|
|
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
|
+
}
|
|
885
1306
|
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
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))
|
|
890
1345
|
return;
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
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
|
|
895
1350
|
return;
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
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
|
+
}
|
|
905
1448
|
|
|
906
1449
|
function findLastSizeOfDom(data) {
|
|
907
1450
|
const listDocs = data
|
|
@@ -931,39 +1474,28 @@ function decodePayloads(payload) {
|
|
|
931
1474
|
try {
|
|
932
1475
|
return decode(payload);
|
|
933
1476
|
}
|
|
934
|
-
catch (
|
|
1477
|
+
catch (_error) {
|
|
935
1478
|
return null;
|
|
936
1479
|
}
|
|
937
1480
|
}
|
|
938
1481
|
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
const elementBottom = (elementRect.top + elementRect.height) * scale;
|
|
957
|
-
// Current scroll position
|
|
958
|
-
const scrollTop = visualRef.current?.scrollTop || 0;
|
|
959
|
-
const viewportHeight = visualRect.height;
|
|
960
|
-
// Visible viewport range in the scrollable content
|
|
961
|
-
const viewportTop = scrollTop;
|
|
962
|
-
const viewportBottom = scrollTop + viewportHeight;
|
|
963
|
-
// Check if element is within the visible viewport
|
|
964
|
-
// Element is visible if it overlaps with the viewport
|
|
965
|
-
return elementBottom > viewportTop && elementTop < viewportBottom;
|
|
966
|
-
}
|
|
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
|
+
};
|
|
967
1499
|
|
|
968
1500
|
function getElementLayout(element) {
|
|
969
1501
|
if (!element?.getBoundingClientRect)
|
|
@@ -1021,9 +1553,15 @@ const buildElementInfo = (hash, rect, heatmapInfo) => {
|
|
|
1021
1553
|
};
|
|
1022
1554
|
};
|
|
1023
1555
|
|
|
1024
|
-
|
|
1025
|
-
const
|
|
1026
|
-
const
|
|
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
|
+
}
|
|
1027
1565
|
|
|
1028
1566
|
const getViewportDimensions = (containerElm) => {
|
|
1029
1567
|
if (containerElm) {
|
|
@@ -1079,7 +1617,8 @@ const calculateHorizontalPlacementPosition = (targetRect, calloutRect, placement
|
|
|
1079
1617
|
|
|
1080
1618
|
const isLeftPositionValid = (leftPos, calloutWidth, viewportWidth, padding, containerRect) => {
|
|
1081
1619
|
if (containerRect) {
|
|
1082
|
-
return leftPos >= containerRect.left + padding &&
|
|
1620
|
+
return (leftPos >= containerRect.left + padding &&
|
|
1621
|
+
leftPos + calloutWidth <= containerRect.right - padding);
|
|
1083
1622
|
}
|
|
1084
1623
|
return leftPos >= padding && leftPos + calloutWidth <= viewportWidth - padding;
|
|
1085
1624
|
};
|
|
@@ -1164,14 +1703,15 @@ const constrainToViewport = (position, calloutRect, viewport, padding, container
|
|
|
1164
1703
|
return { top, left };
|
|
1165
1704
|
};
|
|
1166
1705
|
|
|
1167
|
-
const calcCalloutPosition = (
|
|
1706
|
+
const calcCalloutPosition = (options) => {
|
|
1707
|
+
const { targetElm, calloutElm, setPosition, hozOffset = CALLOUT_HORIZONTAL_OFFSET, alignment = 'center', containerElm, } = options;
|
|
1168
1708
|
return () => {
|
|
1169
1709
|
// 1. Get dimensions
|
|
1170
1710
|
const rectDimensions = getElementDimensions(targetElm, calloutElm);
|
|
1171
1711
|
const viewport = getViewportDimensions(containerElm);
|
|
1172
1712
|
const containerRect = containerElm?.getBoundingClientRect();
|
|
1173
|
-
const padding =
|
|
1174
|
-
const arrowSize =
|
|
1713
|
+
const padding = CALLOUT_PADDING;
|
|
1714
|
+
const arrowSize = CALLOUT_ARROW_SIZE;
|
|
1175
1715
|
// 2. Generate all position candidates
|
|
1176
1716
|
const candidates = generateAllPositionCandidates(rectDimensions, viewport, alignment, hozOffset, padding, arrowSize, containerRect);
|
|
1177
1717
|
// 3. Select best position
|
|
@@ -1695,74 +2235,242 @@ class IframeHelperFixer {
|
|
|
1695
2235
|
console.error('[IframeHelper] Cannot access iframe document');
|
|
1696
2236
|
this.config.onError?.(new Error('Cannot access iframe document'));
|
|
1697
2237
|
return;
|
|
1698
|
-
}
|
|
2238
|
+
}
|
|
2239
|
+
try {
|
|
2240
|
+
console.log('[IframeHelper] Processing viewport units...');
|
|
2241
|
+
// Create replacer instance
|
|
2242
|
+
this.replacer = new IframeStyleReplacer(this.iframe, this.config);
|
|
2243
|
+
// Create navigation blocker
|
|
2244
|
+
this.navigationBlocker = new IframeNavigationBlockerV2(this.iframe);
|
|
2245
|
+
// Run replacement
|
|
2246
|
+
const result = await this.replacer.run();
|
|
2247
|
+
console.log('[IframeHelper] Process completed:', result);
|
|
2248
|
+
// Trigger success callback
|
|
2249
|
+
this.config.onSuccess?.(result);
|
|
2250
|
+
// Dispatch custom event
|
|
2251
|
+
window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', {
|
|
2252
|
+
detail: result,
|
|
2253
|
+
}));
|
|
2254
|
+
}
|
|
2255
|
+
catch (error) {
|
|
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;
|
|
1699
2447
|
try {
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
// Create navigation blocker
|
|
1704
|
-
this.navigationBlocker = new IframeNavigationBlockerV2(this.iframe);
|
|
1705
|
-
// Run replacement
|
|
1706
|
-
const result = await this.replacer.run();
|
|
1707
|
-
console.log('[IframeHelper] Process completed:', result);
|
|
1708
|
-
// Trigger success callback
|
|
1709
|
-
this.config.onSuccess?.(result);
|
|
1710
|
-
// Dispatch custom event
|
|
1711
|
-
window.dispatchEvent(new CustomEvent('iframe-dimensions-applied', {
|
|
1712
|
-
detail: result,
|
|
1713
|
-
}));
|
|
2448
|
+
vizRef?.clearmap?.();
|
|
2449
|
+
vizRef?.scrollmap?.(scrollmap);
|
|
2450
|
+
// setIsInitialized(true);
|
|
1714
2451
|
}
|
|
1715
2452
|
catch (error) {
|
|
1716
|
-
console.error(
|
|
1717
|
-
this.config.onError?.(error);
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
async recalculate() {
|
|
1721
|
-
console.log('[IframeHelper] Recalculating...');
|
|
1722
|
-
await this.process();
|
|
1723
|
-
}
|
|
1724
|
-
updateConfig(config) {
|
|
1725
|
-
this.config = { ...this.config, ...config };
|
|
1726
|
-
if (this.replacer) {
|
|
1727
|
-
this.replacer.updateConfig(config);
|
|
2453
|
+
console.error(`🚀 🐥 ~ useScrollmap ~ error:`, error);
|
|
1728
2454
|
}
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
}
|
|
1733
|
-
enableNavigationBlockingMessage() {
|
|
1734
|
-
this.navigationBlocker?.enableMessage();
|
|
1735
|
-
}
|
|
1736
|
-
disableNavigationBlocking() {
|
|
1737
|
-
this.navigationBlocker?.disable();
|
|
1738
|
-
}
|
|
1739
|
-
disableNavigationBlockingMessage() {
|
|
1740
|
-
this.navigationBlocker?.disableMessage();
|
|
1741
|
-
}
|
|
1742
|
-
destroy() {
|
|
1743
|
-
this.replacer = null;
|
|
1744
|
-
this.navigationBlocker?.destroy();
|
|
1745
|
-
this.navigationBlocker = null;
|
|
1746
|
-
console.log('[IframeHelper] Destroyed');
|
|
1747
|
-
}
|
|
1748
|
-
}
|
|
2455
|
+
}, [vizRef, scrollmap]);
|
|
2456
|
+
return { start };
|
|
2457
|
+
};
|
|
1749
2458
|
|
|
1750
|
-
|
|
1751
|
-
const
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
})
|
|
1764
|
-
|
|
1765
|
-
}
|
|
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
|
+
};
|
|
1766
2474
|
|
|
1767
2475
|
const scrollToElementIfNeeded = (visualRef, rect, scale) => {
|
|
1768
2476
|
if (!visualRef.current)
|
|
@@ -1782,7 +2490,7 @@ const scrollToElementIfNeeded = (visualRef, rect, scale) => {
|
|
|
1782
2490
|
});
|
|
1783
2491
|
};
|
|
1784
2492
|
const useClickedElement = ({ visualRef, getRect }) => {
|
|
1785
|
-
const { selectedElement, shouldShowCallout, setShouldShowCallout } =
|
|
2493
|
+
const { selectedElement, shouldShowCallout, setShouldShowCallout } = useHeatmapClick();
|
|
1786
2494
|
const { widthScale } = useHeatmapViz();
|
|
1787
2495
|
const { dataInfo } = useHeatmapData();
|
|
1788
2496
|
const [clickedElement, setClickedElement] = useState(null);
|
|
@@ -1825,7 +2533,7 @@ const useClickedElement = ({ visualRef, getRect }) => {
|
|
|
1825
2533
|
};
|
|
1826
2534
|
|
|
1827
2535
|
const useElementCalloutVisible = ({ visualRef, getRect }) => {
|
|
1828
|
-
const { selectedElement, setShouldShowCallout } =
|
|
2536
|
+
const { selectedElement, setShouldShowCallout } = useHeatmapClick();
|
|
1829
2537
|
const { widthScale } = useHeatmapViz();
|
|
1830
2538
|
const { dataInfo } = useHeatmapData();
|
|
1831
2539
|
useEffect(() => {
|
|
@@ -1856,7 +2564,7 @@ const useElementCalloutVisible = ({ visualRef, getRect }) => {
|
|
|
1856
2564
|
};
|
|
1857
2565
|
|
|
1858
2566
|
const useHeatmapEffects = ({ isVisible }) => {
|
|
1859
|
-
|
|
2567
|
+
useHeatmapClick();
|
|
1860
2568
|
const resetAll = () => {
|
|
1861
2569
|
// setShouldShowCallout(false);
|
|
1862
2570
|
};
|
|
@@ -2026,7 +2734,7 @@ function HeatmapComponent() {
|
|
|
2026
2734
|
*/
|
|
2027
2735
|
|
|
2028
2736
|
const useHoveredElement = ({ iframeRef, getRect }) => {
|
|
2029
|
-
const { hoveredElement, setHoveredElement, setSelectedElement } =
|
|
2737
|
+
const { hoveredElement, setHoveredElement, setSelectedElement } = useHeatmapClick();
|
|
2030
2738
|
const { widthScale } = useHeatmapViz();
|
|
2031
2739
|
const { dataInfo } = useHeatmapData();
|
|
2032
2740
|
const reset = useCallback(() => {
|
|
@@ -2250,8 +2958,7 @@ function reset(iframe, rect, onSuccess) {
|
|
|
2250
2958
|
|
|
2251
2959
|
const useHeatmapRender = () => {
|
|
2252
2960
|
const { data } = useHeatmapData();
|
|
2253
|
-
const { vizRef, setVizRef, setIsRenderViz, setIframeHeight
|
|
2254
|
-
console.log(`🚀 🐥 ~ useHeatmapRender ~ wrapperHeight:`, wrapperHeight);
|
|
2961
|
+
const { vizRef, setVizRef, setIsRenderViz, setIframeHeight } = useHeatmapViz();
|
|
2255
2962
|
const iframeRef = useRef(null);
|
|
2256
2963
|
const renderHeatmap = useCallback(async (payloads) => {
|
|
2257
2964
|
if (!payloads || payloads.length === 0)
|
|
@@ -3452,19 +4159,6 @@ const BoxStack = forwardRef(({ children, ...props }, ref) => {
|
|
|
3452
4159
|
});
|
|
3453
4160
|
BoxStack.displayName = 'BoxStack';
|
|
3454
4161
|
|
|
3455
|
-
const ContentTopBar = () => {
|
|
3456
|
-
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3457
|
-
useHeatmapConfigStore((state) => state.mode);
|
|
3458
|
-
const TopBar = controls.TopBar;
|
|
3459
|
-
// In compare mode, hide individual top bars since we have a global header
|
|
3460
|
-
// if (mode === 'compare') {
|
|
3461
|
-
// return null;
|
|
3462
|
-
// }
|
|
3463
|
-
return (jsx(BoxStack, { id: "gx-hm-content-header", flexDirection: "row", alignItems: "center", overflow: "auto", zIndex: 1, backgroundColor: "white", style: {
|
|
3464
|
-
borderBottom: `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`,
|
|
3465
|
-
}, children: TopBar && jsx(TopBar, {}) }));
|
|
3466
|
-
};
|
|
3467
|
-
|
|
3468
4162
|
const ContentMetricBar = () => {
|
|
3469
4163
|
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3470
4164
|
const borderBottom = `${HEATMAP_CONFIG.borderWidth}px solid ${HEATMAP_CONFIG.borderColor}`;
|
|
@@ -3472,22 +4166,11 @@ const ContentMetricBar = () => {
|
|
|
3472
4166
|
borderBottom,
|
|
3473
4167
|
}, children: controls.MetricBar ?? null }));
|
|
3474
4168
|
};
|
|
3475
|
-
|
|
3476
|
-
const ContentToolbar = () => {
|
|
3477
|
-
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3478
|
-
return (jsx("div", { id: "gx-hm-content-toolbar", style: {
|
|
3479
|
-
position: 'absolute',
|
|
3480
|
-
bottom: 0,
|
|
3481
|
-
left: '8px',
|
|
3482
|
-
right: '24px',
|
|
3483
|
-
padding: '8px',
|
|
3484
|
-
paddingBlock: '16px',
|
|
3485
|
-
}, children: controls.Toolbar ?? null }));
|
|
3486
|
-
};
|
|
4169
|
+
ContentMetricBar.displayName = 'ContentMetricBar';
|
|
3487
4170
|
|
|
3488
4171
|
const ContentSidebar = () => {
|
|
3489
4172
|
const controls = useHeatmapControlStore((state) => state.controls);
|
|
3490
|
-
const { state } =
|
|
4173
|
+
const { state } = useHeatmapClick();
|
|
3491
4174
|
const isHideSidebar = state.hideSidebar;
|
|
3492
4175
|
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
3493
4176
|
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
@@ -3521,7 +4204,7 @@ const PopoverSidebar = () => {
|
|
|
3521
4204
|
const CompSidebarActivator = useHeatmapControlStore((state) => state.controls.SidebarActivator);
|
|
3522
4205
|
const sidebarWidth = useHeatmapConfigStore((state) => state.sidebarWidth);
|
|
3523
4206
|
const [isPopoverOpen, setIsPopoverOpen] = useState(false);
|
|
3524
|
-
const { state } =
|
|
4207
|
+
const { state } = useHeatmapClick();
|
|
3525
4208
|
const isCompareMode = mode === 'compare';
|
|
3526
4209
|
const isHideSidebar = state.hideSidebar;
|
|
3527
4210
|
const stylePopover = {
|
|
@@ -3546,9 +4229,21 @@ const PopoverSidebar = () => {
|
|
|
3546
4229
|
}, children: jsx(CompSidebar, { closeAction: { onClick: () => setIsPopoverOpen(false) } }) }) }))] }));
|
|
3547
4230
|
};
|
|
3548
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
|
+
|
|
3549
4244
|
const VizContainer = ({ children, isActive = false }) => {
|
|
3550
4245
|
const wrapperRef = useRef(null);
|
|
3551
|
-
const viewId =
|
|
4246
|
+
const viewId = useViewIdContext();
|
|
3552
4247
|
useWrapperRefHeight({
|
|
3553
4248
|
isActive,
|
|
3554
4249
|
wrapperRef,
|
|
@@ -3558,80 +4253,350 @@ const VizContainer = ({ children, isActive = false }) => {
|
|
|
3558
4253
|
}, children: children }), jsx(PopoverSidebar, {})] }));
|
|
3559
4254
|
};
|
|
3560
4255
|
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
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);
|
|
3566
4298
|
return;
|
|
3567
|
-
try {
|
|
3568
|
-
vizRef?.clearmap?.();
|
|
3569
|
-
vizRef?.clickmap?.(clickmap);
|
|
3570
4299
|
}
|
|
3571
|
-
|
|
3572
|
-
|
|
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);
|
|
3573
4312
|
}
|
|
3574
|
-
}
|
|
3575
|
-
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
|
+
} }));
|
|
3576
4328
|
};
|
|
4329
|
+
AreaEditHighlight.displayName = 'AreaEditHighlight';
|
|
3577
4330
|
|
|
3578
|
-
const
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
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}`);
|
|
3584
4472
|
return;
|
|
4473
|
+
}
|
|
3585
4474
|
try {
|
|
3586
|
-
|
|
3587
|
-
|
|
3588
|
-
|
|
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
|
+
});
|
|
3589
4486
|
}
|
|
3590
4487
|
catch (error) {
|
|
3591
|
-
console.error(
|
|
4488
|
+
console.error('[useAreaRenderer] Failed to create area:', error);
|
|
3592
4489
|
}
|
|
3593
|
-
}, [
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
3599
|
-
|
|
3600
|
-
|
|
3601
|
-
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
3607
|
-
|
|
3608
|
-
|
|
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);
|
|
3609
4507
|
}
|
|
3610
|
-
}
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
const
|
|
3617
|
-
|
|
3618
|
-
|
|
3619
|
-
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
|
|
3626
|
-
|
|
3627
|
-
|
|
3628
|
-
|
|
3629
|
-
return
|
|
3630
|
-
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
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] }));
|
|
3634
4598
|
};
|
|
4599
|
+
VizAreaClick.displayName = 'VizAreaClick';
|
|
3635
4600
|
|
|
3636
4601
|
const RankBadge = ({ index, elementRect, widthScale, clickOnElement, }) => {
|
|
3637
4602
|
const style = calculateRankPosition(elementRect, widthScale);
|
|
@@ -3661,7 +4626,7 @@ const DEFAULT_POSITION = {
|
|
|
3661
4626
|
};
|
|
3662
4627
|
const ElementCallout = (props) => {
|
|
3663
4628
|
const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
|
|
3664
|
-
const viewId =
|
|
4629
|
+
const viewId = useViewIdContext();
|
|
3665
4630
|
const { element, target, visualRef, hozOffset, alignment = 'left' } = props;
|
|
3666
4631
|
const calloutRef = useRef(null);
|
|
3667
4632
|
const [position, setPosition] = useState(DEFAULT_POSITION);
|
|
@@ -3745,7 +4710,7 @@ const ELEMENT_CALLOUT = {
|
|
|
3745
4710
|
alignment: 'left',
|
|
3746
4711
|
};
|
|
3747
4712
|
const HeatmapElements = (props) => {
|
|
3748
|
-
const viewId =
|
|
4713
|
+
const viewId = useViewIdContext();
|
|
3749
4714
|
const { iframeHeight } = useHeatmapViz();
|
|
3750
4715
|
const clickedElementId = getClickedElementId(viewId, props.isSecondary);
|
|
3751
4716
|
const hoveredElementId = getHoveredElementId(viewId, props.isSecondary);
|
|
@@ -3794,121 +4759,9 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
|
|
|
3794
4759
|
} }));
|
|
3795
4760
|
};
|
|
3796
4761
|
|
|
3797
|
-
const AverageFoldLine = ({ iframeRef, wrapperRef }) => {
|
|
3798
|
-
const { dataInfo } = useHeatmapData();
|
|
3799
|
-
const { getZonePosition } = useZonePositions();
|
|
3800
|
-
const averageFold = dataInfo?.averageFold || 50;
|
|
3801
|
-
const position = getZonePosition({
|
|
3802
|
-
startY: averageFold,
|
|
3803
|
-
endY: averageFold,
|
|
3804
|
-
});
|
|
3805
|
-
if (!position)
|
|
3806
|
-
return null;
|
|
3807
|
-
return (jsx("div", { style: {
|
|
3808
|
-
position: 'absolute',
|
|
3809
|
-
top: `${position.top}px`,
|
|
3810
|
-
left: 0,
|
|
3811
|
-
width: '100%',
|
|
3812
|
-
height: '2px',
|
|
3813
|
-
backgroundColor: '#0078D4',
|
|
3814
|
-
pointerEvents: 'none',
|
|
3815
|
-
zIndex: 2,
|
|
3816
|
-
boxShadow: '0 0 4px rgba(0,120,212,0.5)',
|
|
3817
|
-
display: 'flex',
|
|
3818
|
-
alignItems: 'center',
|
|
3819
|
-
}, children: jsxs("div", { style: {
|
|
3820
|
-
position: 'absolute',
|
|
3821
|
-
padding: '8px',
|
|
3822
|
-
backgroundColor: 'rgba(0, 120, 212, 0.9)',
|
|
3823
|
-
color: 'white',
|
|
3824
|
-
fontSize: '16px',
|
|
3825
|
-
fontWeight: 600,
|
|
3826
|
-
borderRadius: '4px',
|
|
3827
|
-
whiteSpace: 'nowrap',
|
|
3828
|
-
left: '12px',
|
|
3829
|
-
minWidth: '120px',
|
|
3830
|
-
textAlign: 'center',
|
|
3831
|
-
}, children: ["Average fold - ", averageFold.toFixed(0), "%"] }) }));
|
|
3832
|
-
};
|
|
3833
|
-
|
|
3834
|
-
const ScrollmapMarker = ({ iframeRef, wrapperRef }) => {
|
|
3835
|
-
const scrollType = useHeatmapConfigStore((state) => state.scrollType);
|
|
3836
|
-
const { scrollmap } = useHeatmapData();
|
|
3837
|
-
const { getZonePosition } = useZonePositions();
|
|
3838
|
-
if (!scrollmap || scrollmap.length === 0)
|
|
3839
|
-
return null;
|
|
3840
|
-
const findScrollPositionForUserPercent = (targetPercent) => {
|
|
3841
|
-
for (let i = 0; i < scrollmap.length; i++) {
|
|
3842
|
-
if (scrollmap[i].percUsers <= targetPercent) {
|
|
3843
|
-
if (i > 0) {
|
|
3844
|
-
return scrollmap[i - 1].scrollReachY;
|
|
3845
|
-
}
|
|
3846
|
-
return scrollmap[i].scrollReachY;
|
|
3847
|
-
}
|
|
3848
|
-
}
|
|
3849
|
-
return scrollmap[scrollmap.length - 1]?.scrollReachY || null;
|
|
3850
|
-
};
|
|
3851
|
-
const boundaries = [
|
|
3852
|
-
{ percent: 75, label: '75%', color: '#10B981' },
|
|
3853
|
-
{ percent: 50, label: '50%', color: '#F59E0B' },
|
|
3854
|
-
{ percent: 25, label: '25%', color: '#EF4444' },
|
|
3855
|
-
{ percent: 5, label: '5%', color: '#8B5CF6' },
|
|
3856
|
-
];
|
|
3857
|
-
const isScrollDepth = scrollType === IScrollType.Depth;
|
|
3858
|
-
if (!isScrollDepth)
|
|
3859
|
-
return null;
|
|
3860
|
-
return (jsx(Fragment, { children: boundaries.map((boundary) => {
|
|
3861
|
-
const scrollY = findScrollPositionForUserPercent(boundary.percent);
|
|
3862
|
-
if (scrollY === null)
|
|
3863
|
-
return null;
|
|
3864
|
-
const position = getZonePosition({
|
|
3865
|
-
startY: scrollY,
|
|
3866
|
-
endY: scrollY,
|
|
3867
|
-
});
|
|
3868
|
-
if (!position)
|
|
3869
|
-
return null;
|
|
3870
|
-
return (jsx("div", { className: `marker-boundary-line-${boundary.percent}`, style: {
|
|
3871
|
-
position: 'absolute',
|
|
3872
|
-
top: `${position.top}px`,
|
|
3873
|
-
left: 0,
|
|
3874
|
-
transformOrigin: 'left center',
|
|
3875
|
-
width: '100%',
|
|
3876
|
-
height: '0px',
|
|
3877
|
-
// borderBottom: `2px dashed #323130`,
|
|
3878
|
-
borderBottom: `2px solid ${boundary.color}`,
|
|
3879
|
-
// background: 'repeating-linear-gradient(90deg, #323130, transparent 2px 3px)',
|
|
3880
|
-
zIndex: 1,
|
|
3881
|
-
display: 'flex',
|
|
3882
|
-
alignItems: 'center',
|
|
3883
|
-
}, children: jsx("div", { style: {
|
|
3884
|
-
position: 'absolute',
|
|
3885
|
-
padding: '8px',
|
|
3886
|
-
backgroundColor: boundary.color,
|
|
3887
|
-
color: 'white',
|
|
3888
|
-
fontSize: '16px',
|
|
3889
|
-
fontWeight: 600,
|
|
3890
|
-
borderRadius: '4px',
|
|
3891
|
-
whiteSpace: 'nowrap',
|
|
3892
|
-
left: '12px',
|
|
3893
|
-
minWidth: '120px',
|
|
3894
|
-
textAlign: 'center',
|
|
3895
|
-
// textAlign: 'center',
|
|
3896
|
-
// padding: '8px',
|
|
3897
|
-
// paddingInline: '8px',
|
|
3898
|
-
// fontSize: '16px',
|
|
3899
|
-
// background: '#fff',
|
|
3900
|
-
// width: 'auto',
|
|
3901
|
-
// borderRadius: '4px',
|
|
3902
|
-
// position: 'absolute',
|
|
3903
|
-
// left: '12px',
|
|
3904
|
-
// minWidth: '120px',
|
|
3905
|
-
}, children: boundary.label }) }, boundary.label));
|
|
3906
|
-
}) }));
|
|
3907
|
-
};
|
|
3908
|
-
|
|
3909
4762
|
const ScrollMapMinimap = ({ zones, maxUsers }) => {
|
|
3910
4763
|
const scrollType = useHeatmapConfigStore((state) => state.scrollType);
|
|
3911
|
-
const { showMinimap } =
|
|
4764
|
+
const { showMinimap } = useHeatmapScroll();
|
|
3912
4765
|
const isScrollType = [IScrollType.Attention].includes(scrollType);
|
|
3913
4766
|
if (!showMinimap || !isScrollType)
|
|
3914
4767
|
return null;
|
|
@@ -4064,6 +4917,118 @@ const ScrollMapOverlay = ({ wrapperRef, iframeRef }) => {
|
|
|
4064
4917
|
}, children: jsx(HoverZones, { position: position, currentScrollPercent: currentScrollPercent, iframeRef: iframeRef, wrapperRef: wrapperRef }) }));
|
|
4065
4918
|
};
|
|
4066
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
|
+
|
|
4067
5032
|
const SCROLL_TYPES = [IHeatmapType.Scroll];
|
|
4068
5033
|
const VizScrollMap = ({ iframeRef, wrapperRef }) => {
|
|
4069
5034
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
@@ -4122,7 +5087,7 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
|
4122
5087
|
const heatmapType = useHeatmapConfigStore((state) => state.heatmapType);
|
|
4123
5088
|
const wrapperRef = useRef(null);
|
|
4124
5089
|
const visualRef = useRef(null);
|
|
4125
|
-
const { setSelectedElement } =
|
|
5090
|
+
const { setSelectedElement } = useHeatmapClick();
|
|
4126
5091
|
const { iframeHeight, setIframeHeight, isRenderViz } = useHeatmapViz();
|
|
4127
5092
|
const { iframeRef } = useHeatmapVizRender(mode);
|
|
4128
5093
|
const { scaledHeight, handleScroll } = useHeatmapScale({
|
|
@@ -4145,7 +5110,9 @@ const VizDomRenderer = ({ mode = 'heatmap' }) => {
|
|
|
4145
5110
|
useEffect(() => {
|
|
4146
5111
|
return cleanUp;
|
|
4147
5112
|
}, []);
|
|
4148
|
-
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 })] }));
|
|
4149
5116
|
};
|
|
4150
5117
|
|
|
4151
5118
|
const VizLoading = () => {
|
|
@@ -4163,6 +5130,7 @@ const VizDomHeatmap = () => {
|
|
|
4163
5130
|
}, []);
|
|
4164
5131
|
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizDomRenderer, {}), iframeHeight === 0 && jsx(VizLoading, {})] }));
|
|
4165
5132
|
};
|
|
5133
|
+
VizDomHeatmap.displayName = 'VizDomHeatmap';
|
|
4166
5134
|
|
|
4167
5135
|
const VizLiveRenderer = () => {
|
|
4168
5136
|
const contentWidth = useHeatmapConfigStore((state) => state.width);
|
|
@@ -4196,6 +5164,7 @@ const VizLiveHeatmap = () => {
|
|
|
4196
5164
|
}, []);
|
|
4197
5165
|
return (jsxs(VizContainer, { isActive: true, children: [jsx(VizLiveRenderer, {}), (!iframeHeight || !wrapperHeight) && jsx(VizLoading, {})] }));
|
|
4198
5166
|
};
|
|
5167
|
+
VizLiveHeatmap.displayName = 'VizLiveHeatmap';
|
|
4199
5168
|
|
|
4200
5169
|
const ContentVizByMode = () => {
|
|
4201
5170
|
const mode = useHeatmapConfigStore((state) => state.mode);
|
|
@@ -4212,14 +5181,23 @@ const ContentVizByMode = () => {
|
|
|
4212
5181
|
return jsx(VizDomHeatmap, {});
|
|
4213
5182
|
}
|
|
4214
5183
|
};
|
|
5184
|
+
ContentVizByMode.displayName = 'ContentVizByMode';
|
|
4215
5185
|
|
|
4216
|
-
const
|
|
5186
|
+
const HeatmapPreview = () => {
|
|
4217
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, {})] })] }));
|
|
4218
5188
|
};
|
|
4219
5189
|
|
|
4220
|
-
const
|
|
4221
|
-
|
|
4222
|
-
|
|
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, {}) }));
|
|
4223
5201
|
};
|
|
4224
5202
|
|
|
4225
5203
|
const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
@@ -4230,11 +5208,11 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
4230
5208
|
performanceLogger.configure({
|
|
4231
5209
|
enabled: true,
|
|
4232
5210
|
logToConsole: false,
|
|
4233
|
-
logLevel: 'normal',
|
|
5211
|
+
logLevel: 'normal',
|
|
4234
5212
|
thresholds: {
|
|
4235
|
-
slowRenderMs: 16,
|
|
4236
|
-
slowHookMs: 5,
|
|
4237
|
-
excessiveRenderCount: 10,
|
|
5213
|
+
slowRenderMs: 16,
|
|
5214
|
+
slowHookMs: 5,
|
|
5215
|
+
excessiveRenderCount: 10,
|
|
4238
5216
|
},
|
|
4239
5217
|
externalLogger: (metric) => {
|
|
4240
5218
|
if (metric.name === 'VizDomRenderer') ;
|
|
@@ -4243,7 +5221,7 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
4243
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: {
|
|
4244
5222
|
minHeight: '100%',
|
|
4245
5223
|
display: 'flex',
|
|
4246
|
-
}, children: jsx(
|
|
5224
|
+
}, children: jsxs(BoxStack, { id: "gx-hm-layout", flexDirection: "column", flex: "1", children: [jsx(ContentTopBar, {}), jsx(HeatmapPreview, {})] }) }) }) }));
|
|
4247
5225
|
function getVariableStyle() {
|
|
4248
5226
|
return {
|
|
4249
5227
|
'--gx-hm-border-width': `${HEATMAP_CONFIG.borderWidth}px`,
|
|
@@ -4253,4 +5231,4 @@ const HeatmapLayout = ({ data, clickmap, scrollmap, controls, dataInfo, }) => {
|
|
|
4253
5231
|
}
|
|
4254
5232
|
};
|
|
4255
5233
|
|
|
4256
|
-
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 };
|