@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.js CHANGED
@@ -372,7 +372,7 @@ var presets = {
372
372
  animation: animations.bounce
373
373
  }),
374
374
  hologram: createTheme({
375
- 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)",
375
+ 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)",
376
376
  color: "#ecfeff",
377
377
  fontFamily: "Segoe UI, Arial, sans-serif",
378
378
  fontSize: "12px",
@@ -382,25 +382,23 @@ var presets = {
382
382
  filter: "drop-shadow(0 0 7px rgba(34, 211, 238, 0.75)) drop-shadow(0 0 16px rgba(168, 85, 247, 0.4))",
383
383
  padding: "9px 15px",
384
384
  letterSpacing: "0.06em",
385
- backdropFilter: "blur(8px)",
386
385
  arrowSize: "8px",
387
386
  arrowWidth: "18px",
388
387
  animation: animations.blur
389
388
  }),
390
389
  glass: createTheme({
391
- background: "linear-gradient(135deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.07)), rgba(15, 23, 42, 0.68)",
390
+ background: "linear-gradient(135deg, rgba(255, 255, 255, 0.22), rgba(255, 255, 255, 0.07)), linear-gradient(135deg, #1e293b, #0f172a)",
392
391
  color: "#f8fafc",
393
392
  border: "1px solid rgba(255, 255, 255, 0.34)",
394
393
  borderRadius: "16px",
395
394
  filter: "drop-shadow(0 12px 20px rgba(15, 23, 42, 0.38))",
396
395
  padding: "9px 15px",
397
- backdropFilter: "blur(16px)",
398
396
  arrowSize: "8px",
399
397
  arrowWidth: "18px",
400
398
  animation: animations.blur
401
399
  }),
402
400
  frost: createTheme({
403
- 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))",
401
+ background: "radial-gradient(circle at 15% 20%, rgba(255, 255, 255, 0.85), transparent 35%), linear-gradient(135deg, #e0f2fe, #bae6fd)",
404
402
  color: "#075985",
405
403
  fontWeight: 600,
406
404
  border: "1px solid rgba(125, 211, 252, 0.9)",
