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