react-tooltip 5.26.1 → 5.26.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -105,6 +105,96 @@ function removeStyle({ type = 'base', id = REACT_TOOLTIP_BASE_STYLES_ID, } = {})
105
105
  injected[type] = false;
106
106
  }
107
107
 
108
+ const computeTooltipPosition = async ({ elementReference = null, tooltipReference = null, tooltipArrowReference = null, place = 'top', offset: offsetValue = 10, strategy = 'absolute', middlewares = [
109
+ dom.offset(Number(offsetValue)),
110
+ dom.flip({
111
+ fallbackAxisSideDirection: 'start',
112
+ }),
113
+ dom.shift({ padding: 5 }),
114
+ ], border, }) => {
115
+ if (!elementReference) {
116
+ // elementReference can be null or undefined and we will not compute the position
117
+ // eslint-disable-next-line no-console
118
+ // console.error('The reference element for tooltip was not defined: ', elementReference)
119
+ return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
120
+ }
121
+ if (tooltipReference === null) {
122
+ return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
123
+ }
124
+ const middleware = middlewares;
125
+ if (tooltipArrowReference) {
126
+ middleware.push(dom.arrow({ element: tooltipArrowReference, padding: 5 }));
127
+ return dom.computePosition(elementReference, tooltipReference, {
128
+ placement: place,
129
+ strategy,
130
+ middleware,
131
+ }).then(({ x, y, placement, middlewareData }) => {
132
+ var _a, _b;
133
+ const styles = { left: `${x}px`, top: `${y}px`, border };
134
+ /* c8 ignore start */
135
+ const { x: arrowX, y: arrowY } = (_a = middlewareData.arrow) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
136
+ const staticSide = (_b = {
137
+ top: 'bottom',
138
+ right: 'left',
139
+ bottom: 'top',
140
+ left: 'right',
141
+ }[placement.split('-')[0]]) !== null && _b !== void 0 ? _b : 'bottom';
142
+ /* c8 ignore end */
143
+ const borderSide = border && {
144
+ borderBottom: border,
145
+ borderRight: border,
146
+ };
147
+ let borderWidth = 0;
148
+ if (border) {
149
+ const match = `${border}`.match(/(\d+)px/);
150
+ if (match === null || match === void 0 ? void 0 : match[1]) {
151
+ borderWidth = Number(match[1]);
152
+ }
153
+ else {
154
+ /**
155
+ * this means `border` was set without `width`,
156
+ * or non-px value (such as `medium`, `thick`, ...)
157
+ */
158
+ borderWidth = 1;
159
+ }
160
+ }
161
+ /* c8 ignore start */
162
+ const arrowStyle = {
163
+ left: arrowX != null ? `${arrowX}px` : '',
164
+ top: arrowY != null ? `${arrowY}px` : '',
165
+ right: '',
166
+ bottom: '',
167
+ ...borderSide,
168
+ [staticSide]: `-${4 + borderWidth}px`,
169
+ };
170
+ /* c8 ignore end */
171
+ return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle, place: placement };
172
+ });
173
+ }
174
+ return dom.computePosition(elementReference, tooltipReference, {
175
+ placement: 'bottom',
176
+ strategy,
177
+ middleware,
178
+ }).then(({ x, y, placement }) => {
179
+ const styles = { left: `${x}px`, top: `${y}px` };
180
+ return { tooltipStyles: styles, tooltipArrowStyles: {}, place: placement };
181
+ });
182
+ };
183
+
184
+ const cssSupports = (property, value) => {
185
+ const hasCssSupports = 'CSS' in window && 'supports' in window.CSS;
186
+ return hasCssSupports ? window.CSS.supports(property, value) : true;
187
+ };
188
+
189
+ const cssTimeToMs = (time) => {
190
+ const match = time.match(/^([\d.]+)(ms|s)$/);
191
+ if (!match) {
192
+ return 0;
193
+ }
194
+ const [, amount, unit] = match;
195
+ return Number(amount) * (unit === 'ms' ? 1 : 1000);
196
+ };
197
+
108
198
  /* eslint-disable @typescript-eslint/no-explicit-any */
