react-tooltip 6.0.0-beta.1179.rc.2 → 6.0.0-beta.1179.rc.21
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/{CONTRIBUTION.md → CONTRIBUTING.md} +0 -1
- package/README.md +30 -2
- package/dist/react-tooltip-tokens.css +1 -0
- package/dist/react-tooltip.cjs +1302 -798
- package/dist/react-tooltip.cjs.map +1 -1
- package/dist/react-tooltip.css +19 -4
- package/dist/react-tooltip.d.ts +19 -21
- 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 +1303 -799
- package/dist/react-tooltip.mjs.map +1 -1
- package/dist/react-tooltip.umd.js +1304 -801
- 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 +70 -58
- package/.eslintrc.json +0 -97
- package/.gitattributes +0 -3
- package/.prettierrc.json +0 -10
- package/.stylelintrc.json +0 -19
- package/beta-release.js +0 -81
- package/rollup.config.dev.mjs +0 -88
- package/rollup.config.prod.mjs +0 -126
- package/rollup.config.types.mjs +0 -21
- package/tsconfig.json +0 -109
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
* @license MIT
|
|
7
7
|
*/
|
|
8
8
|
(function (global, factory) {
|
|
9
|
-
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('
|
|
10
|
-
typeof define === 'function' && define.amd ? define(['exports', 'react', '@floating-ui/dom'
|
|
11
|
-
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactTooltip = {}, global.React, global.
|
|
12
|
-
})(this, (function (exports, React,
|
|
9
|
+
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react'), require('clsx'), require('react-dom'), require('@floating-ui/dom')) :
|
|
10
|
+
typeof define === 'function' && define.amd ? define(['exports', 'react', 'clsx', 'react-dom', '@floating-ui/dom'], factory) :
|
|
11
|
+
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ReactTooltip = {}, global.React, global.clsx, global.ReactDOM, global.FloatingUIDOM));
|
|
12
|
+
})(this, (function (exports, React, clsx, reactDom, dom) { 'use strict';
|
|
13
13
|
|
|
14
14
|
// This is the ID for the core styles of ReactTooltip
|
|
15
15
|
const REACT_TOOLTIP_CORE_STYLES_ID = 'react-tooltip-core-styles';
|
|
@@ -19,27 +19,33 @@
|
|
|
19
19
|
core: false,
|
|
20
20
|
base: false,
|
|
21
21
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
/**
|
|
23
|
+
* Note about `state` parameter:
|
|
24
|
+
* This parameter is used to keep track of the state of the styles
|
|
25
|
+
* into the tests since the const `injected` is not acessible or resettable in the tests
|
|
26
|
+
*/
|
|
27
|
+
function injectStyle({ css, id = REACT_TOOLTIP_BASE_STYLES_ID, type = 'base', ref, state = {}, }) {
|
|
28
|
+
if (!css ||
|
|
29
|
+
typeof document === 'undefined' ||
|
|
30
|
+
(typeof state[type] !== 'undefined' ? state[type] : injected[type])) {
|
|
25
31
|
return;
|
|
26
32
|
}
|
|
27
33
|
if (type === 'core' &&
|
|
28
34
|
typeof process !== 'undefined' && // this validation prevents docs from breaking even with `process?`
|
|
29
|
-
|
|
35
|
+
process.env &&
|
|
36
|
+
process.env.REACT_TOOLTIP_DISABLE_CORE_STYLES) {
|
|
30
37
|
return;
|
|
31
38
|
}
|
|
32
|
-
if (type
|
|
39
|
+
if (type === 'base' &&
|
|
33
40
|
typeof process !== 'undefined' && // this validation prevents docs from breaking even with `process?`
|
|
34
|
-
|
|
41
|
+
process.env &&
|
|
42
|
+
process.env.REACT_TOOLTIP_DISABLE_BASE_STYLES) {
|
|
35
43
|
return;
|
|
36
44
|
}
|
|
37
45
|
if (type === 'core') {
|
|
38
|
-
// eslint-disable-next-line no-param-reassign
|
|
39
46
|
id = REACT_TOOLTIP_CORE_STYLES_ID;
|
|
40
47
|
}
|
|
41
48
|
if (!ref) {
|
|
42
|
-
// eslint-disable-next-line no-param-reassign
|
|
43
49
|
ref = {};
|
|
44
50
|
}
|
|
45
51
|
const { insertAt } = ref;
|
|
@@ -69,26 +75,27 @@
|
|
|
69
75
|
else {
|
|
70
76
|
style.appendChild(document.createTextNode(css));
|
|
71
77
|
}
|
|
72
|
-
|
|
78
|
+
if (typeof state[type] !== 'undefined') {
|
|
79
|
+
state[type] = true;
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
injected[type] = true; // internal global state that jest doesn't have access
|
|
83
|
+
}
|
|
73
84
|
}
|
|
74
85
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}),
|
|
80
|
-
dom.shift({ padding: 5 }),
|
|
81
|
-
], border, }) => {
|
|
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, }) => {
|
|
82
90
|
if (!elementReference) {
|
|
83
91
|
// elementReference can be null or undefined and we will not compute the position
|
|
84
|
-
// eslint-disable-next-line no-console
|
|
85
92
|
// console.error('The reference element for tooltip was not defined: ', elementReference)
|
|
86
93
|
return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
|
|
87
94
|
}
|
|
88
95
|
if (tooltipReference === null) {
|
|
89
96
|
return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
|
|
90
97
|
}
|
|
91
|
-
const middleware = middlewares;
|
|
98
|
+
const middleware = [...middlewares];
|
|
92
99
|
if (tooltipArrowReference) {
|
|
93
100
|
middleware.push(dom.arrow({ element: tooltipArrowReference, padding: 5 }));
|
|
94
101
|
return dom.computePosition(elementReference, tooltipReference, {
|
|
@@ -132,7 +139,7 @@
|
|
|
132
139
|
right: '',
|
|
133
140
|
bottom: '',
|
|
134
141
|
...borderSide,
|
|
135
|
-
[staticSide]: `-${
|
|
142
|
+
[staticSide]: `-${arrowSize / 2 + borderWidth - 1}px`,
|
|
136
143
|
};
|
|
137
144
|
/* c8 ignore end */
|
|
138
145
|
return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle, place: placement };
|
|
@@ -166,6 +173,7 @@
|
|
|
166
173
|
*/
|
|
167
174
|
const debounce = (func, wait, immediate) => {
|
|
168
175
|
let timeout = null;
|
|
176
|
+
let currentFunc = func;
|
|
169
177
|
const debounced = function debounced(...args) {
|
|
170
178
|
const later = () => {
|
|
171
179
|
timeout = null;
|
|
@@ -175,7 +183,7 @@
|
|
|
175
183
|
* there's no need to clear the timeout
|
|
176
184
|
* since we expect it to resolve and set `timeout = null`
|
|
177
185
|
*/
|
|
178
|
-
|
|
186
|
+
currentFunc.apply(this, args);
|
|
179
187
|
timeout = setTimeout(later, wait);
|
|
180
188
|
}
|
|
181
189
|
};
|
|
@@ -188,36 +196,12 @@
|
|
|
188
196
|
clearTimeout(timeout);
|
|
189
197
|
timeout = null;
|
|
190
198
|
};
|
|
199
|
+
debounced.setCallback = (newFunc) => {
|
|
200
|
+
currentFunc = newFunc;
|
|
201
|
+
};
|
|
191
202
|
return debounced;
|
|
192
203
|
};
|
|
193
204
|
|
|
194
|
-
const isObject = (object) => {
|
|
195
|
-
return object !== null && !Array.isArray(object) && typeof object === 'object';
|
|
196
|
-
};
|
|
197
|
-
const deepEqual = (object1, object2) => {
|
|
198
|
-
if (object1 === object2) {
|
|
199
|
-
return true;
|
|
200
|
-
}
|
|
201
|
-
if (Array.isArray(object1) && Array.isArray(object2)) {
|
|
202
|
-
if (object1.length !== object2.length) {
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
return object1.every((val, index) => deepEqual(val, object2[index]));
|
|
206
|
-
}
|
|
207
|
-
if (Array.isArray(object1) !== Array.isArray(object2)) {
|
|
208
|
-
return false;
|
|
209
|
-
}
|
|
210
|
-
if (!isObject(object1) || !isObject(object2)) {
|
|
211
|
-
return object1 === object2;
|
|
212
|
-
}
|
|
213
|
-
const keys1 = Object.keys(object1);
|
|
214
|
-
const keys2 = Object.keys(object2);
|
|
215
|
-
if (keys1.length !== keys2.length) {
|
|
216
|
-
return false;
|
|
217
|
-
}
|
|
218
|
-
return keys1.every((key) => deepEqual(object1[key], object2[key]));
|
|
219
|
-
};
|
|
220
|
-
|
|
221
205
|
const isScrollable = (node) => {
|
|
222
206
|
if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
|
|
223
207
|
return false;
|
|
@@ -242,336 +226,390 @@
|
|
|
242
226
|
return document.scrollingElement || document.documentElement;
|
|
243
227
|
};
|
|
244
228
|
|
|
245
|
-
|
|
229
|
+
// React currently throws a warning when using useLayoutEffect on the server.
|
|
230
|
+
// To get around it, we can conditionally useEffect on the server (no-op) and
|
|
231
|
+
// useLayoutEffect in the browser. We need useLayoutEffect to ensure the store
|
|
232
|
+
// subscription callback always has the selector from the latest render commit
|
|
233
|
+
// available, otherwise a store update may happen between render and the effect,
|
|
234
|
+
// which may cause missed updates; we also must ensure the store subscription
|
|
235
|
+
// is created synchronously, otherwise a store update may occur before the
|
|
236
|
+
// subscription is created and an inconsistent state may be observed
|
|
237
|
+
const isHopefullyDomEnvironment = typeof window !== 'undefined' &&
|
|
238
|
+
typeof window.document !== 'undefined' &&
|
|
239
|
+
typeof window.document.createElement !== 'undefined';
|
|
240
|
+
const useIsomorphicLayoutEffect = isHopefullyDomEnvironment ? React.useLayoutEffect : React.useEffect;
|
|
246
241
|
|
|
247
242
|
const clearTimeoutRef = (ref) => {
|
|
248
243
|
if (ref.current) {
|
|
249
244
|
clearTimeout(ref.current);
|
|
250
|
-
// eslint-disable-next-line no-param-reassign
|
|
251
245
|
ref.current = null;
|
|
252
246
|
}
|
|
253
247
|
};
|
|
254
248
|
|
|
255
|
-
|
|
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
256
|
|
|
257
|
-
|
|
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
|
+
}
|
|
258
267
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
* useLayoutEffect runs before useEffect,
|
|
285
|
-
* but should be used carefully because of caveats
|
|
286
|
-
* https://beta.reactjs.org/reference/react/useLayoutEffect#caveats
|
|
287
|
-
*/
|
|
288
|
-
useIsomorphicLayoutEffect(() => {
|
|
289
|
-
mounted.current = true;
|
|
290
|
-
return () => {
|
|
291
|
-
mounted.current = false;
|
|
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"};
|
|
269
|
+
|
|
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"};
|
|
271
|
+
|
|
272
|
+
const registry = new Map();
|
|
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
|
+
}
|
|
282
|
+
function areAnchorListsEqual(left, right) {
|
|
283
|
+
if (left.length !== right.length) {
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
return left.every((anchor, index) => anchor === right[index]);
|
|
287
|
+
}
|
|
288
|
+
function readAnchorsForSelector(selector) {
|
|
289
|
+
try {
|
|
290
|
+
return {
|
|
291
|
+
anchors: Array.from(document.querySelectorAll(selector)),
|
|
292
|
+
error: null,
|
|
292
293
|
};
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
return {
|
|
297
|
+
anchors: [],
|
|
298
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function notifySubscribers(entry) {
|
|
303
|
+
entry.subscribers.forEach((subscriber) => subscriber(entry.anchors, entry.error));
|
|
304
|
+
}
|
|
305
|
+
function refreshEntry(selector, entry) {
|
|
306
|
+
var _a, _b, _c, _d;
|
|
307
|
+
const nextState = readAnchorsForSelector(selector);
|
|
308
|
+
const nextErrorMessage = (_b = (_a = nextState.error) === null || _a === void 0 ? void 0 : _a.message) !== null && _b !== void 0 ? _b : null;
|
|
309
|
+
const previousErrorMessage = (_d = (_c = entry.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : null;
|
|
310
|
+
if (areAnchorListsEqual(entry.anchors, nextState.anchors) &&
|
|
311
|
+
nextErrorMessage === previousErrorMessage) {
|
|
312
|
+
return;
|
|
313
|
+
}
|
|
314
|
+
const nextEntry = {
|
|
315
|
+
...entry,
|
|
316
|
+
anchors: nextState.anchors,
|
|
317
|
+
error: nextState.error,
|
|
318
|
+
};
|
|
319
|
+
registry.set(selector, nextEntry);
|
|
320
|
+
notifySubscribers(nextEntry);
|
|
321
|
+
}
|
|
322
|
+
function refreshAllEntries() {
|
|
323
|
+
registry.forEach((entry, selector) => {
|
|
324
|
+
refreshEntry(selector, entry);
|
|
325
|
+
});
|
|
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();
|
|
297
334
|
}
|
|
298
|
-
|
|
299
|
-
|
|
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();
|
|
300
352
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
* before actually showing
|
|
304
|
-
*/
|
|
305
|
-
setTimeout(() => {
|
|
306
|
-
if (!mounted.current) {
|
|
307
|
-
return;
|
|
308
|
-
}
|
|
309
|
-
setIsOpen === null || setIsOpen === void 0 ? void 0 : setIsOpen(value);
|
|
310
|
-
if (isOpen === undefined) {
|
|
311
|
-
setShow(value);
|
|
312
|
-
}
|
|
313
|
-
}, 10);
|
|
314
|
-
}, [isOpen, setIsOpen]);
|
|
315
|
-
/**
|
|
316
|
-
* this replicates the effect from `handleShow()`
|
|
317
|
-
* when `isOpen` is changed from outside
|
|
318
|
-
*/
|
|
319
|
-
React.useEffect(() => {
|
|
320
|
-
if (isOpen === undefined) {
|
|
321
|
-
return () => null;
|
|
353
|
+
else if (ids && ids.size > 0) {
|
|
354
|
+
refreshEntriesForTooltipIds(ids);
|
|
322
355
|
}
|
|
323
|
-
|
|
324
|
-
|
|
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);
|
|
325
372
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
+
}
|
|
431
|
+
function ensureDocumentObserver() {
|
|
432
|
+
if (documentObserver || typeof MutationObserver === 'undefined') {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
documentObserver = new MutationObserver((records) => {
|
|
436
|
+
const affectedIds = collectAffectedTooltipIds(records);
|
|
437
|
+
scheduleRefresh(affectedIds);
|
|
438
|
+
});
|
|
439
|
+
documentObserver.observe(document.body, {
|
|
440
|
+
childList: true,
|
|
441
|
+
subtree: true,
|
|
442
|
+
attributes: true,
|
|
443
|
+
attributeFilter: ['data-tooltip-id'],
|
|
444
|
+
attributeOldValue: true,
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
function cleanupDocumentObserverIfUnused() {
|
|
448
|
+
if (registry.size !== 0 || !documentObserver) {
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
documentObserver.disconnect();
|
|
452
|
+
documentObserver = null;
|
|
453
|
+
}
|
|
454
|
+
function subscribeAnchorSelector(selector, subscriber) {
|
|
455
|
+
let entry = registry.get(selector);
|
|
456
|
+
if (!entry) {
|
|
457
|
+
const initialState = readAnchorsForSelector(selector);
|
|
458
|
+
entry = {
|
|
459
|
+
anchors: initialState.anchors,
|
|
460
|
+
error: initialState.error,
|
|
461
|
+
subscribers: new Set(),
|
|
462
|
+
tooltipId: extractTooltipId(selector),
|
|
331
463
|
};
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
464
|
+
registry.set(selector, entry);
|
|
465
|
+
}
|
|
466
|
+
entry.subscribers.add(subscriber);
|
|
467
|
+
ensureDocumentObserver();
|
|
468
|
+
subscriber([...entry.anchors], entry.error);
|
|
469
|
+
return () => {
|
|
470
|
+
const currentEntry = registry.get(selector);
|
|
471
|
+
if (!currentEntry) {
|
|
335
472
|
return;
|
|
336
473
|
}
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
afterShow === null || afterShow === void 0 ? void 0 : afterShow();
|
|
341
|
-
}
|
|
342
|
-
else {
|
|
343
|
-
/**
|
|
344
|
-
* see `onTransitionEnd` on tooltip wrapper
|
|
345
|
-
*/
|
|
346
|
-
const style = getComputedStyle(document.body);
|
|
347
|
-
const transitionShowDelay = cssTimeToMs(style.getPropertyValue('--rt-transition-show-delay'));
|
|
348
|
-
missedTransitionTimerRef.current = setTimeout(() => {
|
|
349
|
-
/**
|
|
350
|
-
* if the tooltip switches from `show === true` to `show === false` too fast
|
|
351
|
-
* the transition never runs, so `onTransitionEnd` callback never gets fired
|
|
352
|
-
*/
|
|
353
|
-
setRendered(false);
|
|
354
|
-
setImperativeOptions(null);
|
|
355
|
-
afterHide === null || afterHide === void 0 ? void 0 : afterHide();
|
|
356
|
-
// +25ms just to make sure `onTransitionEnd` (if it gets fired) has time to run
|
|
357
|
-
}, transitionShowDelay + 25);
|
|
474
|
+
currentEntry.subscribers.delete(subscriber);
|
|
475
|
+
if (currentEntry.subscribers.size === 0) {
|
|
476
|
+
registry.delete(selector);
|
|
358
477
|
}
|
|
359
|
-
|
|
360
|
-
const handleComputedPosition = (newComputedPosition) => {
|
|
361
|
-
setComputedPosition((oldComputedPosition) => deepEqual(oldComputedPosition, newComputedPosition)
|
|
362
|
-
? oldComputedPosition
|
|
363
|
-
: newComputedPosition);
|
|
478
|
+
cleanupDocumentObserverIfUnused();
|
|
364
479
|
};
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
const getAnchorSelector = ({ id, anchorSelect, imperativeAnchorSelect, }) => {
|
|
483
|
+
var _a;
|
|
484
|
+
let selector = (_a = imperativeAnchorSelect !== null && imperativeAnchorSelect !== void 0 ? imperativeAnchorSelect : anchorSelect) !== null && _a !== void 0 ? _a : '';
|
|
485
|
+
if (!selector && id) {
|
|
486
|
+
selector = `[data-tooltip-id='${id.replace(/'/g, "\\'")}']`;
|
|
487
|
+
}
|
|
488
|
+
return selector;
|
|
489
|
+
};
|
|
490
|
+
const useTooltipAnchors = ({ id, anchorSelect, imperativeAnchorSelect, activeAnchor, disableTooltip, onActiveAnchorRemoved, trackAnchors, }) => {
|
|
491
|
+
const [rawAnchorElements, setRawAnchorElements] = React.useState([]);
|
|
492
|
+
const [selectorError, setSelectorError] = React.useState(null);
|
|
493
|
+
const warnedSelectorRef = React.useRef(null);
|
|
494
|
+
const selector = React.useMemo(() => getAnchorSelector({ id, anchorSelect, imperativeAnchorSelect }), [id, anchorSelect, imperativeAnchorSelect]);
|
|
495
|
+
const anchorElements = React.useMemo(() => rawAnchorElements.filter((anchor) => !(disableTooltip === null || disableTooltip === void 0 ? void 0 : disableTooltip(anchor))), [rawAnchorElements, disableTooltip]);
|
|
496
|
+
const activeAnchorMatchesSelector = React.useMemo(() => {
|
|
497
|
+
if (!activeAnchor || !selector) {
|
|
498
|
+
return false;
|
|
368
499
|
}
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
handleShow(true);
|
|
372
|
-
return;
|
|
500
|
+
try {
|
|
501
|
+
return activeAnchor.matches(selector);
|
|
373
502
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
}, delay);
|
|
377
|
-
}, [delayShow, handleShow, rendered]);
|
|
378
|
-
const handleHideTooltipDelayed = React.useCallback((delay = delayHide) => {
|
|
379
|
-
if (tooltipHideDelayTimerRef.current) {
|
|
380
|
-
clearTimeout(tooltipHideDelayTimerRef.current);
|
|
503
|
+
catch (_a) {
|
|
504
|
+
return false;
|
|
381
505
|
}
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
return {
|
|
394
|
-
x,
|
|
395
|
-
y,
|
|
396
|
-
width: 0,
|
|
397
|
-
height: 0,
|
|
398
|
-
top: y,
|
|
399
|
-
left: x,
|
|
400
|
-
right: x,
|
|
401
|
-
bottom: y,
|
|
402
|
-
};
|
|
403
|
-
},
|
|
404
|
-
};
|
|
405
|
-
computeTooltipPosition({
|
|
406
|
-
place: (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.place) !== null && _a !== void 0 ? _a : place,
|
|
407
|
-
offset,
|
|
408
|
-
elementReference: virtualElement,
|
|
409
|
-
tooltipReference: tooltipRef.current,
|
|
410
|
-
tooltipArrowReference: tooltipArrowRef.current,
|
|
411
|
-
strategy: positionStrategy,
|
|
412
|
-
middlewares,
|
|
413
|
-
border,
|
|
414
|
-
}).then((computedStylesData) => {
|
|
415
|
-
handleComputedPosition(computedStylesData);
|
|
506
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
507
|
+
}, [activeAnchor, selector, anchorElements]);
|
|
508
|
+
React.useEffect(() => {
|
|
509
|
+
if (!selector || !trackAnchors) {
|
|
510
|
+
setRawAnchorElements([]);
|
|
511
|
+
setSelectorError(null);
|
|
512
|
+
return undefined;
|
|
513
|
+
}
|
|
514
|
+
return subscribeAnchorSelector(selector, (anchors, error) => {
|
|
515
|
+
setRawAnchorElements(anchors);
|
|
516
|
+
setSelectorError(error);
|
|
416
517
|
});
|
|
417
|
-
}, [
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
const actualPosition = (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.position) !== null && _a !== void 0 ? _a : position;
|
|
421
|
-
if (actualPosition) {
|
|
422
|
-
// if `position` is set, override regular and `float` positioning
|
|
423
|
-
handleTooltipPosition(actualPosition);
|
|
518
|
+
}, [selector, trackAnchors]);
|
|
519
|
+
React.useEffect(() => {
|
|
520
|
+
if (!selectorError || warnedSelectorRef.current === selector) {
|
|
424
521
|
return;
|
|
425
522
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
To see why this matters, comment this line, run `yarn dev` and click the
|
|
433
|
-
"Hover me!" anchor.
|
|
434
|
-
*/
|
|
435
|
-
handleTooltipPosition(lastFloatPosition.current);
|
|
436
|
-
}
|
|
437
|
-
// if `float` is set, override regular positioning
|
|
523
|
+
warnedSelectorRef.current = selector;
|
|
524
|
+
/* c8 ignore end */
|
|
525
|
+
}, [selector, selectorError]);
|
|
526
|
+
React.useEffect(() => {
|
|
527
|
+
if (!activeAnchor) {
|
|
438
528
|
return;
|
|
439
529
|
}
|
|
440
|
-
if (!
|
|
530
|
+
if (!activeAnchor.isConnected) {
|
|
531
|
+
onActiveAnchorRemoved();
|
|
441
532
|
return;
|
|
442
533
|
}
|
|
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
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
* at the same time the tooltip gets triggered
|
|
523
|
-
*/
|
|
524
|
-
setActiveAnchor(null);
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
|
-
if (delayShow) {
|
|
528
|
-
handleShowTooltipDelayed();
|
|
529
|
-
}
|
|
530
|
-
else {
|
|
531
|
-
handleShow(true);
|
|
532
|
-
}
|
|
533
|
-
setActiveAnchor(target);
|
|
534
|
-
if (tooltipHideDelayTimerRef.current) {
|
|
535
|
-
clearTimeout(tooltipHideDelayTimerRef.current);
|
|
536
|
-
}
|
|
537
|
-
};
|
|
538
|
-
const handleHideTooltip = () => {
|
|
539
|
-
if (clickable) {
|
|
540
|
-
// allow time for the mouse to reach the tooltip, in case there's a gap
|
|
541
|
-
handleHideTooltipDelayed(delayHide || 100);
|
|
542
|
-
}
|
|
543
|
-
else if (delayHide) {
|
|
544
|
-
handleHideTooltipDelayed();
|
|
545
|
-
}
|
|
546
|
-
else {
|
|
547
|
-
handleShow(false);
|
|
548
|
-
}
|
|
549
|
-
if (tooltipShowDelayTimerRef.current) {
|
|
550
|
-
clearTimeout(tooltipShowDelayTimerRef.current);
|
|
551
|
-
}
|
|
552
|
-
};
|
|
553
|
-
// debounce handler to prevent call twice when
|
|
554
|
-
// mouse enter and focus events being triggered toggether
|
|
555
|
-
const internalDebouncedHandleShowTooltip = debounce(handleShowTooltip, 50);
|
|
556
|
-
const internalDebouncedHandleHideTooltip = debounce(handleHideTooltip, 50);
|
|
557
|
-
// If either of the functions is called while the other is still debounced,
|
|
558
|
-
// reset the timeout. Otherwise if there is a sub-50ms (leave A, enter B, leave B)
|
|
559
|
-
// sequence of events, the tooltip will stay open because the hide debounce
|
|
560
|
-
// from leave A prevented the leave B event from calling it, leaving the
|
|
561
|
-
// tooltip visible.
|
|
562
|
-
const debouncedHandleShowTooltip = (e) => {
|
|
563
|
-
internalDebouncedHandleHideTooltip.cancel();
|
|
564
|
-
internalDebouncedHandleShowTooltip(e);
|
|
565
|
-
};
|
|
566
|
-
const debouncedHandleHideTooltip = () => {
|
|
567
|
-
internalDebouncedHandleShowTooltip.cancel();
|
|
568
|
-
internalDebouncedHandleHideTooltip();
|
|
569
|
-
};
|
|
570
|
-
const handleScrollResize = () => {
|
|
571
|
-
handleShow(false);
|
|
572
|
-
};
|
|
573
|
-
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);
|
|
574
|
-
const actualOpenEvents = openEvents
|
|
534
|
+
if (!anchorElements.includes(activeAnchor) && !activeAnchorMatchesSelector) {
|
|
535
|
+
onActiveAnchorRemoved();
|
|
536
|
+
}
|
|
537
|
+
}, [activeAnchor, anchorElements, activeAnchorMatchesSelector, onActiveAnchorRemoved]);
|
|
538
|
+
return {
|
|
539
|
+
anchorElements,
|
|
540
|
+
selector,
|
|
541
|
+
};
|
|
542
|
+
};
|
|
543
|
+
|
|
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
|
|
575
613
|
? { ...openEvents }
|
|
576
614
|
: {
|
|
577
615
|
mouseenter: true,
|
|
@@ -581,13 +619,25 @@
|
|
|
581
619
|
mousedown: false,
|
|
582
620
|
};
|
|
583
621
|
if (!openEvents && openOnClick) {
|
|
584
|
-
Object.assign(
|
|
622
|
+
Object.assign(events, {
|
|
585
623
|
mouseenter: false,
|
|
586
624
|
focus: false,
|
|
587
625
|
click: true,
|
|
588
626
|
});
|
|
589
627
|
}
|
|
590
|
-
|
|
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
|
|
591
641
|
? { ...closeEvents }
|
|
592
642
|
: {
|
|
593
643
|
mouseleave: true,
|
|
@@ -597,12 +647,24 @@
|
|
|
597
647
|
mouseup: false,
|
|
598
648
|
};
|
|
599
649
|
if (!closeEvents && openOnClick) {
|
|
600
|
-
Object.assign(
|
|
650
|
+
Object.assign(events, {
|
|
651
|
+
mouseleave: false,
|
|
652
|
+
blur: false,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
if (imperativeModeOnly) {
|
|
656
|
+
Object.assign(events, {
|
|
601
657
|
mouseleave: false,
|
|
602
658
|
blur: false,
|
|
659
|
+
click: false,
|
|
660
|
+
dblclick: false,
|
|
661
|
+
mouseup: false,
|
|
603
662
|
});
|
|
604
663
|
}
|
|
605
|
-
|
|
664
|
+
return events;
|
|
665
|
+
}, [closeEvents, openOnClick, imperativeModeOnly]);
|
|
666
|
+
const actualGlobalCloseEvents = React.useMemo(() => {
|
|
667
|
+
const events = globalCloseEvents
|
|
606
668
|
? { ...globalCloseEvents }
|
|
607
669
|
: {
|
|
608
670
|
escape: false,
|
|
@@ -611,30 +673,276 @@
|
|
|
611
673
|
clickOutsideAnchor: hasClickEvent || false,
|
|
612
674
|
};
|
|
613
675
|
if (imperativeModeOnly) {
|
|
614
|
-
Object.assign(
|
|
615
|
-
mouseenter: false,
|
|
616
|
-
focus: false,
|
|
617
|
-
click: false,
|
|
618
|
-
dblclick: false,
|
|
619
|
-
mousedown: false,
|
|
620
|
-
});
|
|
621
|
-
Object.assign(actualCloseEvents, {
|
|
622
|
-
mouseleave: false,
|
|
623
|
-
blur: false,
|
|
624
|
-
click: false,
|
|
625
|
-
dblclick: false,
|
|
626
|
-
mouseup: false,
|
|
627
|
-
});
|
|
628
|
-
Object.assign(actualGlobalCloseEvents, {
|
|
676
|
+
Object.assign(events, {
|
|
629
677
|
escape: false,
|
|
630
678
|
scroll: false,
|
|
631
679
|
resize: false,
|
|
632
680
|
clickOutsideAnchor: false,
|
|
633
681
|
});
|
|
634
682
|
}
|
|
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;
|
|
708
|
+
}
|
|
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
|
+
}
|
|
714
|
+
}
|
|
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
|
+
}
|
|
727
|
+
}
|
|
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);
|
|
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 {
|
|
774
|
+
handleShow(false);
|
|
775
|
+
}
|
|
776
|
+
if (tooltipShowDelayTimerRef.current) {
|
|
777
|
+
clearTimeout(tooltipShowDelayTimerRef.current);
|
|
778
|
+
}
|
|
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));
|
|
793
|
+
};
|
|
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();
|
|
802
|
+
};
|
|
803
|
+
const addDelegatedHoverOpenListener = () => {
|
|
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);
|
|
814
|
+
});
|
|
815
|
+
};
|
|
816
|
+
const addDelegatedHoverCloseListener = () => {
|
|
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();
|
|
827
|
+
});
|
|
828
|
+
};
|
|
829
|
+
if (actualOpenEvents.mouseenter) {
|
|
830
|
+
addDelegatedHoverOpenListener();
|
|
831
|
+
}
|
|
832
|
+
if (actualCloseEvents.mouseleave) {
|
|
833
|
+
addDelegatedHoverCloseListener();
|
|
834
|
+
}
|
|
835
|
+
if (actualOpenEvents.mouseover) {
|
|
836
|
+
addDelegatedHoverOpenListener();
|
|
837
|
+
}
|
|
838
|
+
if (actualCloseEvents.mouseout) {
|
|
839
|
+
addDelegatedHoverCloseListener();
|
|
840
|
+
}
|
|
841
|
+
if (actualOpenEvents.focus) {
|
|
842
|
+
addDelegatedListener('focusin', (event) => {
|
|
843
|
+
debouncedHandleShowTooltip(resolveAnchorElementRef.current(event.target));
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
if (actualCloseEvents.blur) {
|
|
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();
|
|
857
|
+
});
|
|
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
|
+
};
|
|
878
|
+
Object.entries(actualOpenEvents).forEach(([event, enabled]) => {
|
|
879
|
+
if (!enabled || regularEvents.includes(event)) {
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
if (clickEvents.includes(event)) {
|
|
883
|
+
addDelegatedListener(event, handleClickOpenTooltipAnchor);
|
|
884
|
+
}
|
|
885
|
+
});
|
|
886
|
+
Object.entries(actualCloseEvents).forEach(([event, enabled]) => {
|
|
887
|
+
if (!enabled || regularEvents.includes(event)) {
|
|
888
|
+
return;
|
|
889
|
+
}
|
|
890
|
+
if (clickEvents.includes(event)) {
|
|
891
|
+
addDelegatedListener(event, handleClickCloseTooltipAnchor);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
if (float) {
|
|
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;
|
|
911
|
+
});
|
|
912
|
+
}
|
|
635
913
|
const tooltipElement = tooltipRef.current;
|
|
636
|
-
const
|
|
637
|
-
|
|
914
|
+
const handleMouseOverTooltip = () => {
|
|
915
|
+
hoveringTooltip.current = true;
|
|
916
|
+
};
|
|
917
|
+
const handleMouseOutTooltip = () => {
|
|
918
|
+
hoveringTooltip.current = false;
|
|
919
|
+
handleHideTooltipRef.current();
|
|
920
|
+
};
|
|
921
|
+
const addHoveringTooltipListeners = clickable && (actualCloseEvents.mouseout || actualCloseEvents.mouseleave);
|
|
922
|
+
if (addHoveringTooltipListeners) {
|
|
923
|
+
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.addEventListener('mouseover', handleMouseOverTooltip);
|
|
924
|
+
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.addEventListener('mouseout', handleMouseOutTooltip);
|
|
925
|
+
}
|
|
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;
|
|
638
946
|
if (actualGlobalCloseEvents.scroll) {
|
|
639
947
|
window.addEventListener('scroll', handleScrollResize);
|
|
640
948
|
anchorScrollParent === null || anchorScrollParent === void 0 ? void 0 : anchorScrollParent.addEventListener('scroll', handleScrollResize);
|
|
@@ -645,7 +953,7 @@
|
|
|
645
953
|
window.addEventListener('resize', handleScrollResize);
|
|
646
954
|
}
|
|
647
955
|
else if (activeAnchor && tooltipRef.current) {
|
|
648
|
-
updateTooltipCleanup = dom.autoUpdate(activeAnchor, tooltipRef.current,
|
|
956
|
+
updateTooltipCleanup = dom.autoUpdate(activeAnchor, tooltipRef.current, () => updateTooltipPositionRef.current(), {
|
|
649
957
|
ancestorResize: true,
|
|
650
958
|
elementResize: true,
|
|
651
959
|
layoutShift: true,
|
|
@@ -655,295 +963,484 @@
|
|
|
655
963
|
if (event.key !== 'Escape') {
|
|
656
964
|
return;
|
|
657
965
|
}
|
|
658
|
-
|
|
659
|
-
};
|
|
660
|
-
if (actualGlobalCloseEvents.escape) {
|
|
661
|
-
window.addEventListener('keydown', handleEsc);
|
|
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);
|
|
990
|
+
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
991
|
+
};
|
|
992
|
+
if (actualGlobalCloseEvents.clickOutsideAnchor) {
|
|
993
|
+
window.addEventListener('click', handleClickOutsideAnchors);
|
|
994
|
+
}
|
|
995
|
+
return () => {
|
|
996
|
+
if (actualGlobalCloseEvents.scroll) {
|
|
997
|
+
window.removeEventListener('scroll', handleScrollResize);
|
|
998
|
+
anchorScrollParent === null || anchorScrollParent === void 0 ? void 0 : anchorScrollParent.removeEventListener('scroll', handleScrollResize);
|
|
999
|
+
tooltipScrollParent === null || tooltipScrollParent === void 0 ? void 0 : tooltipScrollParent.removeEventListener('scroll', handleScrollResize);
|
|
1000
|
+
}
|
|
1001
|
+
if (actualGlobalCloseEvents.resize) {
|
|
1002
|
+
window.removeEventListener('resize', handleScrollResize);
|
|
1003
|
+
}
|
|
1004
|
+
if (updateTooltipCleanup) {
|
|
1005
|
+
updateTooltipCleanup();
|
|
1006
|
+
}
|
|
1007
|
+
if (actualGlobalCloseEvents.escape) {
|
|
1008
|
+
window.removeEventListener('keydown', handleEsc);
|
|
1009
|
+
}
|
|
1010
|
+
if (actualGlobalCloseEvents.clickOutsideAnchor) {
|
|
1011
|
+
window.removeEventListener('click', handleClickOutsideAnchors);
|
|
1012
|
+
}
|
|
1013
|
+
};
|
|
1014
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1015
|
+
}, [actualGlobalCloseEvents, activeAnchor]);
|
|
1016
|
+
};
|
|
1017
|
+
|
|
1018
|
+
// Shared across all tooltip instances — the CSS variable is on :root and never changes per-instance
|
|
1019
|
+
let globalTransitionShowDelay = null;
|
|
1020
|
+
const Tooltip = ({
|
|
1021
|
+
// props
|
|
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,
|
|
1023
|
+
// props handled by controller
|
|
1024
|
+
content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, previousActiveAnchor, activeAnchor, setActiveAnchor, border, opacity, arrowColor, arrowSize = 8, role = 'tooltip', }) => {
|
|
1025
|
+
var _a;
|
|
1026
|
+
const tooltipRef = React.useRef(null);
|
|
1027
|
+
const tooltipArrowRef = React.useRef(null);
|
|
1028
|
+
const tooltipShowDelayTimerRef = React.useRef(null);
|
|
1029
|
+
const tooltipHideDelayTimerRef = React.useRef(null);
|
|
1030
|
+
const tooltipAutoCloseTimerRef = React.useRef(null);
|
|
1031
|
+
const missedTransitionTimerRef = React.useRef(null);
|
|
1032
|
+
const [computedPosition, setComputedPosition] = React.useState({
|
|
1033
|
+
tooltipStyles: {},
|
|
1034
|
+
tooltipArrowStyles: {},
|
|
1035
|
+
place,
|
|
1036
|
+
});
|
|
1037
|
+
const [show, setShow] = React.useState(false);
|
|
1038
|
+
const [rendered, setRendered] = React.useState(false);
|
|
1039
|
+
const [imperativeOptions, setImperativeOptions] = React.useState(null);
|
|
1040
|
+
const wasShowing = React.useRef(false);
|
|
1041
|
+
const lastFloatPosition = React.useRef(null);
|
|
1042
|
+
const hoveringTooltip = React.useRef(false);
|
|
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
|
+
});
|
|
1056
|
+
/**
|
|
1057
|
+
* useLayoutEffect runs before useEffect,
|
|
1058
|
+
* but should be used carefully because of caveats
|
|
1059
|
+
* https://beta.reactjs.org/reference/react/useLayoutEffect#caveats
|
|
1060
|
+
*/
|
|
1061
|
+
useIsomorphicLayoutEffect(() => {
|
|
1062
|
+
mounted.current = true;
|
|
1063
|
+
return () => {
|
|
1064
|
+
mounted.current = false;
|
|
1065
|
+
};
|
|
1066
|
+
}, []);
|
|
1067
|
+
const handleShow = React.useCallback((value) => {
|
|
1068
|
+
if (!mounted.current) {
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
if (value) {
|
|
1072
|
+
setRendered(true);
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* wait for the component to render and calculate position
|
|
1076
|
+
* before actually showing
|
|
1077
|
+
*/
|
|
1078
|
+
setTimeout(() => {
|
|
1079
|
+
if (!mounted.current) {
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
setIsOpen === null || setIsOpen === void 0 ? void 0 : setIsOpen(value);
|
|
1083
|
+
if (isOpen === undefined) {
|
|
1084
|
+
setShow(value);
|
|
1085
|
+
}
|
|
1086
|
+
}, 10);
|
|
1087
|
+
}, [isOpen, setIsOpen]);
|
|
1088
|
+
/**
|
|
1089
|
+
* Add aria-describedby to activeAnchor when tooltip is active
|
|
1090
|
+
*/
|
|
1091
|
+
React.useEffect(() => {
|
|
1092
|
+
if (!id)
|
|
1093
|
+
return;
|
|
1094
|
+
function getAriaDescribedBy(element) {
|
|
1095
|
+
var _a;
|
|
1096
|
+
return ((_a = element === null || element === void 0 ? void 0 : element.getAttribute('aria-describedby')) === null || _a === void 0 ? void 0 : _a.split(' ')) || [];
|
|
1097
|
+
}
|
|
1098
|
+
function removeAriaDescribedBy(element) {
|
|
1099
|
+
const newDescribedBy = getAriaDescribedBy(element).filter((s) => s !== id);
|
|
1100
|
+
if (newDescribedBy.length) {
|
|
1101
|
+
element === null || element === void 0 ? void 0 : element.setAttribute('aria-describedby', newDescribedBy.join(' '));
|
|
1102
|
+
}
|
|
1103
|
+
else {
|
|
1104
|
+
element === null || element === void 0 ? void 0 : element.removeAttribute('aria-describedby');
|
|
1105
|
+
}
|
|
1106
|
+
}
|
|
1107
|
+
if (show) {
|
|
1108
|
+
removeAriaDescribedBy(previousActiveAnchor);
|
|
1109
|
+
const currentDescribedBy = getAriaDescribedBy(activeAnchor);
|
|
1110
|
+
const describedBy = [...new Set([...currentDescribedBy, id])].filter(Boolean).join(' ');
|
|
1111
|
+
activeAnchor === null || activeAnchor === void 0 ? void 0 : activeAnchor.setAttribute('aria-describedby', describedBy);
|
|
1112
|
+
}
|
|
1113
|
+
else {
|
|
1114
|
+
removeAriaDescribedBy(activeAnchor);
|
|
1115
|
+
}
|
|
1116
|
+
return () => {
|
|
1117
|
+
// cleanup aria-describedby when the tooltip is closed
|
|
1118
|
+
removeAriaDescribedBy(activeAnchor);
|
|
1119
|
+
removeAriaDescribedBy(previousActiveAnchor);
|
|
1120
|
+
};
|
|
1121
|
+
}, [activeAnchor, show, id, previousActiveAnchor]);
|
|
1122
|
+
/**
|
|
1123
|
+
* this replicates the effect from `handleShow()`
|
|
1124
|
+
* when `isOpen` is changed from outside
|
|
1125
|
+
*/
|
|
1126
|
+
React.useEffect(() => {
|
|
1127
|
+
if (isOpen === undefined) {
|
|
1128
|
+
return () => null;
|
|
1129
|
+
}
|
|
1130
|
+
if (isOpen) {
|
|
1131
|
+
setRendered(true);
|
|
1132
|
+
}
|
|
1133
|
+
const timeout = setTimeout(() => {
|
|
1134
|
+
setShow(isOpen);
|
|
1135
|
+
}, 10);
|
|
1136
|
+
return () => {
|
|
1137
|
+
clearTimeout(timeout);
|
|
1138
|
+
};
|
|
1139
|
+
}, [isOpen]);
|
|
1140
|
+
React.useEffect(() => {
|
|
1141
|
+
if (show === wasShowing.current) {
|
|
1142
|
+
return;
|
|
1143
|
+
}
|
|
1144
|
+
clearTimeoutRef(missedTransitionTimerRef);
|
|
1145
|
+
wasShowing.current = show;
|
|
1146
|
+
if (show) {
|
|
1147
|
+
afterShow === null || afterShow === void 0 ? void 0 : afterShow();
|
|
1148
|
+
}
|
|
1149
|
+
else {
|
|
1150
|
+
/**
|
|
1151
|
+
* see `onTransitionEnd` on tooltip wrapper
|
|
1152
|
+
*/
|
|
1153
|
+
if (globalTransitionShowDelay === null) {
|
|
1154
|
+
const style = getComputedStyle(document.body);
|
|
1155
|
+
globalTransitionShowDelay = cssTimeToMs(style.getPropertyValue('--rt-transition-show-delay'));
|
|
1156
|
+
}
|
|
1157
|
+
const transitionShowDelay = globalTransitionShowDelay;
|
|
1158
|
+
missedTransitionTimerRef.current = setTimeout(() => {
|
|
1159
|
+
/**
|
|
1160
|
+
* if the tooltip switches from `show === true` to `show === false` too fast
|
|
1161
|
+
* the transition never runs, so `onTransitionEnd` callback never gets fired
|
|
1162
|
+
*/
|
|
1163
|
+
setRendered(false);
|
|
1164
|
+
setImperativeOptions(null);
|
|
1165
|
+
afterHide === null || afterHide === void 0 ? void 0 : afterHide();
|
|
1166
|
+
// +25ms just to make sure `onTransitionEnd` (if it gets fired) has time to run
|
|
1167
|
+
}, transitionShowDelay + 25);
|
|
1168
|
+
}
|
|
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]);
|
|
1184
|
+
const handleComputedPosition = React.useCallback((newComputedPosition) => {
|
|
1185
|
+
if (!mounted.current) {
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
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
|
+
});
|
|
1208
|
+
}, []);
|
|
1209
|
+
const handleShowTooltipDelayed = React.useCallback((delay = delayShow) => {
|
|
1210
|
+
if (tooltipShowDelayTimerRef.current) {
|
|
1211
|
+
clearTimeout(tooltipShowDelayTimerRef.current);
|
|
1212
|
+
}
|
|
1213
|
+
if (rendered) {
|
|
1214
|
+
// if the tooltip is already rendered, ignore delay
|
|
1215
|
+
handleShow(true);
|
|
1216
|
+
return;
|
|
1217
|
+
}
|
|
1218
|
+
tooltipShowDelayTimerRef.current = setTimeout(() => {
|
|
1219
|
+
handleShow(true);
|
|
1220
|
+
}, delay);
|
|
1221
|
+
}, [delayShow, handleShow, rendered]);
|
|
1222
|
+
const handleHideTooltipDelayed = React.useCallback((delay = delayHide) => {
|
|
1223
|
+
if (tooltipHideDelayTimerRef.current) {
|
|
1224
|
+
clearTimeout(tooltipHideDelayTimerRef.current);
|
|
1225
|
+
}
|
|
1226
|
+
tooltipHideDelayTimerRef.current = setTimeout(() => {
|
|
1227
|
+
if (hoveringTooltip.current) {
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
handleShow(false);
|
|
1231
|
+
}, delay);
|
|
1232
|
+
}, [delayHide, handleShow]);
|
|
1233
|
+
const handleTooltipPosition = React.useCallback(({ x, y }) => {
|
|
1234
|
+
var _a;
|
|
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
|
+
});
|
|
1245
|
+
computeTooltipPosition({
|
|
1246
|
+
place: (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.place) !== null && _a !== void 0 ? _a : place,
|
|
1247
|
+
offset,
|
|
1248
|
+
elementReference: virtualElementRef.current,
|
|
1249
|
+
tooltipReference: tooltipRef.current,
|
|
1250
|
+
tooltipArrowReference: tooltipArrowRef.current,
|
|
1251
|
+
strategy: positionStrategy,
|
|
1252
|
+
middlewares,
|
|
1253
|
+
border,
|
|
1254
|
+
arrowSize,
|
|
1255
|
+
}).then((computedStylesData) => {
|
|
1256
|
+
handleComputedPosition(computedStylesData);
|
|
1257
|
+
});
|
|
1258
|
+
}, [
|
|
1259
|
+
imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.place,
|
|
1260
|
+
place,
|
|
1261
|
+
offset,
|
|
1262
|
+
positionStrategy,
|
|
1263
|
+
middlewares,
|
|
1264
|
+
border,
|
|
1265
|
+
arrowSize,
|
|
1266
|
+
handleComputedPosition,
|
|
1267
|
+
]);
|
|
1268
|
+
const updateTooltipPosition = React.useCallback(() => {
|
|
1269
|
+
var _a, _b;
|
|
1270
|
+
const actualPosition = (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.position) !== null && _a !== void 0 ? _a : position;
|
|
1271
|
+
if (actualPosition) {
|
|
1272
|
+
// if `position` is set, override regular and `float` positioning
|
|
1273
|
+
handleTooltipPosition(actualPosition);
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
if (float) {
|
|
1277
|
+
if (lastFloatPosition.current) {
|
|
1278
|
+
/*
|
|
1279
|
+
Without this, changes to `content`, `place`, `offset`, ..., will only
|
|
1280
|
+
trigger a position calculation after a `mousemove` event.
|
|
1281
|
+
|
|
1282
|
+
To see why this matters, comment this line, run `yarn dev` and click the
|
|
1283
|
+
"Hover me!" anchor.
|
|
1284
|
+
*/
|
|
1285
|
+
handleTooltipPosition(lastFloatPosition.current);
|
|
1286
|
+
}
|
|
1287
|
+
// if `float` is set, override regular positioning
|
|
1288
|
+
return;
|
|
662
1289
|
}
|
|
663
|
-
if (
|
|
664
|
-
|
|
1290
|
+
if (!(activeAnchor === null || activeAnchor === void 0 ? void 0 : activeAnchor.isConnected)) {
|
|
1291
|
+
return;
|
|
665
1292
|
}
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
/**
|
|
680
|
-
* ignore clicking the anchor that was NOT used to open the tooltip.
|
|
681
|
-
* this avoids closing the tooltip when clicking on a
|
|
682
|
-
* new anchor with the tooltip already open.
|
|
683
|
-
*/
|
|
684
|
-
return;
|
|
685
|
-
}
|
|
686
|
-
handleHideTooltip();
|
|
687
|
-
};
|
|
688
|
-
const regularEvents = ['mouseover', 'mouseout', 'mouseenter', 'mouseleave', 'focus', 'blur'];
|
|
689
|
-
const clickEvents = ['click', 'dblclick', 'mousedown', 'mouseup'];
|
|
690
|
-
Object.entries(actualOpenEvents).forEach(([event, enabled]) => {
|
|
691
|
-
if (!enabled) {
|
|
692
|
-
return;
|
|
693
|
-
}
|
|
694
|
-
if (regularEvents.includes(event)) {
|
|
695
|
-
enabledEvents.push({ event, listener: debouncedHandleShowTooltip });
|
|
696
|
-
}
|
|
697
|
-
else if (clickEvents.includes(event)) {
|
|
698
|
-
enabledEvents.push({ event, listener: handleClickOpenTooltipAnchor });
|
|
699
|
-
}
|
|
700
|
-
else ;
|
|
701
|
-
});
|
|
702
|
-
Object.entries(actualCloseEvents).forEach(([event, enabled]) => {
|
|
703
|
-
if (!enabled) {
|
|
1293
|
+
computeTooltipPosition({
|
|
1294
|
+
place: (_b = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.place) !== null && _b !== void 0 ? _b : place,
|
|
1295
|
+
offset,
|
|
1296
|
+
elementReference: activeAnchor,
|
|
1297
|
+
tooltipReference: tooltipRef.current,
|
|
1298
|
+
tooltipArrowReference: tooltipArrowRef.current,
|
|
1299
|
+
strategy: positionStrategy,
|
|
1300
|
+
middlewares,
|
|
1301
|
+
border,
|
|
1302
|
+
arrowSize,
|
|
1303
|
+
}).then((computedStylesData) => {
|
|
1304
|
+
if (!mounted.current) {
|
|
1305
|
+
// invalidate computed positions after remount
|
|
704
1306
|
return;
|
|
705
1307
|
}
|
|
706
|
-
|
|
707
|
-
enabledEvents.push({ event, listener: debouncedHandleHideTooltip });
|
|
708
|
-
}
|
|
709
|
-
else if (clickEvents.includes(event)) {
|
|
710
|
-
enabledEvents.push({ event, listener: handleClickCloseTooltipAnchor });
|
|
711
|
-
}
|
|
712
|
-
else ;
|
|
713
|
-
});
|
|
714
|
-
if (float) {
|
|
715
|
-
enabledEvents.push({
|
|
716
|
-
event: 'pointermove',
|
|
717
|
-
listener: handlePointerMove,
|
|
718
|
-
});
|
|
719
|
-
}
|
|
720
|
-
const handleMouseEnterTooltip = () => {
|
|
721
|
-
hoveringTooltip.current = true;
|
|
722
|
-
};
|
|
723
|
-
const handleMouseLeaveTooltip = () => {
|
|
724
|
-
hoveringTooltip.current = false;
|
|
725
|
-
handleHideTooltip();
|
|
726
|
-
};
|
|
727
|
-
if (clickable && !hasClickEvent) {
|
|
728
|
-
// used to keep the tooltip open when hovering content.
|
|
729
|
-
// not needed if using click events.
|
|
730
|
-
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.addEventListener('mouseenter', handleMouseEnterTooltip);
|
|
731
|
-
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.addEventListener('mouseleave', handleMouseLeaveTooltip);
|
|
732
|
-
}
|
|
733
|
-
enabledEvents.forEach(({ event, listener }) => {
|
|
734
|
-
anchorElements.forEach((anchor) => {
|
|
735
|
-
anchor.addEventListener(event, listener);
|
|
736
|
-
});
|
|
1308
|
+
handleComputedPosition(computedStylesData);
|
|
737
1309
|
});
|
|
738
|
-
return () => {
|
|
739
|
-
if (actualGlobalCloseEvents.scroll) {
|
|
740
|
-
window.removeEventListener('scroll', handleScrollResize);
|
|
741
|
-
anchorScrollParent === null || anchorScrollParent === void 0 ? void 0 : anchorScrollParent.removeEventListener('scroll', handleScrollResize);
|
|
742
|
-
tooltipScrollParent === null || tooltipScrollParent === void 0 ? void 0 : tooltipScrollParent.removeEventListener('scroll', handleScrollResize);
|
|
743
|
-
}
|
|
744
|
-
if (actualGlobalCloseEvents.resize) {
|
|
745
|
-
window.removeEventListener('resize', handleScrollResize);
|
|
746
|
-
}
|
|
747
|
-
else {
|
|
748
|
-
updateTooltipCleanup === null || updateTooltipCleanup === void 0 ? void 0 : updateTooltipCleanup();
|
|
749
|
-
}
|
|
750
|
-
if (actualGlobalCloseEvents.clickOutsideAnchor) {
|
|
751
|
-
window.removeEventListener('click', handleClickOutsideAnchors);
|
|
752
|
-
}
|
|
753
|
-
if (actualGlobalCloseEvents.escape) {
|
|
754
|
-
window.removeEventListener('keydown', handleEsc);
|
|
755
|
-
}
|
|
756
|
-
if (clickable && !hasClickEvent) {
|
|
757
|
-
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.removeEventListener('mouseenter', handleMouseEnterTooltip);
|
|
758
|
-
tooltipElement === null || tooltipElement === void 0 ? void 0 : tooltipElement.removeEventListener('mouseleave', handleMouseLeaveTooltip);
|
|
759
|
-
}
|
|
760
|
-
enabledEvents.forEach(({ event, listener }) => {
|
|
761
|
-
anchorElements.forEach((anchor) => {
|
|
762
|
-
anchor.removeEventListener(event, listener);
|
|
763
|
-
});
|
|
764
|
-
});
|
|
765
|
-
};
|
|
766
|
-
/**
|
|
767
|
-
* rendered is also a dependency to ensure anchor observers are re-registered
|
|
768
|
-
* since `tooltipRef` becomes stale after removing/adding the tooltip to the DOM
|
|
769
|
-
*/
|
|
770
1310
|
}, [
|
|
1311
|
+
imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.position,
|
|
1312
|
+
imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.place,
|
|
1313
|
+
position,
|
|
1314
|
+
float,
|
|
1315
|
+
activeAnchor,
|
|
1316
|
+
place,
|
|
1317
|
+
offset,
|
|
1318
|
+
positionStrategy,
|
|
1319
|
+
middlewares,
|
|
1320
|
+
border,
|
|
1321
|
+
handleTooltipPosition,
|
|
1322
|
+
handleComputedPosition,
|
|
1323
|
+
arrowSize,
|
|
1324
|
+
]);
|
|
1325
|
+
const handleActiveAnchorRemoved = React.useCallback(() => {
|
|
1326
|
+
setRendered(false);
|
|
1327
|
+
handleShow(false);
|
|
1328
|
+
setActiveAnchor(null);
|
|
1329
|
+
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
1330
|
+
clearTimeoutRef(tooltipHideDelayTimerRef);
|
|
1331
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1332
|
+
}, [handleShow, setActiveAnchor]);
|
|
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({
|
|
1339
|
+
id,
|
|
1340
|
+
anchorSelect,
|
|
1341
|
+
imperativeAnchorSelect: imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect,
|
|
1342
|
+
activeAnchor,
|
|
1343
|
+
disableTooltip,
|
|
1344
|
+
onActiveAnchorRemoved: handleActiveAnchorRemoved,
|
|
1345
|
+
trackAnchors: shouldTrackAnchors,
|
|
1346
|
+
});
|
|
1347
|
+
useTooltipEvents({
|
|
771
1348
|
activeAnchor,
|
|
772
1349
|
anchorElements,
|
|
1350
|
+
anchorSelector,
|
|
773
1351
|
clickable,
|
|
774
1352
|
closeEvents,
|
|
775
1353
|
delayHide,
|
|
776
1354
|
delayShow,
|
|
1355
|
+
disableTooltip,
|
|
777
1356
|
float,
|
|
778
1357
|
globalCloseEvents,
|
|
779
1358
|
handleHideTooltipDelayed,
|
|
780
1359
|
handleShow,
|
|
781
1360
|
handleShowTooltipDelayed,
|
|
782
1361
|
handleTooltipPosition,
|
|
1362
|
+
hoveringTooltip,
|
|
783
1363
|
imperativeModeOnly,
|
|
1364
|
+
lastFloatPosition,
|
|
784
1365
|
openEvents,
|
|
785
1366
|
openOnClick,
|
|
786
1367
|
setActiveAnchor,
|
|
787
1368
|
show,
|
|
1369
|
+
tooltipHideDelayTimerRef,
|
|
1370
|
+
tooltipRef,
|
|
1371
|
+
tooltipShowDelayTimerRef,
|
|
788
1372
|
updateTooltipPosition,
|
|
789
|
-
|
|
790
|
-
React.
|
|
791
|
-
|
|
792
|
-
/**
|
|
793
|
-
* TODO(V6): break down observer callback for clarity
|
|
794
|
-
* - `handleAddedAnchors()`
|
|
795
|
-
* - `handleRemovedAnchors()`
|
|
796
|
-
*/
|
|
797
|
-
let selector = (_b = (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect) !== null && _a !== void 0 ? _a : anchorSelect) !== null && _b !== void 0 ? _b : '';
|
|
798
|
-
if (!selector && id) {
|
|
799
|
-
selector = `[data-tooltip-id='${id.replace(/'/g, "\\'")}']`;
|
|
800
|
-
}
|
|
801
|
-
const documentObserverCallback = (mutationList) => {
|
|
802
|
-
const addedAnchors = new Set();
|
|
803
|
-
const removedAnchors = new Set();
|
|
804
|
-
mutationList.forEach((mutation) => {
|
|
805
|
-
if (mutation.type === 'attributes' && mutation.attributeName === 'data-tooltip-id') {
|
|
806
|
-
const target = mutation.target;
|
|
807
|
-
const newId = target.getAttribute('data-tooltip-id');
|
|
808
|
-
if (newId === id) {
|
|
809
|
-
addedAnchors.add(target);
|
|
810
|
-
}
|
|
811
|
-
else if (mutation.oldValue === id) {
|
|
812
|
-
// data-tooltip-id has now been changed, so we need to remove this anchor
|
|
813
|
-
removedAnchors.add(target);
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
if (mutation.type !== 'childList') {
|
|
817
|
-
return;
|
|
818
|
-
}
|
|
819
|
-
const removedNodes = [...mutation.removedNodes].filter((node) => node.nodeType === 1);
|
|
820
|
-
if (activeAnchor) {
|
|
821
|
-
removedNodes.some((node) => {
|
|
822
|
-
var _a;
|
|
823
|
-
/**
|
|
824
|
-
* TODO(V6)
|
|
825
|
-
* - isn't `!activeAnchor.isConnected` better?
|
|
826
|
-
* - maybe move to `handleDisconnectedAnchor()`
|
|
827
|
-
*/
|
|
828
|
-
if ((_a = node === null || node === void 0 ? void 0 : node.contains) === null || _a === void 0 ? void 0 : _a.call(node, activeAnchor)) {
|
|
829
|
-
setRendered(false);
|
|
830
|
-
handleShow(false);
|
|
831
|
-
setActiveAnchor(null);
|
|
832
|
-
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
833
|
-
clearTimeoutRef(tooltipHideDelayTimerRef);
|
|
834
|
-
return true;
|
|
835
|
-
}
|
|
836
|
-
return false;
|
|
837
|
-
});
|
|
838
|
-
}
|
|
839
|
-
if (!selector) {
|
|
840
|
-
return;
|
|
841
|
-
}
|
|
842
|
-
try {
|
|
843
|
-
removedNodes.forEach((node) => {
|
|
844
|
-
const element = node;
|
|
845
|
-
if (element.matches(selector)) {
|
|
846
|
-
// the element itself is an anchor
|
|
847
|
-
removedAnchors.add(element);
|
|
848
|
-
}
|
|
849
|
-
else {
|
|
850
|
-
/**
|
|
851
|
-
* TODO(V6): do we care if an element which is an anchor,
|
|
852
|
-
* has children which are also anchors?
|
|
853
|
-
* (i.e. should we remove `else` and always do this)
|
|
854
|
-
*/
|
|
855
|
-
// the element has children which are anchors
|
|
856
|
-
element
|
|
857
|
-
.querySelectorAll(selector)
|
|
858
|
-
.forEach((innerNode) => removedAnchors.add(innerNode));
|
|
859
|
-
}
|
|
860
|
-
});
|
|
861
|
-
}
|
|
862
|
-
catch (_a) {
|
|
863
|
-
/* c8 ignore start */
|
|
864
|
-
{
|
|
865
|
-
// eslint-disable-next-line no-console
|
|
866
|
-
console.warn(`[react-tooltip] "${selector}" is not a valid CSS selector`);
|
|
867
|
-
}
|
|
868
|
-
/* c8 ignore end */
|
|
869
|
-
}
|
|
870
|
-
try {
|
|
871
|
-
const addedNodes = [...mutation.addedNodes].filter((node) => node.nodeType === 1);
|
|
872
|
-
addedNodes.forEach((node) => {
|
|
873
|
-
const element = node;
|
|
874
|
-
if (element.matches(selector)) {
|
|
875
|
-
// the element itself is an anchor
|
|
876
|
-
addedAnchors.add(element);
|
|
877
|
-
}
|
|
878
|
-
else {
|
|
879
|
-
/**
|
|
880
|
-
* TODO(V6): do we care if an element which is an anchor,
|
|
881
|
-
* has children which are also anchors?
|
|
882
|
-
* (i.e. should we remove `else` and always do this)
|
|
883
|
-
*/
|
|
884
|
-
// the element has children which are anchors
|
|
885
|
-
element
|
|
886
|
-
.querySelectorAll(selector)
|
|
887
|
-
.forEach((innerNode) => addedAnchors.add(innerNode));
|
|
888
|
-
}
|
|
889
|
-
});
|
|
890
|
-
}
|
|
891
|
-
catch (_b) {
|
|
892
|
-
/* c8 ignore start */
|
|
893
|
-
{
|
|
894
|
-
// eslint-disable-next-line no-console
|
|
895
|
-
console.warn(`[react-tooltip] "${selector}" is not a valid CSS selector`);
|
|
896
|
-
}
|
|
897
|
-
/* c8 ignore end */
|
|
898
|
-
}
|
|
899
|
-
});
|
|
900
|
-
if (addedAnchors.size || removedAnchors.size) {
|
|
901
|
-
setAnchorElements((anchors) => [
|
|
902
|
-
...anchors.filter((anchor) => !removedAnchors.has(anchor)),
|
|
903
|
-
...addedAnchors,
|
|
904
|
-
]);
|
|
905
|
-
}
|
|
906
|
-
};
|
|
907
|
-
const documentObserver = new MutationObserver(documentObserverCallback);
|
|
908
|
-
// watch for anchor being removed from the DOM
|
|
909
|
-
documentObserver.observe(document.body, {
|
|
910
|
-
childList: true,
|
|
911
|
-
subtree: true,
|
|
912
|
-
attributes: true,
|
|
913
|
-
attributeFilter: ['data-tooltip-id'],
|
|
914
|
-
// to track the prev value if we need to remove anchor when data-tooltip-id gets changed
|
|
915
|
-
attributeOldValue: true,
|
|
916
|
-
});
|
|
917
|
-
return () => {
|
|
918
|
-
documentObserver.disconnect();
|
|
919
|
-
};
|
|
920
|
-
}, [id, anchorSelect, imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect, activeAnchor, handleShow, setActiveAnchor]);
|
|
1373
|
+
});
|
|
1374
|
+
const updateTooltipPositionRef = React.useRef(updateTooltipPosition);
|
|
1375
|
+
updateTooltipPositionRef.current = updateTooltipPosition;
|
|
921
1376
|
React.useEffect(() => {
|
|
1377
|
+
if (!rendered) {
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
922
1380
|
updateTooltipPosition();
|
|
923
|
-
}, [updateTooltipPosition]);
|
|
1381
|
+
}, [rendered, updateTooltipPosition]);
|
|
924
1382
|
React.useEffect(() => {
|
|
925
|
-
if (!(contentWrapperRef === null || contentWrapperRef === void 0 ? void 0 : contentWrapperRef.current)) {
|
|
1383
|
+
if (!rendered || !(contentWrapperRef === null || contentWrapperRef === void 0 ? void 0 : contentWrapperRef.current)) {
|
|
926
1384
|
return () => null;
|
|
927
1385
|
}
|
|
1386
|
+
let timeoutId = null;
|
|
928
1387
|
const contentObserver = new ResizeObserver(() => {
|
|
929
|
-
|
|
1388
|
+
// Clear any existing timeout to prevent memory leaks
|
|
1389
|
+
if (timeoutId) {
|
|
1390
|
+
clearTimeout(timeoutId);
|
|
1391
|
+
}
|
|
1392
|
+
timeoutId = setTimeout(() => {
|
|
1393
|
+
if (mounted.current) {
|
|
1394
|
+
updateTooltipPositionRef.current();
|
|
1395
|
+
}
|
|
1396
|
+
timeoutId = null;
|
|
1397
|
+
}, 0);
|
|
930
1398
|
});
|
|
931
1399
|
contentObserver.observe(contentWrapperRef.current);
|
|
932
1400
|
return () => {
|
|
933
1401
|
contentObserver.disconnect();
|
|
1402
|
+
if (timeoutId) {
|
|
1403
|
+
clearTimeout(timeoutId);
|
|
1404
|
+
}
|
|
934
1405
|
};
|
|
935
|
-
}, [content, contentWrapperRef,
|
|
1406
|
+
}, [content, contentWrapperRef, rendered]);
|
|
936
1407
|
React.useEffect(() => {
|
|
937
1408
|
var _a;
|
|
1409
|
+
const shouldResolveInitialActiveAnchor = rendered || defaultIsOpen || Boolean(isOpen);
|
|
1410
|
+
if (!shouldResolveInitialActiveAnchor) {
|
|
1411
|
+
return;
|
|
1412
|
+
}
|
|
1413
|
+
const activeAnchorMatchesImperativeSelector = (() => {
|
|
1414
|
+
if (!activeAnchor || !(imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect)) {
|
|
1415
|
+
return false;
|
|
1416
|
+
}
|
|
1417
|
+
try {
|
|
1418
|
+
return activeAnchor.matches(imperativeOptions.anchorSelect);
|
|
1419
|
+
}
|
|
1420
|
+
catch (_a) {
|
|
1421
|
+
return false;
|
|
1422
|
+
}
|
|
1423
|
+
})();
|
|
938
1424
|
if (!activeAnchor || !anchorElements.includes(activeAnchor)) {
|
|
939
1425
|
/**
|
|
940
1426
|
* if there is no active anchor,
|
|
941
1427
|
* or if the current active anchor is not amongst the allowed ones,
|
|
942
1428
|
* reset it
|
|
943
1429
|
*/
|
|
1430
|
+
if (activeAnchorMatchesImperativeSelector) {
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
944
1433
|
setActiveAnchor((_a = anchorElements[0]) !== null && _a !== void 0 ? _a : null);
|
|
945
1434
|
}
|
|
946
|
-
}, [
|
|
1435
|
+
}, [
|
|
1436
|
+
activeAnchor,
|
|
1437
|
+
anchorElements,
|
|
1438
|
+
defaultIsOpen,
|
|
1439
|
+
imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect,
|
|
1440
|
+
isOpen,
|
|
1441
|
+
rendered,
|
|
1442
|
+
setActiveAnchor,
|
|
1443
|
+
]);
|
|
947
1444
|
React.useEffect(() => {
|
|
948
1445
|
if (defaultIsOpen) {
|
|
949
1446
|
handleShow(true);
|
|
@@ -951,26 +1448,10 @@
|
|
|
951
1448
|
return () => {
|
|
952
1449
|
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
953
1450
|
clearTimeoutRef(tooltipHideDelayTimerRef);
|
|
1451
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1452
|
+
clearTimeoutRef(missedTransitionTimerRef);
|
|
954
1453
|
};
|
|
955
1454
|
}, [defaultIsOpen, handleShow]);
|
|
956
|
-
React.useEffect(() => {
|
|
957
|
-
var _a;
|
|
958
|
-
let selector = (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect) !== null && _a !== void 0 ? _a : anchorSelect;
|
|
959
|
-
if (!selector && id) {
|
|
960
|
-
selector = `[data-tooltip-id='${id.replace(/'/g, "\\'")}']`;
|
|
961
|
-
}
|
|
962
|
-
if (!selector) {
|
|
963
|
-
return;
|
|
964
|
-
}
|
|
965
|
-
try {
|
|
966
|
-
const anchors = Array.from(document.querySelectorAll(selector));
|
|
967
|
-
setAnchorElements(anchors);
|
|
968
|
-
}
|
|
969
|
-
catch (_b) {
|
|
970
|
-
// warning was already issued in the controller
|
|
971
|
-
setAnchorElements([]);
|
|
972
|
-
}
|
|
973
|
-
}, [id, anchorSelect, imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.anchorSelect]);
|
|
974
1455
|
React.useEffect(() => {
|
|
975
1456
|
if (tooltipShowDelayTimerRef.current) {
|
|
976
1457
|
/**
|
|
@@ -982,20 +1463,37 @@
|
|
|
982
1463
|
}
|
|
983
1464
|
}, [delayShow, handleShowTooltipDelayed]);
|
|
984
1465
|
const actualContent = (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.content) !== null && _a !== void 0 ? _a : content;
|
|
985
|
-
const
|
|
1466
|
+
const hasContent = actualContent !== null && actualContent !== undefined;
|
|
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]);
|
|
986
1481
|
React.useImperativeHandle(forwardRef, () => ({
|
|
987
1482
|
open: (options) => {
|
|
1483
|
+
let imperativeAnchor = null;
|
|
988
1484
|
if (options === null || options === void 0 ? void 0 : options.anchorSelect) {
|
|
989
1485
|
try {
|
|
990
|
-
document.querySelector(options.anchorSelect);
|
|
1486
|
+
imperativeAnchor = document.querySelector(options.anchorSelect);
|
|
991
1487
|
}
|
|
992
1488
|
catch (_a) {
|
|
993
|
-
{
|
|
994
|
-
// eslint-disable-next-line no-console
|
|
995
|
-
console.warn(`[react-tooltip] "${options.anchorSelect}" is not a valid CSS selector`);
|
|
996
|
-
}
|
|
997
1489
|
return;
|
|
998
1490
|
}
|
|
1491
|
+
if (!imperativeAnchor) {
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
}
|
|
1495
|
+
if (imperativeAnchor) {
|
|
1496
|
+
setActiveAnchor(imperativeAnchor);
|
|
999
1497
|
}
|
|
1000
1498
|
setImperativeOptions(options !== null && options !== void 0 ? options : null);
|
|
1001
1499
|
if (options === null || options === void 0 ? void 0 : options.delay) {
|
|
@@ -1015,9 +1513,18 @@
|
|
|
1015
1513
|
},
|
|
1016
1514
|
activeAnchor,
|
|
1017
1515
|
place: computedPosition.place,
|
|
1018
|
-
isOpen: Boolean(rendered && !hidden &&
|
|
1516
|
+
isOpen: Boolean(rendered && !hidden && hasContent && canShow),
|
|
1019
1517
|
}));
|
|
1020
|
-
|
|
1518
|
+
React.useEffect(() => {
|
|
1519
|
+
return () => {
|
|
1520
|
+
// Final cleanup to ensure no memory leaks
|
|
1521
|
+
clearTimeoutRef(tooltipShowDelayTimerRef);
|
|
1522
|
+
clearTimeoutRef(tooltipHideDelayTimerRef);
|
|
1523
|
+
clearTimeoutRef(tooltipAutoCloseTimerRef);
|
|
1524
|
+
clearTimeoutRef(missedTransitionTimerRef);
|
|
1525
|
+
};
|
|
1526
|
+
}, []);
|
|
1527
|
+
const tooltipNode = rendered && !hidden && hasContent ? (React.createElement(WrapperElement, { id: id, role: role, className: clsx('react-tooltip', coreStyles['tooltip'], styles['tooltip'], styles[variant], className, `react-tooltip__place-${computedPosition.place}`, coreStyles[canShow ? 'show' : 'closing'], canShow ? 'react-tooltip__show' : 'react-tooltip__closing', positionStrategy === 'fixed' && coreStyles['fixed'], clickable && coreStyles['clickable']), onTransitionEnd: (event) => {
|
|
1021
1528
|
clearTimeoutRef(missedTransitionTimerRef);
|
|
1022
1529
|
if (show || event.propertyName !== 'opacity') {
|
|
1023
1530
|
return;
|
|
@@ -1025,34 +1532,96 @@
|
|
|
1025
1532
|
setRendered(false);
|
|
1026
1533
|
setImperativeOptions(null);
|
|
1027
1534
|
afterHide === null || afterHide === void 0 ? void 0 : afterHide();
|
|
1028
|
-
}, style:
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1535
|
+
}, style: tooltipStyle, ref: tooltipRef },
|
|
1536
|
+
React.createElement(WrapperElement, { className: clsx('react-tooltip-content-wrapper', coreStyles['content'], styles['content']) }, actualContent),
|
|
1537
|
+
React.createElement(WrapperElement, { className: clsx('react-tooltip-arrow', coreStyles['arrow'], styles['arrow'], classNameArrow, noArrow && coreStyles['noArrow']), style: arrowStyle, ref: tooltipArrowRef }))) : null;
|
|
1538
|
+
if (!tooltipNode) {
|
|
1539
|
+
return null;
|
|
1540
|
+
}
|
|
1541
|
+
if (portalRoot) {
|
|
1542
|
+
return reactDom.createPortal(tooltipNode, portalRoot);
|
|
1543
|
+
}
|
|
1544
|
+
return tooltipNode;
|
|
1545
|
+
};
|
|
1546
|
+
var Tooltip$1 = React.memo(Tooltip);
|
|
1547
|
+
|
|
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,
|
|
1040
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
|
+
}
|
|
1041
1609
|
|
|
1042
|
-
const TooltipController = React.forwardRef(({ id, anchorSelect, content, render, className, classNameArrow, variant = 'dark', 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, setIsOpen, afterShow, afterHide, role = 'tooltip', }, ref) => {
|
|
1043
|
-
|
|
1044
|
-
const [tooltipPlace, setTooltipPlace] = React.useState(place);
|
|
1045
|
-
const [tooltipVariant, setTooltipVariant] = React.useState(variant);
|
|
1046
|
-
const [tooltipOffset, setTooltipOffset] = React.useState(offset);
|
|
1047
|
-
const [tooltipDelayShow, setTooltipDelayShow] = React.useState(delayShow);
|
|
1048
|
-
const [tooltipDelayHide, setTooltipDelayHide] = React.useState(delayHide);
|
|
1049
|
-
const [tooltipFloat, setTooltipFloat] = React.useState(float);
|
|
1050
|
-
const [tooltipHidden, setTooltipHidden] = React.useState(hidden);
|
|
1051
|
-
const [tooltipWrapper, setTooltipWrapper] = React.useState(wrapper);
|
|
1052
|
-
const [tooltipPositionStrategy, setTooltipPositionStrategy] = React.useState(positionStrategy);
|
|
1053
|
-
const [tooltipClassName, setTooltipClassName] = React.useState(null);
|
|
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) => {
|
|
1611
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
1054
1612
|
const [activeAnchor, setActiveAnchor] = React.useState(null);
|
|
1613
|
+
const [anchorDataAttributes, setAnchorDataAttributes] = React.useState({});
|
|
1614
|
+
const previousActiveAnchorRef = React.useRef(null);
|
|
1055
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
|
+
}, []);
|
|
1624
|
+
/* c8 ignore start */
|
|
1056
1625
|
const getDataAttributesFromAnchorElement = (elementReference) => {
|
|
1057
1626
|
const dataAttributes = elementReference === null || elementReference === void 0 ? void 0 : elementReference.getAttributeNames().reduce((acc, name) => {
|
|
1058
1627
|
var _a;
|
|
@@ -1064,101 +1633,11 @@
|
|
|
1064
1633
|
}, {});
|
|
1065
1634
|
return dataAttributes;
|
|
1066
1635
|
};
|
|
1067
|
-
|
|
1068
|
-
const handleDataAttributes = {
|
|
1069
|
-
place: (value) => {
|
|
1070
|
-
var _a;
|
|
1071
|
-
setTooltipPlace((_a = value) !== null && _a !== void 0 ? _a : place);
|
|
1072
|
-
},
|
|
1073
|
-
content: (value) => {
|
|
1074
|
-
setTooltipContent(value !== null && value !== void 0 ? value : content);
|
|
1075
|
-
},
|
|
1076
|
-
variant: (value) => {
|
|
1077
|
-
var _a;
|
|
1078
|
-
setTooltipVariant((_a = value) !== null && _a !== void 0 ? _a : variant);
|
|
1079
|
-
},
|
|
1080
|
-
offset: (value) => {
|
|
1081
|
-
setTooltipOffset(value === null ? offset : Number(value));
|
|
1082
|
-
},
|
|
1083
|
-
wrapper: (value) => {
|
|
1084
|
-
var _a;
|
|
1085
|
-
setTooltipWrapper((_a = value) !== null && _a !== void 0 ? _a : wrapper);
|
|
1086
|
-
},
|
|
1087
|
-
'position-strategy': (value) => {
|
|
1088
|
-
var _a;
|
|
1089
|
-
setTooltipPositionStrategy((_a = value) !== null && _a !== void 0 ? _a : positionStrategy);
|
|
1090
|
-
},
|
|
1091
|
-
'delay-show': (value) => {
|
|
1092
|
-
setTooltipDelayShow(value === null ? delayShow : Number(value));
|
|
1093
|
-
},
|
|
1094
|
-
'delay-hide': (value) => {
|
|
1095
|
-
setTooltipDelayHide(value === null ? delayHide : Number(value));
|
|
1096
|
-
},
|
|
1097
|
-
float: (value) => {
|
|
1098
|
-
setTooltipFloat(value === null ? float : value === 'true');
|
|
1099
|
-
},
|
|
1100
|
-
hidden: (value) => {
|
|
1101
|
-
setTooltipHidden(value === null ? hidden : value === 'true');
|
|
1102
|
-
},
|
|
1103
|
-
'class-name': (value) => {
|
|
1104
|
-
setTooltipClassName(value);
|
|
1105
|
-
},
|
|
1106
|
-
};
|
|
1107
|
-
// reset unset data attributes to default values
|
|
1108
|
-
// without this, data attributes from the last active anchor will still be used
|
|
1109
|
-
Object.values(handleDataAttributes).forEach((handler) => handler(null));
|
|
1110
|
-
Object.entries(dataAttributes).forEach(([key, value]) => {
|
|
1111
|
-
var _a;
|
|
1112
|
-
(_a = handleDataAttributes[key]) === null || _a === void 0 ? void 0 : _a.call(handleDataAttributes, value);
|
|
1113
|
-
});
|
|
1114
|
-
}, [
|
|
1115
|
-
content,
|
|
1116
|
-
delayHide,
|
|
1117
|
-
delayShow,
|
|
1118
|
-
float,
|
|
1119
|
-
hidden,
|
|
1120
|
-
offset,
|
|
1121
|
-
place,
|
|
1122
|
-
positionStrategy,
|
|
1123
|
-
variant,
|
|
1124
|
-
wrapper,
|
|
1125
|
-
]);
|
|
1126
|
-
React.useEffect(() => {
|
|
1127
|
-
setTooltipContent(content);
|
|
1128
|
-
}, [content]);
|
|
1129
|
-
React.useEffect(() => {
|
|
1130
|
-
setTooltipPlace(place);
|
|
1131
|
-
}, [place]);
|
|
1132
|
-
React.useEffect(() => {
|
|
1133
|
-
setTooltipVariant(variant);
|
|
1134
|
-
}, [variant]);
|
|
1135
|
-
React.useEffect(() => {
|
|
1136
|
-
setTooltipOffset(offset);
|
|
1137
|
-
}, [offset]);
|
|
1138
|
-
React.useEffect(() => {
|
|
1139
|
-
setTooltipDelayShow(delayShow);
|
|
1140
|
-
}, [delayShow]);
|
|
1141
|
-
React.useEffect(() => {
|
|
1142
|
-
setTooltipDelayHide(delayHide);
|
|
1143
|
-
}, [delayHide]);
|
|
1144
|
-
React.useEffect(() => {
|
|
1145
|
-
setTooltipFloat(float);
|
|
1146
|
-
}, [float]);
|
|
1147
|
-
React.useEffect(() => {
|
|
1148
|
-
setTooltipHidden(hidden);
|
|
1149
|
-
}, [hidden]);
|
|
1150
|
-
React.useEffect(() => {
|
|
1151
|
-
setTooltipPositionStrategy(positionStrategy);
|
|
1152
|
-
}, [positionStrategy]);
|
|
1636
|
+
/* c8 ignore end */
|
|
1153
1637
|
React.useEffect(() => {
|
|
1154
1638
|
if (styleInjectionRef.current === disableStyleInjection) {
|
|
1155
1639
|
return;
|
|
1156
1640
|
}
|
|
1157
|
-
/* c8 ignore start */
|
|
1158
|
-
{
|
|
1159
|
-
// eslint-disable-next-line no-console
|
|
1160
|
-
console.warn('[react-tooltip] Do not change `disableStyleInjection` dynamically.');
|
|
1161
|
-
}
|
|
1162
1641
|
/* c8 ignore end */
|
|
1163
1642
|
}, [disableStyleInjection]);
|
|
1164
1643
|
React.useEffect(() => {
|
|
@@ -1173,58 +1652,61 @@
|
|
|
1173
1652
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
1174
1653
|
}, []);
|
|
1175
1654
|
React.useEffect(() => {
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1655
|
+
if (!activeAnchor) {
|
|
1656
|
+
setAnchorDataAttributes({});
|
|
1657
|
+
return () => { };
|
|
1658
|
+
}
|
|
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;
|
|
1183
1666
|
}
|
|
1184
|
-
|
|
1185
|
-
const dataAttributes = getDataAttributesFromAnchorElement(activeAnchor);
|
|
1186
|
-
applyAllDataAttributesFromAnchorElement(dataAttributes);
|
|
1667
|
+
return attrs;
|
|
1187
1668
|
});
|
|
1188
1669
|
};
|
|
1189
|
-
|
|
1190
|
-
const
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
const observerConfig = { attributes: true, childList: false, subtree: false };
|
|
1194
|
-
if (activeAnchor) {
|
|
1195
|
-
const dataAttributes = getDataAttributesFromAnchorElement(activeAnchor);
|
|
1196
|
-
applyAllDataAttributesFromAnchorElement(dataAttributes);
|
|
1197
|
-
// Start observing the target node for configured mutations
|
|
1198
|
-
observer.observe(activeAnchor, observerConfig);
|
|
1199
|
-
}
|
|
1200
|
-
return () => {
|
|
1201
|
-
// Remove the observer when the tooltip is destroyed
|
|
1202
|
-
observer.disconnect();
|
|
1203
|
-
};
|
|
1204
|
-
}, [activeAnchor, anchorSelect, applyAllDataAttributesFromAnchorElement]);
|
|
1670
|
+
updateAttributes(activeAnchor);
|
|
1671
|
+
const unsubscribe = observeAnchorAttributes(activeAnchor, updateAttributes);
|
|
1672
|
+
return unsubscribe;
|
|
1673
|
+
}, [activeAnchor, anchorSelect]);
|
|
1205
1674
|
React.useEffect(() => {
|
|
1206
|
-
/* c8 ignore
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
console.warn('[react-tooltip] Do not set `style.border`. Use `border` prop instead.');
|
|
1210
|
-
}
|
|
1211
|
-
if (style === null || style === void 0 ? void 0 : style.opacity) {
|
|
1212
|
-
// eslint-disable-next-line no-console
|
|
1213
|
-
console.warn('[react-tooltip] Do not set `style.opacity`. Use `opacity` prop instead.');
|
|
1675
|
+
/* c8 ignore start */
|
|
1676
|
+
{
|
|
1677
|
+
return;
|
|
1214
1678
|
}
|
|
1215
1679
|
}, [border, opacity, style === null || style === void 0 ? void 0 : style.border, style === null || style === void 0 ? void 0 : style.opacity]);
|
|
1216
1680
|
/**
|
|
1217
1681
|
* content priority: children < render or content < html
|
|
1218
1682
|
* children should be lower priority so that it can be used as the "default" content
|
|
1219
1683
|
*/
|
|
1684
|
+
const tooltipContent = (_a = anchorDataAttributes.content) !== null && _a !== void 0 ? _a : content;
|
|
1685
|
+
const tooltipPlace = (_b = anchorDataAttributes.place) !== null && _b !== void 0 ? _b : place;
|
|
1686
|
+
const tooltipVariant = (_c = anchorDataAttributes.variant) !== null && _c !== void 0 ? _c : variant;
|
|
1687
|
+
const tooltipOffset = anchorDataAttributes.offset == null ? offset : Number(anchorDataAttributes.offset);
|
|
1688
|
+
const tooltipWrapper = (_d = anchorDataAttributes.wrapper) !== null && _d !== void 0 ? _d : wrapper;
|
|
1689
|
+
const tooltipPositionStrategy = (_e = anchorDataAttributes['position-strategy']) !== null && _e !== void 0 ? _e : positionStrategy;
|
|
1690
|
+
const tooltipDelayShow = anchorDataAttributes['delay-show'] == null
|
|
1691
|
+
? delayShow
|
|
1692
|
+
: Number(anchorDataAttributes['delay-show']);
|
|
1693
|
+
const tooltipDelayHide = anchorDataAttributes['delay-hide'] == null
|
|
1694
|
+
? delayHide
|
|
1695
|
+
: Number(anchorDataAttributes['delay-hide']);
|
|
1696
|
+
const tooltipAutoClose = anchorDataAttributes['auto-close'] == null
|
|
1697
|
+
? autoClose
|
|
1698
|
+
: Number(anchorDataAttributes['auto-close']);
|
|
1699
|
+
const tooltipFloat = anchorDataAttributes.float == null ? float : anchorDataAttributes.float === 'true';
|
|
1700
|
+
const tooltipHidden = anchorDataAttributes.hidden == null ? hidden : anchorDataAttributes.hidden === 'true';
|
|
1701
|
+
const tooltipClassName = (_f = anchorDataAttributes['class-name']) !== null && _f !== void 0 ? _f : null;
|
|
1220
1702
|
let renderedContent = children;
|
|
1221
1703
|
const contentWrapperRef = React.useRef(null);
|
|
1222
1704
|
if (render) {
|
|
1223
|
-
const actualContent = (
|
|
1705
|
+
const actualContent = (_h = (_g = anchorDataAttributes.content) !== null && _g !== void 0 ? _g : tooltipContent) !== null && _h !== void 0 ? _h : null;
|
|
1224
1706
|
const rendered = render({ content: actualContent, activeAnchor });
|
|
1225
1707
|
renderedContent = rendered ? (React.createElement("div", { ref: contentWrapperRef, className: "react-tooltip-content-wrapper" }, rendered)) : null;
|
|
1226
1708
|
}
|
|
1227
|
-
else if (tooltipContent) {
|
|
1709
|
+
else if (tooltipContent !== null && tooltipContent !== undefined) {
|
|
1228
1710
|
renderedContent = tooltipContent;
|
|
1229
1711
|
}
|
|
1230
1712
|
const props = {
|
|
@@ -1235,6 +1717,7 @@
|
|
|
1235
1717
|
classNameArrow,
|
|
1236
1718
|
content: renderedContent,
|
|
1237
1719
|
contentWrapperRef,
|
|
1720
|
+
portalRoot,
|
|
1238
1721
|
place: tooltipPlace,
|
|
1239
1722
|
variant: tooltipVariant,
|
|
1240
1723
|
offset: tooltipOffset,
|
|
@@ -1244,6 +1727,7 @@
|
|
|
1244
1727
|
middlewares,
|
|
1245
1728
|
delayShow: tooltipDelayShow,
|
|
1246
1729
|
delayHide: tooltipDelayHide,
|
|
1730
|
+
autoClose: tooltipAutoClose,
|
|
1247
1731
|
float: tooltipFloat,
|
|
1248
1732
|
hidden: tooltipHidden,
|
|
1249
1733
|
noArrow,
|
|
@@ -1259,15 +1743,19 @@
|
|
|
1259
1743
|
border,
|
|
1260
1744
|
opacity,
|
|
1261
1745
|
arrowColor,
|
|
1746
|
+
arrowSize,
|
|
1262
1747
|
setIsOpen,
|
|
1263
1748
|
afterShow,
|
|
1264
1749
|
afterHide,
|
|
1750
|
+
disableTooltip,
|
|
1265
1751
|
activeAnchor,
|
|
1266
|
-
|
|
1752
|
+
previousActiveAnchor: previousActiveAnchorRef.current,
|
|
1753
|
+
setActiveAnchor: handleSetActiveAnchor,
|
|
1267
1754
|
role,
|
|
1268
1755
|
};
|
|
1269
|
-
return React.createElement(Tooltip, { ...props });
|
|
1756
|
+
return React.createElement(Tooltip$1, { ...props });
|
|
1270
1757
|
});
|
|
1758
|
+
var TooltipController_default = React.memo(TooltipController);
|
|
1271
1759
|
|
|
1272
1760
|
// those content will be replaced in build time with the `react-tooltip.css` builded content
|
|
1273
1761
|
const TooltipCoreStyles = `:root {
|
|
@@ -1280,6 +1768,7 @@
|
|
|
1280
1768
|
--rt-opacity: 0.9;
|
|
1281
1769
|
--rt-transition-show-delay: 0.15s;
|
|
1282
1770
|
--rt-transition-closing-delay: 0.15s;
|
|
1771
|
+
--rt-arrow-size: 8px;
|
|
1283
1772
|
}
|
|
1284
1773
|
|
|
1285
1774
|
.core-styles-module_tooltip__3vRRp {
|
|
@@ -1288,7 +1777,6 @@
|
|
|
1288
1777
|
left: 0;
|
|
1289
1778
|
pointer-events: none;
|
|
1290
1779
|
opacity: 0;
|
|
1291
|
-
will-change: opacity;
|
|
1292
1780
|
}
|
|
1293
1781
|
|
|
1294
1782
|
.core-styles-module_fixed__pcSol {
|
|
@@ -1298,6 +1786,14 @@
|
|
|
1298
1786
|
.core-styles-module_arrow__cvMwQ {
|
|
1299
1787
|
position: absolute;
|
|
1300
1788
|
background: inherit;
|
|
1789
|
+
z-index: -1;
|
|
1790
|
+
-webkit-backface-visibility: hidden;
|
|
1791
|
+
backface-visibility: hidden;
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
.core-styles-module_content__BRKdB {
|
|
1795
|
+
position: relative;
|
|
1796
|
+
z-index: 1;
|
|
1301
1797
|
}
|
|
1302
1798
|
|
|
1303
1799
|
.core-styles-module_noArrow__xock6 {
|
|
@@ -1311,26 +1807,33 @@
|
|
|
1311
1807
|
.core-styles-module_show__Nt9eE {
|
|
1312
1808
|
opacity: var(--rt-opacity);
|
|
1313
1809
|
transition: opacity var(--rt-transition-show-delay) ease-out;
|
|
1810
|
+
will-change: opacity;
|
|
1314
1811
|
}
|
|
1315
1812
|
|
|
1316
1813
|
.core-styles-module_closing__sGnxF {
|
|
1317
1814
|
opacity: 0;
|
|
1318
1815
|
transition: opacity var(--rt-transition-closing-delay) ease-in;
|
|
1816
|
+
will-change: opacity;
|
|
1319
1817
|
}
|
|
1320
1818
|
|
|
1321
1819
|
`;
|
|
1322
1820
|
const TooltipStyles = `
|
|
1323
1821
|
|
|
1324
1822
|
.styles-module_tooltip__mnnfp {
|
|
1325
|
-
padding: 8px 16px;
|
|
1326
1823
|
border-radius: 3px;
|
|
1327
1824
|
font-size: 90%;
|
|
1328
1825
|
width: max-content;
|
|
1329
1826
|
}
|
|
1330
1827
|
|
|
1828
|
+
.styles-module_content__ydYdI {
|
|
1829
|
+
background: inherit;
|
|
1830
|
+
border-radius: inherit;
|
|
1831
|
+
padding: 8px 16px;
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1331
1834
|
.styles-module_arrow__K0L3T {
|
|
1332
|
-
width:
|
|
1333
|
-
height:
|
|
1835
|
+
width: var(--rt-arrow-size);
|
|
1836
|
+
height: var(--rt-arrow-size);
|
|
1334
1837
|
}
|
|
1335
1838
|
|
|
1336
1839
|
[class*='react-tooltip__place-top'] > .styles-module_arrow__K0L3T {
|
|
@@ -1391,7 +1894,7 @@
|
|
|
1391
1894
|
}));
|
|
1392
1895
|
}
|
|
1393
1896
|
|
|
1394
|
-
exports.Tooltip =
|
|
1897
|
+
exports.Tooltip = TooltipController_default;
|
|
1395
1898
|
|
|
1396
1899
|
}));
|
|
1397
1900
|
//# sourceMappingURL=react-tooltip.umd.js.map
|