@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/README.md +318 -403
- package/dist/index.d.mts +4 -7
- package/dist/index.d.ts +4 -7
- package/dist/index.js +430 -48
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +430 -48
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
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)),
|
|
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)),
|
|
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,
|
|
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
|
|
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
|
-
}
|
|
1341
|
+
};
|
|
1342
|
+
hideTimerRef.current = window.setTimeout(
|
|
1343
|
+
attemptHide,
|
|
1344
|
+
resolvedHideDelay
|
|
1345
|
+
);
|
|
1138
1346
|
},
|
|
1139
|
-
[
|
|
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
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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 ||
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
}, [
|
|
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
|
|
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
|
-
|
|
2689
|
+
resolvedInteractive ? "tooltip-container--interactive" : null
|
|
2308
2690
|
].filter(Boolean).join(" "),
|
|
2309
2691
|
style: containerStyle,
|
|
2310
2692
|
children: /* @__PURE__ */ jsxs2(
|