@ssaprt/tooltip 1.0.0 → 1.0.3

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/index.mjs CHANGED
@@ -354,7 +354,7 @@ var presets = {
354
354
  animation: animations.bounce
355
355
  }),
356
356
  hologram: createTheme({
357
- background: "linear-gradient(115deg, rgba(34, 211, 238, 0.42), rgba(168, 85, 247, 0.35), rgba(236, 72, 153, 0.35), rgba(34, 211, 238, 0.42)), rgba(8, 47, 73, 0.72)",
357
+ background: "linear-gradient(115deg, rgba(34, 211, 238, 0.42), rgba(168, 85, 247, 0.35), rgba(236, 72, 153, 0.35), rgba(34, 211, 238, 0.42)), linear-gradient(135deg, #082f49, #312e81)",
358
358
  color: "#ecfeff",
359
359
  fontFamily: "Segoe UI, Arial, sans-serif",
360
360
  fontSize: "12px",
@@ -364,25 +364,23 @@ var presets = {
364
364
  filter: "drop-shadow(0 0 7px rgba(34, 211, 238, 0.75)) drop-shadow(0 0 16px rgba(168, 85, 247, 0.4))",
365
365
  padding: "9px 15px",
366
366
  letterSpacing: "0.06em",
367
- backdropFilter: "blur(8px)",
368
367
  arrowSize: "8px",
369
368
  arrowWidth: "18px",
370
369
  animation: animations.blur
371
370
  }),
372
371
  glass: createTheme({
373
- background: "linear-gradient(135deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.07)), rgba(15, 23, 42, 0.68)",
372
+ background: "linear-gradient(135deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.07)), linear-gradient(135deg, #1e293b, #0f172a)",
374
373
  color: "#f8fafc",
375
374
  border: "1px solid rgba(255, 255, 255, 0.34)",
376
375
  borderRadius: "16px",
377
376
  filter: "drop-shadow(0 12px 20px rgba(15, 23, 42, 0.38))",
378
377
  padding: "9px 15px",
379
- backdropFilter: "blur(16px)",
380
378
  arrowSize: "8px",
381
379
  arrowWidth: "18px",
382
380
  animation: animations.blur
383
381
  }),
384
382
  frost: createTheme({
385
- background: "radial-gradient(circle at 15% 20%, rgba(255, 255, 255, 0.85), transparent 35%), linear-gradient(135deg, rgba(224, 242, 254, 0.92), rgba(186, 230, 253, 0.72))",
383
+ background: "radial-gradient(circle at 15% 20%, rgba(255, 255, 255, 0.85), transparent 35%), linear-gradient(135deg, #e0f2fe, #bae6fd)",
386
384
  color: "#075985",
387
385
  fontWeight: 600,
388
386
  border: "1px solid rgba(125, 211, 252, 0.9)",
@@ -390,7 +388,6 @@ var presets = {
390
388
  filter: "drop-shadow(0 8px 15px rgba(14, 116, 144, 0.24))",
391
389
  padding: "9px 15px",
392
390
  textShadow: "0 1px 0 rgba(255, 255, 255, 0.8)",
393
- backdropFilter: "blur(14px)",
394
391
  arrowSize: "8px",
395
392
  arrowWidth: "19px",
396
393
  animation: animations.blur
@@ -755,7 +752,6 @@ var presets = {
755
752
  borderRadius: "20px 8px 20px 8px",
756
753
  filter: "drop-shadow(0 10px 17px rgba(49, 46, 129, 0.48))",
757
754
  padding: "10px 16px",
758
- backdropFilter: "blur(8px)",
759
755
  arrowSize: "9px",
760
756
  arrowWidth: "21px",
761
757
  animation: animations.blur
@@ -948,6 +944,13 @@ var VIEWPORT_PADDING = 8;
948
944
  var DEFAULT_ARROW_SIZE = 6;
949
945
  var DEFAULT_TOOLTIP_GAP = 10;
950
946
  var ARROW_EDGE_OFFSET = 10;
947
+ var INTERACTIVE_DIRECT_PADDING = 4;
948
+ var INTERACTIVE_BRIDGE_PADDING = 8;
949
+ var INTERACTIVE_BRIDGE_TIMEOUT = 700;
950
+ var INTERACTIVE_RECHECK_INTERVAL = 50;
951
+ var TOUCH_MOVE_THRESHOLD = 10;
952
+ var TOUCH_MAX_TAP_DURATION = 600;
953
+ var TOUCH_FOCUS_SUPPRESSION = 700;
951
954
  var oppositePlacement = {
952
955
  top: "bottom",
953
956
  bottom: "top",
@@ -958,6 +961,112 @@ var clamp = (value, min, max) => {
958
961
  const resolvedMax = Math.max(min, max);
959
962
  return Math.min(Math.max(value, min), resolvedMax);
960
963
  };
964
+ var isPointInsideRect = (point, rect, padding = 0) => {
965
+ return point.x >= rect.left - padding && point.x <= rect.right + padding && point.y >= rect.top - padding && point.y <= rect.bottom + padding;
966
+ };
967
+ var isPointOnSegment = (point, start, end) => {
968
+ const cross = (point.y - start.y) * (end.x - start.x) - (point.x - start.x) * (end.y - start.y);
969
+ if (Math.abs(cross) > 1e-3) {
970
+ return false;
971
+ }
972
+ return point.x >= Math.min(start.x, end.x) && point.x <= Math.max(start.x, end.x) && point.y >= Math.min(start.y, end.y) && point.y <= Math.max(start.y, end.y);
973
+ };
974
+ var isPointInsidePolygon = (point, polygon) => {
975
+ let inside = false;
976
+ for (let currentIndex = 0, previousIndex = polygon.length - 1; currentIndex < polygon.length; previousIndex = currentIndex, currentIndex += 1) {
977
+ const current = polygon[currentIndex];
978
+ const previous = polygon[previousIndex];
979
+ if (isPointOnSegment(point, previous, current)) {
980
+ return true;
981
+ }
982
+ const crosses = current.y > point.y !== previous.y > point.y && point.x < (previous.x - current.x) * (point.y - current.y) / (previous.y - current.y) + current.x;
983
+ if (crosses) {
984
+ inside = !inside;
985
+ }
986
+ }
987
+ return inside;
988
+ };
989
+ var createInteractiveBridge = (anchorRect, tooltipRect, placement) => {
990
+ const padding = INTERACTIVE_BRIDGE_PADDING;
991
+ if (placement === "top") {
992
+ return [
993
+ {
994
+ x: tooltipRect.left - padding,
995
+ y: tooltipRect.bottom - padding
996
+ },
997
+ {
998
+ x: tooltipRect.right + padding,
999
+ y: tooltipRect.bottom - padding
1000
+ },
1001
+ {
1002
+ x: anchorRect.right + padding,
1003
+ y: anchorRect.top + padding
1004
+ },
1005
+ {
1006
+ x: anchorRect.left - padding,
1007
+ y: anchorRect.top + padding
1008
+ }
1009
+ ];
1010
+ }
1011
+ if (placement === "bottom") {
1012
+ return [
1013
+ {
1014
+ x: anchorRect.left - padding,
1015
+ y: anchorRect.bottom - padding
1016
+ },
1017
+ {
1018
+ x: anchorRect.right + padding,
1019
+ y: anchorRect.bottom - padding
1020
+ },
1021
+ {
1022
+ x: tooltipRect.right + padding,
1023
+ y: tooltipRect.top + padding
1024
+ },
1025
+ {
1026
+ x: tooltipRect.left - padding,
1027
+ y: tooltipRect.top + padding
1028
+ }
1029
+ ];
1030
+ }
1031
+ if (placement === "left") {
1032
+ return [
1033
+ {
1034
+ x: tooltipRect.right - padding,
1035
+ y: tooltipRect.top - padding
1036
+ },
1037
+ {
1038
+ x: anchorRect.left + padding,
1039
+ y: anchorRect.top - padding
1040
+ },
1041
+ {
1042
+ x: anchorRect.left + padding,
1043
+ y: anchorRect.bottom + padding
1044
+ },
1045
+ {
1046
+ x: tooltipRect.right - padding,
1047
+ y: tooltipRect.bottom + padding
1048
+ }
1049
+ ];
1050
+ }
1051
+ return [
1052
+ {
1053
+ x: anchorRect.right - padding,
1054
+ y: anchorRect.top - padding
1055
+ },
1056
+ {
1057
+ x: tooltipRect.left + padding,
1058
+ y: tooltipRect.top - padding
1059
+ },
1060
+ {
1061
+ x: tooltipRect.left + padding,
1062
+ y: tooltipRect.bottom + padding
1063
+ },
1064
+ {
1065
+ x: anchorRect.right - padding,
1066
+ y: anchorRect.bottom + padding
1067
+ }
1068
+ ];
1069
+ };
961
1070
  var useTooltip = ({
962
1071
  anchor,
963
1072
  preferredPlacement,
@@ -970,8 +1079,17 @@ var useTooltip = ({
970
1079
  const hideTimerRef = useRef(null);
971
1080
  const firstFrameRef = useRef(null);
972
1081
  const secondFrameRef = useRef(null);
1082
+ const interactiveFrameRef = useRef(null);
973
1083
  const phaseRef = useRef("hidden");
974
1084
  const pendingHideRef = useRef(false);
1085
+ const interactiveBridgeDeadlineRef = useRef(0);
1086
+ const pointerRef = useRef({
1087
+ x: Number.NaN,
1088
+ y: Number.NaN,
1089
+ pointerType: ""
1090
+ });
1091
+ const touchGestureRef = useRef(null);
1092
+ const suppressFocusUntilRef = useRef(0);
975
1093
  const [mounted, setMounted] = useState(false);
976
1094
  const [phase, setPhase] = useState("hidden");
977
1095
  const [placement, setPlacement] = useState(preferredPlacement);
@@ -1003,6 +1121,13 @@ var useTooltip = ({
1003
1121
  secondFrameRef.current = null;
1004
1122
  }
1005
1123
  }, []);
1124
+ const clearInteractiveFrame = useCallback(() => {
1125
+ if (interactiveFrameRef.current === null) {
1126
+ return;
1127
+ }
1128
+ window.cancelAnimationFrame(interactiveFrameRef.current);
1129
+ interactiveFrameRef.current = null;
1130
+ }, []);
1006
1131
  const getAvailableSpace = useCallback(
1007
1132
  (currentPlacement, rect) => {
1008
1133
  switch (currentPlacement) {
@@ -1042,10 +1167,7 @@ var useTooltip = ({
1042
1167
  }
1043
1168
  return bodyWidth + distance;
1044
1169
  };
1045
- const preferredAvailable = getAvailableSpace(
1046
- preferredPlacement,
1047
- rect
1048
- );
1170
+ const preferredAvailable = getAvailableSpace(preferredPlacement, rect);
1049
1171
  const preferredRequired = getRequiredSpace(preferredPlacement);
1050
1172
  let nextPlacement = preferredPlacement;
1051
1173
  if (preferredAvailable < preferredRequired) {
@@ -1103,12 +1225,61 @@ var useTooltip = ({
1103
1225
  "--tooltip-arrow-offset": arrowOffset
1104
1226
  });
1105
1227
  }, [anchor, getAvailableSpace, preferredPlacement]);
1228
+ const isFocusInsideTooltipRegion = useCallback(() => {
1229
+ if (!anchor || typeof document === "undefined") {
1230
+ return false;
1231
+ }
1232
+ const activeElement = document.activeElement;
1233
+ const tooltip = tooltipRef.current;
1234
+ if (!(activeElement instanceof Node)) {
1235
+ return false;
1236
+ }
1237
+ if (interactive && tooltip?.contains(activeElement)) {
1238
+ return true;
1239
+ }
1240
+ return anchor.contains(activeElement) && activeElement instanceof Element && activeElement.matches(":focus-visible");
1241
+ }, [anchor, interactive]);
1242
+ const getInteractivePointerArea = useCallback(() => {
1243
+ if (!interactive || !anchor) {
1244
+ return "outside";
1245
+ }
1246
+ const pointer = pointerRef.current;
1247
+ const tooltip = tooltipRef.current;
1248
+ if (!tooltip || pointer.pointerType !== "mouse" && pointer.pointerType !== "pen" || !Number.isFinite(pointer.x) || !Number.isFinite(pointer.y)) {
1249
+ return "outside";
1250
+ }
1251
+ const point = {
1252
+ x: pointer.x,
1253
+ y: pointer.y
1254
+ };
1255
+ const anchorRect = anchor.getBoundingClientRect();
1256
+ const tooltipRect = tooltip.getBoundingClientRect();
1257
+ if (isPointInsideRect(
1258
+ point,
1259
+ anchorRect,
1260
+ INTERACTIVE_DIRECT_PADDING
1261
+ ) || isPointInsideRect(
1262
+ point,
1263
+ tooltipRect,
1264
+ INTERACTIVE_DIRECT_PADDING
1265
+ )) {
1266
+ return "direct";
1267
+ }
1268
+ const bridge = createInteractiveBridge(
1269
+ anchorRect,
1270
+ tooltipRect,
1271
+ placement
1272
+ );
1273
+ return isPointInsidePolygon(point, bridge) ? "bridge" : "outside";
1274
+ }, [anchor, interactive, placement]);
1106
1275
  const removeTooltip = useCallback(() => {
1107
1276
  clearHideTimer();
1108
1277
  clearEnterFrames();
1278
+ clearInteractiveFrame();
1109
1279
  pendingHideRef.current = false;
1280
+ interactiveBridgeDeadlineRef.current = 0;
1110
1281
  updatePhase("hidden");
1111
- }, [clearEnterFrames, clearHideTimer, updatePhase]);
1282
+ }, [clearEnterFrames, clearHideTimer, clearInteractiveFrame, updatePhase]);
1112
1283
  const runHide = useCallback(() => {
1113
1284
  const currentPhase = phaseRef.current;
1114
1285
  if (currentPhase === "hidden" || currentPhase === "leaving") {
@@ -1126,17 +1297,61 @@ var useTooltip = ({
1126
1297
  }, [removeTooltip, updatePhase]);
1127
1298
  const hideTooltip = useCallback(
1128
1299
  (immediate = false) => {
1129
- clearHideTimer();
1130
1300
  if (immediate) {
1301
+ clearHideTimer();
1302
+ interactiveBridgeDeadlineRef.current = 0;
1131
1303
  runHide();
1132
1304
  return;
1133
1305
  }
1134
- hideTimerRef.current = window.setTimeout(() => {
1306
+ if (hideTimerRef.current !== null) {
1307
+ return;
1308
+ }
1309
+ const attemptHide = () => {
1135
1310
  hideTimerRef.current = null;
1311
+ if (isFocusInsideTooltipRegion()) {
1312
+ interactiveBridgeDeadlineRef.current = 0;
1313
+ return;
1314
+ }
1315
+ if (interactive) {
1316
+ const pointerArea = getInteractivePointerArea();
1317
+ if (pointerArea === "direct") {
1318
+ interactiveBridgeDeadlineRef.current = 0;
1319
+ return;
1320
+ }
1321
+ if (pointerArea === "bridge") {
1322
+ const now = window.performance.now();
1323
+ if (interactiveBridgeDeadlineRef.current === 0) {
1324
+ interactiveBridgeDeadlineRef.current = now + INTERACTIVE_BRIDGE_TIMEOUT;
1325
+ }
1326
+ const remaining = interactiveBridgeDeadlineRef.current - now;
1327
+ if (remaining > 0) {
1328
+ hideTimerRef.current = window.setTimeout(
1329
+ attemptHide,
1330
+ Math.min(
1331
+ INTERACTIVE_RECHECK_INTERVAL,
1332
+ remaining
1333
+ )
1334
+ );
1335
+ return;
1336
+ }
1337
+ }
1338
+ }
1339
+ interactiveBridgeDeadlineRef.current = 0;
1136
1340
  runHide();
1137
- }, resolvedHideDelay);
1341
+ };
1342
+ hideTimerRef.current = window.setTimeout(
1343
+ attemptHide,
1344
+ resolvedHideDelay
1345
+ );
1138
1346
  },
1139
- [clearHideTimer, resolvedHideDelay, runHide]
1347
+ [
1348
+ clearHideTimer,
1349
+ getInteractivePointerArea,
1350
+ interactive,
1351
+ isFocusInsideTooltipRegion,
1352
+ resolvedHideDelay,
1353
+ runHide
1354
+ ]
1140
1355
  );
1141
1356
  const showTooltip = useCallback(() => {
1142
1357
  if (!anchor || disabled) {
@@ -1144,6 +1359,7 @@ var useTooltip = ({
1144
1359
  }
1145
1360
  clearHideTimer();
1146
1361
  clearEnterFrames();
1362
+ interactiveBridgeDeadlineRef.current = 0;
1147
1363
  pendingHideRef.current = false;
1148
1364
  setMounted(true);
1149
1365
  if (phaseRef.current === "entering" || phaseRef.current === "visible") {
@@ -1157,11 +1373,51 @@ var useTooltip = ({
1157
1373
  return;
1158
1374
  }
1159
1375
  clearHideTimer();
1376
+ interactiveBridgeDeadlineRef.current = 0;
1160
1377
  pendingHideRef.current = false;
1161
1378
  if (phaseRef.current === "leaving") {
1162
1379
  updatePhase("visible");
1163
1380
  }
1164
1381
  }, [clearHideTimer, disabled, interactive, updatePhase]);
1382
+ const processInteractivePointer = useCallback(() => {
1383
+ if (!interactive || disabled || phaseRef.current === "hidden") {
1384
+ return;
1385
+ }
1386
+ if (isFocusInsideTooltipRegion()) {
1387
+ keepTooltipOpen();
1388
+ return;
1389
+ }
1390
+ const pointerArea = getInteractivePointerArea();
1391
+ if (pointerArea === "direct") {
1392
+ keepTooltipOpen();
1393
+ return;
1394
+ }
1395
+ if (pointerArea === "bridge") {
1396
+ if (interactiveBridgeDeadlineRef.current === 0) {
1397
+ interactiveBridgeDeadlineRef.current = window.performance.now() + INTERACTIVE_BRIDGE_TIMEOUT;
1398
+ }
1399
+ hideTooltip(false);
1400
+ return;
1401
+ }
1402
+ interactiveBridgeDeadlineRef.current = 0;
1403
+ hideTooltip(false);
1404
+ }, [
1405
+ disabled,
1406
+ getInteractivePointerArea,
1407
+ hideTooltip,
1408
+ interactive,
1409
+ isFocusInsideTooltipRegion,
1410
+ keepTooltipOpen
1411
+ ]);
1412
+ const scheduleInteractivePointerProcessing = useCallback(() => {
1413
+ if (interactiveFrameRef.current !== null) {
1414
+ return;
1415
+ }
1416
+ interactiveFrameRef.current = window.requestAnimationFrame(() => {
1417
+ interactiveFrameRef.current = null;
1418
+ processInteractivePointer();
1419
+ });
1420
+ }, [processInteractivePointer]);
1165
1421
  const onAnimationEnd = useCallback(() => {
1166
1422
  const currentPhase = phaseRef.current;
1167
1423
  if (currentPhase === "entering") {
@@ -1206,35 +1462,112 @@ var useTooltip = ({
1206
1462
  removeTooltip();
1207
1463
  return;
1208
1464
  }
1465
+ const updatePointer = (event) => {
1466
+ pointerRef.current = {
1467
+ x: event.clientX,
1468
+ y: event.clientY,
1469
+ pointerType: event.pointerType
1470
+ };
1471
+ };
1209
1472
  const onPointerEnter = (event) => {
1473
+ updatePointer(event);
1210
1474
  if (event.pointerType === "touch") {
1211
1475
  return;
1212
1476
  }
1213
1477
  showTooltip();
1214
1478
  };
1215
1479
  const onPointerLeave = (event) => {
1480
+ updatePointer(event);
1216
1481
  if (event.pointerType === "touch") {
1217
1482
  return;
1218
1483
  }
1219
- const relatedTarget = event.relatedTarget;
1220
- const tooltip = tooltipRef.current;
1221
- if (interactive && relatedTarget instanceof Node && tooltip?.contains(relatedTarget)) {
1222
- keepTooltipOpen();
1484
+ if (interactive) {
1485
+ interactiveBridgeDeadlineRef.current = window.performance.now() + INTERACTIVE_BRIDGE_TIMEOUT;
1486
+ hideTooltip(false);
1487
+ scheduleInteractivePointerProcessing();
1223
1488
  return;
1224
1489
  }
1225
1490
  hideTooltip(false);
1226
1491
  };
1227
1492
  const onPointerDown = (event) => {
1493
+ updatePointer(event);
1494
+ if (event.pointerType !== "touch") {
1495
+ return;
1496
+ }
1497
+ const now = window.performance.now();
1498
+ touchGestureRef.current = {
1499
+ pointerId: event.pointerId,
1500
+ startX: event.clientX,
1501
+ startY: event.clientY,
1502
+ startedAt: now,
1503
+ moved: false,
1504
+ cancelled: false
1505
+ };
1506
+ suppressFocusUntilRef.current = now + TOUCH_FOCUS_SUPPRESSION;
1507
+ };
1508
+ const onPointerMove = (event) => {
1509
+ updatePointer(event);
1510
+ if (event.pointerType !== "touch") {
1511
+ return;
1512
+ }
1513
+ const gesture = touchGestureRef.current;
1514
+ if (!gesture || gesture.pointerId !== event.pointerId) {
1515
+ return;
1516
+ }
1517
+ const distance = Math.hypot(
1518
+ event.clientX - gesture.startX,
1519
+ event.clientY - gesture.startY
1520
+ );
1521
+ if (distance <= TOUCH_MOVE_THRESHOLD) {
1522
+ return;
1523
+ }
1524
+ gesture.moved = true;
1525
+ suppressFocusUntilRef.current = window.performance.now() + TOUCH_FOCUS_SUPPRESSION;
1526
+ hideTooltip(true);
1527
+ };
1528
+ const onPointerUp = (event) => {
1529
+ updatePointer(event);
1228
1530
  if (event.pointerType !== "touch") {
1229
1531
  return;
1230
1532
  }
1533
+ const gesture = touchGestureRef.current;
1534
+ touchGestureRef.current = null;
1535
+ if (!gesture || gesture.pointerId !== event.pointerId) {
1536
+ return;
1537
+ }
1538
+ const now = window.performance.now();
1539
+ const distance = Math.hypot(
1540
+ event.clientX - gesture.startX,
1541
+ event.clientY - gesture.startY
1542
+ );
1543
+ const duration = now - gesture.startedAt;
1544
+ suppressFocusUntilRef.current = now + TOUCH_FOCUS_SUPPRESSION;
1545
+ if (gesture.cancelled || gesture.moved || distance > TOUCH_MOVE_THRESHOLD || duration > TOUCH_MAX_TAP_DURATION) {
1546
+ return;
1547
+ }
1231
1548
  if (phaseRef.current === "hidden" || phaseRef.current === "leaving") {
1232
1549
  showTooltip();
1233
1550
  } else {
1234
1551
  hideTooltip(true);
1235
1552
  }
1236
1553
  };
1554
+ const onPointerCancel = (event) => {
1555
+ if (event.pointerType !== "touch") {
1556
+ return;
1557
+ }
1558
+ const gesture = touchGestureRef.current;
1559
+ if (!gesture || gesture.pointerId !== event.pointerId) {
1560
+ return;
1561
+ }
1562
+ gesture.cancelled = true;
1563
+ touchGestureRef.current = null;
1564
+ suppressFocusUntilRef.current = window.performance.now() + TOUCH_FOCUS_SUPPRESSION;
1565
+ hideTooltip(true);
1566
+ };
1237
1567
  const onFocusIn = () => {
1568
+ if (window.performance.now() < suppressFocusUntilRef.current) {
1569
+ return;
1570
+ }
1238
1571
  showTooltip();
1239
1572
  };
1240
1573
  const onFocusOut = (event) => {
@@ -1248,51 +1581,66 @@ var useTooltip = ({
1248
1581
  anchor.addEventListener("pointerenter", onPointerEnter);
1249
1582
  anchor.addEventListener("pointerleave", onPointerLeave);
1250
1583
  anchor.addEventListener("pointerdown", onPointerDown);
1584
+ anchor.addEventListener("pointermove", onPointerMove);
1585
+ anchor.addEventListener("pointerup", onPointerUp);
1586
+ anchor.addEventListener("pointercancel", onPointerCancel);
1251
1587
  anchor.addEventListener("focusin", onFocusIn);
1252
1588
  anchor.addEventListener("focusout", onFocusOut);
1253
1589
  return () => {
1254
1590
  anchor.removeEventListener("pointerenter", onPointerEnter);
1255
1591
  anchor.removeEventListener("pointerleave", onPointerLeave);
1256
1592
  anchor.removeEventListener("pointerdown", onPointerDown);
1593
+ anchor.removeEventListener("pointermove", onPointerMove);
1594
+ anchor.removeEventListener("pointerup", onPointerUp);
1595
+ anchor.removeEventListener("pointercancel", onPointerCancel);
1257
1596
  anchor.removeEventListener("focusin", onFocusIn);
1258
1597
  anchor.removeEventListener("focusout", onFocusOut);
1598
+ touchGestureRef.current = null;
1259
1599
  clearHideTimer();
1260
1600
  clearEnterFrames();
1601
+ clearInteractiveFrame();
1261
1602
  };
1262
1603
  }, [
1263
1604
  anchor,
1264
1605
  clearEnterFrames,
1265
1606
  clearHideTimer,
1607
+ clearInteractiveFrame,
1266
1608
  hideTooltip,
1267
1609
  interactive,
1268
- keepTooltipOpen,
1269
1610
  removeTooltip,
1611
+ scheduleInteractivePointerProcessing,
1270
1612
  showTooltip
1271
1613
  ]);
1272
1614
  useEffect(() => {
1273
- if (!interactive || !mounted) {
1615
+ if (!interactive || phase === "hidden") {
1274
1616
  return;
1275
1617
  }
1276
1618
  const tooltip = tooltipRef.current;
1277
1619
  if (!tooltip) {
1278
1620
  return;
1279
1621
  }
1622
+ const updatePointer = (event) => {
1623
+ pointerRef.current = {
1624
+ x: event.clientX,
1625
+ y: event.clientY,
1626
+ pointerType: event.pointerType
1627
+ };
1628
+ };
1280
1629
  const onPointerEnter = (event) => {
1630
+ updatePointer(event);
1281
1631
  if (event.pointerType === "touch") {
1282
1632
  return;
1283
1633
  }
1284
1634
  keepTooltipOpen();
1285
1635
  };
1286
1636
  const onPointerLeave = (event) => {
1637
+ updatePointer(event);
1287
1638
  if (event.pointerType === "touch") {
1288
1639
  return;
1289
1640
  }
1290
- const relatedTarget = event.relatedTarget;
1291
- if (relatedTarget instanceof Node && anchor?.contains(relatedTarget)) {
1292
- keepTooltipOpen();
1293
- return;
1294
- }
1641
+ interactiveBridgeDeadlineRef.current = window.performance.now() + INTERACTIVE_BRIDGE_TIMEOUT;
1295
1642
  hideTooltip(false);
1643
+ scheduleInteractivePointerProcessing();
1296
1644
  };
1297
1645
  const onFocusIn = () => {
1298
1646
  keepTooltipOpen();
@@ -1319,7 +1667,36 @@ var useTooltip = ({
1319
1667
  hideTooltip,
1320
1668
  interactive,
1321
1669
  keepTooltipOpen,
1322
- mounted
1670
+ phase,
1671
+ scheduleInteractivePointerProcessing
1672
+ ]);
1673
+ useEffect(() => {
1674
+ if (!interactive || phase === "hidden") {
1675
+ return;
1676
+ }
1677
+ const onDocumentPointerMove = (event) => {
1678
+ if (event.pointerType === "touch") {
1679
+ return;
1680
+ }
1681
+ pointerRef.current = {
1682
+ x: event.clientX,
1683
+ y: event.clientY,
1684
+ pointerType: event.pointerType
1685
+ };
1686
+ scheduleInteractivePointerProcessing();
1687
+ };
1688
+ document.addEventListener("pointermove", onDocumentPointerMove, {
1689
+ passive: true
1690
+ });
1691
+ return () => {
1692
+ document.removeEventListener("pointermove", onDocumentPointerMove);
1693
+ clearInteractiveFrame();
1694
+ };
1695
+ }, [
1696
+ clearInteractiveFrame,
1697
+ interactive,
1698
+ phase,
1699
+ scheduleInteractivePointerProcessing
1323
1700
  ]);
1324
1701
  useEffect(() => {
1325
1702
  if (!anchor || phase === "hidden") {
@@ -1331,6 +1708,7 @@ var useTooltip = ({
1331
1708
  if (target instanceof Node && (anchor.contains(target) || interactive && tooltip?.contains(target))) {
1332
1709
  return;
1333
1710
  }
1711
+ touchGestureRef.current = null;
1334
1712
  hideTooltip(true);
1335
1713
  };
1336
1714
  const onKeyDown = (event) => {
@@ -1338,11 +1716,7 @@ var useTooltip = ({
1338
1716
  hideTooltip(true);
1339
1717
  }
1340
1718
  };
1341
- document.addEventListener(
1342
- "pointerdown",
1343
- onDocumentPointerDown,
1344
- true
1345
- );
1719
+ document.addEventListener("pointerdown", onDocumentPointerDown, true);
1346
1720
  document.addEventListener("keydown", onKeyDown);
1347
1721
  window.addEventListener("resize", calculatePosition);
1348
1722
  window.addEventListener("scroll", calculatePosition, true);
@@ -1356,13 +1730,7 @@ var useTooltip = ({
1356
1730
  window.removeEventListener("resize", calculatePosition);
1357
1731
  window.removeEventListener("scroll", calculatePosition, true);
1358
1732
  };
1359
- }, [
1360
- anchor,
1361
- calculatePosition,
1362
- hideTooltip,
1363
- interactive,
1364
- phase
1365
- ]);
1733
+ }, [anchor, calculatePosition, hideTooltip, interactive, phase]);
1366
1734
  useLayoutEffect(() => {
1367
1735
  if (phase === "hidden") {
1368
1736
  return;
@@ -1729,7 +2097,8 @@ import { createContext, useContext, useMemo } from "react";
1729
2097
  import { jsx } from "react/jsx-runtime";
1730
2098
  var DEFAULT_TOOLTIP_VALUES = {
1731
2099
  defaultRenderPosition: "top",
1732
- selectTheme: "primary"
2100
+ selectTheme: "primary",
2101
+ interactive: false
1733
2102
  };
1734
2103
  var TooltipContext = createContext(DEFAULT_TOOLTIP_VALUES);
1735
2104
  var TooltipProvider = ({
@@ -1737,16 +2106,27 @@ var TooltipProvider = ({
1737
2106
  defaultRenderPosition = DEFAULT_TOOLTIP_VALUES.defaultRenderPosition,
1738
2107
  selectTheme = DEFAULT_TOOLTIP_VALUES.selectTheme,
1739
2108
  customTheme,
1740
- animation
2109
+ animation,
2110
+ interactive = DEFAULT_TOOLTIP_VALUES.interactive,
2111
+ hideDelay
1741
2112
  }) => {
1742
2113
  const value = useMemo(() => {
1743
2114
  return {
1744
2115
  defaultRenderPosition,
1745
2116
  selectTheme,
1746
2117
  customTheme,
1747
- animation
2118
+ animation,
2119
+ interactive,
2120
+ hideDelay
1748
2121
  };
1749
- }, [animation, customTheme, defaultRenderPosition, selectTheme]);
2122
+ }, [
2123
+ animation,
2124
+ customTheme,
2125
+ defaultRenderPosition,
2126
+ hideDelay,
2127
+ interactive,
2128
+ selectTheme
2129
+ ]);
1750
2130
  return /* @__PURE__ */ jsx(TooltipContext.Provider, { value, children });
1751
2131
  };
1752
2132
  var useTooltipDefaults = () => {
@@ -2211,7 +2591,7 @@ var Tooltip = ({
2211
2591
  customTheme,
2212
2592
  animation,
2213
2593
  disabled = false,
2214
- interactive = false,
2594
+ interactive,
2215
2595
  hideDelay
2216
2596
  }) => {
2217
2597
  const defaults = useTooltipDefaults();
@@ -2255,6 +2635,8 @@ var Tooltip = ({
2255
2635
  const animationSpeed = animation?.speed ?? localThemeAnimation?.speed ?? defaults.animation?.speed ?? inheritedTheme.animation?.speed ?? "120ms";
2256
2636
  const animationEasing = animation?.easing ?? localThemeAnimation?.easing ?? defaults.animation?.easing ?? inheritedTheme.animation?.easing ?? "ease-in-out";
2257
2637
  const preferredPlacement = position ?? defaults.defaultRenderPosition;
2638
+ const resolvedInteractive = interactive ?? defaults.interactive;
2639
+ const resolvedHideDelay = hideDelay ?? defaults.hideDelay;
2258
2640
  const {
2259
2641
  shouldRender,
2260
2642
  phase,
@@ -2267,8 +2649,8 @@ var Tooltip = ({
2267
2649
  anchor,
2268
2650
  preferredPlacement,
2269
2651
  disabled: disabled || content === null || content === void 0,
2270
- interactive,
2271
- hideDelay
2652
+ interactive: resolvedInteractive,
2653
+ hideDelay: resolvedHideDelay
2272
2654
  });
2273
2655
  const arrowSize = theme.arrow?.size ?? "6px";
2274
2656
  const arrowWidth = theme.arrow?.width ?? `calc(${arrowSize} * 2)`;
@@ -2304,7 +2686,7 @@ var Tooltip = ({
2304
2686
  `tooltip-container--phase-${phase}`,
2305
2687
  `tooltip-container--show-${showAnimation}`,
2306
2688
  `tooltip-container--hide-${hideAnimation}`,
2307
- interactive ? "tooltip-container--interactive" : null
2689
+ resolvedInteractive ? "tooltip-container--interactive" : null
2308
2690
  ].filter(Boolean).join(" "),
2309
2691
  style: containerStyle,
2310
2692
  children: /* @__PURE__ */ jsxs2(