@gemx-dev/heatmap-react 3.5.58 → 3.5.60

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.
Files changed (59) hide show
  1. package/dist/esm/components/VizElement/ElementCallout.d.ts.map +1 -1
  2. package/dist/esm/components/VizElement/ElementCalloutOverlay.d.ts.map +1 -1
  3. package/dist/esm/components/VizElement/ElementOverlay.d.ts +1 -0
  4. package/dist/esm/components/VizElement/ElementOverlay.d.ts.map +1 -1
  5. package/dist/esm/components/VizElement/HeatmapElements.d.ts +1 -1
  6. package/dist/esm/components/VizElement/HeatmapElements.d.ts.map +1 -1
  7. package/dist/esm/components/VizElement/HoveredElementOverlay.d.ts.map +1 -1
  8. package/dist/esm/components/VizElement/RankBadge.d.ts.map +1 -1
  9. package/dist/esm/configs/backdrop.d.ts +1 -1
  10. package/dist/esm/configs/elm-callout.d.ts +6 -0
  11. package/dist/esm/configs/elm-callout.d.ts.map +1 -0
  12. package/dist/esm/configs/index.d.ts +1 -0
  13. package/dist/esm/configs/index.d.ts.map +1 -1
  14. package/dist/esm/helpers/viewport/element.d.ts +2 -1
  15. package/dist/esm/helpers/viewport/element.d.ts.map +1 -1
  16. package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts +3 -2
  17. package/dist/esm/helpers/viz-elm-callout/dimensions.d.ts.map +1 -1
  18. package/dist/esm/helpers/viz-elm-callout/getter.d.ts +4 -1
  19. package/dist/esm/helpers/viz-elm-callout/getter.d.ts.map +1 -1
  20. package/dist/esm/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -1
  21. package/dist/esm/helpers/viz-elm-callout/position-selector.d.ts.map +1 -1
  22. package/dist/esm/helpers/viz-elm-callout/position-validator.d.ts.map +1 -1
  23. package/dist/esm/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -1
  24. package/dist/esm/hooks/viz-elm/useHeatmapEffects.d.ts.map +1 -1
  25. package/dist/esm/hooks/viz-elm/useHoveredElement.d.ts.map +1 -1
  26. package/dist/esm/index.js +165 -74
  27. package/dist/esm/index.mjs +165 -74
  28. package/dist/esm/types/viz-elm-callout.d.ts +13 -2
  29. package/dist/esm/types/viz-elm-callout.d.ts.map +1 -1
  30. package/dist/style.css +8 -3
  31. package/dist/umd/components/VizElement/ElementCallout.d.ts.map +1 -1
  32. package/dist/umd/components/VizElement/ElementCalloutOverlay.d.ts.map +1 -1
  33. package/dist/umd/components/VizElement/ElementOverlay.d.ts +1 -0
  34. package/dist/umd/components/VizElement/ElementOverlay.d.ts.map +1 -1
  35. package/dist/umd/components/VizElement/HeatmapElements.d.ts +1 -1
  36. package/dist/umd/components/VizElement/HeatmapElements.d.ts.map +1 -1
  37. package/dist/umd/components/VizElement/HoveredElementOverlay.d.ts.map +1 -1
  38. package/dist/umd/components/VizElement/RankBadge.d.ts.map +1 -1
  39. package/dist/umd/configs/backdrop.d.ts +1 -1
  40. package/dist/umd/configs/elm-callout.d.ts +6 -0
  41. package/dist/umd/configs/elm-callout.d.ts.map +1 -0
  42. package/dist/umd/configs/index.d.ts +1 -0
  43. package/dist/umd/configs/index.d.ts.map +1 -1
  44. package/dist/umd/helpers/viewport/element.d.ts +2 -1
  45. package/dist/umd/helpers/viewport/element.d.ts.map +1 -1
  46. package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts +3 -2
  47. package/dist/umd/helpers/viz-elm-callout/dimensions.d.ts.map +1 -1
  48. package/dist/umd/helpers/viz-elm-callout/getter.d.ts +4 -1
  49. package/dist/umd/helpers/viz-elm-callout/getter.d.ts.map +1 -1
  50. package/dist/umd/helpers/viz-elm-callout/position-candidates.d.ts.map +1 -1
  51. package/dist/umd/helpers/viz-elm-callout/position-selector.d.ts.map +1 -1
  52. package/dist/umd/helpers/viz-elm-callout/position-validator.d.ts.map +1 -1
  53. package/dist/umd/helpers/viz-elm-callout/viz-elm.d.ts.map +1 -1
  54. package/dist/umd/hooks/viz-elm/useHeatmapEffects.d.ts.map +1 -1
  55. package/dist/umd/hooks/viz-elm/useHoveredElement.d.ts.map +1 -1
  56. package/dist/umd/index.js +2 -2
  57. package/dist/umd/types/viz-elm-callout.d.ts +13 -2
  58. package/dist/umd/types/viz-elm-callout.d.ts.map +1 -1
  59. package/package.json +1 -1