109
199
  /**
110
200
  * This function debounce the received function
@@ -123,7 +213,7 @@ const debounce = (func, wait, immediate) => {
123
213
  };
124
214
  if (immediate && !timeout) {
125
215
  /**
126
- * there's not need to clear the timeout
216
+ * there's no need to clear the timeout
127
217
  * since we expect it to resolve and set `timeout = null`
128
218
  */
129
219
  func.apply(this, args);
@@ -137,15 +227,70 @@ const debounce = (func, wait, immediate) => {
137
227
  }
138
228
  };
139
229
  debounced.cancel = () => {
230
+ /* c8 ignore start */
140
231
  if (!timeout) {
141
232
  return;
142
233
  }
234
+ /* c8 ignore end */
143
235
  clearTimeout(timeout);
144
236
  timeout = null;
145
237
  };
146
238
  return debounced;
147
239
  };
148
240
 
241
+ const isObject = (object) => {
242
+ return object !== null && !Array.isArray(object) && typeof object === 'object';
243
+ };
244
+ const deepEqual = (object1, object2) => {
245
+ if (object1 === object2) {
246
+ return true;
247
+ }
248
+ if (Array.isArray(object1) && Array.isArray(object2)) {
249
+ if (object1.length !== object2.length) {
250
+ return false;
251
+ }
252
+ return object1.every((val, index) => deepEqual(val, object2[index]));
253
+ }
254
+ if (Array.isArray(object1) !== Array.isArray(object2)) {
255
+ return false;
256
+ }
257
+ if (!isObject(object1) || !isObject(object2)) {
258
+ return object1 === object2;
259
+ }
260
+ const keys1 = Object.keys(object1);
261
+ const keys2 = Object.keys(object2);
262
+ if (keys1.length !== keys2.length) {
263
+ return false;
264
+ }
265
+ return keys1.every((key) => deepEqual(object1[key], object2[key]));
266
+ };
267
+
268
+ const isScrollable = (node) => {
269
+ if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
270
+ return false;
271
+ }
272
+ const style = getComputedStyle(node);
273
+ return ['overflow', 'overflow-x', 'overflow-y'].some((propertyName) => {
274
+ const value = style.getPropertyValue(propertyName);
275
+ return value === 'auto' || value === 'scroll';
276
+ });
277
+ };
278
+ const getScrollParent = (node) => {
279
+ if (!node) {
280
+ return null;
281
+ }
282
+ let currentParent = node.parentElement;
283
+ while (currentParent) {
284
+ if (isScrollable(currentParent)) {
285
+ return currentParent;
286
+ }
287
+ currentParent = currentParent.parentElement;
288
+ }
289
+ return document.scrollingElement || document.documentElement;
290
+ };
291
+
292
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
293
+
149
294
  const DEFAULT_TOOLTIP_ID = 'DEFAULT_TOOLTIP_ID';
