react-tooltip 6.0.0-beta.1179.rc.8 → 6.0.0
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 +3 -1
- package/dist/react-tooltip.cjs +716 -391
- package/dist/react-tooltip.cjs.map +1 -1
- package/dist/react-tooltip.css +2 -1
- package/dist/react-tooltip.d.ts +5 -1
- package/dist/react-tooltip.min.cjs +2 -2
- package/dist/react-tooltip.min.cjs.map +1 -1
- package/dist/react-tooltip.min.css +1 -1
- package/dist/react-tooltip.min.mjs +2 -2
- package/dist/react-tooltip.min.mjs.map +1 -1
- package/dist/react-tooltip.mjs +717 -392
- package/dist/react-tooltip.mjs.map +1 -1
- package/dist/react-tooltip.umd.js +716 -391
- package/dist/react-tooltip.umd.js.map +1 -1
- package/dist/react-tooltip.umd.min.js +2 -2
- package/dist/react-tooltip.umd.min.js.map +1 -1
- package/eslint.config.js +155 -0
- package/package.json +47 -46
|
@@ -43,11 +43,9 @@
|
|
|
43
43
|
return;
|
|
44
44
|
}
|
|
45
45
|
if (type === 'core') {
|
|
46
|
-
// eslint-disable-next-line no-param-reassign
|
|
47
46
|
id = REACT_TOOLTIP_CORE_STYLES_ID;
|
|
48
47
|
}
|
|
49
48
|
if (!ref) {
|
|
50
|
-
// eslint-disable-next-line no-param-reassign
|
|
51
49
|
ref = {};
|
|
52
50
|
}
|
|
53
51
|
const { insertAt } = ref;
|
|
@@ -78,7 +76,6 @@
|
|
|
78
76
|
style.appendChild(document.createTextNode(css));
|
|
79
77
|
}
|
|
80
78
|
if (typeof state[type] !== 'undefined') {
|
|
81
|
-
// eslint-disable-next-line no-param-reassign
|
|
82
79
|
state[type] = true;
|
|
83
80
|
}
|
|
84
81
|
else {
|
|
@@ -86,16 +83,12 @@
|
|
|
86
83
|
}
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}),
|
|
94
|
-
dom.shift({ padding: 5 }),
|
|
95
|
-
], border, arrowSize = 8, }) => {
|
|
86
|
+
// Hoisted constant middlewares — these configs never change
|
|
87
|
+
const defaultFlip = dom.flip({ fallbackAxisSideDirection: 'start' });
|
|
88
|
+
const defaultShift = dom.shift({ padding: 5 });
|
|
89
|
+
const computeTooltipPosition = async ({ elementReference = null, tooltipReference = null, tooltipArrowReference = null, place = 'top', offset: offsetValue = 10, strategy = 'absolute', middlewares = [dom.offset(Number(offsetValue)), defaultFlip, defaultShift], border, arrowSize = 8, }) => {
|
|
96
90
|
if (!elementReference) {
|
|
97
91
|
// elementReference can be null or undefined and we will not compute the position
|
|
98
|
-
// eslint-disable-next-line no-console
|
|
99
92
|
// console.error('The reference element for tooltip was not defined: ', elementReference)
|
|
100
93
|
return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
|
|
101
94
|
}
|
|
@@ -180,6 +173,7 @@
|
|
|
180
173
|
*/
|
|
181
174
|
const debounce = (func, wait, immediate) => {
|
|
182
175
|
let timeout = null;
|
|
176
|
+
let currentFunc = func;
|
|
183
177
|
const debounced = function debounced(...args) {
|
|
184
178
|
const later = () => {
|
|
185
179
|
timeout = null;
|
|
@@ -189,7 +183,7 @@
|
|
|
189
183
|
* there's no need to clear the timeout
|
|
190
184
|
* since we expect it to resolve and set `timeout = null`
|
|
191
185
|
*/
|
|
192
|
-
|
|
186
|
+
currentFunc.apply(this, args);
|
|
193
187
|
timeout = setTimeout(later, wait);
|
|
194
188
|
}
|
|
195
189
|
};
|
|
@@ -202,36 +196,12 @@
|
|
|
202
196
|
clearTimeout(timeout);
|
|
203
197
|
timeout = null;
|
|
204
198
|
};
|
|
199
|
+
debounced.setCallback = (newFunc) => {
|
|
200
|
+
currentFunc = newFunc;
|
|
201
|
+
};
|
|
205
202
|
return debounced;
|
|
206
203
|
};
|
|
207
204
|
|
|
208
|
-
const isObject = (object) => {
|
|
209
|
-
return object !== null && !Array.isArray(object) && typeof object === 'object';
|
|
210
|
-
};
|
|
211
|
-
const deepEqual = (object1, object2) => {
|
|
212
|
-
if (object1 === object2) {
|
|
213
|
-
return true;
|
|
214
|
-
}
|
|
215
|
-
if (Array.isArray(object1) && Array.isArray(object2)) {
|
|
216
|
-
if (object1.length !== object2.length) {
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
return object1.every((val, index) => deepEqual(val, object2[index]));
|
|
220
|
-
}
|
|
221
|
-
if (Array.isArray(object1) !== Array.isArray(object2)) {
|
|
222
|
-
return false;
|
|
223
|
-
}
|
|
224
|
-
if (!isObject(object1) || !isObject(object2)) {
|
|
225
|
-
return object1 === object2;
|
|
226
|
-
}
|
|
227
|
-
const keys1 = Object.keys(object1);
|
|
228
|
-
const keys2 = Object.keys(object2);
|
|
229
|
-
if (keys1.length !== keys2.length) {
|
|
230
|
-
return false;
|
|
231
|
-
}
|
|
232
|
-
return keys1.every((key) => deepEqual(object1[key], object2[key]));
|
|
233
|
-
};
|
|
234
|
-
|
|
235
205
|
const isScrollable = (node) => {
|
|
236
206
|
if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
237
207
|
return false;
|
|
@@ -272,17 +242,43 @@
|
|
|
272
242
|
const clearTimeoutRef = (ref) => {
|
|
273
243
|
if (ref.current) {
|
|
274
244
|
clearTimeout(ref.current);
|
|
275
|
-
// eslint-disable-next-line no-param-reassign
|
|
276
245
|
ref.current = null;
|
|
277
246
|
}
|
|
278
247
|
};
|
|
279
248
|
|
|
249
|
+
function parseDataTooltipIdSelector(selector) {
|
|
250
|
+
const match = selector.match(/^\[data-tooltip-id=(['"])((?:\\.|(?!\1).)*)\1\]$/);
|
|
251
|
+
if (!match) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
return match[2].replace(/\\(['"])/g, '$1');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function resolveDataTooltipAnchor(targetElement, tooltipId) {
|
|
258
|
+
let currentElement = targetElement;
|
|
259
|
+
while (currentElement) {
|
|
260
|
+
if (currentElement.dataset.tooltipId === tooltipId) {
|
|
261
|
+
return currentElement;
|
|
262
|
+
}
|
|
263
|
+
currentElement = currentElement.parentElement;
|
|
264
|
+
}
|
|
265
|
+
return null;
|
|
266
|
+
}
|
|
267
|
+
|
|
280
268
|
var coreStyles = {"tooltip":"core-styles-module_tooltip__3vRRp","fixed":"core-styles-module_fixed__pcSol","arrow":"core-styles-module_arrow__cvMwQ","content":"core-styles-module_content__BRKdB","noArrow":"core-styles-module_noArrow__xock6","clickable":"core-styles-module_clickable__ZuTTB","show":"core-styles-module_show__Nt9eE","closing":"core-styles-module_closing__sGnxF"};
|
|
281
269
|
|
|
282
270
|
var styles = {"tooltip":"styles-module_tooltip__mnnfp","content":"styles-module_content__ydYdI","arrow":"styles-module_arrow__K0L3T","dark":"styles-module_dark__xNqje","light":"styles-module_light__Z6W-X","success":"styles-module_success__A2AKt","warning":"styles-module_warning__SCK0X","error":"styles-module_error__JvumD","info":"styles-module_info__BWdHW"};
|
|
283
271
|
|
|
284
272
|
const registry = new Map();
|
|
285
273
|
let documentObserver = null;
|
|
274
|
+
/**
|
|
275
|
+
* Extract a tooltip ID from a simple `[data-tooltip-id='value']` selector.
|
|
276
|
+
* Returns null for complex or custom selectors.
|
|
277
|
+
*/
|
|
278
|
+
function extractTooltipId(selector) {
|
|
279
|
+
const match = selector.match(/^\[data-tooltip-id=(['"])((?:\\.|(?!\1).)*)\1\]$/);
|
|
280
|
+
return match ? match[2].replace(/\\(['"])/g, '$1') : null;
|
|
281
|
+
}
|
|
286
282
|
function areAnchorListsEqual(left, right) {
|
|
287
283
|
if (left.length !== right.length) {
|
|
288
284
|
return false;
|
|
@@ -304,8 +300,7 @@
|
|
|
304
300
|
}
|
|
305
301
|
}
|
|
306
302
|
function notifySubscribers(entry) {
|
|
307
|
-
|
|
308
|
-
entry.subscribers.forEach((subscriber) => subscriber(anchors, entry.error));
|
|
303
|
+
entry.subscribers.forEach((subscriber) => subscriber(entry.anchors, entry.error));
|
|
309
304
|
}
|
|
310
305
|
function refreshEntry(selector, entry) {
|
|
311
306
|
var _a, _b, _c, _d;
|
|
@@ -329,12 +324,117 @@
|
|
|
329
324
|
refreshEntry(selector, entry);
|
|
330
325
|
});
|
|
331
326
|
}
|
|
327
|
+
let refreshScheduled = false;
|
|
328
|
+
let pendingTooltipIds = null;
|
|
329
|
+
let pendingFullRefresh = false;
|
|
330
|
+
function scheduleRefresh(affectedTooltipIds) {
|
|
331
|
+
if (affectedTooltipIds) {
|
|
332
|
+
if (!pendingTooltipIds) {
|
|
333
|
+
pendingTooltipIds = new Set();
|
|
334
|
+
}
|
|
335
|
+
affectedTooltipIds.forEach((id) => pendingTooltipIds.add(id));
|
|
336
|
+
}
|
|
337
|
+
else {
|
|
338
|
+
pendingFullRefresh = true;
|
|
339
|
+
}
|
|
340
|
+
if (refreshScheduled) {
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
refreshScheduled = true;
|
|
344
|
+
const flush = () => {
|
|
345
|
+
refreshScheduled = false;
|
|
346
|
+
const fullRefresh = pendingFullRefresh;
|
|
347
|
+
const ids = pendingTooltipIds;
|
|
348
|
+
pendingFullRefresh = false;
|
|
349
|
+
pendingTooltipIds = null;
|
|
350
|
+
if (fullRefresh) {
|
|
351
|
+
refreshAllEntries();
|
|
352
|
+
}
|
|
353
|
+
else if (ids && ids.size > 0) {
|
|
354
|
+
refreshEntriesForTooltipIds(ids);
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
if (typeof requestAnimationFrame === 'function') {
|
|
358
|
+
requestAnimationFrame(flush);
|
|
359
|
+
}
|
|
360
|
+
else {
|
|
361
|
+
Promise.resolve().then(flush);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* Only refresh entries whose tooltipId is in the affected set,
|
|
366
|
+
* plus any entries with custom (non-tooltipId) selectors.
|
|
367
|
+
*/
|
|
368
|
+
function refreshEntriesForTooltipIds(affectedIds) {
|
|
369
|
+
registry.forEach((entry, selector) => {
|
|
370
|
+
if (entry.tooltipId === null || affectedIds.has(entry.tooltipId)) {
|
|
371
|
+
refreshEntry(selector, entry);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Collect tooltip IDs from mutation records. Returns null when targeted
|
|
377
|
+
* analysis is not worthwhile (few registry entries, or too many nodes to scan).
|
|
378
|
+
*/
|
|
379
|
+
function collectAffectedTooltipIds(records) {
|
|
380
|
+
var _a;
|
|
381
|
+
// Targeted refresh only pays off when there are many distinct selectors.
|
|
382
|
+
// With few entries, full refresh is already cheap — skip the analysis overhead.
|
|
383
|
+
if (registry.size <= 4) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
const ids = new Set();
|
|
387
|
+
for (const record of records) {
|
|
388
|
+
if (record.type === 'attributes') {
|
|
389
|
+
const target = record.target;
|
|
390
|
+
const currentId = (_a = target.getAttribute) === null || _a === void 0 ? void 0 : _a.call(target, 'data-tooltip-id');
|
|
391
|
+
if (currentId)
|
|
392
|
+
ids.add(currentId);
|
|
393
|
+
if (record.oldValue)
|
|
394
|
+
ids.add(record.oldValue);
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (record.type === 'childList') {
|
|
398
|
+
const gatherIds = (nodes) => {
|
|
399
|
+
var _a, _b;
|
|
400
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
401
|
+
const node = nodes[i];
|
|
402
|
+
if (node.nodeType !== Node.ELEMENT_NODE)
|
|
403
|
+
continue;
|
|
404
|
+
const el = node;
|
|
405
|
+
const id = (_a = el.getAttribute) === null || _a === void 0 ? void 0 : _a.call(el, 'data-tooltip-id');
|
|
406
|
+
if (id)
|
|
407
|
+
ids.add(id);
|
|
408
|
+
// For large subtrees, bail out to full refresh to avoid double-scanning
|
|
409
|
+
const descendants = (_b = el.querySelectorAll) === null || _b === void 0 ? void 0 : _b.call(el, '[data-tooltip-id]');
|
|
410
|
+
if (descendants) {
|
|
411
|
+
if (descendants.length > 50) {
|
|
412
|
+
return true; // signal bail-out
|
|
413
|
+
}
|
|
414
|
+
for (let j = 0; j < descendants.length; j++) {
|
|
415
|
+
const descId = descendants[j].getAttribute('data-tooltip-id');
|
|
416
|
+
if (descId)
|
|
417
|
+
ids.add(descId);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
return false;
|
|
422
|
+
};
|
|
423
|
+
if (gatherIds(record.addedNodes) || gatherIds(record.removedNodes)) {
|
|
424
|
+
return null; // large mutation — full refresh is cheaper
|
|
425
|
+
}
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return ids;
|
|
430
|
+
}
|
|
332
431
|
function ensureDocumentObserver() {
|
|
333
432
|
if (documentObserver || typeof MutationObserver === 'undefined') {
|
|
334
433
|
return;
|
|
335
434
|
}
|
|
336
|
-
documentObserver = new MutationObserver(() => {
|
|
337
|
-
|
|
435
|
+
documentObserver = new MutationObserver((records) => {
|
|
436
|
+
const affectedIds = collectAffectedTooltipIds(records);
|
|
437
|
+
scheduleRefresh(affectedIds);
|
|
338
438
|
});
|
|
339
439
|
documentObserver.observe(document.body, {
|
|
340
440
|
childList: true,
|
|
@@ -359,6 +459,7 @@
|
|
|
359
459
|
anchors: initialState.anchors,
|
|
360
460
|
error: initialState.error,
|
|
361
461
|
subscribers: new Set(),
|
|
462
|
+
tooltipId: extractTooltipId(selector),
|
|
362
463
|
};
|
|
363
464
|
registry.set(selector, entry);
|
|
364
465
|
}
|
|
@@ -386,7 +487,7 @@
|
|
|
386
487
|
}
|
|
387
488
|
return selector;
|
|
388
489
|
};
|
|
389
|
-
const useTooltipAnchors = ({ id, anchorSelect, imperativeAnchorSelect, activeAnchor, disableTooltip, onActiveAnchorRemoved, }) => {
|
|
490
|
+
const useTooltipAnchors = ({ id, anchorSelect, imperativeAnchorSelect, activeAnchor, disableTooltip, onActiveAnchorRemoved, trackAnchors, }) => {
|
|
390
491
|
const [rawAnchorElements, setRawAnchorElements] = React.useState([]);
|
|
391
492
|
const [selectorError, setSelectorError] = React.useState(null);
|
|
392
493
|
const warnedSelectorRef = React.useRef(null);
|
|
@@ -402,9 +503,10 @@
|
|
|
402
503
|
catch (_a) {
|
|
403
504
|
return false;
|
|
404
505
|
}
|
|
405
|
-
|
|
506
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
507
|
+
}, [activeAnchor, selector, anchorElements]);
|
|
406
508
|
React.useEffect(() => {
|
|
407
|
-
if (!selector) {
|
|
509
|
+
if (!selector || !trackAnchors) {
|
|
408
510
|
setRawAnchorElements([]);
|
|
409
511
|
setSelectorError(null);
|
|
410
512
|
return undefined;
|
|
@@ -413,7 +515,7 @@
|
|
|
413
515
|
setRawAnchorElements(anchors);
|
|
414
516
|
setSelectorError(error);
|
|
415
517
|
});
|
|
416
|
-
}, [selector]);
|
|
518
|
+
}, [selector, trackAnchors]);
|
|
417
519
|
React.useEffect(() => {
|
|
418
520
|
if (!selectorError || warnedSelectorRef.current === selector) {
|
|
419
521
|
return;
|
|
@@ -433,111 +535,81 @@
|
|
|
433
535
|
onActiveAnchorRemoved();
|
|
434
536
|
}
|
|
435
537
|
}, [activeAnchor, anchorElements, activeAnchorMatchesSelector, onActiveAnchorRemoved]);
|
|
436
|
-
return
|
|
538
|
+
return {
|
|
539
|
+
anchorElements,
|
|
540
|
+
selector,
|
|
541
|
+
};
|
|
437
542
|
};
|
|
438
543
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
if (tooltipHideDelayTimerRef.current) {
|
|
509
|
-
clearTimeout(tooltipHideDelayTimerRef.current);
|
|
510
|
-
}
|
|
511
|
-
};
|
|
512
|
-
const handleHideTooltip = () => {
|
|
513
|
-
if (clickable) {
|
|
514
|
-
handleHideTooltipDelayed(delayHide || 100);
|
|
515
|
-
}
|
|
516
|
-
else if (delayHide) {
|
|
517
|
-
handleHideTooltipDelayed();
|
|
518
|
-
}
|
|
519
|
-
else {
|
|
520
|
-
handleShow(false);
|
|
521
|
-
}
|
|
522
|
-
if (tooltipShowDelayTimerRef.current) {
|
|
523
|
-
clearTimeout(tooltipShowDelayTimerRef.current);
|
|
524
|
-
}
|
|
525
|
-
};
|
|
526
|
-
const internalDebouncedHandleShowTooltip = debounce(handleShowTooltip, 50);
|
|
527
|
-
const internalDebouncedHandleHideTooltip = debounce(handleHideTooltip, 50);
|
|
528
|
-
const debouncedHandleShowTooltip = (anchor) => {
|
|
529
|
-
internalDebouncedHandleHideTooltip.cancel();
|
|
530
|
-
internalDebouncedHandleShowTooltip(anchor);
|
|
531
|
-
};
|
|
532
|
-
const debouncedHandleHideTooltip = () => {
|
|
533
|
-
internalDebouncedHandleShowTooltip.cancel();
|
|
534
|
-
internalDebouncedHandleHideTooltip();
|
|
535
|
-
};
|
|
536
|
-
const handleScrollResize = () => {
|
|
537
|
-
handleShow(false);
|
|
538
|
-
};
|
|
539
|
-
const hasClickEvent = openOnClick || (openEvents === null || openEvents === void 0 ? void 0 : openEvents.click) || (openEvents === null || openEvents === void 0 ? void 0 : openEvents.dblclick) || (openEvents === null || openEvents === void 0 ? void 0 : openEvents.mousedown);
|
|
540
|
-
const actualOpenEvents = openEvents
|
|
544
|
+
/**
|
|
545
|
+
* Shared document event delegation.
|
|
546
|
+
*
|
|
547
|
+
* Instead of N tooltips each calling document.addEventListener(type, handler),
|
|
548
|
+
* we maintain ONE document listener per event type. When the event fires,
|
|
549
|
+
* we iterate through all registered handlers for that type.
|
|
550
|
+
*
|
|
551
|
+
* This reduces document-level listeners from O(N × eventTypes) to O(eventTypes).
|
|
552
|
+
*/
|
|
553
|
+
const handlersByType = new Map();
|
|
554
|
+
function getOrCreateSet(eventType) {
|
|
555
|
+
let set = handlersByType.get(eventType);
|
|
556
|
+
if (!set) {
|
|
557
|
+
set = new Set();
|
|
558
|
+
handlersByType.set(eventType, set);
|
|
559
|
+
document.addEventListener(eventType, dispatch);
|
|
560
|
+
}
|
|
561
|
+
return set;
|
|
562
|
+
}
|
|
563
|
+
function dispatch(event) {
|
|
564
|
+
const handlers = handlersByType.get(event.type);
|
|
565
|
+
if (handlers) {
|
|
566
|
+
// Safe to iterate directly — mutations (add/remove) only happen in
|
|
567
|
+
// setup/cleanup, not during dispatch. Set iteration is stable for
|
|
568
|
+
// entries that existed when iteration began.
|
|
569
|
+
handlers.forEach((handler) => {
|
|
570
|
+
handler(event);
|
|
571
|
+
});
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
/**
|
|
575
|
+
* Register a handler for a document-level event type.
|
|
576
|
+
* Returns an unsubscribe function.
|
|
577
|
+
*/
|
|
578
|
+
function addDelegatedEventListener(eventType, handler) {
|
|
579
|
+
const set = getOrCreateSet(eventType);
|
|
580
|
+
set.add(handler);
|
|
581
|
+
return () => {
|
|
582
|
+
set.delete(handler);
|
|
583
|
+
if (set.size === 0) {
|
|
584
|
+
handlersByType.delete(eventType);
|
|
585
|
+
document.removeEventListener(eventType, dispatch);
|
|
586
|
+
}
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
const useTooltipEvents = ({ activeAnchor, anchorElements, anchorSelector, clickable, closeEvents, delayHide, delayShow, disableTooltip, float, globalCloseEvents, handleHideTooltipDelayed, handleShow, handleShowTooltipDelayed, handleTooltipPosition, hoveringTooltip, imperativeModeOnly, lastFloatPosition, openEvents, openOnClick, setActiveAnchor, show, tooltipHideDelayTimerRef, tooltipRef, tooltipShowDelayTimerRef, updateTooltipPosition, }) => {
|
|
591
|
+
// Ref-stable debounced handlers — avoids recreating debounce instances on every effect run
|
|
592
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
593
|
+
const debouncedShowRef = React.useRef(debounce((_anchor) => { }, 50));
|
|
594
|
+
const debouncedHideRef = React.useRef(debounce(() => { }, 50));
|
|
595
|
+
// Cache scroll parents — only recompute when the element actually changes
|
|
596
|
+
const anchorScrollParentRef = React.useRef(null);
|
|
597
|
+
const tooltipScrollParentRef = React.useRef(null);
|
|
598
|
+
const prevAnchorRef = React.useRef(null);
|
|
599
|
+
const prevTooltipRef = React.useRef(null);
|
|
600
|
+
if (activeAnchor !== prevAnchorRef.current) {
|
|
601
|
+
prevAnchorRef.current = activeAnchor;
|
|
602
|
+
anchorScrollParentRef.current = getScrollParent(activeAnchor);
|
|
603
|
+
}
|
|
604
|
+
const currentTooltipEl = tooltipRef.current;
|
|
605
|
+
if (currentTooltipEl !== prevTooltipRef.current) {
|
|
606
|
+
prevTooltipRef.current = currentTooltipEl;
|
|
607
|
+
tooltipScrollParentRef.current = getScrollParent(currentTooltipEl);
|
|
608
|
+
}
|
|
609
|
+
// Memoize event config objects — only rebuild when the relevant props change
|
|
610
|
+
const hasClickEvent = openOnClick || (openEvents === null || openEvents === void 0 ? void 0 : openEvents.click) || (openEvents === null || openEvents === void 0 ? void 0 : openEvents.dblclick) || (openEvents === null || openEvents === void 0 ? void 0 : openEvents.mousedown);
|
|
611
|
+
const actualOpenEvents = React.useMemo(() => {
|
|
612
|
+
const events = openEvents
|
|
541
613
|
? { ...openEvents }
|
|
542
614
|
: {
|
|
543
615
|
mouseenter: true,
|
|
@@ -547,13 +619,25 @@
|
|
|
547
619
|
mousedown: false,
|
|
548
620
|
};
|
|
549
621
|
if (!openEvents && openOnClick) {
|
|
550
|
-
Object.assign(
|
|
622
|
+
Object.assign(events, {
|
|
551
623
|
mouseenter: false,
|
|
552
624
|
focus: false,
|
|
553
625
|
click: true,
|
|
554
626
|
});
|
|
555
627
|
}
|
|
556
|
-
|
|
628
|
+
if (imperativeModeOnly) {
|
|
629
|
+
Object.assign(events, {
|
|
630
|
+
mouseenter: false,
|
|
631
|
+
focus: false,
|
|
632
|
+
click: false,
|
|
633
|
+
dblclick: false,
|
|
634
|
+
mousedown: false,
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
return events;
|
|
638
|
+
}, [openEvents, openOnClick, imperativeModeOnly]);
|
|
639
|
+
const actualCloseEvents = React.useMemo(() => {
|
|
640
|
+
const events = closeEvents
|
|
557
641
|
? { ...closeEvents }
|
|
558
642
|
: {
|
|
559
643
|
mouseleave: true,
|
|
@@ -563,12 +647,24 @@
|
|
|
563
647
|
mouseup: false,
|
|
564
648
|
};
|
|
565
649
|
if (!closeEvents && openOnClick) {
|
|
566
|
-
Object.assign(
|
|
650
|
+
Object.assign(events, {
|
|
567
651
|
mouseleave: false,
|
|
568
652
|
blur: false,
|
|
569
653
|
});
|
|
570
654
|
}
|
|
571
|
-
|
|
655
|
+
if (imperativeModeOnly) {
|
|
656
|
+
Object.assign(events, {
|
|
657
|
+
mouseleave: false,
|
|
658
|
+
blur: false,
|
|
659
|
+
click: false,
|
|
660
|
+
dblclick: false,
|
|
661
|
+
mouseup: false,
|
|
662
|
+
});
|
|
663
|
+
}
|
|
664
|
+
return events;
|
|
665
|
+
}, [closeEvents, openOnClick, imperativeModeOnly]);
|
|
666
|
+
const actualGlobalCloseEvents = React.useMemo(() => {
|
|
667
|
+
const events = globalCloseEvents
|
|
572
668
|
? { ...globalCloseEvents }
|
|
573
669
|
: {
|
|
574
670
|
escape: false,
|
|
@@ -577,108 +673,157 @@
|
|
|
577
673
|
clickOutsideAnchor: hasClickEvent || false,
|
|
578
674
|
};
|
|
579
675
|
if (imperativeModeOnly) {
|
|
580
|
-
Object.assign(
|
|
581
|
-
mouseenter: false,
|
|
582
|
-
focus: false,
|
|
583
|
-
click: false,
|
|
584
|
-
dblclick: false,
|
|
585
|
-
mousedown: false,
|
|
586
|
-
});
|
|
587
|
-
Object.assign(actualCloseEvents, {
|
|
588
|
-
mouseleave: false,
|
|
589
|
-
blur: false,
|
|
590
|
-
click: false,
|
|
591
|
-
dblclick: false,
|
|
592
|
-
mouseup: false,
|
|
593
|
-
});
|
|
594
|
-
Object.assign(actualGlobalCloseEvents, {
|
|
676
|
+
Object.assign(events, {
|
|
595
677
|
escape: false,
|
|
596
678
|
scroll: false,
|
|
597
679
|
resize: false,
|
|
598
680
|
clickOutsideAnchor: false,
|
|
599
681
|
});
|
|
600
682
|
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
683
|
+
return events;
|
|
684
|
+
}, [globalCloseEvents, hasClickEvent, imperativeModeOnly]);
|
|
685
|
+
// --- Refs for values read inside event handlers (avoids effect deps) ---
|
|
686
|
+
const activeAnchorRef = React.useRef(activeAnchor);
|
|
687
|
+
activeAnchorRef.current = activeAnchor;
|
|
688
|
+
const showRef = React.useRef(show);
|
|
689
|
+
showRef.current = show;
|
|
690
|
+
const anchorElementsRef = React.useRef(anchorElements);
|
|
691
|
+
anchorElementsRef.current = anchorElements;
|
|
692
|
+
const handleShowRef = React.useRef(handleShow);
|
|
693
|
+
handleShowRef.current = handleShow;
|
|
694
|
+
const handleTooltipPositionRef = React.useRef(handleTooltipPosition);
|
|
695
|
+
handleTooltipPositionRef.current = handleTooltipPosition;
|
|
696
|
+
const updateTooltipPositionRef = React.useRef(updateTooltipPosition);
|
|
697
|
+
updateTooltipPositionRef.current = updateTooltipPosition;
|
|
698
|
+
// --- Handler refs (updated every render, read via ref indirection in effects) ---
|
|
699
|
+
const resolveAnchorElementRef = React.useRef(() => null);
|
|
700
|
+
const handleShowTooltipRef = React.useRef(() => { });
|
|
701
|
+
const handleHideTooltipRef = React.useRef(() => { });
|
|
702
|
+
const dataTooltipId = anchorSelector ? parseDataTooltipIdSelector(anchorSelector) : null;
|
|
703
|
+
resolveAnchorElementRef.current = (target) => {
|
|
704
|
+
var _a, _b;
|
|
705
|
+
const targetElement = target;
|
|
706
|
+
if (!(targetElement === null || targetElement === void 0 ? void 0 : targetElement.isConnected)) {
|
|
707
|
+
return null;
|
|
608
708
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
709
|
+
if (dataTooltipId) {
|
|
710
|
+
const matchedAnchor = resolveDataTooltipAnchor(targetElement, dataTooltipId);
|
|
711
|
+
if (matchedAnchor && !(disableTooltip === null || disableTooltip === void 0 ? void 0 : disableTooltip(matchedAnchor))) {
|
|
712
|
+
return matchedAnchor;
|
|
713
|
+
}
|
|
612
714
|
}
|
|
613
|
-
else if (
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
715
|
+
else if (anchorSelector) {
|
|
716
|
+
try {
|
|
717
|
+
const matchedAnchor = (_a = (targetElement.matches(anchorSelector)
|
|
718
|
+
? targetElement
|
|
719
|
+
: targetElement.closest(anchorSelector))) !== null && _a !== void 0 ? _a : null;
|
|
720
|
+
if (matchedAnchor && !(disableTooltip === null || disableTooltip === void 0 ? void 0 : disableTooltip(matchedAnchor))) {
|
|
721
|
+
return matchedAnchor;
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
catch (_c) {
|
|
725
|
+
return null;
|
|
726
|
+
}
|
|
619
727
|
}
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
728
|
+
return ((_b = anchorElementsRef.current.find((anchor) => anchor === targetElement || anchor.contains(targetElement))) !== null && _b !== void 0 ? _b : null);
|
|
729
|
+
};
|
|
730
|
+
handleShowTooltipRef.current = (anchor) => {
|
|
731
|
+
if (!anchor) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (!anchor.isConnected) {
|
|
735
|
+
setActiveAnchor(null);
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
if (disableTooltip === null || disableTooltip === void 0 ? void 0 : disableTooltip(anchor)) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (delayShow) {
|
|
742
|
+
handleShowTooltipDelayed();
|
|
743
|
+
}
|
|
744
|
+
else {
|
|
745
|
+
handleShow(true);
|
|
746
|
+
}
|
|
747
|
+
if (delayShow && activeAnchorRef.current && anchor !== activeAnchorRef.current) {
|
|
748
|
+
// Moving to a different anchor while one is already active — defer the anchor
|
|
749
|
+
// switch until the show delay fires to prevent content/position from updating
|
|
750
|
+
// before visibility transitions complete.
|
|
751
|
+
if (tooltipShowDelayTimerRef.current) {
|
|
752
|
+
clearTimeout(tooltipShowDelayTimerRef.current);
|
|
623
753
|
}
|
|
754
|
+
tooltipShowDelayTimerRef.current = setTimeout(() => {
|
|
755
|
+
setActiveAnchor(anchor);
|
|
756
|
+
handleShow(true);
|
|
757
|
+
}, delayShow);
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
setActiveAnchor(anchor);
|
|
761
|
+
}
|
|
762
|
+
if (tooltipHideDelayTimerRef.current) {
|
|
763
|
+
clearTimeout(tooltipHideDelayTimerRef.current);
|
|
764
|
+
}
|
|
765
|
+
};
|
|
766
|
+
handleHideTooltipRef.current = () => {
|
|
767
|
+
if (clickable) {
|
|
768
|
+
handleHideTooltipDelayed(delayHide || 100);
|
|
769
|
+
}
|
|
770
|
+
else if (delayHide) {
|
|
771
|
+
handleHideTooltipDelayed();
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
624
774
|
handleShow(false);
|
|
625
|
-
};
|
|
626
|
-
if (actualGlobalCloseEvents.escape) {
|
|
627
|
-
window.addEventListener('keydown', handleEsc);
|
|
628
775
|
}
|
|
629
|
-
if (
|
|
630
|
-
|
|
776
|
+
if (tooltipShowDelayTimerRef.current) {
|
|
777
|
+
clearTimeout(tooltipShowDelayTimerRef.current);
|
|
631
778
|
}
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
779
|
+
};
|
|
780
|
+
// Update debounced callbacks to always delegate to latest handler refs
|
|
781
|
+
const debouncedShow = debouncedShowRef.current;
|
|
782
|
+
const debouncedHide = debouncedHideRef.current;
|
|
783
|
+
debouncedShow.setCallback((anchor) => handleShowTooltipRef.current(anchor));
|
|
784
|
+
debouncedHide.setCallback(() => handleHideTooltipRef.current());
|
|
785
|
+
// --- Effect 1: Delegated anchor events + tooltip hover ---
|
|
786
|
+
// Only re-runs when the set of active event types or interaction mode changes.
|
|
787
|
+
// Handlers read reactive values (activeAnchor, show, etc.) from refs at invocation
|
|
788
|
+
// time, so this effect is decoupled from show/hide state changes.
|
|
789
|
+
React.useEffect(() => {
|
|
790
|
+
const cleanupFns = [];
|
|
791
|
+
const addDelegatedListener = (eventType, listener) => {
|
|
792
|
+
cleanupFns.push(addDelegatedEventListener(eventType, listener));
|
|
643
793
|
};
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
794
|
+
const activeAnchorContainsTarget = (event) => { var _a; return Boolean((event === null || event === void 0 ? void 0 : event.target) && ((_a = activeAnchorRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.target))); };
|
|
795
|
+
const debouncedHandleShowTooltip = (anchor) => {
|
|
796
|
+
debouncedHide.cancel();
|
|
797
|
+
debouncedShow(anchor);
|
|
798
|
+
};
|
|
799
|
+
const debouncedHandleHideTooltip = () => {
|
|
800
|
+
debouncedShow.cancel();
|
|
801
|
+
debouncedHide();
|
|
649
802
|
};
|
|
650
|
-
const regularEvents = ['mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'focus', 'blur'];
|
|
651
|
-
const clickEvents = ['click', 'dblclick', 'mousedown', 'mouseup'];
|
|
652
|
-
const delegatedEvents = [];
|
|
653
803
|
const addDelegatedHoverOpenListener = () => {
|
|
654
|
-
|
|
655
|
-
event
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
}
|
|
665
|
-
debouncedHandleShowTooltip(anchor);
|
|
666
|
-
},
|
|
804
|
+
addDelegatedListener('mouseover', (event) => {
|
|
805
|
+
const anchor = resolveAnchorElementRef.current(event.target);
|
|
806
|
+
if (!anchor) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
const relatedAnchor = resolveAnchorElementRef.current(event.relatedTarget);
|
|
810
|
+
if (relatedAnchor === anchor) {
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
debouncedHandleShowTooltip(anchor);
|
|
667
814
|
});
|
|
668
815
|
};
|
|
669
816
|
const addDelegatedHoverCloseListener = () => {
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
debouncedHandleHideTooltip();
|
|
681
|
-
},
|
|
817
|
+
addDelegatedListener('mouseout', (event) => {
|
|
818
|
+
var _a;
|
|
819
|
+
if (!activeAnchorContainsTarget(event)) {
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
const relatedTarget = event.relatedTarget;
|
|
823
|
+
if ((_a = activeAnchorRef.current) === null || _a === void 0 ? void 0 : _a.contains(relatedTarget)) {
|
|
824
|
+
return;
|
|
825
|
+
}
|
|
826
|
+
debouncedHandleHideTooltip();
|
|
682
827
|
});
|
|
683
828
|
};
|
|
684
829
|
if (actualOpenEvents.mouseenter) {
|
|
@@ -694,37 +839,48 @@
|
|
|
694
839
|
addDelegatedHoverCloseListener();
|
|
695
840
|
}
|
|
696
841
|
if (actualOpenEvents.focus) {
|
|
697
|
-
|
|
698
|
-
event
|
|
699
|
-
listener: (event) => {
|
|
700
|
-
debouncedHandleShowTooltip(resolveAnchorElement(event.target));
|
|
701
|
-
},
|
|
842
|
+
addDelegatedListener('focusin', (event) => {
|
|
843
|
+
debouncedHandleShowTooltip(resolveAnchorElementRef.current(event.target));
|
|
702
844
|
});
|
|
703
845
|
}
|
|
704
846
|
if (actualCloseEvents.blur) {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
debouncedHandleHideTooltip();
|
|
716
|
-
},
|
|
847
|
+
addDelegatedListener('focusout', (event) => {
|
|
848
|
+
var _a;
|
|
849
|
+
if (!activeAnchorContainsTarget(event)) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
const relatedTarget = event.relatedTarget;
|
|
853
|
+
if ((_a = activeAnchorRef.current) === null || _a === void 0 ? void 0 : _a.contains(relatedTarget)) {
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
debouncedHandleHideTooltip();
|
|
717
857
|
});
|
|
718
858
|
}
|
|
859
|
+
const regularEvents = ['mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'focus', 'blur'];
|
|
860
|
+
const clickEvents = ['click', 'dblclick', 'mousedown', 'mouseup'];
|
|
861
|
+
const handleClickOpenTooltipAnchor = (event) => {
|
|
862
|
+
var _a;
|
|
863
|
+
const anchor = resolveAnchorElementRef.current((_a = event === null || event === void 0 ? void 0 : event.target) !== null && _a !== void 0 ? _a : null);
|
|
864
|
+
if (!anchor) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
if (showRef.current && activeAnchorRef.current === anchor) {
|
|
868
|
+
return;
|
|
869
|
+
}
|
|
870
|
+
handleShowTooltipRef.current(anchor);
|
|
871
|
+
};
|
|
872
|
+
const handleClickCloseTooltipAnchor = (event) => {
|
|
873
|
+
if (!showRef.current || !activeAnchorContainsTarget(event)) {
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
handleHideTooltipRef.current();
|
|
877
|
+
};
|
|
719
878
|
Object.entries(actualOpenEvents).forEach(([event, enabled]) => {
|
|
720
879
|
if (!enabled || regularEvents.includes(event)) {
|
|
721
880
|
return;
|
|
722
881
|
}
|
|
723
882
|
if (clickEvents.includes(event)) {
|
|
724
|
-
|
|
725
|
-
event,
|
|
726
|
-
listener: handleClickOpenTooltipAnchor,
|
|
727
|
-
});
|
|
883
|
+
addDelegatedListener(event, handleClickOpenTooltipAnchor);
|
|
728
884
|
}
|
|
729
885
|
});
|
|
730
886
|
Object.entries(actualCloseEvents).forEach(([event, enabled]) => {
|
|
@@ -732,38 +888,111 @@
|
|
|
732
888
|
return;
|
|
733
889
|
}
|
|
734
890
|
if (clickEvents.includes(event)) {
|
|
735
|
-
|
|
736
|
-
event,
|
|
737
|
-
listener: handleClickCloseTooltipAnchor,
|
|
738
|
-
});
|
|
891
|
+
addDelegatedListener(event, handleClickCloseTooltipAnchor);
|
|
739
892
|
}
|
|
740
893
|
});
|
|
741
894
|
if (float) {
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
895
|
+
addDelegatedListener('pointermove', (event) => {
|
|
896
|
+
const currentActiveAnchor = activeAnchorRef.current;
|
|
897
|
+
if (!currentActiveAnchor) {
|
|
898
|
+
return;
|
|
899
|
+
}
|
|
900
|
+
const targetAnchor = resolveAnchorElementRef.current(event.target);
|
|
901
|
+
if (targetAnchor !== currentActiveAnchor) {
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
const mouseEvent = event;
|
|
905
|
+
const mousePosition = {
|
|
906
|
+
x: mouseEvent.clientX,
|
|
907
|
+
y: mouseEvent.clientY,
|
|
908
|
+
};
|
|
909
|
+
handleTooltipPositionRef.current(mousePosition);
|
|
910
|
+
lastFloatPosition.current = mousePosition;
|
|
745
911
|
});
|
|
746
912
|
}
|
|
913
|
+
const tooltipElement = tooltipRef.current;
|
|
747
914
|
const handleMouseOverTooltip = () => {
|
|
748
|
-
// eslint-disable-next-line no-param-reassign
|
|
749
915
|
hoveringTooltip.current = true;
|
|
750
916
|
};
|
|
751
917
|
const handleMouseOutTooltip = () => {
|
|
752
|
-
// eslint-disable-next-line no-param-reassign
|
|
753
918
|
hoveringTooltip.current = false;
|
|
754
|
-
|
|
919
|
+
handleHideTooltipRef.current();
|
|
755
920
|
};
|
|
756
921
|
const addHoveringTooltipListeners = clickable && (actualCloseEvents.mouseout || actualCloseEvents.mouseleave);
|
|
757
922
|
if (addHoveringTooltipListeners) {
|
|
758
923
|
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.addEventListener('mouseover', handleMouseOverTooltip);
|
|
759
924
|
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.addEventListener('mouseout', handleMouseOutTooltip);
|
|
760
925
|
}
|
|
761
|
-
delegatedEvents.forEach(({ event, listener }) => {
|
|
762
|
-
document.addEventListener(event, listener);
|
|
763
|
-
});
|
|
764
926
|
return () => {
|
|
927
|
+
cleanupFns.forEach((fn) => fn());
|
|
928
|
+
if (addHoveringTooltipListeners) {
|
|
929
|
+
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.removeEventListener('mouseover', handleMouseOverTooltip);
|
|
930
|
+
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.removeEventListener('mouseout', handleMouseOutTooltip);
|
|
931
|
+
}
|
|
932
|
+
debouncedShow.cancel();
|
|
933
|
+
debouncedHide.cancel();
|
|
934
|
+
};
|
|
935
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
936
|
+
}, [actualOpenEvents, actualCloseEvents, float, clickable]);
|
|
937
|
+
// --- Effect 2: Global close events + auto-update ---
|
|
938
|
+
// Re-runs when the global close config changes, or when the active anchor changes
|
|
939
|
+
// (for scroll parent listeners and floating-ui autoUpdate).
|
|
940
|
+
React.useEffect(() => {
|
|
941
|
+
const handleScrollResize = () => {
|
|
942
|
+
handleShowRef.current(false);
|
|
943
|
+
};
|
|
944
|
+
const tooltipScrollParent = tooltipScrollParentRef.current;
|
|
945
|
+
const anchorScrollParent = anchorScrollParentRef.current;
|
|
946
|
+
if (actualGlobalCloseEvents.scroll) {
|
|
947
|
+
window.addEventListener('scroll', handleScrollResize);
|
|
948
|
+
anchorScrollParent === null || anchorScrollParent === void 0 ? void 0 : anchorScrollParent.addEventListener('scroll', handleScrollResize);
|
|
949
|
+
tooltipScrollParent === null || tooltipScrollParent === void 0 ? void 0 : tooltipScrollParent.addEventListener('scroll', handleScrollResize);
|
|
950
|
+
}
|
|
951
|
+
let updateTooltipCleanup = null;
|
|
952
|
+
if (actualGlobalCloseEvents.resize) {
|
|
953
|
+
window.addEventListener('resize', handleScrollResize);
|
|
954
|
+
}
|
|
955
|
+
else if (activeAnchor && tooltipRef.current) {
|
|
956
|
+
updateTooltipCleanup = dom.autoUpdate(activeAnchor, tooltipRef.current, () => updateTooltipPositionRef.current(), {
|
|
957
|
+
ancestorResize: true,
|
|
958
|
+
elementResize: true,
|
|
959
|
+
layoutShift: true,
|
|
960
|
+
});
|
|
961
|
+
}
|
|
962
|
+
const handleEsc = (event) => {
|
|
963
|
+
if (event.key !== 'Escape') {
|
|
964
|
+
return;
|
|
965
|
+
}
|
|
966
|
+
handleShowRef.current(false);
|
|
967
|
+
};
|
|
968
|
+
if (actualGlobalCloseEvents.escape) {
|
|
969
|
+
window.addEventListener('keydown', handleEsc);
|
|
970
|
+
}
|
|
971
|
+
const handleClickOutsideAnchors = (event) => {
|
|
972
|
+
var _a, _b;
|
|
973
|
+
if (!showRef.current) {
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
976
|
+
const target = event.target;
|
|
977
|
+
if (!(target === null || target === void 0 ? void 0 : target.isConnected)) {
|
|
978
|
+
return;
|
|
979
|
+
}
|
|
980
|
+
if ((_a = tooltipRef.current) === null || _a === void 0 ? void 0 : _a.contains(target)) {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
if ((_b = activeAnchorRef.current) === null || _b === void 0 ? void 0 : _b.contains(target)) {
|
|
984
|
+
return;
|
|
985
|
+
}
|
|
986
|
+
if (anchorElementsRef.current.some((anchor) => anchor === null || anchor === void 0 ? void 0 : anchor.contains(target))) {
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
handleShowRef.current(false);
|
|
765
990
|
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
766
|
-
|
|
991
|
+
};
|
|
992
|
+
if (actualGlobalCloseEvents.clickOutsideAnchor) {
|
|
993
|
+
window.addEventListener('click', handleClickOutsideAnchors);
|
|
994
|
+
}
|
|
995
|
+
return () => {
|
|
767
996
|
if (actualGlobalCloseEvents.scroll) {
|
|
768
997
|
window.removeEventListener('scroll', handleScrollResize);
|
|
769
998
|
anchorScrollParent === null || anchorScrollParent === void 0 ? void 0 : anchorScrollParent.removeEventListener('scroll', handleScrollResize);
|
|
@@ -775,53 +1004,22 @@
|
|
|
775
1004
|
if (updateTooltipCleanup) {
|
|
776
1005
|
updateTooltipCleanup();
|
|
777
1006
|
}
|
|
778
|
-
if (actualGlobalCloseEvents.clickOutsideAnchor) {
|
|
779
|
-
window.removeEventListener('click', handleClickOutsideAnchors);
|
|
780
|
-
}
|
|
781
1007
|
if (actualGlobalCloseEvents.escape) {
|
|
782
1008
|
window.removeEventListener('keydown', handleEsc);
|
|
783
1009
|
}
|
|
784
|
-
if (
|
|
785
|
-
|
|
786
|
-
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.removeEventListener('mouseout', handleMouseOutTooltip);
|
|
1010
|
+
if (actualGlobalCloseEvents.clickOutsideAnchor) {
|
|
1011
|
+
window.removeEventListener('click', handleClickOutsideAnchors);
|
|
787
1012
|
}
|
|
788
|
-
delegatedEvents.forEach(({ event, listener }) => {
|
|
789
|
-
document.removeEventListener(event, listener);
|
|
790
|
-
});
|
|
791
|
-
internalDebouncedHandleShowTooltip.cancel();
|
|
792
|
-
internalDebouncedHandleHideTooltip.cancel();
|
|
793
1013
|
};
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
anchorElements,
|
|
797
|
-
clickable,
|
|
798
|
-
closeEvents,
|
|
799
|
-
delayHide,
|
|
800
|
-
delayShow,
|
|
801
|
-
disableTooltip,
|
|
802
|
-
float,
|
|
803
|
-
globalCloseEvents,
|
|
804
|
-
handleHideTooltipDelayed,
|
|
805
|
-
handleShow,
|
|
806
|
-
handleShowTooltipDelayed,
|
|
807
|
-
handleTooltipPosition,
|
|
808
|
-
imperativeModeOnly,
|
|
809
|
-
lastFloatPosition,
|
|
810
|
-
openEvents,
|
|
811
|
-
openOnClick,
|
|
812
|
-
setActiveAnchor,
|
|
813
|
-
show,
|
|
814
|
-
tooltipHideDelayTimerRef,
|
|
815
|
-
tooltipRef,
|
|
816
|
-
tooltipShowDelayTimerRef,
|
|
817
|
-
updateTooltipPosition,
|
|
818
|
-
hoveringTooltip,
|
|
819
|
-
]);
|
|
1014
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1015
|
+
}, [actualGlobalCloseEvents, activeAnchor]);
|
|
820
1016
|
};
|
|
821
1017
|
|
|
1018
|
+
// Shared across all tooltip instances — the CSS variable is on :root and never changes per-instance
|
|
1019
|
+
let globalTransitionShowDelay = null;
|
|
822
1020
|
const Tooltip = ({
|
|
823
1021
|
// props
|
|
824
|
-
forwardRef, id, className, classNameArrow, variant = 'dark', portalRoot, anchorSelect, place = 'top', offset = 10, openOnClick = false, positionStrategy = 'absolute', middlewares, wrapper: WrapperElement, delayShow = 0, delayHide = 0, float = false, hidden = false, noArrow = false, clickable = false, openEvents, closeEvents, globalCloseEvents, imperativeModeOnly, style: externalStyles, position, afterShow, afterHide, disableTooltip,
|
|
1022
|
+
forwardRef, id, className, classNameArrow, variant = 'dark', portalRoot, anchorSelect, place = 'top', offset = 10, openOnClick = false, positionStrategy = 'absolute', middlewares, wrapper: WrapperElement, delayShow = 0, delayHide = 0, autoClose, float = false, hidden = false, noArrow = false, clickable = false, openEvents, closeEvents, globalCloseEvents, imperativeModeOnly, style: externalStyles, position, afterShow, afterHide, disableTooltip,
|
|
825
1023
|
// props handled by controller
|
|
826
1024
|
content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, previousActiveAnchor, activeAnchor, setActiveAnchor, border, opacity, arrowColor, arrowSize = 8, role = 'tooltip', }) => {
|
|
827
1025
|
var _a;
|
|
@@ -829,6 +1027,7 @@
|
|
|
829
1027
|
const tooltipArrowRef = React.useRef(null);
|
|
830
1028
|
const tooltipShowDelayTimerRef = React.useRef(null);
|
|
831
1029
|
const tooltipHideDelayTimerRef = React.useRef(null);
|
|
1030
|
+
const tooltipAutoCloseTimerRef = React.useRef(null);
|
|
832
1031
|
const missedTransitionTimerRef = React.useRef(null);
|
|
833
1032
|
const [computedPosition, setComputedPosition] = React.useState({
|
|
834
1033
|
tooltipStyles: {},
|
|
@@ -842,6 +1041,18 @@
|
|
|
842
1041
|
const lastFloatPosition = React.useRef(null);
|
|
843
1042
|
const hoveringTooltip = React.useRef(false);
|
|
844
1043
|
const mounted = React.useRef(false);
|
|
1044
|
+
const virtualElementRef = React.useRef({
|
|
1045
|
+
getBoundingClientRect: () => ({
|
|
1046
|
+
x: 0,
|
|
1047
|
+
y: 0,
|
|
1048
|
+
width: 0,
|
|
1049
|
+
height: 0,
|
|
1050
|
+
top: 0,
|
|
1051
|
+
left: 0,
|
|
1052
|
+
right: 0,
|
|
1053
|
+
bottom: 0,
|
|
1054
|
+
}),
|
|
1055
|
+
});
|
|
845
1056
|
/**
|
|
846
1057
|
* useLayoutEffect runs before useEffect,
|
|
847
1058
|
* but should be used carefully because of caveats
|
|
@@ -902,7 +1113,6 @@
|
|
|
902
1113
|
else {
|
|
903
1114
|
removeAriaDescribedBy(activeAnchor);
|
|
904
1115
|
}
|
|
905
|
-
// eslint-disable-next-line consistent-return
|
|
906
1116
|
return () => {
|
|
907
1117
|
// cleanup aria-describedby when the tooltip is closed
|
|
908
1118
|
removeAriaDescribedBy(activeAnchor);
|
|
@@ -940,8 +1150,11 @@
|
|
|
940
1150
|
/**
|
|
941
1151
|
* see `onTransitionEnd` on tooltip wrapper
|
|
942
1152
|
*/
|
|
943
|
-
|
|
944
|
-
|
|
1153
|
+
if (globalTransitionShowDelay === null) {
|
|
1154
|
+
const style = getComputedStyle(document.body);
|
|
1155
|
+
globalTransitionShowDelay = cssTimeToMs(style.getPropertyValue('--rt-transition-show-delay'));
|
|
1156
|
+
}
|
|
1157
|
+
const transitionShowDelay = globalTransitionShowDelay;
|
|
945
1158
|
missedTransitionTimerRef.current = setTimeout(() => {
|
|
946
1159
|
/**
|
|
947
1160
|
* if the tooltip switches from `show === true` to `show === false` too fast
|
|
@@ -954,13 +1167,44 @@
|
|
|
954
1167
|
}, transitionShowDelay + 25);
|
|
955
1168
|
}
|
|
956
1169
|
}, [afterHide, afterShow, show]);
|
|
1170
|
+
React.useEffect(() => {
|
|
1171
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1172
|
+
if (!show || !autoClose || autoClose <= 0) {
|
|
1173
|
+
return () => {
|
|
1174
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1175
|
+
};
|
|
1176
|
+
}
|
|
1177
|
+
tooltipAutoCloseTimerRef.current = setTimeout(() => {
|
|
1178
|
+
handleShow(false);
|
|
1179
|
+
}, autoClose);
|
|
1180
|
+
return () => {
|
|
1181
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1182
|
+
};
|
|
1183
|
+
}, [activeAnchor, autoClose, handleShow, show]);
|
|
957
1184
|
const handleComputedPosition = React.useCallback((newComputedPosition) => {
|
|
958
1185
|
if (!mounted.current) {
|
|
959
1186
|
return;
|
|
960
1187
|
}
|
|
961
|
-
setComputedPosition((oldComputedPosition) =>
|
|
962
|
-
|
|
963
|
-
|
|
1188
|
+
setComputedPosition((oldComputedPosition) => {
|
|
1189
|
+
if (oldComputedPosition.place === newComputedPosition.place &&
|
|
1190
|
+
oldComputedPosition.tooltipStyles.left === newComputedPosition.tooltipStyles.left &&
|
|
1191
|
+
oldComputedPosition.tooltipStyles.top === newComputedPosition.tooltipStyles.top &&
|
|
1192
|
+
oldComputedPosition.tooltipStyles.border === newComputedPosition.tooltipStyles.border &&
|
|
1193
|
+
oldComputedPosition.tooltipArrowStyles.left ===
|
|
1194
|
+
newComputedPosition.tooltipArrowStyles.left &&
|
|
1195
|
+
oldComputedPosition.tooltipArrowStyles.top === newComputedPosition.tooltipArrowStyles.top &&
|
|
1196
|
+
oldComputedPosition.tooltipArrowStyles.right ===
|
|
1197
|
+
newComputedPosition.tooltipArrowStyles.right &&
|
|
1198
|
+
oldComputedPosition.tooltipArrowStyles.bottom ===
|
|
1199
|
+
newComputedPosition.tooltipArrowStyles.bottom &&
|
|
1200
|
+
oldComputedPosition.tooltipArrowStyles.borderBottom ===
|
|
1201
|
+
newComputedPosition.tooltipArrowStyles.borderBottom &&
|
|
1202
|
+
oldComputedPosition.tooltipArrowStyles.borderRight ===
|
|
1203
|
+
newComputedPosition.tooltipArrowStyles.borderRight) {
|
|
1204
|
+
return oldComputedPosition;
|
|
1205
|
+
}
|
|
1206
|
+
return newComputedPosition;
|
|
1207
|
+
});
|
|
964
1208
|
}, []);
|
|
965
1209
|
const handleShowTooltipDelayed = React.useCallback((delay = delayShow) => {
|
|
966
1210
|
if (tooltipShowDelayTimerRef.current) {
|
|
@@ -988,24 +1232,20 @@
|
|
|
988
1232
|
}, [delayHide, handleShow]);
|
|
989
1233
|
const handleTooltipPosition = React.useCallback(({ x, y }) => {
|
|
990
1234
|
var _a;
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
bottom: y,
|
|
1002
|
-
};
|
|
1003
|
-
},
|
|
1004
|
-
};
|
|
1235
|
+
virtualElementRef.current.getBoundingClientRect = () => ({
|
|
1236
|
+
x,
|
|
1237
|
+
y,
|
|
1238
|
+
width: 0,
|
|
1239
|
+
height: 0,
|
|
1240
|
+
top: y,
|
|
1241
|
+
left: x,
|
|
1242
|
+
right: x,
|
|
1243
|
+
bottom: y,
|
|
1244
|
+
});
|
|
1005
1245
|
computeTooltipPosition({
|
|
1006
1246
|
place: (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.place) !== null && _a !== void 0 ? _a : place,
|
|
1007
1247
|
offset,
|
|
1008
|
-
elementReference:
|
|
1248
|
+
elementReference: virtualElementRef.current,
|
|
1009
1249
|
tooltipReference: tooltipRef.current,
|
|
1010
1250
|
tooltipArrowReference: tooltipArrowRef.current,
|
|
1011
1251
|
strategy: positionStrategy,
|
|
@@ -1088,18 +1328,26 @@
|
|
|
1088
1328
|
setActiveAnchor(null);
|
|
1089
1329
|
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
1090
1330
|
clearTimeoutRef(tooltipHideDelayTimerRef);
|
|
1331
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1091
1332
|
}, [handleShow, setActiveAnchor]);
|
|
1092
|
-
const
|
|
1333
|
+
const shouldTrackAnchors = rendered ||
|
|
1334
|
+
defaultIsOpen ||
|
|
1335
|
+
Boolean(isOpen) ||
|
|
1336
|
+
Boolean(activeAnchor) ||
|
|
1337
|
+
Boolean(imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect);
|
|
1338
|
+
const { anchorElements, selector: anchorSelector } = useTooltipAnchors({
|
|
1093
1339
|
id,
|
|
1094
1340
|
anchorSelect,
|
|
1095
1341
|
imperativeAnchorSelect: imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect,
|
|
1096
1342
|
activeAnchor,
|
|
1097
1343
|
disableTooltip,
|
|
1098
1344
|
onActiveAnchorRemoved: handleActiveAnchorRemoved,
|
|
1345
|
+
trackAnchors: shouldTrackAnchors,
|
|
1099
1346
|
});
|
|
1100
1347
|
useTooltipEvents({
|
|
1101
1348
|
activeAnchor,
|
|
1102
1349
|
anchorElements,
|
|
1350
|
+
anchorSelector,
|
|
1103
1351
|
clickable,
|
|
1104
1352
|
closeEvents,
|
|
1105
1353
|
delayHide,
|
|
@@ -1123,11 +1371,16 @@
|
|
|
1123
1371
|
tooltipShowDelayTimerRef,
|
|
1124
1372
|
updateTooltipPosition,
|
|
1125
1373
|
});
|
|
1374
|
+
const updateTooltipPositionRef = React.useRef(updateTooltipPosition);
|
|
1375
|
+
updateTooltipPositionRef.current = updateTooltipPosition;
|
|
1126
1376
|
React.useEffect(() => {
|
|
1377
|
+
if (!rendered) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1127
1380
|
updateTooltipPosition();
|
|
1128
|
-
}, [updateTooltipPosition]);
|
|
1381
|
+
}, [rendered, updateTooltipPosition]);
|
|
1129
1382
|
React.useEffect(() => {
|
|
1130
|
-
if (!(contentWrapperRef === null || contentWrapperRef === void 0 ? void 0 : contentWrapperRef.current)) {
|
|
1383
|
+
if (!rendered || !(contentWrapperRef === null || contentWrapperRef === void 0 ? void 0 : contentWrapperRef.current)) {
|
|
1131
1384
|
return () => null;
|
|
1132
1385
|
}
|
|
1133
1386
|
let timeoutId = null;
|
|
@@ -1138,7 +1391,7 @@
|
|
|
1138
1391
|
}
|
|
1139
1392
|
timeoutId = setTimeout(() => {
|
|
1140
1393
|
if (mounted.current) {
|
|
1141
|
-
|
|
1394
|
+
updateTooltipPositionRef.current();
|
|
1142
1395
|
}
|
|
1143
1396
|
timeoutId = null;
|
|
1144
1397
|
}, 0);
|
|
@@ -1150,9 +1403,13 @@
|
|
|
1150
1403
|
clearTimeout(timeoutId);
|
|
1151
1404
|
}
|
|
1152
1405
|
};
|
|
1153
|
-
}, [content, contentWrapperRef,
|
|
1406
|
+
}, [content, contentWrapperRef, rendered]);
|
|
1154
1407
|
React.useEffect(() => {
|
|
1155
1408
|
var _a;
|
|
1409
|
+
const shouldResolveInitialActiveAnchor = rendered || defaultIsOpen || Boolean(isOpen);
|
|
1410
|
+
if (!shouldResolveInitialActiveAnchor) {
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1156
1413
|
const activeAnchorMatchesImperativeSelector = (() => {
|
|
1157
1414
|
if (!activeAnchor || !(imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect)) {
|
|
1158
1415
|
return false;
|
|
@@ -1175,7 +1432,15 @@
|
|
|
1175
1432
|
}
|
|
1176
1433
|
setActiveAnchor((_a = anchorElements[0]) !== null && _a !== void 0 ? _a : null);
|
|
1177
1434
|
}
|
|
1178
|
-
}, [
|
|
1435
|
+
}, [
|
|
1436
|
+
activeAnchor,
|
|
1437
|
+
anchorElements,
|
|
1438
|
+
defaultIsOpen,
|
|
1439
|
+
imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect,
|
|
1440
|
+
isOpen,
|
|
1441
|
+
rendered,
|
|
1442
|
+
setActiveAnchor,
|
|
1443
|
+
]);
|
|
1179
1444
|
React.useEffect(() => {
|
|
1180
1445
|
if (defaultIsOpen) {
|
|
1181
1446
|
handleShow(true);
|
|
@@ -1183,6 +1448,7 @@
|
|
|
1183
1448
|
return () => {
|
|
1184
1449
|
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
1185
1450
|
clearTimeoutRef(tooltipHideDelayTimerRef);
|
|
1451
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1186
1452
|
clearTimeoutRef(missedTransitionTimerRef);
|
|
1187
1453
|
};
|
|
1188
1454
|
}, [defaultIsOpen, handleShow]);
|
|
@@ -1198,7 +1464,20 @@
|
|
|
1198
1464
|
}, [delayShow, handleShowTooltipDelayed]);
|
|
1199
1465
|
const actualContent = (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.content) !== null && _a !== void 0 ? _a : content;
|
|
1200
1466
|
const hasContent = actualContent !== null && actualContent !== undefined;
|
|
1201
|
-
const canShow = show &&
|
|
1467
|
+
const canShow = show && computedPosition.tooltipStyles.left !== undefined;
|
|
1468
|
+
const tooltipStyle = React.useMemo(() => ({
|
|
1469
|
+
...externalStyles,
|
|
1470
|
+
...computedPosition.tooltipStyles,
|
|
1471
|
+
opacity: opacity !== undefined && canShow ? opacity : undefined,
|
|
1472
|
+
}), [externalStyles, computedPosition.tooltipStyles, opacity, canShow]);
|
|
1473
|
+
const arrowBackground = React.useMemo(() => arrowColor
|
|
1474
|
+
? `linear-gradient(to right bottom, transparent 50%, ${arrowColor} 50%)`
|
|
1475
|
+
: undefined, [arrowColor]);
|
|
1476
|
+
const arrowStyle = React.useMemo(() => ({
|
|
1477
|
+
...computedPosition.tooltipArrowStyles,
|
|
1478
|
+
background: arrowBackground,
|
|
1479
|
+
'--rt-arrow-size': `${arrowSize}px`,
|
|
1480
|
+
}), [computedPosition.tooltipArrowStyles, arrowBackground, arrowSize]);
|
|
1202
1481
|
React.useImperativeHandle(forwardRef, () => ({
|
|
1203
1482
|
open: (options) => {
|
|
1204
1483
|
let imperativeAnchor = null;
|
|
@@ -1241,6 +1520,7 @@
|
|
|
1241
1520
|
// Final cleanup to ensure no memory leaks
|
|
1242
1521
|
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
1243
1522
|
clearTimeoutRef(tooltipHideDelayTimerRef);
|
|
1523
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1244
1524
|
clearTimeoutRef(missedTransitionTimerRef);
|
|
1245
1525
|
};
|
|
1246
1526
|
}, []);
|
|
@@ -1252,19 +1532,9 @@
|
|
|
1252
1532
|
setRendered(false);
|
|
1253
1533
|
setImperativeOptions(null);
|
|
1254
1534
|
afterHide === null || afterHide === void 0 ? void 0 : afterHide();
|
|
1255
|
-
}, style:
|
|
1256
|
-
...externalStyles,
|
|
1257
|
-
...computedPosition.tooltipStyles,
|
|
1258
|
-
opacity: opacity !== undefined && canShow ? opacity : undefined,
|
|
1259
|
-
}, ref: tooltipRef },
|
|
1535
|
+
}, style: tooltipStyle, ref: tooltipRef },
|
|
1260
1536
|
React.createElement(WrapperElement, { className: clsx('react-tooltip-content-wrapper', coreStyles['content'], styles['content']) }, actualContent),
|
|
1261
|
-
React.createElement(WrapperElement, { className: clsx('react-tooltip-arrow', coreStyles['arrow'], styles['arrow'], classNameArrow, noArrow && coreStyles['noArrow']), style:
|
|
1262
|
-
...computedPosition.tooltipArrowStyles,
|
|
1263
|
-
background: arrowColor
|
|
1264
|
-
? `linear-gradient(to right bottom, transparent 50%, ${arrowColor} 50%)`
|
|
1265
|
-
: undefined,
|
|
1266
|
-
'--rt-arrow-size': `${arrowSize}px`,
|
|
1267
|
-
}, ref: tooltipArrowRef }))) : null;
|
|
1537
|
+
React.createElement(WrapperElement, { className: clsx('react-tooltip-arrow', coreStyles['arrow'], styles['arrow'], classNameArrow, noArrow && coreStyles['noArrow']), style: arrowStyle, ref: tooltipArrowRef }))) : null;
|
|
1268
1538
|
if (!tooltipNode) {
|
|
1269
1539
|
return null;
|
|
1270
1540
|
}
|
|
@@ -1275,12 +1545,82 @@
|
|
|
1275
1545
|
};
|
|
1276
1546
|
var Tooltip$1 = React.memo(Tooltip);
|
|
1277
1547
|
|
|
1278
|
-
|
|
1548
|
+
/**
|
|
1549
|
+
* Shared MutationObserver for data-tooltip-* attribute changes.
|
|
1550
|
+
* Instead of N observers (one per tooltip), a single observer watches
|
|
1551
|
+
* all active anchors and dispatches changes to registered callbacks.
|
|
1552
|
+
*/
|
|
1553
|
+
const observedElements = new Map();
|
|
1554
|
+
let sharedObserver = null;
|
|
1555
|
+
const observerConfig = {
|
|
1556
|
+
attributes: true,
|
|
1557
|
+
childList: false,
|
|
1558
|
+
subtree: false,
|
|
1559
|
+
};
|
|
1560
|
+
function getObserver() {
|
|
1561
|
+
if (!sharedObserver) {
|
|
1562
|
+
sharedObserver = new MutationObserver((mutationList) => {
|
|
1563
|
+
var _a;
|
|
1564
|
+
for (const mutation of mutationList) {
|
|
1565
|
+
if (mutation.type !== 'attributes' ||
|
|
1566
|
+
!((_a = mutation.attributeName) === null || _a === void 0 ? void 0 : _a.startsWith('data-tooltip-'))) {
|
|
1567
|
+
continue;
|
|
1568
|
+
}
|
|
1569
|
+
const target = mutation.target;
|
|
1570
|
+
const callbacks = observedElements.get(target);
|
|
1571
|
+
if (callbacks) {
|
|
1572
|
+
callbacks.forEach((cb) => cb(target));
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1575
|
+
});
|
|
1576
|
+
}
|
|
1577
|
+
return sharedObserver;
|
|
1578
|
+
}
|
|
1579
|
+
function observeAnchorAttributes(element, callback) {
|
|
1580
|
+
const observer = getObserver();
|
|
1581
|
+
let callbacks = observedElements.get(element);
|
|
1582
|
+
if (!callbacks) {
|
|
1583
|
+
callbacks = new Set();
|
|
1584
|
+
observedElements.set(element, callbacks);
|
|
1585
|
+
observer.observe(element, observerConfig);
|
|
1586
|
+
}
|
|
1587
|
+
callbacks.add(callback);
|
|
1588
|
+
return () => {
|
|
1589
|
+
const cbs = observedElements.get(element);
|
|
1590
|
+
if (cbs) {
|
|
1591
|
+
cbs.delete(callback);
|
|
1592
|
+
if (cbs.size === 0) {
|
|
1593
|
+
observedElements.delete(element);
|
|
1594
|
+
// MutationObserver doesn't have unobserve — if no elements left, disconnect & reset
|
|
1595
|
+
if (observedElements.size === 0) {
|
|
1596
|
+
observer.disconnect();
|
|
1597
|
+
}
|
|
1598
|
+
else {
|
|
1599
|
+
// Re-observe remaining elements (MutationObserver has no per-target unobserve)
|
|
1600
|
+
observer.disconnect();
|
|
1601
|
+
observedElements.forEach((_cbs, el) => {
|
|
1602
|
+
observer.observe(el, observerConfig);
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
}
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1609
|
+
|
|
1610
|
+
const TooltipController = React.forwardRef(({ id, anchorSelect, content, render, className, classNameArrow, variant = 'dark', portalRoot, place = 'top', offset = 10, wrapper = 'div', children = null, openOnClick = false, positionStrategy = 'absolute', middlewares, delayShow = 0, delayHide = 0, autoClose, float = false, hidden = false, noArrow = false, clickable = false, openEvents, closeEvents, globalCloseEvents, imperativeModeOnly = false, style, position, isOpen, defaultIsOpen = false, disableStyleInjection = false, border, opacity, arrowColor, arrowSize, setIsOpen, afterShow, afterHide, disableTooltip, role = 'tooltip', }, ref) => {
|
|
1279
1611
|
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1280
1612
|
const [activeAnchor, setActiveAnchor] = React.useState(null);
|
|
1281
1613
|
const [anchorDataAttributes, setAnchorDataAttributes] = React.useState({});
|
|
1282
1614
|
const previousActiveAnchorRef = React.useRef(null);
|
|
1283
1615
|
const styleInjectionRef = React.useRef(disableStyleInjection);
|
|
1616
|
+
const handleSetActiveAnchor = React.useCallback((anchor) => {
|
|
1617
|
+
setActiveAnchor((prev) => {
|
|
1618
|
+
if (!(anchor === null || anchor === void 0 ? void 0 : anchor.isSameNode(prev))) {
|
|
1619
|
+
previousActiveAnchorRef.current = prev;
|
|
1620
|
+
}
|
|
1621
|
+
return anchor;
|
|
1622
|
+
});
|
|
1623
|
+
}, []);
|
|
1284
1624
|
/* c8 ignore start */
|
|
1285
1625
|
const getDataAttributesFromAnchorElement = (elementReference) => {
|
|
1286
1626
|
const dataAttributes = elementReference === null || elementReference === void 0 ? void 0 : elementReference.getAttributeNames().reduce((acc, name) => {
|
|
@@ -1312,37 +1652,24 @@
|
|
|
1312
1652
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1313
1653
|
}, []);
|
|
1314
1654
|
React.useEffect(() => {
|
|
1315
|
-
|
|
1316
|
-
mutationList.forEach((mutation) => {
|
|
1317
|
-
var _a;
|
|
1318
|
-
if (!activeAnchor ||
|
|
1319
|
-
mutation.type !== 'attributes' ||
|
|
1320
|
-
!((_a = mutation.attributeName) === null || _a === void 0 ? void 0 : _a.startsWith('data-tooltip-'))) {
|
|
1321
|
-
return;
|
|
1322
|
-
}
|
|
1323
|
-
// make sure to get all set attributes, since all unset attributes are reset
|
|
1324
|
-
const dataAttributes = getDataAttributesFromAnchorElement(activeAnchor);
|
|
1325
|
-
setAnchorDataAttributes(dataAttributes);
|
|
1326
|
-
});
|
|
1327
|
-
};
|
|
1328
|
-
// Create an observer instance linked to the callback function
|
|
1329
|
-
const observer = new MutationObserver(observerCallback);
|
|
1330
|
-
// do not check for subtree and childrens, we only want to know attribute changes
|
|
1331
|
-
// to stay watching `data-attributes-*` from anchor element
|
|
1332
|
-
const observerConfig = { attributes: true, childList: false, subtree: false };
|
|
1333
|
-
if (activeAnchor) {
|
|
1334
|
-
const dataAttributes = getDataAttributesFromAnchorElement(activeAnchor);
|
|
1335
|
-
setAnchorDataAttributes(dataAttributes);
|
|
1336
|
-
// Start observing the target node for configured mutations
|
|
1337
|
-
observer.observe(activeAnchor, observerConfig);
|
|
1338
|
-
}
|
|
1339
|
-
else {
|
|
1655
|
+
if (!activeAnchor) {
|
|
1340
1656
|
setAnchorDataAttributes({});
|
|
1657
|
+
return () => { };
|
|
1341
1658
|
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1659
|
+
const updateAttributes = (element) => {
|
|
1660
|
+
const attrs = getDataAttributesFromAnchorElement(element);
|
|
1661
|
+
setAnchorDataAttributes((prev) => {
|
|
1662
|
+
const keys = Object.keys(attrs);
|
|
1663
|
+
const prevKeys = Object.keys(prev);
|
|
1664
|
+
if (keys.length === prevKeys.length && keys.every((key) => attrs[key] === prev[key])) {
|
|
1665
|
+
return prev;
|
|
1666
|
+
}
|
|
1667
|
+
return attrs;
|
|
1668
|
+
});
|
|
1345
1669
|
};
|
|
1670
|
+
updateAttributes(activeAnchor);
|
|
1671
|
+
const unsubscribe = observeAnchorAttributes(activeAnchor, updateAttributes);
|
|
1672
|
+
return unsubscribe;
|
|
1346
1673
|
}, [activeAnchor, anchorSelect]);
|
|
1347
1674
|
React.useEffect(() => {
|
|
1348
1675
|
/* c8 ignore start */
|
|
@@ -1366,6 +1693,9 @@
|
|
|
1366
1693
|
const tooltipDelayHide = anchorDataAttributes['delay-hide'] == null
|
|
1367
1694
|
? delayHide
|
|
1368
1695
|
: Number(anchorDataAttributes['delay-hide']);
|
|
1696
|
+
const tooltipAutoClose = anchorDataAttributes['auto-close'] == null
|
|
1697
|
+
? autoClose
|
|
1698
|
+
: Number(anchorDataAttributes['auto-close']);
|
|
1369
1699
|
const tooltipFloat = anchorDataAttributes.float == null ? float : anchorDataAttributes.float === 'true';
|
|
1370
1700
|
const tooltipHidden = anchorDataAttributes.hidden == null ? hidden : anchorDataAttributes.hidden === 'true';
|
|
1371
1701
|
const tooltipClassName = (_f = anchorDataAttributes['class-name']) !== null && _f !== void 0 ? _f : null;
|
|
@@ -1397,6 +1727,7 @@
|
|
|
1397
1727
|
middlewares,
|
|
1398
1728
|
delayShow: tooltipDelayShow,
|
|
1399
1729
|
delayHide: tooltipDelayHide,
|
|
1730
|
+
autoClose: tooltipAutoClose,
|
|
1400
1731
|
float: tooltipFloat,
|
|
1401
1732
|
hidden: tooltipHidden,
|
|
1402
1733
|
noArrow,
|
|
@@ -1419,19 +1750,12 @@
|
|
|
1419
1750
|
disableTooltip,
|
|
1420
1751
|
activeAnchor,
|
|
1421
1752
|
previousActiveAnchor: previousActiveAnchorRef.current,
|
|
1422
|
-
setActiveAnchor:
|
|
1423
|
-
setActiveAnchor((prev) => {
|
|
1424
|
-
if (!(anchor === null || anchor === void 0 ? void 0 : anchor.isSameNode(prev))) {
|
|
1425
|
-
previousActiveAnchorRef.current = prev;
|
|
1426
|
-
}
|
|
1427
|
-
return anchor;
|
|
1428
|
-
});
|
|
1429
|
-
},
|
|
1753
|
+
setActiveAnchor: handleSetActiveAnchor,
|
|
1430
1754
|
role,
|
|
1431
1755
|
};
|
|
1432
1756
|
return React.createElement(Tooltip$1, { ...props });
|
|
1433
1757
|
});
|
|
1434
|
-
var
|
|
1758
|
+
var TooltipController_default = React.memo(TooltipController);
|
|
1435
1759
|
|
|
1436
1760
|
// those content will be replaced in build time with the `react-tooltip.css` builded content
|
|
1437
1761
|
const TooltipCoreStyles = `:root {
|
|
@@ -1453,7 +1777,6 @@
|
|
|
1453
1777
|
left: 0;
|
|
1454
1778
|
pointer-events: none;
|
|
1455
1779
|
opacity: 0;
|
|
1456
|
-
will-change: opacity;
|
|
1457
1780
|
}
|
|
1458
1781
|
|
|
1459
1782
|
.core-styles-module_fixed__pcSol {
|
|
@@ -1484,11 +1807,13 @@
|
|
|
1484
1807
|
.core-styles-module_show__Nt9eE {
|
|
1485
1808
|
opacity: var(--rt-opacity);
|
|
1486
1809
|
transition: opacity var(--rt-transition-show-delay) ease-out;
|
|
1810
|
+
will-change: opacity;
|
|
1487
1811
|
}
|
|
1488
1812
|
|
|
1489
1813
|
.core-styles-module_closing__sGnxF {
|
|
1490
1814
|
opacity: 0;
|
|
1491
1815
|
transition: opacity var(--rt-transition-closing-delay) ease-in;
|
|
1816
|
+
will-change: opacity;
|
|
1492
1817
|
}
|
|
1493
1818
|
|
|
1494
1819
|
`;
|
|
@@ -1569,7 +1894,7 @@
|
|
|
1569
1894
|
}));
|
|
1570
1895
|
}
|
|
1571
1896
|
|
|
1572
|
-
exports.Tooltip =
|
|
1897
|
+
exports.Tooltip = TooltipController_default;
|
|
1573
1898
|
|
|
1574
1899
|
}));
|
|
1575
1900
|
//# sourceMappingURL=react-tooltip.umd.js.map
|