@gemx-dev/heatmap-react 3.5.92-dev.20 → 3.5.92-dev.22
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/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
- package/dist/esm/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
- package/dist/esm/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
- package/dist/esm/index.js +230 -166
- package/dist/esm/index.mjs +230 -166
- package/dist/esm/libs/iframe-processor/orchestrator.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/height-observer/index.d.ts +2 -0
- package/dist/esm/libs/iframe-processor/processors/height-observer/index.d.ts.map +1 -1
- package/dist/esm/libs/iframe-processor/processors/height-observer/listeners.d.ts +11 -0
- package/dist/esm/libs/iframe-processor/processors/height-observer/listeners.d.ts.map +1 -0
- package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts +6 -1
- package/dist/esm/libs/iframe-processor/processors/height-observer/types.d.ts.map +1 -1
- package/dist/esm/libs/visualizer/GXVisualizer.d.ts +12 -1
- package/dist/esm/libs/visualizer/GXVisualizer.d.ts.map +1 -1
- package/dist/esm/libs/visualizer/index.d.ts +1 -0
- package/dist/esm/libs/visualizer/index.d.ts.map +1 -1
- package/dist/esm/libs/visualizer/renderers/AttentionMapRenderer.d.ts +1 -1
- package/dist/esm/libs/visualizer/renderers/AttentionMapRenderer.d.ts.map +1 -1
- package/dist/esm/libs/visualizer/renderers/ScrollBucketRenderer.d.ts +44 -0
- package/dist/esm/libs/visualizer/renderers/ScrollBucketRenderer.d.ts.map +1 -0
- package/dist/esm/libs/visualizer/renderers/index.d.ts +1 -1
- package/dist/esm/libs/visualizer/renderers/index.d.ts.map +1 -1
- package/dist/esm/types/clarity.d.ts +18 -0
- package/dist/esm/types/clarity.d.ts.map +1 -1
- package/dist/esm/types/control.d.ts +2 -0
- package/dist/esm/types/control.d.ts.map +1 -1
- package/dist/esm/types/viz-scrollmap.d.ts +4 -0
- package/dist/esm/types/viz-scrollmap.d.ts.map +1 -1
- package/dist/esm/utils/retry.d.ts +1 -0
- package/dist/esm/utils/retry.d.ts.map +1 -1
- package/dist/umd/hooks/viz-canvas/useClickmap.d.ts.map +1 -1
- package/dist/umd/hooks/viz-canvas/useScrollmap.d.ts.map +1 -1
- package/dist/umd/hooks/viz-scale/useHeatmapScale.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/dist/umd/libs/iframe-processor/orchestrator.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/height-observer/index.d.ts +2 -0
- package/dist/umd/libs/iframe-processor/processors/height-observer/index.d.ts.map +1 -1
- package/dist/umd/libs/iframe-processor/processors/height-observer/listeners.d.ts +11 -0
- package/dist/umd/libs/iframe-processor/processors/height-observer/listeners.d.ts.map +1 -0
- package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts +6 -1
- package/dist/umd/libs/iframe-processor/processors/height-observer/types.d.ts.map +1 -1
- package/dist/umd/libs/visualizer/GXVisualizer.d.ts +12 -1
- package/dist/umd/libs/visualizer/GXVisualizer.d.ts.map +1 -1
- package/dist/umd/libs/visualizer/index.d.ts +1 -0
- package/dist/umd/libs/visualizer/index.d.ts.map +1 -1
- package/dist/umd/libs/visualizer/renderers/AttentionMapRenderer.d.ts +1 -1
- package/dist/umd/libs/visualizer/renderers/AttentionMapRenderer.d.ts.map +1 -1
- package/dist/umd/libs/visualizer/renderers/ScrollBucketRenderer.d.ts +44 -0
- package/dist/umd/libs/visualizer/renderers/ScrollBucketRenderer.d.ts.map +1 -0
- package/dist/umd/libs/visualizer/renderers/index.d.ts +1 -1
- package/dist/umd/libs/visualizer/renderers/index.d.ts.map +1 -1
- package/dist/umd/types/clarity.d.ts +18 -0
- package/dist/umd/types/clarity.d.ts.map +1 -1
- package/dist/umd/types/control.d.ts +2 -0
- package/dist/umd/types/control.d.ts.map +1 -1
- package/dist/umd/types/viz-scrollmap.d.ts +4 -0
- package/dist/umd/types/viz-scrollmap.d.ts.map +1 -1
- package/dist/umd/utils/retry.d.ts +1 -0
- package/dist/umd/utils/retry.d.ts.map +1 -1
- package/package.json +4 -4
- package/dist/esm/libs/visualizer/renderers/ClickHeatmapRenderer.d.ts +0 -30
- package/dist/esm/libs/visualizer/renderers/ClickHeatmapRenderer.d.ts.map +0 -1
- package/dist/umd/libs/visualizer/renderers/ClickHeatmapRenderer.d.ts +0 -30
- package/dist/umd/libs/visualizer/renderers/ClickHeatmapRenderer.d.ts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useClickmap.d.ts","sourceRoot":"","sources":["../../../src/hooks/viz-canvas/useClickmap.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW;;
|
|
1
|
+
{"version":3,"file":"useClickmap.d.ts","sourceRoot":"","sources":["../../../src/hooks/viz-canvas/useClickmap.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,WAAW;;CAiBvB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScrollmap.d.ts","sourceRoot":"","sources":["../../../src/hooks/viz-canvas/useScrollmap.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useScrollmap.d.ts","sourceRoot":"","sources":["../../../src/hooks/viz-canvas/useScrollmap.ts"],"names":[],"mappings":"AAWA,eAAO,MAAM,YAAY;;CAuDxB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHeatmapScale.d.ts","sourceRoot":"","sources":["../../../src/hooks/viz-scale/useHeatmapScale.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useHeatmapScale.d.ts","sourceRoot":"","sources":["../../../src/hooks/viz-scale/useHeatmapScale.ts"],"names":[],"mappings":"AAMA,UAAU,kBAAkB;IAC1B,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;IACnD,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAAC;IACrD,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAC;CACnD;AAED,UAAU,mBAAmB;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED,eAAO,MAAM,eAAe,GAAI,OAAO,kBAAkB,KAAG,mBA4B3D,CAAC"}
|
package/dist/esm/index.js
CHANGED
|
@@ -3528,10 +3528,8 @@ const useClickmap = () => {
|
|
|
3528
3528
|
if (!vizRef || !clickmap || clickmap.length === 0 || !isDomLoaded)
|
|
3529
3529
|
return;
|
|
3530
3530
|
try {
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
vizRef?.clickmap?.(clickmap);
|
|
3534
|
-
}, { timeout: 300 });
|
|
3531
|
+
vizRef?.clearmap?.();
|
|
3532
|
+
requestIdleCallback(() => vizRef?.clickmap?.(clickmap), { timeout: 300 });
|
|
3535
3533
|
}
|
|
3536
3534
|
catch (error) {
|
|
3537
3535
|
console.error(`🚀 🐥 ~ useClickmap ~ error:`, error);
|
|
@@ -3545,31 +3543,52 @@ const useScrollmap = () => {
|
|
|
3545
3543
|
const scrollType = useHeatmapSettingContext((s) => s.scrollType);
|
|
3546
3544
|
const scrollmap = useHeatmapDataContext((s) => s.scrollmap);
|
|
3547
3545
|
const isDomLoaded = useHeatmapVizContext((s) => s.isDomLoaded);
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
return scrollmap;
|
|
3554
|
-
case EScrollType.Revenue:
|
|
3555
|
-
return scrollmap;
|
|
3556
|
-
default:
|
|
3557
|
-
return scrollmap;
|
|
3546
|
+
const renderScrollmap = useCallback(() => {
|
|
3547
|
+
if (!vizRef || !scrollmap || scrollmap.length === 0 || !isDomLoaded)
|
|
3548
|
+
return;
|
|
3549
|
+
try {
|
|
3550
|
+
requestIdleCallback(() => vizRef?.scrollmap?.(scrollmap), { timeout: 300 });
|
|
3558
3551
|
}
|
|
3559
|
-
|
|
3560
|
-
|
|
3552
|
+
catch (error) {
|
|
3553
|
+
console.error(`🚀 🐥 ~ useScrollmap ~ renderScrollmap:`, error);
|
|
3554
|
+
}
|
|
3555
|
+
}, [vizRef, scrollmap, isDomLoaded]);
|
|
3556
|
+
const renderScrollBucket = useCallback(() => {
|
|
3561
3557
|
if (!vizRef || !scrollmap || scrollmap.length === 0 || !isDomLoaded)
|
|
3562
3558
|
return;
|
|
3559
|
+
const bucketData = scrollmap.map((point) => ({
|
|
3560
|
+
position: point.scrollReachY,
|
|
3561
|
+
value: point.cumulativeSum,
|
|
3562
|
+
percent: point.percUsers,
|
|
3563
|
+
}));
|
|
3563
3564
|
try {
|
|
3564
|
-
requestIdleCallback(() => {
|
|
3565
|
-
vizRef?.clearmap?.();
|
|
3566
|
-
vizRef?.scrollmap?.(scrollmap);
|
|
3567
|
-
}, { timeout: 300 });
|
|
3565
|
+
requestIdleCallback(() => vizRef?.scrollBucket?.(bucketData), { timeout: 300 });
|
|
3568
3566
|
}
|
|
3569
3567
|
catch (error) {
|
|
3570
|
-
|
|
3568
|
+
console.error(`🚀 🐥 ~ useScrollmap ~ renderScrollBucket:`, error);
|
|
3571
3569
|
}
|
|
3572
3570
|
}, [vizRef, scrollmap, isDomLoaded]);
|
|
3571
|
+
const start = useCallback(() => {
|
|
3572
|
+
if (!vizRef || !scrollmap || scrollmap.length === 0 || !isDomLoaded)
|
|
3573
|
+
return;
|
|
3574
|
+
try {
|
|
3575
|
+
vizRef?.clearmap?.();
|
|
3576
|
+
}
|
|
3577
|
+
catch (error) {
|
|
3578
|
+
console.error(`🚀 🐥 ~ useScrollmap ~ start:`, error);
|
|
3579
|
+
}
|
|
3580
|
+
switch (scrollType) {
|
|
3581
|
+
case EScrollType.Attention:
|
|
3582
|
+
case EScrollType.Revenue: {
|
|
3583
|
+
renderScrollBucket();
|
|
3584
|
+
break;
|
|
3585
|
+
}
|
|
3586
|
+
case EScrollType.Depth:
|
|
3587
|
+
default:
|
|
3588
|
+
renderScrollmap();
|
|
3589
|
+
break;
|
|
3590
|
+
}
|
|
3591
|
+
}, [vizRef, scrollmap, isDomLoaded, scrollType, renderScrollmap, renderScrollBucket]);
|
|
3573
3592
|
return { start };
|
|
3574
3593
|
};
|
|
3575
3594
|
|
|
@@ -4299,14 +4318,25 @@ function handleHeightChange(s) {
|
|
|
4299
4318
|
s.throttleTimeout = setTimeout(() => {
|
|
4300
4319
|
s.throttleTimeout = null;
|
|
4301
4320
|
const currentHeight = getActualHeight(s);
|
|
4302
|
-
if (currentHeight === s.lastHeight)
|
|
4321
|
+
if (currentHeight === s.lastHeight) {
|
|
4322
|
+
// Height returned to known state — cancel any stale pending debounce
|
|
4323
|
+
// to prevent it from firing with an outdated height and corrupting lastHeight.
|
|
4324
|
+
if (s.debounceTimeout) {
|
|
4325
|
+
clearTimeout(s.debounceTimeout);
|
|
4326
|
+
s.debounceTimeout = null;
|
|
4327
|
+
}
|
|
4303
4328
|
return;
|
|
4329
|
+
}
|
|
4304
4330
|
s.logger.log(`Height changed: ${s.lastHeight}px -> ${currentHeight}px`);
|
|
4305
4331
|
if (s.debounceTimeout)
|
|
4306
4332
|
clearTimeout(s.debounceTimeout);
|
|
4307
4333
|
s.debounceTimeout = setTimeout(() => {
|
|
4308
4334
|
s.debounceTimeout = null;
|
|
4309
|
-
|
|
4335
|
+
// Re-read height at dispatch time to avoid using a stale closure value.
|
|
4336
|
+
const finalHeight = getActualHeight(s);
|
|
4337
|
+
if (finalHeight !== s.lastHeight) {
|
|
4338
|
+
processHeightChange(s, finalHeight);
|
|
4339
|
+
}
|
|
4310
4340
|
}, s.debounceMs);
|
|
4311
4341
|
}, s.throttleMs);
|
|
4312
4342
|
}
|
|
@@ -4316,16 +4346,16 @@ function observe(s) {
|
|
|
4316
4346
|
return;
|
|
4317
4347
|
}
|
|
4318
4348
|
s.observerCleanup?.();
|
|
4319
|
-
s.lastHeight = s
|
|
4349
|
+
s.lastHeight = getActualHeight(s);
|
|
4320
4350
|
s.logger.log('Initial height:', s.lastHeight);
|
|
4321
4351
|
s.observerCleanup = setup(s.iframe.contentDocument, () => handleHeightChange(s));
|
|
4322
4352
|
}
|
|
4323
|
-
function start$5(s, cfg) {
|
|
4353
|
+
function start$5(s, iframe, cfg) {
|
|
4324
4354
|
if (s.running) {
|
|
4325
4355
|
s.logger.warn('Observer is already running. Call stop() first.');
|
|
4326
4356
|
return;
|
|
4327
4357
|
}
|
|
4328
|
-
s.iframe =
|
|
4358
|
+
s.iframe = iframe;
|
|
4329
4359
|
s.config = cfg;
|
|
4330
4360
|
s.throttleMs = cfg.throttleMs ?? 25;
|
|
4331
4361
|
s.debounceMs = cfg.debounceMs ?? 500;
|
|
@@ -4382,7 +4412,7 @@ function createHeightObserver() {
|
|
|
4382
4412
|
running: false,
|
|
4383
4413
|
};
|
|
4384
4414
|
return {
|
|
4385
|
-
start: (cfg) => start$5(s, cfg),
|
|
4415
|
+
start: (iframe, cfg) => start$5(s, iframe, cfg),
|
|
4386
4416
|
stop: () => stop$5(s),
|
|
4387
4417
|
observe: () => observe(s),
|
|
4388
4418
|
clear: () => clear(s),
|
|
@@ -5426,6 +5456,20 @@ async function process(s) {
|
|
|
5426
5456
|
perf$3.endSession();
|
|
5427
5457
|
s.logger.groupEnd();
|
|
5428
5458
|
s.logger.log('Process completed:', result);
|
|
5459
|
+
s.heightObserver.stop();
|
|
5460
|
+
s.heightObserver.start(s.iframe, {
|
|
5461
|
+
iframe: s.iframe,
|
|
5462
|
+
debug: s.config.debug,
|
|
5463
|
+
throttleMs: 25,
|
|
5464
|
+
debounceMs: 500,
|
|
5465
|
+
onHeightChange: (result) => {
|
|
5466
|
+
s.config?.onSuccess?.(result);
|
|
5467
|
+
dispatchDimensionsEvent(result);
|
|
5468
|
+
},
|
|
5469
|
+
onError: (error) => {
|
|
5470
|
+
s.config?.onError?.(error);
|
|
5471
|
+
},
|
|
5472
|
+
});
|
|
5429
5473
|
s.config.onSuccess?.(result);
|
|
5430
5474
|
dispatchDimensionsEvent(result);
|
|
5431
5475
|
}
|
|
@@ -5783,7 +5827,7 @@ const htmlCache = {
|
|
|
5783
5827
|
|
|
5784
5828
|
const CANVAS_ID = 'clarity-heatmap-canvas';
|
|
5785
5829
|
const ATTENTION_HUES = [240, 210, 180, 150, 120, 90, 60, 30, 0];
|
|
5786
|
-
const CANVAS_MAX_HEIGHT = 65535;
|
|
5830
|
+
const CANVAS_MAX_HEIGHT$1 = 65535;
|
|
5787
5831
|
const Z_INDEX = 2147483647;
|
|
5788
5832
|
const DEFAULT_IFRAME_ID = 'clarity-snapshot-heatmap-iframe';
|
|
5789
5833
|
const SECONDARY_IFRAME_ID = 'clarity-snapshot-heatmap-iframe-secondary';
|
|
@@ -5848,7 +5892,7 @@ class AttentionMapRenderer {
|
|
|
5848
5892
|
const body = doc.body;
|
|
5849
5893
|
const de = doc.documentElement;
|
|
5850
5894
|
const contentHeight = Math.max(body.scrollHeight, body.offsetHeight, de.clientHeight, de.scrollHeight, de.offsetHeight);
|
|
5851
|
-
canvas.height = Math.min(contentHeight, CANVAS_MAX_HEIGHT);
|
|
5895
|
+
canvas.height = Math.min(contentHeight, CANVAS_MAX_HEIGHT$1);
|
|
5852
5896
|
canvas.style.top = '0px';
|
|
5853
5897
|
if (canvas.width <= 0 || canvas.height <= 0 || !this.attentionMapData)
|
|
5854
5898
|
return;
|
|
@@ -5870,7 +5914,7 @@ class AttentionMapRenderer {
|
|
|
5870
5914
|
const body = doc.body;
|
|
5871
5915
|
const de = doc.documentElement;
|
|
5872
5916
|
const contentHeight = Math.max(body.scrollHeight, body.offsetHeight, de.clientHeight, de.scrollHeight, de.offsetHeight);
|
|
5873
|
-
canvas.height = Math.min(contentHeight, CANVAS_MAX_HEIGHT);
|
|
5917
|
+
canvas.height = Math.min(contentHeight, CANVAS_MAX_HEIGHT$1);
|
|
5874
5918
|
canvas.style.top = '0px';
|
|
5875
5919
|
let iframeEl = document.getElementById(this.heatmapIframeId);
|
|
5876
5920
|
if (!iframeEl) {
|
|
@@ -5886,9 +5930,9 @@ class AttentionMapRenderer {
|
|
|
5886
5930
|
for (const point of this.attentionMapData) {
|
|
5887
5931
|
const timePercent = Math.floor((100 * point.cumulativeTime) / maxCumulativeTime);
|
|
5888
5932
|
const colorIndex = timePercent <= 5 ? 0 : Math.min(Math.ceil(timePercent / 10), 8);
|
|
5889
|
-
if (point.
|
|
5933
|
+
if (point.scrollReachY / 100 <= 1) {
|
|
5890
5934
|
point.colorIndex = colorIndex;
|
|
5891
|
-
colorStops[point.
|
|
5935
|
+
colorStops[point.scrollReachY] = colorIndex;
|
|
5892
5936
|
}
|
|
5893
5937
|
}
|
|
5894
5938
|
// Smooth the top portion based on the fold/viewport ratio
|
|
@@ -5919,7 +5963,7 @@ class AttentionMapRenderer {
|
|
|
5919
5963
|
buildAggregatedData = (doc, contentHeight, iframeEl) => {
|
|
5920
5964
|
const elementCache = new Map();
|
|
5921
5965
|
this.attentionMapData = Array.from({ length: 100 }, (_, i) => ({
|
|
5922
|
-
|
|
5966
|
+
scrollReachY: i,
|
|
5923
5967
|
cumulativeTime: 0,
|
|
5924
5968
|
colorIndex: 0,
|
|
5925
5969
|
}));
|
|
@@ -5989,6 +6033,128 @@ class AttentionMapRenderer {
|
|
|
5989
6033
|
};
|
|
5990
6034
|
}
|
|
5991
6035
|
|
|
6036
|
+
const CANVAS_MAX_HEIGHT = 65535;
|
|
6037
|
+
const REDRAW_INTERVAL = 30;
|
|
6038
|
+
class ScrollBucketRenderer {
|
|
6039
|
+
heatmap;
|
|
6040
|
+
lastData = null;
|
|
6041
|
+
dimensionsListener = null;
|
|
6042
|
+
redrawTimeout = null;
|
|
6043
|
+
constructor(heatmap) {
|
|
6044
|
+
this.heatmap = heatmap;
|
|
6045
|
+
this.attachDimensionsListener();
|
|
6046
|
+
}
|
|
6047
|
+
redraw = () => {
|
|
6048
|
+
if (!this.lastData)
|
|
6049
|
+
return;
|
|
6050
|
+
if (this.redrawTimeout)
|
|
6051
|
+
clearTimeout(this.redrawTimeout);
|
|
6052
|
+
this.redrawTimeout = setTimeout(() => this.draw(this.lastData), REDRAW_INTERVAL);
|
|
6053
|
+
};
|
|
6054
|
+
attachDimensionsListener = () => {
|
|
6055
|
+
this.dimensionsListener = () => this.redraw();
|
|
6056
|
+
window.addEventListener('iframe-dimensions-applied', this.dimensionsListener);
|
|
6057
|
+
};
|
|
6058
|
+
detachDimensionsListener = () => {
|
|
6059
|
+
if (this.dimensionsListener) {
|
|
6060
|
+
window.removeEventListener('iframe-dimensions-applied', this.dimensionsListener);
|
|
6061
|
+
this.dimensionsListener = null;
|
|
6062
|
+
}
|
|
6063
|
+
if (this.redrawTimeout) {
|
|
6064
|
+
clearTimeout(this.redrawTimeout);
|
|
6065
|
+
this.redrawTimeout = null;
|
|
6066
|
+
}
|
|
6067
|
+
};
|
|
6068
|
+
reset = () => {
|
|
6069
|
+
// no-op: canvas state is managed by the shared heatmap instance
|
|
6070
|
+
};
|
|
6071
|
+
clear = () => {
|
|
6072
|
+
this.lastData = null;
|
|
6073
|
+
this.detachDimensionsListener();
|
|
6074
|
+
};
|
|
6075
|
+
/**
|
|
6076
|
+
* Render discrete color bands for scrollRevenue / scrollAttention data.
|
|
6077
|
+
* Uses the shared heatmap canvas (same as HeatmapHelper.scroll()) to avoid
|
|
6078
|
+
* duplicate portal canvas management.
|
|
6079
|
+
* Stores data so the canvas can be redrawn when iframe height changes.
|
|
6080
|
+
*/
|
|
6081
|
+
renderBucket = async (data) => {
|
|
6082
|
+
if (!this.heatmap)
|
|
6083
|
+
return;
|
|
6084
|
+
this.attachDimensionsListener();
|
|
6085
|
+
this.lastData = data;
|
|
6086
|
+
await this.heatmap.waitForDialogs();
|
|
6087
|
+
this.draw(data);
|
|
6088
|
+
};
|
|
6089
|
+
draw = (data) => {
|
|
6090
|
+
if (!this.heatmap)
|
|
6091
|
+
return;
|
|
6092
|
+
const canvas = this.heatmap.overlay();
|
|
6093
|
+
if (!canvas)
|
|
6094
|
+
return;
|
|
6095
|
+
const context = canvas.getContext('2d');
|
|
6096
|
+
const doc = this.heatmap.state.window.document;
|
|
6097
|
+
const body = doc.body;
|
|
6098
|
+
const de = doc.documentElement;
|
|
6099
|
+
const height = Math.max(body.scrollHeight, body.offsetHeight, de.clientHeight, de.scrollHeight, de.offsetHeight);
|
|
6100
|
+
// Same canvas setup as HeatmapHelper.scroll(): full content height, top=0
|
|
6101
|
+
canvas.height = Math.min(height, CANVAS_MAX_HEIGHT);
|
|
6102
|
+
canvas.style.top = '0px';
|
|
6103
|
+
if (canvas.width <= 0 || canvas.height <= 0)
|
|
6104
|
+
return;
|
|
6105
|
+
const buckets = this.mapToBuckets(data);
|
|
6106
|
+
if (buckets.length === 0)
|
|
6107
|
+
return;
|
|
6108
|
+
const maxPercent = Math.max(...buckets.map((b) => b.percent));
|
|
6109
|
+
if (maxPercent <= 0)
|
|
6110
|
+
return;
|
|
6111
|
+
const gradient = context.createLinearGradient(0, 0, 0, canvas.height);
|
|
6112
|
+
for (const bucket of buckets) {
|
|
6113
|
+
const stopMid = (bucket.startY + bucket.endY) / 2 / 100;
|
|
6114
|
+
// Same hue direction as HeatmapHelper.scroll() (MaxHue = 240):
|
|
6115
|
+
// high percent → hue 0 (red/hot), low percent → hue 240 (blue/cold)
|
|
6116
|
+
const hue = (1 - bucket.percent / maxPercent) * 240;
|
|
6117
|
+
const color = `hsla(${Math.round(hue)}, 100%, 50%, 0.6)`;
|
|
6118
|
+
gradient.addColorStop(stopMid, color);
|
|
6119
|
+
}
|
|
6120
|
+
context.fillStyle = gradient;
|
|
6121
|
+
context.fillRect(0, 0, canvas.width, canvas.height);
|
|
6122
|
+
};
|
|
6123
|
+
/**
|
|
6124
|
+
* Convert flat position markers → structured buckets with startY/endY.
|
|
6125
|
+
*
|
|
6126
|
+
* Input positions: [0, 5, 10, 15, ..., 95]
|
|
6127
|
+
* position=0 → { startY: 0, endY: 5 } (special start marker)
|
|
6128
|
+
* position=10 → { startY: 5, endY: 10 }
|
|
6129
|
+
* position=15 → { startY: 10, endY: 15 }
|
|
6130
|
+
*/
|
|
6131
|
+
mapToBuckets = (data) => {
|
|
6132
|
+
const sorted = [...data].sort((a, b) => a.position - b.position);
|
|
6133
|
+
const buckets = [];
|
|
6134
|
+
for (let i = 0; i < sorted.length; i++) {
|
|
6135
|
+
const current = sorted[i];
|
|
6136
|
+
let startY;
|
|
6137
|
+
let endY;
|
|
6138
|
+
if (current.position === 0) {
|
|
6139
|
+
// position=0 is the start marker → range 0 to next position
|
|
6140
|
+
const next = sorted[i + 1];
|
|
6141
|
+
if (!next)
|
|
6142
|
+
continue;
|
|
6143
|
+
startY = 0;
|
|
6144
|
+
endY = next.position;
|
|
6145
|
+
}
|
|
6146
|
+
else {
|
|
6147
|
+
// Each non-zero position is the END of its bucket; start = previous position
|
|
6148
|
+
const prev = sorted[i - 1];
|
|
6149
|
+
startY = prev ? prev.position : 0;
|
|
6150
|
+
endY = current.position;
|
|
6151
|
+
}
|
|
6152
|
+
buckets.push({ startY, endY, value: current.value, percent: current.percent });
|
|
6153
|
+
}
|
|
6154
|
+
return buckets;
|
|
6155
|
+
};
|
|
6156
|
+
}
|
|
6157
|
+
|
|
5992
6158
|
var Event;
|
|
5993
6159
|
(function (Event) {
|
|
5994
6160
|
/* Data */
|
|
@@ -6301,15 +6467,23 @@ const renderLoop = async (ctx, options) => {
|
|
|
6301
6467
|
|
|
6302
6468
|
class GXVisualizer extends Visualizer {
|
|
6303
6469
|
attentionMap;
|
|
6470
|
+
scrollBucketMap;
|
|
6304
6471
|
originalClearmap;
|
|
6305
6472
|
originalSetup;
|
|
6473
|
+
originalScrollmap;
|
|
6474
|
+
originalClickmap;
|
|
6306
6475
|
constructor() {
|
|
6307
6476
|
super();
|
|
6308
6477
|
this.attentionMap = new AttentionMapRenderer(null);
|
|
6478
|
+
this.scrollBucketMap = new ScrollBucketRenderer(null);
|
|
6309
6479
|
this.originalSetup = this.setup;
|
|
6310
6480
|
this.originalClearmap = this.clearmap;
|
|
6481
|
+
this.originalScrollmap = this.scrollmap;
|
|
6482
|
+
this.originalClickmap = this.clickmap;
|
|
6311
6483
|
this.clearmap = this.clearmapOverride;
|
|
6312
6484
|
this.setup = this.setupOverride;
|
|
6485
|
+
this.scrollmap = this.scrollmapOverride;
|
|
6486
|
+
this.clickmap = this.clickmapOverride;
|
|
6313
6487
|
}
|
|
6314
6488
|
htmlRender = async (props) => {
|
|
6315
6489
|
const { decoded, target, portalCanvasId, useproxy, logerror } = props;
|
|
@@ -6354,12 +6528,27 @@ class GXVisualizer extends Visualizer {
|
|
|
6354
6528
|
this.clearmapOverride();
|
|
6355
6529
|
this.attentionMap.attention(attentionData, avgFold, this, isSecondary);
|
|
6356
6530
|
};
|
|
6531
|
+
/**
|
|
6532
|
+
* Render discrete color bands for scrollRevenue / scrollAttention data.
|
|
6533
|
+
* @param data - Array of bucket inputs with position markers (0, 5, 10, ..., 95) and percent values
|
|
6534
|
+
*/
|
|
6535
|
+
scrollBucket = async (data) => {
|
|
6536
|
+
this.clearmapOverride();
|
|
6537
|
+
await this.scrollBucketMap.renderBucket(data);
|
|
6538
|
+
};
|
|
6539
|
+
scrollmapOverride = (data, averageFold, addMarkers) => {
|
|
6540
|
+
this.clearmapOverride();
|
|
6541
|
+
this.originalScrollmap(data, averageFold, addMarkers);
|
|
6542
|
+
};
|
|
6543
|
+
clickmapOverride = (activity) => {
|
|
6544
|
+
this.clearmapOverride();
|
|
6545
|
+
this.originalClickmap(activity);
|
|
6546
|
+
};
|
|
6357
6547
|
buildHtmlByCached = async (cached, options) => {
|
|
6358
6548
|
const { target, useproxy, portalCanvasId } = options;
|
|
6359
6549
|
if (!cached || !target)
|
|
6360
6550
|
throw new Error('Failed to render HTML cached');
|
|
6361
6551
|
const doc = target.document;
|
|
6362
|
-
target.window;
|
|
6363
6552
|
try {
|
|
6364
6553
|
await this.setup(target, { version: cached.version, useproxy, portalCanvasId });
|
|
6365
6554
|
doc.open();
|
|
@@ -6408,14 +6597,19 @@ class GXVisualizer extends Visualizer {
|
|
|
6408
6597
|
await renderLoop(ctx, options);
|
|
6409
6598
|
};
|
|
6410
6599
|
setupOverride = async (target, options) => {
|
|
6411
|
-
|
|
6600
|
+
// Mirror original Visualizer.setup(): only reset (remove listeners/state), not clear canvas.
|
|
6601
|
+
// The canvas lives in the old window's DOM which will be replaced by originalSetup's reset().
|
|
6602
|
+
this.attentionMap?.reset();
|
|
6603
|
+
this.scrollBucketMap?.reset();
|
|
6412
6604
|
await this.originalSetup(target, options);
|
|
6413
6605
|
this.attentionMap = new AttentionMapRenderer(this.state);
|
|
6606
|
+
this.scrollBucketMap = new ScrollBucketRenderer(this.heatmap);
|
|
6414
6607
|
return this;
|
|
6415
6608
|
};
|
|
6416
6609
|
clearmapOverride = () => {
|
|
6417
6610
|
this.originalClearmap();
|
|
6418
6611
|
this.attentionMap?.clear();
|
|
6612
|
+
this.scrollBucketMap?.clear();
|
|
6419
6613
|
};
|
|
6420
6614
|
}
|
|
6421
6615
|
|
|
@@ -6739,136 +6933,6 @@ const useContainerDimensions = (props) => {
|
|
|
6739
6933
|
return { containerWidth, containerHeight };
|
|
6740
6934
|
};
|
|
6741
6935
|
|
|
6742
|
-
const useObserveIframeHeight = (props) => {
|
|
6743
|
-
const iframeHeight = useHeatmapVizRectContext((s) => s.iframeHeight);
|
|
6744
|
-
const setIframeHeight = useHeatmapVizRectContext((s) => s.setIframeHeight);
|
|
6745
|
-
const isDomLoaded = useHeatmapVizContext((s) => s.isDomLoaded);
|
|
6746
|
-
const wrapperHeight = useHeatmapVizRectContext((s) => s.wrapperHeight);
|
|
6747
|
-
const { iframeRef } = props;
|
|
6748
|
-
const resizeObserverRef = useRef(null);
|
|
6749
|
-
const mutationObserverRef = useRef(null);
|
|
6750
|
-
const debounceTimerRef = useRef(null);
|
|
6751
|
-
const lastHeightRef = useRef(0);
|
|
6752
|
-
const animationFrameRef = useRef(null);
|
|
6753
|
-
const updateIframeHeight = useCallback(() => {
|
|
6754
|
-
const iframe = iframeRef.current;
|
|
6755
|
-
if (!iframe)
|
|
6756
|
-
return;
|
|
6757
|
-
try {
|
|
6758
|
-
const iframeDocument = iframe.contentDocument;
|
|
6759
|
-
const iframeBody = iframeDocument?.body;
|
|
6760
|
-
const iframeDocumentElement = iframeDocument?.documentElement;
|
|
6761
|
-
if (!iframeBody || !iframeDocumentElement)
|
|
6762
|
-
return;
|
|
6763
|
-
// iframe.style.height = 'auto'; // TODO: check if this is needed
|
|
6764
|
-
requestAnimationFrame(() => {
|
|
6765
|
-
iframe.style.height = `${wrapperHeight}px`;
|
|
6766
|
-
const bodyHeight = Math.max(iframeBody.scrollHeight, iframeBody.offsetHeight, iframeBody.clientHeight);
|
|
6767
|
-
const documentHeight = Math.max(iframeDocumentElement.scrollHeight, iframeDocumentElement.offsetHeight, iframeDocumentElement.clientHeight);
|
|
6768
|
-
const actualHeight = Math.max(bodyHeight, documentHeight);
|
|
6769
|
-
if (actualHeight > 0) {
|
|
6770
|
-
lastHeightRef.current = actualHeight;
|
|
6771
|
-
// iframe.height = `${actualHeight}px`;
|
|
6772
|
-
iframe.style.height = `${actualHeight}px`;
|
|
6773
|
-
setIframeHeight(actualHeight);
|
|
6774
|
-
}
|
|
6775
|
-
});
|
|
6776
|
-
}
|
|
6777
|
-
catch (error) {
|
|
6778
|
-
console.warn('Cannot measure iframe content:', error);
|
|
6779
|
-
}
|
|
6780
|
-
}, [iframeRef, wrapperHeight]);
|
|
6781
|
-
useCallback(() => {
|
|
6782
|
-
// Cancel pending updates
|
|
6783
|
-
if (debounceTimerRef.current) {
|
|
6784
|
-
clearTimeout(debounceTimerRef.current);
|
|
6785
|
-
}
|
|
6786
|
-
if (animationFrameRef.current) {
|
|
6787
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
6788
|
-
}
|
|
6789
|
-
debounceTimerRef.current = setTimeout(() => {
|
|
6790
|
-
animationFrameRef.current = requestAnimationFrame(() => {
|
|
6791
|
-
updateIframeHeight();
|
|
6792
|
-
});
|
|
6793
|
-
}, 50);
|
|
6794
|
-
}, [updateIframeHeight]);
|
|
6795
|
-
// Immediate update không debounce (cho ResizeObserver)
|
|
6796
|
-
const immediateUpdate = useCallback(() => {
|
|
6797
|
-
if (animationFrameRef.current) {
|
|
6798
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
6799
|
-
}
|
|
6800
|
-
animationFrameRef.current = requestAnimationFrame(() => {
|
|
6801
|
-
updateIframeHeight();
|
|
6802
|
-
});
|
|
6803
|
-
}, [updateIframeHeight]);
|
|
6804
|
-
useEffect(() => {
|
|
6805
|
-
const iframe = iframeRef.current;
|
|
6806
|
-
if (!iframe || !iframeHeight || !isDomLoaded)
|
|
6807
|
-
return;
|
|
6808
|
-
const setupObservers = () => {
|
|
6809
|
-
try {
|
|
6810
|
-
const iframeDocument = iframe.contentDocument;
|
|
6811
|
-
const iframeBody = iframeDocument?.body;
|
|
6812
|
-
if (!iframeBody)
|
|
6813
|
-
return;
|
|
6814
|
-
// Cleanup existing observers
|
|
6815
|
-
if (resizeObserverRef.current) {
|
|
6816
|
-
resizeObserverRef.current.disconnect();
|
|
6817
|
-
}
|
|
6818
|
-
if (mutationObserverRef.current) {
|
|
6819
|
-
mutationObserverRef.current.disconnect();
|
|
6820
|
-
}
|
|
6821
|
-
if (typeof window.ResizeObserver !== 'undefined') {
|
|
6822
|
-
resizeObserverRef.current = new ResizeObserver(immediateUpdate);
|
|
6823
|
-
resizeObserverRef.current.observe(iframeBody);
|
|
6824
|
-
const iframeDocumentElement = iframeDocument?.documentElement;
|
|
6825
|
-
if (iframeDocumentElement) {
|
|
6826
|
-
resizeObserverRef.current.observe(iframeDocumentElement);
|
|
6827
|
-
}
|
|
6828
|
-
}
|
|
6829
|
-
if (typeof window.MutationObserver !== 'undefined') {
|
|
6830
|
-
mutationObserverRef.current = new MutationObserver(immediateUpdate);
|
|
6831
|
-
mutationObserverRef.current.observe(iframeBody, {
|
|
6832
|
-
childList: true,
|
|
6833
|
-
subtree: true,
|
|
6834
|
-
attributes: true,
|
|
6835
|
-
attributeFilter: ['style', 'class'],
|
|
6836
|
-
characterData: false,
|
|
6837
|
-
});
|
|
6838
|
-
}
|
|
6839
|
-
updateIframeHeight();
|
|
6840
|
-
}
|
|
6841
|
-
catch (error) {
|
|
6842
|
-
console.warn('Cannot access iframe content:', error);
|
|
6843
|
-
}
|
|
6844
|
-
};
|
|
6845
|
-
if (iframe.contentDocument?.readyState === 'complete') {
|
|
6846
|
-
setupObservers();
|
|
6847
|
-
}
|
|
6848
|
-
else {
|
|
6849
|
-
iframe.addEventListener('load', setupObservers, { once: true });
|
|
6850
|
-
}
|
|
6851
|
-
return () => {
|
|
6852
|
-
// Cleanup observers
|
|
6853
|
-
if (resizeObserverRef.current) {
|
|
6854
|
-
resizeObserverRef.current.disconnect();
|
|
6855
|
-
}
|
|
6856
|
-
if (mutationObserverRef.current) {
|
|
6857
|
-
mutationObserverRef.current.disconnect();
|
|
6858
|
-
}
|
|
6859
|
-
// Cleanup timers
|
|
6860
|
-
if (debounceTimerRef.current) {
|
|
6861
|
-
clearTimeout(debounceTimerRef.current);
|
|
6862
|
-
}
|
|
6863
|
-
if (animationFrameRef.current) {
|
|
6864
|
-
cancelAnimationFrame(animationFrameRef.current);
|
|
6865
|
-
}
|
|
6866
|
-
iframe.removeEventListener('load', setupObservers);
|
|
6867
|
-
};
|
|
6868
|
-
}, [iframeRef, iframeHeight, isDomLoaded]);
|
|
6869
|
-
return {};
|
|
6870
|
-
};
|
|
6871
|
-
|
|
6872
6936
|
const useScaleCalculation = (props) => {
|
|
6873
6937
|
const widthScale = useHeatmapVizContext((s) => s.widthScale);
|
|
6874
6938
|
const zoomRatio = useHeatmapVizContext((s) => s.zoomRatio);
|
|
@@ -6948,7 +7012,7 @@ const useHeatmapScale = (props) => {
|
|
|
6948
7012
|
// 2. Get content dimensions from config
|
|
6949
7013
|
const viewport = useHeatmapViewportByDevice();
|
|
6950
7014
|
// 3. Observe iframe height (now reacts to width changes)
|
|
6951
|
-
useObserveIframeHeight({ iframeRef });
|
|
7015
|
+
// useObserveIframeHeight({ iframeRef });
|
|
6952
7016
|
// 4. Calculate scale
|
|
6953
7017
|
const { widthScale } = useScaleCalculation({ containerWidth, containerHeight, iframeHeight });
|
|
6954
7018
|
// 5. Setup scroll sync
|