150
295
  const DEFAULT_CONTEXT_DATA = {
151
296
  anchorRefs: new Set(),
@@ -244,115 +389,6 @@ const TooltipWrapper = ({ tooltipId, children, className, place, content, html,
244
389
  return (React__default["default"].createElement("span", { ref: anchorRef, className: classNames__default["default"]('react-tooltip-wrapper', className), "data-tooltip-place": place, "data-tooltip-content": content, "data-tooltip-html": html, "data-tooltip-variant": variant, "data-tooltip-offset": offset, "data-tooltip-wrapper": wrapper, "data-tooltip-events": events, "data-tooltip-position-strategy": positionStrategy, "data-tooltip-delay-show": delayShow, "data-tooltip-delay-hide": delayHide }, children));
245
390
  };
246
391
 
247
- const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
248
-
249
- const isScrollable = (node) => {
250
- if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
251
- return false;
252
- }
253
- const style = getComputedStyle(node);
254
- return ['overflow', 'overflow-x', 'overflow-y'].some((propertyName) => {
255
- const value = style.getPropertyValue(propertyName);
256
- return value === 'auto' || value === 'scroll';
257
- });
258
- };
259
- const getScrollParent = (node) => {
260
- if (!node) {
261
- return null;
262
- }
263
- let currentParent = node.parentElement;
264
- while (currentParent) {
265
- if (isScrollable(currentParent)) {
266
- return currentParent;
267
- }
268
- currentParent = currentParent.parentElement;
269
- }
270
- return document.scrollingElement || document.documentElement;
271
- };
272
-
273
- const computeTooltipPosition = async ({ elementReference = null, tooltipReference = null, tooltipArrowReference = null, place = 'top', offset: offsetValue = 10, strategy = 'absolute', middlewares = [
274
- dom.offset(Number(offsetValue)),
275
- dom.flip({
276
- fallbackAxisSideDirection: 'start',
277
- }),
278
- dom.shift({ padding: 5 }),
279
- ], border, }) => {
280
- if (!elementReference) {
281
- // elementReference can be null or undefined and we will not compute the position
282
- // eslint-disable-next-line no-console
283
- // console.error('The reference element for tooltip was not defined: ', elementReference)
284
- return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
285
- }
286
- if (tooltipReference === null) {
287
- return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
288
- }
289
- const middleware = middlewares;
290
- if (tooltipArrowReference) {
291
- middleware.push(dom.arrow({ element: tooltipArrowReference, padding: 5 }));
292
- return dom.computePosition(elementReference, tooltipReference, {
293
- placement: place,
294
- strategy,
295
- middleware,
296
- }).then(({ x, y, placement, middlewareData }) => {
297
- var _a, _b;
298
- const styles = { left: `${x}px`, top: `${y}px`, border };
299
- const { x: arrowX, y: arrowY } = (_a = middlewareData.arrow) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
300
- const staticSide = (_b = {
301
- top: 'bottom',
302
- right: 'left',
303
- bottom: 'top',
304
- left: 'right',
305
- }[placement.split('-')[0]]) !== null && _b !== void 0 ? _b : 'bottom';
306
- const borderSide = border && {
307
- borderBottom: border,
308
- borderRight: border,
309
- };
310
- let borderWidth = 0;
311
- if (border) {
312
- const match = `${border}`.match(/(\d+)px/);
313
- if (match === null || match === void 0 ? void 0 : match[1]) {
314
- borderWidth = Number(match[1]);
315
- }
316
- else {
317
- /**
318
- * this means `border` was set without `width`, or non-px value
319
- */
320
- borderWidth = 1;
321
- }
322
- }
323
- const arrowStyle = {
324
- left: arrowX != null ? `${arrowX}px` : '',
325
- top: arrowY != null ? `${arrowY}px` : '',
326
- right: '',
327
- bottom: '',
328
- ...borderSide,
329
- [staticSide]: `-${4 + borderWidth}px`,
330
- };
331
- return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle, place: placement };
332
- });
333
- }
334
- return dom.computePosition(elementReference, tooltipReference, {
335
- placement: 'bottom',
336
- strategy,
337
- middleware,
338
- }).then(({ x, y, placement }) => {
339
- const styles = { left: `${x}px`, top: `${y}px` };
340
- return { tooltipStyles: styles, tooltipArrowStyles: {}, place: placement };
341
- });
342
- };
343
-
344
- const cssTimeToMs = (time) => {
345
- const match = time.match(/^([\d.]+)(m?s?)$/);
346
- if (!match) {
347
- return 0;
348
- }
349
- const [, amount, unit] = match;
350
- if (unit !== 's' && unit !== 'ms') {
351
- return 0;
352
- }
353
- return Number(amount) * (unit === 'ms' ? 1 : 1000);
354
- };
355
-
356
392
  var coreStyles = {"tooltip":"core-styles-module_tooltip__3vRRp","fixed":"core-styles-module_fixed__pcSol","arrow":"core-styles-module_arrow__cvMwQ","noArrow":"core-styles-module_noArrow__xock6","clickable":"core-styles-module_clickable__ZuTTB","show":"core-styles-module_show__Nt9eE","closing":"core-styles-module_closing__sGnxF"};
357
393
 
358
394
  var styles = {"tooltip":"styles-module_tooltip__mnnfp","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"};
@@ -368,9 +404,11 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
368
404
  const tooltipShowDelayTimerRef = React.useRef(null);
369
405
  const tooltipHideDelayTimerRef = React.useRef(null);
370
406
  const missedTransitionTimerRef = React.useRef(null);