package/dist/esm/index.js CHANGED
@@ -57,13 +57,19 @@ const BACKDROP_CONFIG = {
57
57
  * Default cutout expansion (pixels)
58
58
  * Adds padding around the active element cutout
59
59
  */
60
- CUTOUT_EXPANSION: 2,
60
+ CUTOUT_EXPANSION: 0,
61
61
  /**
62
62
  * Z-index for backdrop canvas
63
63
  */
64
64
  Z_INDEX: 999,
65
65
  };
66
66
 
67
+ const ELM_CALLOUT_CONFIG = {
68
+ MOUSE_POSITION: true,
69
+ SHOW_RANK_BADGE: false,
70
+ HIDE_OUTLINE_ON_CLICKED: true,
71
+ };
72
+
67
73
  // Portal mode: Full permissions for proper functionality
68
74
  // Need allow-forms for add to cart, allow-popups for some features
69
75
  const HEATMAP_IFRAME = {
@@ -1351,6 +1357,25 @@ function isElementInViewport(elementRect, visualRef, scale) {
1351
1357
  // Element is visible if it overlaps with the viewport
1352
1358
  return elementBottom > viewportTop && elementTop < viewportBottom;
1353
1359
  }
1360
+ function isElementRectInViewport(elementRect, visualRect, scale) {
1361
+ if (!elementRect)
1362
+ return false;
1363
+ if (!visualRect)
1364
+ return false;
1365
+ // Element position relative to the document (or container's content)
1366
+ const elementTop = elementRect.top * scale;
1367
+ console.log(`🚀 🐥 ~ isElementRectInViewport ~ scale:`, scale);
1368
+ console.log(`🚀 🐥 ~ isElementRectInViewport ~ elementRect.top:`, elementRect.top);
1369
+ const elementBottom = (elementRect.top + elementRect.height) * scale;
1370
+ // Current scroll position
1371
+ const scrollTop = (visualRect?.scrollTop || 0) - 8;
1372
+ console.log(`🚀 🐥 ~ isElementRectInViewport ~ visualRect?.scrollTop:`, visualRect?.scrollTop);
1373
+ const viewportHeight = visualRect.height;
1374
+ // Visible viewport range in the scrollable content
1375
+ const viewportTop = scrollTop;
1376
+ const viewportBottom = scrollTop + viewportHeight;
1377
+ return elementTop > viewportTop && elementBottom < viewportBottom;
1378
+ }
1354
1379
 
1355
1380
  const CLARITY_HEATMAP_CANVAS_ID = 'clarity-heatmap-canvas';
1356
1381
  const HEATMAP_ELEMENT_ATTRIBUTE = 'data-clarity-hashalpha';
@@ -2211,6 +2236,25 @@ const buildElementInfo = (hash, rect, heatmapInfo) => {
2211
2236
  ...rect,
2212
2237
  };
2213
2238
  };
2239
+ // GETTERS
2240
+ const getScaledCalloutRect = (_element, _widthScale) => {
2241
+ return {
2242
+ width: 230,
2243
+ height: 263,
2244
+ };
2245
+ };
2246
+ const getStyleFromCandidate = (candidate, widthScale) => {
2247
+ const { horizontalAlign, placement, top, left } = candidate;
2248
+ const yTransformAlign = placement === 'top' ? 'bottom' : 'top';
2249
+ const xTransformAlign = horizontalAlign === 'left' ? 'left' : 'right';
2250
+ return {
2251
+ top,
2252
+ left,
2253
+ zIndex: Z_INDEX.CALLOUT,
2254
+ transform: `scale(${1 / widthScale})`, // TODO: remove this when we have a better way to handle the scale
2255
+ transformOrigin: `${xTransformAlign} ${yTransformAlign}`,
2256
+ };
2257
+ };
2214
2258
 
2215
2259
  function calculateRankPosition(rect, widthScale) {
2216
2260
  const top = rect.top <= 18 ? rect.top + 3 : rect.top - 18;
@@ -2222,11 +2266,11 @@ function calculateRankPosition(rect, widthScale) {
2222
2266
  };
2223
2267
  }
2224
2268
 