@@ -408,7 +406,6 @@ var presets = {
408
406
  filter: "drop-shadow(0 8px 15px rgba(14, 116, 144, 0.24))",
409
407
  padding: "9px 15px",
410
408
  textShadow: "0 1px 0 rgba(255, 255, 255, 0.8)",
411
- backdropFilter: "blur(14px)",
412
409
  arrowSize: "8px",
413
410
  arrowWidth: "19px",
414
411
  animation: animations.blur
@@ -773,7 +770,6 @@ var presets = {
773
770
  borderRadius: "20px 8px 20px 8px",
774
771
  filter: "drop-shadow(0 10px 17px rgba(49, 46, 129, 0.48))",
775
772
  padding: "10px 16px",
776
- backdropFilter: "blur(8px)",
777
773
  arrowSize: "9px",
778
774
  arrowWidth: "21px",
779
775
  animation: animations.blur
@@ -960,6 +956,13 @@ var VIEWPORT_PADDING = 8;
960
956
  var DEFAULT_ARROW_SIZE = 6;
961
957
  var DEFAULT_TOOLTIP_GAP = 10;
962
958
  var ARROW_EDGE_OFFSET = 10;
959
+ var INTERACTIVE_DIRECT_PADDING = 4;
960
+ var INTERACTIVE_BRIDGE_PADDING = 8;
961
+ var INTERACTIVE_BRIDGE_TIMEOUT = 700;
962
+ var INTERACTIVE_RECHECK_INTERVAL = 50;
963
+ var TOUCH_MOVE_THRESHOLD = 10;
964
+ var TOUCH_MAX_TAP_DURATION = 600;
965
+ var TOUCH_FOCUS_SUPPRESSION = 700;
963
966
  var oppositePlacement = {
964
967
  top: "bottom",
965
968
  bottom: "top",
@@ -970,6 +973,112 @@ var clamp = (value, min, max) => {
970
973
  const resolvedMax = Math.max(min, max);
971
974
  return Math.min(Math.max(value, min), resolvedMax);
972
975
  };
976
+ var isPointInsideRect = (point, rect, padding = 0) => {
977
+ return point.x >= rect.left - padding && point.x <= rect.right + padding && point.y >= rect.top - padding && point.y <= rect.bottom + padding;
978
+ };
979
+ var isPointOnSegment = (point, start, end) => {
980
+ const cross = (point.y - start.y) * (end.x - start.x) - (point.x - start.x) * (end.y - start.y);
981
+ if (Math.abs(cross) > 1e-3) {
982
+ return false;
983
+ }
984
+ 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);
985
+ };
986
+ var isPointInsidePolygon = (point, polygon) => {
987
+ let inside = false;
988
+ for (let currentIndex = 0, previousIndex = polygon.length - 1; currentIndex < polygon.length; previousIndex = currentIndex, currentIndex += 1) {
989
+ const current = polygon[currentIndex];
990
+ const previous = polygon[previousIndex];
991
+ if (isPointOnSegment(point, previous, current)) {
992
+ return true;
993
+ }
994
+ 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;
995
+ if (crosses) {
996
+ inside = !inside;
997
+ }
998
+ }
999
+ return inside;
1000
+ };
1001
+ var createInteractiveBridge = (anchorRect, tooltipRect, placement) => {
1002
+ const padding = INTERACTIVE_BRIDGE_PADDING;
1003
+ if (placement === "top") {
1004
+ return [
1005
+ {
1006
+ x: tooltipRect.left - padding,
1007
+ y: tooltipRect.bottom - padding
1008
+ },
1009
+ {
1010
+ x: tooltipRect.right + padding,
1011
+ y: tooltipRect.bottom - padding
1012
+ },
1013
+ {
1014
+ x: anchorRect.right + padding,
1015
+ y: anchorRect.top + padding
1016
+ },
1017
+ {
1018
+ x: anchorRect.left - padding,
1019
+ y: anchorRect.top + padding
1020
+ }
1021
+ ];
1022
+ }
1023
+ if (placement === "bottom") {
1024
+ return [
1025
+ {
1026
+ x: anchorRect.left - padding,
1027
+ y: anchorRect.bottom - padding
1028
+ },
1029
+ {
1030
+ x: anchorRect.right + padding,
1031
+ y: anchorRect.bottom - padding
1032
+ },
1033
+ {
1034
+ x: tooltipRect.right + padding,
1035
+ y: tooltipRect.top + padding
1036
+ },
1037
+ {
1038
+ x: tooltipRect.left - padding,
1039
+ y: tooltipRect.top + padding
1040
+ }
1041
+ ];
1042
+ }
1043
+ if (placement === "left") {
1044
+ return [
1045
+ {
1046
+ x: tooltipRect.right - padding,
1047
+ y: tooltipRect.top - padding
1048
+ },
1049
+ {
1050
+ x: anchorRect.left + padding,
1051
+ y: anchorRect.top - padding
1052
+ },
1053
+ {
1054
+ x: anchorRect.left + padding,
1055
+ y: anchorRect.bottom + padding
1056
+ },
1057
+ {
1058
+ x: tooltipRect.right - padding,
1059
+ y: tooltipRect.bottom + padding
1060
+ }
1061
+ ];
1062
+ }
1063
+ return [
1064
+ {
1065
+ x: anchorRect.right - padding,
1066
+ y: anchorRect.top - padding
1067
+ },
1068
+ {
1069
+ x: tooltipRect.left + padding,
1070
+ y: tooltipRect.top - padding
1071
+ },
1072
+ {
1073
+ x: tooltipRect.left + padding,
1074
+ y: tooltipRect.bottom + padding
1075
+ },
1076
+ {
1077
+ x: anchorRect.right - padding,
1078
+ y: anchorRect.bottom + padding
1079
+ }
1080
+ ];
1081
+ };
973
1082
  var useTooltip = ({
974
1083
  anchor,
975
1084
  preferredPlacement,
@@ -982,8 +1091,17 @@ var useTooltip = ({
982
1091
  const hideTimerRef = (0, import_react.useRef)(null);
983
1092
  const firstFrameRef = (0, import_react.useRef)(null);
984
1093
  const secondFrameRef = (0, import_react.useRef)(null);
1094
+ const interactiveFrameRef = (0, import_react.useRef)(null);
985
1095
  const phaseRef = (0, import_react.useRef)("hidden");
986
1096
  const pendingHideRef = (0, import_react.useRef)(false);
1097
+ const interactiveBridgeDeadlineRef = (0, import_react.useRef)(0);
1098
+ const pointerRef = (0, import_react.useRef)({
1099
+ x: Number.NaN,
1100
+ y: Number.NaN,
1101
+ pointerType: ""
1102
+ });
1103
+ const touchGestureRef = (0, import_react.useRef)(null);
1104
+ const suppressFocusUntilRef = (0, import_react.useRef)(0);
987
1105
  const [mounted, setMounted] = (0, import_react.useState)(false);
988
1106
  const [phase, setPhase] = (0, import_react.useState)("hidden");
989
1107
  const [placement, setPlacement] = (0, import_react.useState)(preferredPlacement);
@@ -1015,6 +1133,13 @@ var useTooltip = ({
1015
1133
  secondFrameRef.current = null;
1016
1134
  }
1017
1135
  }, []);
1136
+ const clearInteractiveFrame = (0, import_react.useCallback)(() => {
1137
+ if (interactiveFrameRef.current === null) {
1138
+ return;
1139
+ }
1140
+ window.cancelAnimationFrame(interactiveFrameRef.current);
1141
+ interactiveFrameRef.current = null;
1142
+ }, []);
1018
1143
  const getAvailableSpace = (0, import_react.useCallback)(
1019
1144
  (currentPlacement, rect) => {
1020
1145
  switch (currentPlacement) {
@@ -1054,10 +1179,7 @@ var useTooltip = ({
1054
1179
  }
1055
1180
  return bodyWidth + distance;
1056
1181
  };
1057
- const preferredAvailable = getAvailableSpace(
1058
- preferredPlacement,
1059
- rect
1060
- );
1182
+ const preferredAvailable = getAvailableSpace(preferredPlacement, rect);
1061
1183
  const preferredRequired = getRequiredSpace(preferredPlacement);
1062
1184
  let nextPlacement = preferredPlacement;
1063
1185
  if (preferredAvailable < preferredRequired) {
@@ -1115,12 +1237,61 @@ var useTooltip = ({
1115
1237
  "--tooltip-arrow-offset": arrowOffset
1116
1238
  });
1117
1239
  }, [anchor, getAvailableSpace, preferredPlacement]);
1240
+ const isFocusInsideTooltipRegion = (0, import_react.useCallback)(() => {
1241
+ if (!anchor || typeof document === "undefined") {
1242
+ return false;
1243
+ }
1244
+ const activeElement = document.activeElement;
1245
+ const tooltip = tooltipRef.current;
1246
+ if (!(activeElement instanceof Node)) {
1247
+ return false;
1248
+ }
1249
+ if (interactive && tooltip?.contains(activeElement)) {
1250
+ return true;
1251
+ }
1252
+ return anchor.contains(activeElement) && activeElement instanceof Element && activeElement.matches(":focus-visible");
1253
+ }, [anchor, interactive]);
1254
+ const getInteractivePointerArea = (0, import_react.useCallback)(() => {
1255
+ if (!interactive || !anchor) {
1256
+ return "outside";
1257
+ }
1258
+ const pointer = pointerRef.current;
1259
+ const tooltip = tooltipRef.current;
1260
+ if (!tooltip || pointer.pointerType !== "mouse" && pointer.pointerType !== "pen" || !Number.isFinite(pointer.x) || !Number.isFinite(pointer.y)) {
1261
+ return "outside";
1262
+ }
1263
+ const point = {
1264
+ x: pointer.x,
1265
+ y: pointer.y
1266
+ };
1267
+ const anchorRect = anchor.getBoundingClientRect();
1268
+ const tooltipRect = tooltip.getBoundingClientRect();
1269
+ if (isPointInsideRect(
1270
+ point,
1271
+ anchorRect,
1272
+ INTERACTIVE_DIRECT_PADDING
1273
+ ) || isPointInsideRect(
1274
+ point,
1275
+ tooltipRect,
1276
+ INTERACTIVE_DIRECT_PADDING
1277
+ )) {
1278
+ return "direct";
1279
+ }
1280
+ const bridge = createInteractiveBridge(
1281
+ anchorRect,
1282
+ tooltipRect,
1283
+ placement
1284
+ );
1285
+ return isPointInsidePolygon(point, bridge) ? "bridge" : "outside";
1286
+ }, [anchor, interactive, placement]);
1118
1287
  const removeTooltip = (0, import_react.useCallback)(() => {
1119
1288
  clearHideTimer();
1120
1289
  clearEnterFrames();
1290
+ clearInteractiveFrame();
1121
1291
  pendingHideRef.current = false;
1292
+ interactiveBridgeDeadlineRef.current = 0;
1122
1293
  updatePhase("hidden");
1123
- }, [clearEnterFrames, clearHideTimer, updatePhase]);
1294
+ }, [clearEnterFrames, clearHideTimer, clearInteractiveFrame, updatePhase]);
1124
1295
  const runHide = (0, import_react.useCallback)(() => {
1125
1296
  const currentPhase = phaseRef.current;
1126
1297
  if (currentPhase === "hidden" || currentPhase === "leaving") {
@@ -1138,17 +1309,61 @@ var useTooltip = ({
1138
1309
  }, [removeTooltip, updatePhase]);
1139
1310
  const hideTooltip = (0, import_react.useCallback)(
1140
1311
  (immediate = false) => {
1141
- clearHideTimer();
1142
1312
  if (immediate) {
1313
+ clearHideTimer();
1314
+ interactiveBridgeDeadlineRef.current = 0;
1143
1315
  runHide();
1144
1316
  return;
1145
1317
  }
1146
- hideTimerRef.current = window.setTimeout(() => {
1318
+ if (hideTimerRef.current !== null) {
1319
+ return;
1320
+ }
1321
+ const attemptHide = () => {
1147
1322
  hideTimerRef.current = null;
1323
+ if (isFocusInsideTooltipRegion()) {
1324
+ interactiveBridgeDeadlineRef.current = 0;
1325
+ return;
1326
+ }
1327
+ if (interactive) {
1328
+ const pointerArea = getInteractivePointerArea();
1329
+ if (pointerArea === "direct") {
1330
+ interactiveBridgeDeadlineRef.current = 0;
1331
+ return;
1332
+ }
1333
+ if (pointerArea === "bridge") {
1334
+ const now = window.performance.now();
1335
+ if (interactiveBridgeDeadlineRef.current === 0) {
1336
+ interactiveBridgeDeadlineRef.current = now + INTERACTIVE_BRIDGE_TIMEOUT;
1337
+ }
1338
+ const remaining = interactiveBridgeDeadlineRef.current - now;
1339
+ if (remaining > 0) {
1340
+ hideTimerRef.current = window.setTimeout(
1341
+ attemptHide,
1342
+ Math.min(
1343
+ INTERACTIVE_RECHECK_INTERVAL,
1344
+ remaining
1345
+ )
1346
+ );
1347
+ return;
1348
+ }
1349
+ }
1350
+ }
1351
+ interactiveBridgeDeadlineRef.current = 0;
1148
1352
  runHide();
1149
- }, resolvedHideDelay);
1353
+ };
1354
+ hideTimerRef.current = window.setTimeout(
1355
+ attemptHide,
1356
+ resolvedHideDelay
1357
+ );
1150
1358
  },
1151
- [clearHideTimer, resolvedHideDelay, runHide]
1359
+ [
1360
+ clearHideTimer,
1361
+ getInteractivePointerArea,
1362
+ interactive,
1363
+ isFocusInsideTooltipRegion,
1364
+ resolvedHideDelay,
1365
+ runHide
1366
+ ]
1152
1367
  );
1153
1368
  const showTooltip = (0, import_react.useCallback)(() => {
1154
1369
  if (!anchor || disabled) {
@@ -1156,6 +1371,7 @@ var useTooltip = ({
1156
1371
  }
1157
1372
  clearHideTimer();
1158
1373
  clearEnterFrames();
1374
+ interactiveBridgeDeadlineRef.current = 0;
1159
1375
  pendingHideRef.current = false;
1160
1376
  setMounted(true);
1161
1377
  if (phaseRef.current === "entering" || phaseRef.current === "visible") {
@@ -1169,11 +1385,51 @@ var useTooltip = ({
1169
1385
  return;
1170
1386
  }
1171
1387
  clearHideTimer();
1388
+ interactiveBridgeDeadlineRef.current = 0;
1172
1389
  pendingHideRef.current = false;
1173
1390
  if (phaseRef.current === "leaving") {
1174
1391
  updatePhase("visible");
1175
1392
  }
1176
1393
  }, [clearHideTimer, disabled, interactive, updatePhase]);
1394
+ const processInteractivePointer = (0, import_react.useCallback)(() => {
1395
+ if (!interactive || disabled || phaseRef.current === "hidden") {
1396
+ return;
1397
+ }
1398
+ if (isFocusInsideTooltipRegion()) {
1399
+ keepTooltipOpen();
1400
+ return;
1401
+ }
1402
+ const pointerArea = getInteractivePointerArea();
1403
+ if (pointerArea === "direct") {
1404
+ keepTooltipOpen();
1405
+ return;
1406
+ }
1407
+ if (pointerArea === "bridge") {
1408
+ if (interactiveBridgeDeadlineRef.current === 0) {
1409
+ interactiveBridgeDeadlineRef.current = window.performance.now() + INTERACTIVE_BRIDGE_TIMEOUT;
1410
+ }
1411
+ hideTooltip(false);
1412
+ return;
1413
+ }
1414
+ interactiveBridgeDeadlineRef.current = 0;
1415
+ hideTooltip(false);
1416
+ }, [
1417
+ disabled,
1418
+ getInteractivePointerArea,
1419
+ hideTooltip,
1420
+ interactive,
1421
+ isFocusInsideTooltipRegion,
1422
+ keepTooltipOpen
1423
+ ]);
1424
+ const scheduleInteractivePointerProcessing = (0, import_react.useCallback)(() => {
1425
+ if (interactiveFrameRef.current !== null) {
1426
+ return;
1427
+ }
1428
+ interactiveFrameRef.current = window.requestAnimationFrame(() => {
1429
+ interactiveFrameRef.current = null;
1430
+ processInteractivePointer();
1431
+ });
1432
+ }, [processInteractivePointer]);
1177
1433
  const onAnimationEnd = (0, import_react.useCallback)(() => {
1178
1434
  const currentPhase = phaseRef.current;
1179
1435
  if (currentPhase === "entering") {
@@ -1218,35 +1474,112 @@ var useTooltip = ({
1218
1474
  removeTooltip();
1219
1475
  return;
1220
1476
  }
1477
+ const updatePointer = (event) => {
1478
+ pointerRef.current = {
1479
+ x: event.clientX,
1480
+ y: event.clientY,
1481
+ pointerType: event.pointerType
1482
+ };
1483
+ };
1221
1484
  const onPointerEnter = (event) => {
1485
+ updatePointer(event);
1222
1486
  if (event.pointerType === "touch") {
1223
1487
  return;
1224
1488
  }
1225
1489
  showTooltip();
1226
1490
  };
1227
1491
  const onPointerLeave = (event) => {
1492
+ updatePointer(event);
1228
1493
  if (event.pointerType === "touch") {
1229
1494
  return;
1230
1495
  }
1231
- const relatedTarget = event.relatedTarget;
1232
- const tooltip = tooltipRef.current;
1233
- if (interactive && relatedTarget instanceof Node && tooltip?.contains(relatedTarget)) {
1234
- keepTooltipOpen();
1496
+ if (interactive) {
1497
+ interactiveBridgeDeadlineRef.current = window.performance.now() + INTERACTIVE_BRIDGE_TIMEOUT;
1498
+ hideTooltip(false);
1499
+ scheduleInteractivePointerProcessing();
1235
1500
  return;
1236
1501
  }
1237
1502
  hideTooltip(false);
1238
1503
  };
1239
1504
  const onPointerDown = (event) => {
1505
+ updatePointer(event);
1506
+ if (event.pointerType !== "touch") {
1507
+ return;
1508
+ }
1509
+ const now = window.performance.now();
1510
+ touchGestureRef.current = {
1511
+ pointerId: event.pointerId,
1512
+ startX: event.clientX,
1513
+ startY: event.clientY,
1514
+ startedAt: now,
1515
+ moved: false,
1516
+ cancelled: false
1517
+ };
1518
+ suppressFocusUntilRef.current = now + TOUCH_FOCUS_SUPPRESSION;
1519
+ };
1520
+ const onPointerMove = (event) => {
1521
+ updatePointer(event);
1522
+ if (event.pointerType !== "touch") {
1523
+ return;
1524
+ }
1525
+ const gesture = touchGestureRef.current;
1526
+ if (!gesture || gesture.pointerId !== event.pointerId) {
1527
+ return;
1528
+ }
1529
+ const distance = Math.hypot(
1530
+ event.clientX - gesture.startX,
1531
+ event.clientY - gesture.startY
1532
+ );
1533
+ if (distance <= TOUCH_MOVE_THRESHOLD) {
1534
+ return;
1535
+ }
1536
+ gesture.moved = true;
1537
+ suppressFocusUntilRef.current = window.performance.now() + TOUCH_FOCUS_SUPPRESSION;
1538
+ hideTooltip(true);
1539
+ };
1540
+ const onPointerUp = (event) => {
1541
+ updatePointer(event);
1240
1542
  if (event.pointerType !== "touch") {
1241
1543
  return;
1242
1544
  }
1545
+ const gesture = touchGestureRef.current;
1546
+ touchGestureRef.current = null;
1547
+ if (!gesture || gesture.pointerId !== event.pointerId) {
1548
+ return;
1549
+ }
1550
+ const now = window.performance.now();
1551
+ const distance = Math.hypot(
1552
+ event.clientX - gesture.startX,
1553
+ event.clientY - gesture.startY
1554
+ );
1555
+ const duration = now - gesture.startedAt;
1556
+ suppressFocusUntilRef.current = now + TOUCH_FOCUS_SUPPRESSION;
1557
+ if (gesture.cancelled || gesture.moved || distance > TOUCH_MOVE_THRESHOLD || duration > TOUCH_MAX_TAP_DURATION) {
1558
+ return;
1559
+ }
1243
1560
  if (phaseRef.current === "hidden" || phaseRef.current === "leaving") {
1244
1561
  showTooltip();
1245
1562
  } else {
1246
1563
  hideTooltip(true);
1247
1564
  }
1248
1565
  };
1566
+ const onPointerCancel = (event) => {
1567
+ if (event.pointerType !== "touch") {
1568
+ return;
1569
+ }
1570
+ const gesture = touchGestureRef.current;
1571
+ if (!gesture || gesture.pointerId !== event.pointerId) {
1572
+ return;
1573
+ }
1574
+ gesture.cancelled = true;
1575
+ touchGestureRef.current = null;
1576
+ suppressFocusUntilRef.current = window.performance.now() + TOUCH_FOCUS_SUPPRESSION;
1577
+ hideTooltip(true);
1578
+ };
1249
1579
  const onFocusIn = () => {
1580
+ if (window.performance.now() < suppressFocusUntilRef.current) {
1581
+ return;
1582
+ }
1250
1583
  showTooltip();
1251
1584
  };
1252
1585
  const onFocusOut = (event) => {
@@ -1260,51 +1593,66 @@ var useTooltip = ({
1260
1593
  anchor.addEventListener("pointerenter", onPointerEnter);
1261
1594
  anchor.addEventListener("pointerleave", onPointerLeave);
1262
1595
  anchor.addEventListener("pointerdown", onPointerDown);
1596
+ anchor.addEventListener("pointermove", onPointerMove);
1597
+ anchor.addEventListener("pointerup", onPointerUp);
1598
+ anchor.addEventListener("pointercancel", onPointerCancel);
1263
1599
  anchor.addEventListener("focusin", onFocusIn);
1264
1600
  anchor.addEventListener("focusout", onFocusOut);
1265
1601
  return () => {
1266
1602
  anchor.removeEventListener("pointerenter", onPointerEnter);
1267
1603
  anchor.removeEventListener("pointerleave", onPointerLeave);
1268
1604
  anchor.removeEventListener("pointerdown", onPointerDown);
1605
+ anchor.removeEventListener("pointermove", onPointerMove);
1606
+ anchor.removeEventListener("pointerup", onPointerUp);
1607
+ anchor.removeEventListener("pointercancel", onPointerCancel);
1269
1608
  anchor.removeEventListener("focusin", onFocusIn);
1270
1609
  anchor.removeEventListener("focusout", onFocusOut);
1610
+ touchGestureRef.current = null;
1271
1611
  clearHideTimer();
1272
1612
  clearEnterFrames();
1613
+ clearInteractiveFrame();
1273
1614
  };
1274
1615
  }, [
1275
1616
  anchor,
1276
1617
  clearEnterFrames,
1277
1618
  clearHideTimer,
1619
+ clearInteractiveFrame,
1278
1620
  hideTooltip,
1279
1621
  interactive,
1280
- keepTooltipOpen,
1281
1622
  removeTooltip,
1623
+ scheduleInteractivePointerProcessing,
1282
1624
  showTooltip
1283
1625
  ]);
1284
1626
  (0, import_react.useEffect)(() => {
1285
- if (!interactive || !mounted) {
1627
+ if (!interactive || phase === "hidden") {
1286
1628
  return;
1287
1629
  }
1288
1630
  const tooltip = tooltipRef.current;
1289
1631
  if (!tooltip) {
1290
1632
  return;
1291
1633
  }
1634
+ const updatePointer = (event) => {
1635
+ pointerRef.current = {
1636
+ x: event.clientX,
1637
+ y: event.clientY,
1638
+ pointerType: event.pointerType
1639
+ };
1640
+ };
1292
1641
  const onPointerEnter = (event) => {
1642
+ updatePointer(event);
1293
1643
  if (event.pointerType === "touch") {
1294
1644
  return;
1295
1645
  }
1296
1646
  keepTooltipOpen();
1297
1647
  };
1298
1648
  const onPointerLeave = (event) => {
1649
+ updatePointer(event);
1299
1650
  if (event.pointerType === "touch") {
1300
1651
  return;
1301
1652
  }
1302
- const relatedTarget = event.relatedTarget;
1303
- if (relatedTarget instanceof Node && anchor?.contains(relatedTarget)) {
1304
- keepTooltipOpen();
1305
- return;
1306
- }
1653
+ interactiveBridgeDeadlineRef.current = window.performance.now() + INTERACTIVE_BRIDGE_TIMEOUT;
1307
1654
  hideTooltip(false);
1655
+ scheduleInteractivePointerProcessing();
1308
1656
  };
1309
1657
  const onFocusIn = () => {
1310
1658
  keepTooltipOpen();
@@ -1331,7 +1679,36 @@ var useTooltip = ({
1331
1679
  hideTooltip,
1332
1680
  interactive,
1333
1681
  keepTooltipOpen,
1334
- mounted
1682
+ phase,
1683
+ scheduleInteractivePointerProcessing
1684
+ ]);
1685
+ (0, import_react.useEffect)(() => {
1686
+ if (!interactive || phase === "hidden") {
1687
+ return;
1688
+ }
1689
+ const onDocumentPointerMove = (event) => {
1690
+ if (event.pointerType === "touch") {
1691
+ return;
1692
+ }
1693
+ pointerRef.current = {
1694
+ x: event.clientX,
1695
+ y: event.clientY,
1696
+ pointerType: event.pointerType
1697
+ };
1698
+ scheduleInteractivePointerProcessing();
1699
+ };
1700
+ document.addEventListener("pointermove", onDocumentPointerMove, {
1701
+ passive: true
1702
+ });
1703
+ return () => {
1704
+ document.removeEventListener("pointermove", onDocumentPointerMove);
1705
+ clearInteractiveFrame();
1706
+ };
1707
+ }, [
1708
+ clearInteractiveFrame,
1709
+ interactive,
1710
+ phase,
1711
+ scheduleInteractivePointerProcessing
1335
1712
  ]);
1336
1713
  (0, import_react.useEffect)(() => {
1337
1714
  if (!anchor || phase === "hidden") {
@@ -1343,6 +1720,7 @@ var useTooltip = ({
1343
1720
  if (target instanceof Node && (anchor.contains(target) || interactive && tooltip?.contains(target))) {
1344
1721
  return;
1345
1722
  }
1723
+ touchGestureRef.current = null;
1346
1724
  hideTooltip(true);
1347
1725
  };
1348
1726
  const onKeyDown = (event) => {
@@ -1350,11 +1728,7 @@ var useTooltip = ({
1350
1728
  hideTooltip(true);
1351
1729
  }
1352
1730
  };
1353
- document.addEventListener(
1354
- "pointerdown",
1355
- onDocumentPointerDown,
1356
- true
1357
- );
1731
+ document.addEventListener("pointerdown", onDocumentPointerDown, true);
1358
1732
  document.addEventListener("keydown", onKeyDown);
1359
1733
  window.addEventListener("resize", calculatePosition);
1360
1734
  window.addEventListener("scroll", calculatePosition, true);
@@ -1368,13 +1742,7 @@ var useTooltip = ({
1368
1742
  window.removeEventListener("resize", calculatePosition);
1369
1743
  window.removeEventListener("scroll", calculatePosition, true);
1370
1744
  };
1371
- }, [
1372
- anchor,
1373
- calculatePosition,
1374
- hideTooltip,
1375
- interactive,
1376
- phase
1377
- ]);
1745
+ }, [anchor, calculatePosition, hideTooltip, interactive, phase]);
1378
1746
  (0, import_react.useLayoutEffect)(() => {
1379
1747
  if (phase === "hidden") {
1380
1748
  return;
@@ -1741,7 +2109,8 @@ var import_react2 = require("react");
1741
2109
  var import_jsx_runtime = require("react/jsx-runtime");
1742
2110
  var DEFAULT_TOOLTIP_VALUES = {
1743
2111
  defaultRenderPosition: "top",
1744
- selectTheme: "primary"
2112
+ selectTheme: "primary",
2113
+ interactive: false
1745
2114
  };
1746
2115
  var TooltipContext = (0, import_react2.createContext)(DEFAULT_TOOLTIP_VALUES);
1747
2116
  var TooltipProvider = ({
@@ -1749,16 +2118,27 @@ var TooltipProvider = ({
1749
2118
  defaultRenderPosition = DEFAULT_TOOLTIP_VALUES.defaultRenderPosition,
1750
2119
  selectTheme = DEFAULT_TOOLTIP_VALUES.selectTheme,
1751
2120
  customTheme,
1752
- animation
2121
+ animation,
2122
+ interactive = DEFAULT_TOOLTIP_VALUES.interactive,
2123
+ hideDelay
1753
2124
  }) => {
1754
2125
  const value = (0, import_react2.useMemo)(() => {
1755
2126
  return {
1756
2127
  defaultRenderPosition,
1757
2128
  selectTheme,
1758
2129
  customTheme,
1759
- animation
2130
+ animation,
2131
+ interactive,
2132
+ hideDelay
1760
2133
  };
1761
- }, [animation, customTheme, defaultRenderPosition, selectTheme]);
2134
+ }, [
2135
+ animation,
2136
+ customTheme,
2137
+ defaultRenderPosition,
2138
+ hideDelay,
2139
+ interactive,
2140
+ selectTheme
2141
+ ]);
1762
2142
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipContext.Provider, { value, children });
1763
2143
  };
1764
2144
  var useTooltipDefaults = () => {
@@ -2219,7 +2599,7 @@ var Tooltip = ({
2219
2599
  customTheme,
2220
2600
  animation,
2221
2601
  disabled = false,
2222
- interactive = false,
2602
+ interactive,
2223
2603
  hideDelay
2224
2604
  }) => {
2225
2605
  const defaults = useTooltipDefaults();
@@ -2263,6 +2643,8 @@ var Tooltip = ({
2263
2643
  const animationSpeed = animation?.speed ?? localThemeAnimation?.speed ?? defaults.animation?.speed ?? inheritedTheme.animation?.speed ?? "120ms";
2264
2644
  const animationEasing = animation?.easing ?? localThemeAnimation?.easing ?? defaults.animation?.easing ?? inheritedTheme.animation?.easing ?? "ease-in-out";
2265
2645
  const preferredPlacement = position ?? defaults.defaultRenderPosition;
2646
+ const resolvedInteractive = interactive ?? defaults.interactive;
2647
+ const resolvedHideDelay = hideDelay ?? defaults.hideDelay;
2266
2648
  const {
2267
2649
  shouldRender,
2268
2650
  phase,
@@ -2275,8 +2657,8 @@ var Tooltip = ({
2275
2657
  anchor,
2276
2658
  preferredPlacement,
2277
2659
  disabled: disabled || content === null || content === void 0,
2278
- interactive,
2279
- hideDelay
2660
+ interactive: resolvedInteractive,
2661
+ hideDelay: resolvedHideDelay
2280
2662
  });
2281
2663
  const arrowSize = theme.arrow?.size ?? "6px";
2282
2664
  const arrowWidth = theme.arrow?.width ?? `calc(${arrowSize} * 2)`;
@@ -2312,7 +2694,7 @@ var Tooltip = ({
2312
2694
  `tooltip-container--phase-${phase}`,
2313
2695
  `tooltip-container--show-${showAnimation}`,
2314
2696
  `tooltip-container--hide-${hideAnimation}`,
2315
- interactive ? "tooltip-container--interactive" : null
2697
+ resolvedInteractive ? "tooltip-container--interactive" : null
2316
2698
  ].filter(Boolean).join(" "),
2317
2699
  style: containerStyle,
2318
2700
  children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(