371
- const [actualPlacement, setActualPlacement] = React.useState(place);
372
- const [inlineStyles, setInlineStyles] = React.useState({});
373
- const [inlineArrowStyles, setInlineArrowStyles] = React.useState({});
407
+ const [computedPosition, setComputedPosition] = React.useState({
408
+ tooltipStyles: {},
409
+ tooltipArrowStyles: {},
410
+ place,
411
+ });
374
412
  const [show, setShow] = React.useState(false);
375
413
  const [rendered, setRendered] = React.useState(false);
376
414
  const [imperativeOptions, setImperativeOptions] = React.useState(null);
@@ -528,6 +566,11 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
528
566
  }, transitionShowDelay + 25);
529
567
  }
530
568
  }, [show]);
569
+ const handleComputedPosition = (newComputedPosition) => {
570
+ setComputedPosition((oldComputedPosition) => deepEqual(oldComputedPosition, newComputedPosition)
571
+ ? oldComputedPosition
572
+ : newComputedPosition);
573
+ };
531
574
  const handleShowTooltipDelayed = (delay = delayShow) => {
532
575
  if (tooltipShowDelayTimerRef.current) {
533
576
  clearTimeout(tooltipShowDelayTimerRef.current);
@@ -620,13 +663,7 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
620
663
  middlewares,
621
664
  border,
622
665
  }).then((computedStylesData) => {
623
- if (Object.keys(computedStylesData.tooltipStyles).length) {
624
- setInlineStyles(computedStylesData.tooltipStyles);
625
- }
626
- if (Object.keys(computedStylesData.tooltipArrowStyles).length) {
627
- setInlineArrowStyles(computedStylesData.tooltipArrowStyles);
628
- }
629
- setActualPlacement(computedStylesData.place);
666
+ handleComputedPosition(computedStylesData);
630
667
  });
631
668
  };
632
669
  const handlePointerMove = (event) => {
@@ -719,13 +756,7 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
719
756
  // invalidate computed positions after remount
720
757
  return;
721
758
  }
722
- if (Object.keys(computedStylesData.tooltipStyles).length) {
723
- setInlineStyles(computedStylesData.tooltipStyles);
724
- }
725
- if (Object.keys(computedStylesData.tooltipArrowStyles).length) {
726
- setInlineArrowStyles(computedStylesData.tooltipArrowStyles);
727
- }
728
- setActualPlacement(computedStylesData.place);
759
+ handleComputedPosition(computedStylesData);
729
760
  });
730
761
  }, [
731
762
  show,
@@ -1068,7 +1099,7 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
1068
1099
  }
1069
1100
  }, [delayShow]);
1070
1101
  const actualContent = (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.content) !== null && _a !== void 0 ? _a : content;
1071
- const canShow = show && Object.keys(inlineStyles).length > 0;
1102
+ const canShow = show && Object.keys(computedPosition.tooltipStyles).length > 0;
1072
1103
  React.useImperativeHandle(forwardRef, () => ({
1073
1104
  open: (options) => {
1074
1105
  if (options === null || options === void 0 ? void 0 : options.anchorSelect) {
@@ -1100,10 +1131,10 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
1100
1131
  }
1101
1132
  },
1102
1133
  activeAnchor,
1103
- place: actualPlacement,
1134
+ place: computedPosition.place,
1104
1135
  isOpen: Boolean(rendered && !hidden && actualContent && canShow),
1105
1136
  }));