2225
- const getViewportDimensions = (containerElm, scale) => {
2269
+ const getContainerViewport = (containerElm, _scale) => {
2226
2270
  if (containerElm) {
2227
2271
  const containerRect = containerElm.getBoundingClientRect();
2228
- const width = scale ? containerRect.width / scale : containerRect.width;
2229
- const height = scale ? containerRect.height / scale : containerRect.height;
2272
+ const width = containerRect.width;
2273
+ const height = containerRect.width;
2230
2274
  return { width, height };
2231
2275
  }
2232
2276
  return {
@@ -2234,11 +2278,29 @@ const getViewportDimensions = (containerElm, scale) => {
2234
2278
  height: window.innerHeight,
2235
2279
  };
2236
2280
  };
2281
+ const getVisualDomViewport = (visualDomElm, scale = 1) => {
2282
+ if (visualDomElm) {
2283
+ const rect = visualDomElm.getBoundingClientRect();
2284
+ return {
2285
+ width: rect.width,
2286
+ height: rect.height,
2287
+ scrollTop: visualDomElm.scrollTop,
2288
+ scrollLeft: visualDomElm.scrollLeft,
2289
+ };
2290
+ }
2291
+ return {
2292
+ width: window.innerWidth,
2293
+ height: window.innerHeight,
2294
+ scrollTop: 0,
2295
+ scrollLeft: 0,
2296
+ };
2297
+ };
2237
2298
  const getElementDimensions = (options) => {
2238
2299
  const { targetElm, calloutElm, scale, containerElm } = options;
2239
2300
  const targetRect = targetElm.getBoundingClientRect();
2240
2301
  const calloutRect = calloutElm.getBoundingClientRect();
2241
2302
  const containerRect = containerElm.getBoundingClientRect();
2303
+ const scaledCalloutRect = getScaledCalloutRect();
2242
2304
  if (scale && containerRect) {
2243
2305
  const relativeTop = (targetRect.top - containerRect.top) / scale;
2244
2306
  const relativeLeft = (targetRect.left - containerRect.left) / scale;
@@ -2254,21 +2316,13 @@ const getElementDimensions = (options) => {
2254
2316
  width: scaledWidth,
2255
2317
  height: scaledHeight,
2256
2318
  },
2257
- calloutRect: {
2258
- ...calloutRect,
2259
- width: calloutRect.width / scale,
2260
- height: calloutRect.height / scale,
2261
- },
2319
+ calloutRect: scaledCalloutRect,
2262
2320
  };
2263
2321
  }
2264
2322
  if (scale) {
2265
2323
  return {
2266
2324
  targetRect,
2267
- calloutRect: {
2268
- ...calloutRect,
2269
- width: calloutRect.width / scale,
2270
- height: calloutRect.height / scale,
2271
- },
2325
+ calloutRect: scaledCalloutRect,
2272
2326
  };
2273
2327
  }
2274
2328
  return { targetRect, calloutRect };
@@ -2334,36 +2388,50 @@ const isLeftPositionValid = (leftPos, options) => {
2334
2388
  const { width: viewportWidth } = viewport;
2335
2389
  const absLeft = rectDimensions.targetAbsoluteRect?.left ?? 0;
2336
2390
  const relLeftPos = absLeft + leftPos - offset.x;
2337
- const maxViewportWidth = viewportWidth - padding + EPSILON;
2391
+ const calloutWidthScaled = calloutWidth / options.widthScale;
2392
+ const maxViewportWidth = viewportWidth + EPSILON;
2338
2393
  const isValidLeft = relLeftPos >= padding - EPSILON;
2339
- const isRectCalloutShowValid = relLeftPos + calloutWidth <= maxViewportWidth;
2394
+ const isRectCalloutShowValid = relLeftPos + calloutWidthScaled - EPSILON <= maxViewportWidth / options.widthScale;
2340
2395
  return isValidLeft && isRectCalloutShowValid;
2341
2396
  };
2342
2397
  const isRightPositionValid = (leftPos, options) => {
2343
- const { rectDimensions, viewport, padding } = options;
2398
+ const { rectDimensions, viewport } = options;
2344
2399
  const { width: calloutWidth } = rectDimensions.calloutRect;
2345
2400
  const { width: viewportWidth } = viewport;
2346
- const maxViewportWidth = viewportWidth - padding + EPSILON;
2347
- const isValidRight = leftPos - calloutWidth - padding - EPSILON <= maxViewportWidth;
2401
+ const calloutWidthScaled = calloutWidth / options.widthScale;
2402
+ const absLeft = rectDimensions.targetAbsoluteRect?.left ?? 0;
2403
+ const relLeftPos = absLeft + leftPos;
2404
+ const maxViewportWidth = viewportWidth + EPSILON;
2405
+ const isValidRight = relLeftPos - calloutWidthScaled - EPSILON <= maxViewportWidth / options.widthScale;
2348
2406
  return isValidRight;
2349
2407
  };
2350
2408
  const isVerticalPositionValid = (placement, options) => {
2351
2409
  const { rectDimensions, viewport, padding, arrowSize } = options;
2352
- const { targetRect, calloutRect } = rectDimensions;
2410
+ const { targetRect, targetAbsoluteRect, calloutRect } = rectDimensions;
2353
2411
  const { height: viewportHeight } = viewport;
2354
2412
  const { height: calloutHeight } = calloutRect;
2355
- return placement === 'top'
2356
- ? targetRect.top - calloutHeight - padding - arrowSize > -EPSILON
2357
- : targetRect.bottom + calloutHeight + padding + arrowSize < viewportHeight + EPSILON;
2413
+ const relativeTop = (targetAbsoluteRect?.top ?? 0) + targetRect.top;
2414
+ const calloutHeightScaled = calloutHeight / options.widthScale;
2415
+ switch (placement) {
2416
+ case 'top':
2417
+ return relativeTop - calloutHeightScaled - padding - arrowSize > -EPSILON;
2418
+ case 'bottom':
2419
+ return targetRect.bottom + calloutHeight + padding + arrowSize < viewportHeight + EPSILON;
2420
+ }
2358
2421
  };
2359
2422
  const isHorizontalPositionValid = (placement, options) => {
2360
2423
  const { rectDimensions, viewport, padding, arrowSize } = options;
2361
- const { targetRect, calloutRect } = rectDimensions;
2424
+ const { targetRect, targetAbsoluteRect, calloutRect } = rectDimensions;
2362
2425
  const { width: viewportWidth } = viewport;
2363
2426
  const { width: calloutWidth } = calloutRect;
2364
- return placement === 'right'
2365
- ? targetRect.right + calloutWidth + padding + arrowSize < viewportWidth + EPSILON
2366
- : targetRect.left - calloutWidth - padding - arrowSize > -EPSILON;
2427
+ const relativeLeft = (targetAbsoluteRect?.left ?? 0) + targetRect.left;
2428
+ const relativeRight = relativeLeft + targetRect.width;
2429
+ switch (placement) {
2430
+ case 'right':
2431
+ return relativeRight + calloutWidth + padding + arrowSize < viewportWidth + EPSILON;
2432
+ case 'left':
2433
+ return relativeLeft - calloutWidth - padding - arrowSize > -EPSILON;
2434
+ }
2367
2435
  };
2368
2436
 
2369
2437
  const generateVerticalPositionCandidates = (options) => {
@@ -2403,12 +2471,24 @@ const generateHorizontalPositionCandidates = (options) => {
2403
2471
  });
2404
2472
  };
2405
2473
  const generateAllCandidates = (options) => {
2474
+ const { visualViewport, rectDimensions } = options;
2406
2475
  const verticalCandidates = generateVerticalPositionCandidates(options);
2407
2476
  const horizontalCandidates = generateHorizontalPositionCandidates(options);
2408
- return [...verticalCandidates, ...horizontalCandidates];
2477
+ const allCandidates = [...verticalCandidates, ...horizontalCandidates];
2478
+ // Thêm isVisibleInViewport cho mỗi candidate
2479
+ return allCandidates.map((candidate) => ({
2480
+ ...candidate,
2481
+ isVisibleInViewport: isElementRectInViewport({
2482
+ top: candidate.top,
2483
+ left: candidate.left,
2484
+ width: rectDimensions.calloutRect.width,
2485
+ height: rectDimensions.calloutRect.height,
2486
+ }, visualViewport || { scrollTop: 0, scrollLeft: 0, width: 0, height: 0 }, options.widthScale),
2487
+ }));
2409
2488
  };
2410
2489
 
2411
2490
  const selectBestPosition = (candidates) => {
2491
+ // return candidates.find((p) => p.valid && p.isVisibleInViewport) || candidates[0];
2412
2492
  return candidates.find((p) => p.valid) || candidates[0];
2413
2493
  };
2414
2494
  const constrainToViewport = (candidate, options) => {
@@ -2493,17 +2573,20 @@ const calcCalloutPosition = (options) => {
2493
2573
  const containerElm = isAbsolute ? calloutElm.parentElement : visualRef?.current;
2494
2574
  if (!containerElm)
2495
2575
  return;
2496
- const viewport = getViewportDimensions(visualRef?.current, scale);
2576
+ const viewport = getContainerViewport(containerElm);
2577
+ const visualViewport = getVisualDomViewport(visualRef?.current, scale);
2497
2578
  const rectDimensions = getElementDimensions({ targetElm, calloutElm, scale, containerElm });
2498
2579
  const containerRect = createAdjustedContainerRect({ containerElm, scale, isAbsolute, visualRef });
2499
2580
  const options = {
2500
2581
  rectDimensions,
2501
2582
  viewport,
2583
+ visualViewport,
2502
2584
  alignment,
2503
2585
  offset,
2504
2586
  padding,
2505
2587
  arrowSize,
2506
2588
  containerRect,
2589
+ widthScale,
2507
2590
  };
2508
2591
  const candidates = generateAllCandidates(options);
2509
2592
  const candidate = selectBestPosition(candidates);
@@ -2520,7 +2603,7 @@ const calcCalloutPosition = (options) => {
2520
2603
  };
2521
2604
  };
2522
2605
  const calcCalloutPositionAbsolute = (props) => {
2523
- const { widthScale, calloutElm, containerElm, element, onChange } = props;
2606
+ const { widthScale, calloutElm, containerElm, element, setPosition } = props;
2524
2607
  const mousePosition = element?.mousePosition;
2525
2608
  if (!mousePosition)
2526
2609
  return;
@@ -2529,14 +2612,7 @@ const calcCalloutPositionAbsolute = (props) => {
2529
2612
  const rawCalloutRect = calloutElm.getBoundingClientRect();
2530
2613
  if (rawCalloutRect.width === 0 || rawCalloutRect.height === 0)
2531
2614
  return;
2532
- const calloutRect = {
2533
- ...rawCalloutRect,
2534
- width: rawCalloutRect.width / widthScale,
2535
- height: rawCalloutRect.height / widthScale,
2536
- };
2537
2615
  const containerRect = containerElm.getBoundingClientRect();
2538
- const containerWidth = containerRect.width / widthScale;
2539
- const containerHeight = containerRect.height / widthScale;
2540
2616
  const mouseX = mousePosition.x;
2541
2617
  const mouseY = mousePosition.y;
2542
2618
  const targetRect = {
@@ -2552,16 +2628,13 @@ const calcCalloutPositionAbsolute = (props) => {
2552
2628
  };
2553
2629
  const rectDimensions = {
2554
2630
  targetRect,
2555
- calloutRect,
2631
+ calloutRect: getScaledCalloutRect(),
2556
2632
  targetAbsoluteRect: {
2557
2633
  top: element.top,
2558
2634
  left: element.left,
2559
2635
  },
2560
2636
  };
2561
- const viewport = {
2562
- width: containerWidth,
2563
- height: containerHeight,
2564
- };
2637
+ const viewport = getContainerViewport(containerElm);
2565
2638
  const options = {
2566
2639
  rectDimensions,
2567
2640
  viewport,
@@ -2570,16 +2643,19 @@ const calcCalloutPositionAbsolute = (props) => {
2570
2643
  padding,
2571
2644
  arrowSize,
2572
2645
  containerRect,
2646
+ widthScale,
2573
2647
  };
2574
2648
  const candidates = generateAllCandidates(options);
2575
2649
  const bestPosition = selectBestPosition(candidates);
2576
- const style = {
2577
- position: 'absolute',
2650
+ const finalPosition = {
2578
2651
  top: bestPosition.top,
2579
2652
  left: bestPosition.left,
2580
- zIndex: 1000,
2653
+ placement: bestPosition.placement,
2654
+ horizontalAlign: bestPosition.horizontalAlign,
2581
2655
  };
2582
- onChange(style);
2656
+ setPosition(finalPosition);
2657
+ // const styleBestPosition = getStyleFromCandidate(bestPosition, widthScale);
2658
+ // onChange(styleBestPosition);
2583
2659
  };
2584
2660
 
2585
2661
  /**
@@ -4395,8 +4471,8 @@ const useElementCalloutVisible = ({ visualRef, getRect, positionMode }) => {
4395
4471
  };
4396
4472
 
4397
4473
  const useHeatmapEffects = ({ isVisible }) => {
4398
- useHeatmapClick((s) => s.selectedElement);
4399
- useHeatmapClick((s) => s.setShouldShowCallout);
4474
+ // const selectedElement = useHeatmapClick((s) => s.selectedElement);
4475
+ // const setShouldShowCallout = useHeatmapClick((s) => s.setShouldShowCallout);
4400
4476
  const resetAll = () => {
4401
4477
  // setShouldShowCallout(false);
4402
4478
  };
@@ -4621,6 +4697,8 @@ const useHoveredElement = ({ iframeRef, getRect }) => {
4621
4697
  };
4622
4698
  };
4623
4699
  const getElementMousePosition = (event, widthScale) => {
4700
+ if (!ELM_CALLOUT_CONFIG.MOUSE_POSITION)
4701
+ return;
4624
4702
  const containerElm = event.target;
4625
4703
  if (!containerElm)
4626
4704
  return;
@@ -6567,7 +6645,7 @@ const useObserveIframeHeight = (props) => {
6567
6645
  };
6568
6646
 
6569
6647
  // Max zoom ratio constant: 100% = fit to width
6570
- const MAX_ZOOM_RATIO = 100;
6648
+ const MAX_ZOOM_RATIO = 200;
6571
6649
  const useScaleCalculation = (props) => {
6572
6650
  const widthScale = useHeatmapViz((s) => s.widthScale);
6573
6651
  const zoomRatio = useHeatmapViz((s) => s.zoomRatio);
@@ -7745,7 +7823,7 @@ VizAreaClick.displayName = 'VizAreaClick';
7745
7823
  const RankBadgeComponent = ({ index, hash, elementRect, widthScale, show = true, clickOnElement, }) => {
7746
7824
  const clickedHash = useHeatmapClick((s) => s.selectedElement?.hash);
7747
7825
  const isShow = !!show && clickedHash !== hash;
7748
- if (!isShow)
7826
+ if (!isShow || !ELM_CALLOUT_CONFIG.SHOW_RANK_BADGE)
7749
7827
  return null;
7750
7828
  const style = calculateRankPosition(elementRect, widthScale);
7751
7829
  return (jsx("div", { className: "gx-hm-rank-badge", style: style, onClick: clickOnElement, children: index }));
@@ -7778,6 +7856,7 @@ const DEFAULT_POSITION = {
7778
7856
  };
7779
7857
  const ElementCallout = (props) => {
7780
7858
  const viewId = useViewIdContext();
7859
+ const widthScale = useHeatmapViz((s) => s.widthScale);
7781
7860
  const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
7782
7861
  const calloutRef = useRef(null);
7783
7862
  const element = props.element;
@@ -7790,9 +7869,7 @@ const ElementCallout = (props) => {
7790
7869
  return null;
7791
7870
  const calloutContent = (jsx("div", { ref: calloutRef, className: className, style: {
7792
7871
  position: positionMode,
7793
- top: position.top,
7794
- left: position.left,
7795
- zIndex: Z_INDEX.CALLOUT,
7872
+ ...getStyleFromCandidate(position, widthScale),
7796
7873
  }, "aria-live": "assertive", role: "tooltip", children: CompElementCallout && jsx(CompElementCallout, { elementHash: element.hash }) }));
7797
7874
  if (!document.getElementById(portalContainerId))
7798
7875
  return null;
@@ -7820,10 +7897,17 @@ const useAnchorPosition = (calloutRef, props) => {
7820
7897
  const calloutElm = calloutRef.current;
7821
7898
  if (!targetElm || !calloutElm)
7822
7899
  return;
7900
+ const onSetPosition = (position) => {
7901
+ setPosition((prev) => {
7902
+ if (prev.top === position.top && prev.left === position.left)
7903
+ return prev;
7904
+ return position;
7905
+ });
7906
+ };
7823
7907
  const positionFn = calcCalloutPosition({
7824
7908
  targetElm,
7825
7909
  calloutElm,
7826
- setPosition,
7910
+ setPosition: onSetPosition,
7827
7911
  alignment,
7828
7912
  positionMode,
7829
7913
  visualRef,
@@ -7974,7 +8058,18 @@ const ElementCalloutOverlay = (props) => {
7974
8058
  const widthScale = useHeatmapViz((s) => s.widthScale);
7975
8059
  const CompElementCallout = useHeatmapControlStore((state) => state.controls.ElementCallout);
7976
8060
  const calloutRef = useRef(null);
7977
- const [calloutStyle, setCalloutStyle] = useState(undefined);
8061
+ const [position, setPosition] = useState({
8062
+ top: 0,
8063
+ left: 0,
8064
+ placement: 'bottom',
8065
+ horizontalAlign: 'left',
8066
+ });
8067
+ const calloutStyle = useMemo(() => {
8068
+ return {
8069
+ ...getStyleFromCandidate(position, widthScale),
8070
+ position: 'absolute',
8071
+ };
8072
+ }, [position, widthScale]);
7978
8073
  useEffect(() => {
7979
8074
  const calloutElm = calloutRef.current;
7980
8075
  const containerElm = containerRef?.current;
@@ -7985,7 +8080,7 @@ const ElementCalloutOverlay = (props) => {
7985
8080
  calloutElm,
7986
8081
  containerElm,
7987
8082
  element,
7988
- onChange: setCalloutStyle,
8083
+ setPosition,
7989
8084
  });
7990
8085
  }, [element, widthScale, containerRef]);
7991
8086
  if (!element)
@@ -7995,7 +8090,7 @@ const ElementCalloutOverlay = (props) => {
7995
8090
  ElementCalloutOverlay.displayName = 'ElementCalloutOverlay';
7996
8091
 
7997
8092
  const ElementOverlayComponent = (props) => {
7998
- const { type, element, onClick, elementId } = props;
8093
+ const { type, element, onClick, elementId, hideOutline } = props;
7999
8094
  const widthScale = useHeatmapViz((s) => s.widthScale);
8000
8095
  const viewportHeight = useHeatmapVizRect((s) => s.iframeHeight);
8001
8096
  const viewportWidth = useHeatmapConfigStore((s) => s.width);
@@ -8015,7 +8110,7 @@ const ElementOverlayComponent = (props) => {
8015
8110
  const isHovered = type === 'hovered';
8016
8111
  const badgeWidthScale = isHovered ? 1 : widthScale;
8017
8112
  const showCallout = !!element?.mousePosition && !isHovered;
8018
- return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: `heatmapElement heatmapElement--${type}`, id: elementId, style: overlayStyle, children: showCallout && jsx(ElementCalloutOverlay, { ...props }) }), jsx(BackdropCanvas, { activeElement: overlayStyle, viewportWidth: viewportWidth, viewportHeight: viewportHeight, show: !isHovered }), jsx(RankBadge, { hash: element.hash, show: isHovered, index: element.rank, elementRect: element, widthScale: badgeWidthScale, clickOnElement: onClick })] }));
8113
+ return (jsxs(Fragment$1, { children: [jsx("div", { onClick: onClick, className: `heatmapElement heatmapElement--${type} ${hideOutline ? 'heatmapElement--hide-outline' : ''}`, id: elementId, style: overlayStyle, children: showCallout && jsx(ElementCalloutOverlay, { ...props }) }), jsx(BackdropCanvas, { activeElement: overlayStyle, viewportWidth: viewportWidth, viewportHeight: viewportHeight, show: !isHovered }), jsx(RankBadge, { hash: element.hash, show: isHovered, index: element.rank, elementRect: element, widthScale: badgeWidthScale, clickOnElement: onClick })] }));
8019
8114
  };
8020
8115
  ElementOverlayComponent.displayName = 'ElementOverlay';
8021
8116
  const ElementOverlay = memo(ElementOverlayComponent);
@@ -8039,6 +8134,7 @@ const ElementCalloutClicked = memo(ElementCalloutClickedComponent);
8039
8134
  const HoveredElementOverlayComponent = ({ onClick }) => {
8040
8135
  const viewId = useViewIdContext();
8041
8136
  const hoveredElement = useHeatmapHover((s) => s.hoveredElement);
8137
+ const clickedElement = useHeatmapClick((s) => s.selectedElement);
8042
8138
  const handleClick = (event) => {
8043
8139
  if (onClick) {
8044
8140
  onClick(event, hoveredElement?.hash ?? '');
@@ -8047,7 +8143,8 @@ const HoveredElementOverlayComponent = ({ onClick }) => {
8047
8143
  if (!hoveredElement)
8048
8144
  return null;
8049
8145
  const elementId = getHoveredElementId(viewId, false);
8050
- return (jsx(Fragment$1, { children: jsx(ElementOverlay, { type: "hovered", element: hoveredElement, elementId: elementId, onClick: handleClick }) }));
8146
+ const hideOutline = clickedElement?.hash === hoveredElement?.hash && ELM_CALLOUT_CONFIG.HIDE_OUTLINE_ON_CLICKED;
8147
+ return (jsx(Fragment$1, { children: jsx(ElementOverlay, { type: "hovered", element: hoveredElement, elementId: elementId, onClick: handleClick, hideOutline: hideOutline }) }));
8051
8148
  };
8052
8149
  const HoveredElementOverlay = memo(HoveredElementOverlayComponent);
8053
8150
 
@@ -8065,22 +8162,16 @@ const HeatmapElements = (props) => {
8065
8162
  const viewId = useViewIdContext();
8066
8163
  const iframeHeight = useHeatmapVizRect((s) => s.iframeHeight);
8067
8164
  const elementCalloutRef = useRef(null);
8068
- const { iframeDimensions, isVisible = true, areDefaultRanksHidden, positionMode } = props;
8069
- const { getRect } = useHeatmapElementPosition({
8070
- iframeRef: props.iframeRef,
8071
- wrapperRef: props.wrapperRef,
8072
- visualizer: props.visualizer,
8073
- });
8074
- const { handleMouseMove, handleMouseLeave, handleClick } = useHoveredElement({
8075
- iframeRef: props.iframeRef,
8076
- getRect,
8077
- });
8078
- useElementCalloutVisible({ visualRef: props.visualRef, getRect, positionMode });
8165
+ const { iframeDimensions, positionMode } = props;
8166
+ const { visualRef, iframeRef, wrapperRef, visualizer } = props;
8167
+ const { isVisible = true, isSecondary, isHideTopRank } = props;
8168
+ const { getRect } = useHeatmapElementPosition({ iframeRef, wrapperRef, visualizer });
8169
+ const { handleMouseMove, handleMouseLeave, handleClick } = useHoveredElement({ iframeRef, getRect });
8170
+ useElementCalloutVisible({ visualRef, getRect, positionMode });
8079
8171
  useHeatmapEffects({ isVisible });
8080
- useRenderCount('HeatmapElements');
8081
8172
  if (!isVisible)
8082
8173
  return null;
8083
- return (jsxs("div", { id: `gx-hm-elements-${viewId}`, ref: elementCalloutRef, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, className: "gx-hm-elements", style: { ...iframeDimensions, height: `${iframeHeight}px` }, children: [jsx(DefaultRankBadges, { getRect: getRect, hidden: areDefaultRanksHidden }), jsx(ElementCalloutClicked, { visualRef: props.visualRef, positionMode: props.positionMode, getRect: getRect, isSecondary: props.isSecondary, containerRef: elementCalloutRef }), jsx(ElementCalloutHovered, { visualRef: props.visualRef, onClick: handleClick, isSecondary: props.isSecondary, positionMode: props.positionMode })] }));
8174
+ return (jsxs("div", { id: `gx-hm-elements-${viewId}`, ref: elementCalloutRef, onMouseMove: handleMouseMove, onMouseLeave: handleMouseLeave, className: "gx-hm-elements", style: { ...iframeDimensions, height: `${iframeHeight}px` }, children: [jsx(DefaultRankBadges, { getRect: getRect, hidden: isHideTopRank }), jsx(ElementCalloutClicked, { visualRef: visualRef, positionMode: positionMode, getRect: getRect, isSecondary: isSecondary, containerRef: elementCalloutRef }), jsx(ElementCalloutHovered, { visualRef: visualRef, onClick: handleClick, isSecondary: isSecondary, positionMode: positionMode })] }));
8084
8175
  };
8085
8176
 
8086
8177
  const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
@@ -8097,7 +8188,7 @@ const VizElements = ({ iframeRef, visualRef, wrapperRef }) => {
8097
8188
  // useRenderCount('VizElements');
8098
8189
  if (!iframeRef.current)
8099
8190
  return null;
8100
- return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo: dataInfo, isVisible: true, positionMode: DEFAULT_POSITION_MODE, areDefaultRanksHidden: true, iframeDimensions: {
8191
+ return (jsx(HeatmapElements, { visualizer: visualizer, visualRef: visualRef, iframeRef: iframeRef, wrapperRef: wrapperRef, heatmapInfo: dataInfo, isVisible: true, positionMode: DEFAULT_POSITION_MODE, isHideTopRank: true, iframeDimensions: {
8101
8192
  width: contentWidth,
8102
8193
  position: 'absolute',
8103
8194
  top: 0,
@@ -8542,4 +8633,4 @@ const HeatmapLayout = ({ data, clickmap, clickAreas, scrollmap, controls, dataIn
8542
8633
  }
8543
8634
  };
8544
8635
 
8545
- export { BACKDROP_CONFIG, DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, IClickMode, IClickType, IHeatmapType, IScrollType, ViewIdContext, Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, downloadPerformanceReport, getCompareViewId, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, performanceLogger, printPerformanceSummary, scrollToElementIfNeeded, sendPerformanceReport, serializeAreas, trackStoreAction, useAreaCreation, useAreaEditMode, useAreaFilterVisible, useAreaHydration, useAreaInteraction, useAreaPositionsUpdater, useAreaRectSync, useAreaRendererContainer, useAreaTopAutoDetect, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapAreaClick, useHeatmapCanvas, useHeatmapClick, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapData, useHeatmapEffects, useHeatmapElementPosition, useHeatmapHover, useHeatmapLiveStore, useHeatmapRenderByMode, useHeatmapScale, useHeatmapScroll, useHeatmapViz, useHeatmapVizRect, useHoveredElement, useIframeHeight, useIframeHeightProcessor, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };
8636
+ export { BACKDROP_CONFIG, DEFAULT_SIDEBAR_WIDTH, DEFAULT_VIEW_ID, ELM_CALLOUT_CONFIG, GraphView, HEATMAP_CONFIG, HEATMAP_IFRAME, HEATMAP_STYLE, HeatmapLayout, IClickMode, IClickType, IHeatmapType, IScrollType, ViewIdContext, Z_INDEX, compareViewPerformance, convertViewportToIframeCoords, createStorePerformanceTracker, downloadPerformanceReport, getCompareViewId, getMetricsByViewId, getPerformanceReportJSON, getScrollGradientColor, performanceLogger, printPerformanceSummary, scrollToElementIfNeeded, sendPerformanceReport, serializeAreas, trackStoreAction, useAreaCreation, useAreaEditMode, useAreaFilterVisible, useAreaHydration, useAreaInteraction, useAreaPositionsUpdater, useAreaRectSync, useAreaRendererContainer, useAreaTopAutoDetect, useClickedElement, useDebounceCallback, useElementCalloutVisible, useHeatmapAreaClick, useHeatmapCanvas, useHeatmapClick, useHeatmapCompareStore, useHeatmapConfigStore, useHeatmapCopyView, useHeatmapData, useHeatmapEffects, useHeatmapElementPosition, useHeatmapHover, useHeatmapLiveStore, useHeatmapRenderByMode, useHeatmapScale, useHeatmapScroll, useHeatmapViz, useHeatmapVizRect, useHoveredElement, useIframeHeight, useIframeHeightProcessor, useMeasureFunction, useRegisterConfig, useRegisterControl, useRegisterData, useRegisterHeatmap, useRenderCount, useScrollmapZones, useTrackHookCall, useViewIdContext, useVizLiveRender, useWhyDidYouUpdate, useWrapperRefHeight, useZonePositions, withPerformanceTracking };