1106
- return rendered && !hidden && actualContent ? (React__default["default"].createElement(WrapperElement, { id: id, role: role, className: classNames__default["default"]('react-tooltip', coreStyles['tooltip'], styles['tooltip'], styles[variant], className, `react-tooltip__place-${actualPlacement}`, coreStyles[canShow ? 'show' : 'closing'], canShow ? 'react-tooltip__show' : 'react-tooltip__closing', positionStrategy === 'fixed' && coreStyles['fixed'], clickable && coreStyles['clickable']), onTransitionEnd: (event) => {
1137
+ return rendered && !hidden && actualContent ? (React__default["default"].createElement(WrapperElement, { id: id, role: role, className: classNames__default["default"]('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) => {
1107
1138
  if (missedTransitionTimerRef.current) {
1108
1139
  clearTimeout(missedTransitionTimerRef.current);
1109
1140
  }
@@ -1115,12 +1146,12 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
1115
1146
  afterHide === null || afterHide === void 0 ? void 0 : afterHide();
1116
1147
  }, style: {
1117
1148
  ...externalStyles,
1118
- ...inlineStyles,
1149
+ ...computedPosition.tooltipStyles,
1119
1150
  opacity: opacity !== undefined && canShow ? opacity : undefined,
1120
1151
  }, ref: tooltipRef },
1121
1152
  actualContent,
1122
1153
  React__default["default"].createElement(WrapperElement, { className: classNames__default["default"]('react-tooltip-arrow', coreStyles['arrow'], styles['arrow'], classNameArrow, noArrow && coreStyles['noArrow']), style: {
1123
- ...inlineArrowStyles,
1154
+ ...computedPosition.tooltipArrowStyles,
1124
1155
  background: arrowColor
1125
1156
  ? `linear-gradient(to right bottom, transparent 50%, ${arrowColor} 50%)`
1126
1157
  : undefined,
@@ -1132,11 +1163,6 @@ const TooltipContent = ({ content }) => {
1132
1163
  return React__default["default"].createElement("span", { dangerouslySetInnerHTML: { __html: content } });
1133
1164
  };
1134
1165
 
1135
- const cssSupports = (property, value) => {
1136
- const hasCssSupports = 'CSS' in window && 'supports' in window.CSS;
1137
- return hasCssSupports ? window.CSS.supports(property, value) : true;
1138
- };
1139
-
1140
1166
  const TooltipController = React__default["default"].forwardRef(({ id, anchorId, anchorSelect, content, html, render, className, classNameArrow, variant = 'dark', place = 'top', offset = 10, wrapper = 'div', children = null, events = ['hover'], openOnClick = false, positionStrategy = 'absolute', middlewares, delayShow = 0, delayHide = 0, float = false, hidden = false, noArrow = false, clickable = false, closeOnEsc = false, closeOnScroll = false, closeOnResize = false, openEvents, closeEvents, globalCloseEvents, imperativeModeOnly = false, style, position, isOpen, defaultIsOpen = false, disableStyleInjection = false, border, opacity, arrowColor, setIsOpen, afterShow, afterHide, role = 'tooltip', }, ref) => {
1141
1167
  const [tooltipContent, setTooltipContent] = React.useState(content);
1142
1168
  const [tooltipHtml, setTooltipHtml] = React.useState(html);
@@ -1257,10 +1283,12 @@ const TooltipController = React__default["default"].forwardRef(({ id, anchorId,
1257
1283
  if (styleInjectionRef.current === disableStyleInjection) {
1258
1284
  return;
1259
1285
  }
1286
+ /* c8 ignore start */
1260
1287
  {
1261
1288
  // eslint-disable-next-line no-console
1262
1289
  console.warn('[react-tooltip] Do not change `disableStyleInjection` dynamically.');
1263
1290
  }
1291
+ /* c8 ignore end */
1264
1292
  }, [disableStyleInjection]);
1265
1293
  React.useEffect(() => {
1266
1294
  if (typeof window !== 'undefined') {
@@ -1287,10 +1315,12 @@ const TooltipController = React__default["default"].forwardRef(({ id, anchorId,
1287
1315
  });
1288
1316
  }
1289
1317
  catch (_b) {
1318
+ /* c8 ignore start */
1290
1319
  {
1291
1320
  // eslint-disable-next-line no-console
1292
1321
  console.warn(`[react-tooltip] "${selector}" is not a valid CSS selector`);
1293
1322
  }
1323
+ /* c8 ignore end */
1294
1324
  }
1295
1325
  }
1296
1326
  const anchorById = document.querySelector(`[id='${anchorId}']`);
@@ -1331,6 +1361,7 @@ const TooltipController = React__default["default"].forwardRef(({ id, anchorId,
1331
1361
  };
1332
1362
  }, [anchorRefs, providerActiveAnchor, activeAnchor, anchorId, anchorSelect]);
1333
1363
  React.useEffect(() => {
1364
+ /* c8 ignore end */
1334
1365
  if (style === null || style === void 0 ? void 0 : style.border) {
1335
1366
  // eslint-disable-next-line no-console
1336
1367
  console.warn('[react-tooltip] Do not set `style.border`. Use `border` prop instead.');