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.
@@ -5,7 +5,7 @@
5
5
  * @copyright ReactTooltip Team
6
6
  * @license MIT
7
7
  */
8
- import React, { createContext, useState, useCallback, useMemo, useContext, useRef, useEffect, useLayoutEffect, useImperativeHandle } from 'react';
8
+ import React, { useLayoutEffect, useEffect, createContext, useState, useCallback, useMemo, useContext, useRef, useImperativeHandle } from 'react';
9
9
  import { arrow, computePosition, offset, flip, shift, autoUpdate } from '@floating-ui/dom';
10
10
  import classNames from 'classnames';
11
11
 
@@ -96,6 +96,96 @@ function removeStyle({ type = 'base', id = REACT_TOOLTIP_BASE_STYLES_ID, } = {})
96
96
  injected[type] = false;
97
97
  }
98
98
 
99
+ const computeTooltipPosition = async ({ elementReference = null, tooltipReference = null, tooltipArrowReference = null, place = 'top', offset: offsetValue = 10, strategy = 'absolute', middlewares = [
100
+ offset(Number(offsetValue)),
101
+ flip({
102
+ fallbackAxisSideDirection: 'start',
103
+ }),
104
+ shift({ padding: 5 }),
105
+ ], border, }) => {
106
+ if (!elementReference) {
107
+ // elementReference can be null or undefined and we will not compute the position
108
+ // eslint-disable-next-line no-console
109
+ // console.error('The reference element for tooltip was not defined: ', elementReference)
110
+ return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
111
+ }
112
+ if (tooltipReference === null) {
113
+ return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
114
+ }
115
+ const middleware = middlewares;
116
+ if (tooltipArrowReference) {
117
+ middleware.push(arrow({ element: tooltipArrowReference, padding: 5 }));
118
+ return computePosition(elementReference, tooltipReference, {
119
+ placement: place,
120
+ strategy,
121
+ middleware,
122
+ }).then(({ x, y, placement, middlewareData }) => {
123
+ var _a, _b;
124
+ const styles = { left: `${x}px`, top: `${y}px`, border };
125
+ /* c8 ignore start */
126
+ const { x: arrowX, y: arrowY } = (_a = middlewareData.arrow) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
127
+ const staticSide = (_b = {
128
+ top: 'bottom',
129
+ right: 'left',
130
+ bottom: 'top',
131
+ left: 'right',
132
+ }[placement.split('-')[0]]) !== null && _b !== void 0 ? _b : 'bottom';
133
+ /* c8 ignore end */
134
+ const borderSide = border && {
135
+ borderBottom: border,
136
+ borderRight: border,
137
+ };
138
+ let borderWidth = 0;
139
+ if (border) {
140
+ const match = `${border}`.match(/(\d+)px/);
141
+ if (match === null || match === void 0 ? void 0 : match[1]) {
142
+ borderWidth = Number(match[1]);
143
+ }
144
+ else {
145
+ /**
146
+ * this means `border` was set without `width`,
147
+ * or non-px value (such as `medium`, `thick`, ...)
148
+ */
149
+ borderWidth = 1;
150
+ }
151
+ }
152
+ /* c8 ignore start */
153
+ const arrowStyle = {
154
+ left: arrowX != null ? `${arrowX}px` : '',
155
+ top: arrowY != null ? `${arrowY}px` : '',
156
+ right: '',
157
+ bottom: '',
158
+ ...borderSide,
159
+ [staticSide]: `-${4 + borderWidth}px`,
160
+ };
161
+ /* c8 ignore end */
162
+ return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle, place: placement };
163
+ });
164
+ }
165
+ return computePosition(elementReference, tooltipReference, {
166
+ placement: 'bottom',
167
+ strategy,
168
+ middleware,
169
+ }).then(({ x, y, placement }) => {
170
+ const styles = { left: `${x}px`, top: `${y}px` };
171
+ return { tooltipStyles: styles, tooltipArrowStyles: {}, place: placement };
172
+ });
173
+ };
174
+
175
+ const cssSupports = (property, value) => {
176
+ const hasCssSupports = 'CSS' in window && 'supports' in window.CSS;
177
+ return hasCssSupports ? window.CSS.supports(property, value) : true;
178
+ };
179
+
180
+ const cssTimeToMs = (time) => {
181
+ const match = time.match(/^([\d.]+)(ms|s)$/);
182
+ if (!match) {
183
+ return 0;
184
+ }
185
+ const [, amount, unit] = match;
186
+ return Number(amount) * (unit === 'ms' ? 1 : 1000);
187
+ };
188
+
99
189
  /* eslint-disable @typescript-eslint/no-explicit-any */
100
190
  /**
101
191
  * This function debounce the received function
@@ -114,7 +204,7 @@ const debounce = (func, wait, immediate) => {
114
204
  };
115
205
  if (immediate && !timeout) {
116
206
  /**
117
- * there's not need to clear the timeout
207
+ * there's no need to clear the timeout
118
208
  * since we expect it to resolve and set `timeout = null`
119
209
  */
120
210
  func.apply(this, args);
@@ -128,15 +218,70 @@ const debounce = (func, wait, immediate) => {
128
218
  }
129
219
  };
130
220
  debounced.cancel = () => {
221
+ /* c8 ignore start */
131
222
  if (!timeout) {
132
223
  return;
133
224
  }
225
+ /* c8 ignore end */
134
226
  clearTimeout(timeout);
135
227
  timeout = null;
136
228
  };
137
229
  return debounced;
138
230
  };
139
231
 
232
+ const isObject = (object) => {
233
+ return object !== null && !Array.isArray(object) && typeof object === 'object';
234
+ };
235
+ const deepEqual = (object1, object2) => {
236
+ if (object1 === object2) {
237
+ return true;
238
+ }
239
+ if (Array.isArray(object1) && Array.isArray(object2)) {
240
+ if (object1.length !== object2.length) {
241
+ return false;
242
+ }
243
+ return object1.every((val, index) => deepEqual(val, object2[index]));
244
+ }
245
+ if (Array.isArray(object1) !== Array.isArray(object2)) {
246
+ return false;
247
+ }
248
+ if (!isObject(object1) || !isObject(object2)) {
249
+ return object1 === object2;
250
+ }
251
+ const keys1 = Object.keys(object1);
252
+ const keys2 = Object.keys(object2);
253
+ if (keys1.length !== keys2.length) {
254
+ return false;
255
+ }
256
+ return keys1.every((key) => deepEqual(object1[key], object2[key]));
257
+ };
258
+
259
+ const isScrollable = (node) => {
260
+ if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
261
+ return false;
262
+ }
263
+ const style = getComputedStyle(node);
264
+ return ['overflow', 'overflow-x', 'overflow-y'].some((propertyName) => {
265
+ const value = style.getPropertyValue(propertyName);
266
+ return value === 'auto' || value === 'scroll';
267
+ });
268
+ };
269
+ const getScrollParent = (node) => {
270
+ if (!node) {
271
+ return null;
272
+ }
273
+ let currentParent = node.parentElement;
274
+ while (currentParent) {
275
+ if (isScrollable(currentParent)) {
276
+ return currentParent;
277
+ }
278
+ currentParent = currentParent.parentElement;
279
+ }
280
+ return document.scrollingElement || document.documentElement;
281
+ };
282
+
283
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
284
+
140
285
  const DEFAULT_TOOLTIP_ID = 'DEFAULT_TOOLTIP_ID';
141
286
  const DEFAULT_CONTEXT_DATA = {
142
287
  anchorRefs: new Set(),
@@ -235,115 +380,6 @@ const TooltipWrapper = ({ tooltipId, children, className, place, content, html,
235
380
  return (React.createElement("span", { ref: anchorRef, className: classNames('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));
236
381
  };
237
382
 
238
- const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
239
-
240
- const isScrollable = (node) => {
241
- if (!(node instanceof HTMLElement || node instanceof SVGElement)) {
242
- return false;
243
- }
244
- const style = getComputedStyle(node);
245
- return ['overflow', 'overflow-x', 'overflow-y'].some((propertyName) => {
246
- const value = style.getPropertyValue(propertyName);
247
- return value === 'auto' || value === 'scroll';
248
- });
249
- };
250
- const getScrollParent = (node) => {
251
- if (!node) {
252
- return null;
253
- }
254
- let currentParent = node.parentElement;
255
- while (currentParent) {
256
- if (isScrollable(currentParent)) {
257
- return currentParent;
258
- }
259
- currentParent = currentParent.parentElement;
260
- }
261
- return document.scrollingElement || document.documentElement;
262
- };
263
-
264
- const computeTooltipPosition = async ({ elementReference = null, tooltipReference = null, tooltipArrowReference = null, place = 'top', offset: offsetValue = 10, strategy = 'absolute', middlewares = [
265
- offset(Number(offsetValue)),
266
- flip({
267
- fallbackAxisSideDirection: 'start',
268
- }),
269
- shift({ padding: 5 }),
270
- ], border, }) => {
271
- if (!elementReference) {
272
- // elementReference can be null or undefined and we will not compute the position
273
- // eslint-disable-next-line no-console
274
- // console.error('The reference element for tooltip was not defined: ', elementReference)
275
- return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
276
- }
277
- if (tooltipReference === null) {
278
- return { tooltipStyles: {}, tooltipArrowStyles: {}, place };
279
- }
280
- const middleware = middlewares;
281
- if (tooltipArrowReference) {
282
- middleware.push(arrow({ element: tooltipArrowReference, padding: 5 }));
283
- return computePosition(elementReference, tooltipReference, {
284
- placement: place,
285
- strategy,
286
- middleware,
287
- }).then(({ x, y, placement, middlewareData }) => {
288
- var _a, _b;
289
- const styles = { left: `${x}px`, top: `${y}px`, border };
290
- const { x: arrowX, y: arrowY } = (_a = middlewareData.arrow) !== null && _a !== void 0 ? _a : { x: 0, y: 0 };
291
- const staticSide = (_b = {
292
- top: 'bottom',
293
- right: 'left',
294
- bottom: 'top',
295
- left: 'right',
296
- }[placement.split('-')[0]]) !== null && _b !== void 0 ? _b : 'bottom';
297
- const borderSide = border && {
298
- borderBottom: border,
299
- borderRight: border,
300
- };
301
- let borderWidth = 0;
302
- if (border) {
303
- const match = `${border}`.match(/(\d+)px/);
304
- if (match === null || match === void 0 ? void 0 : match[1]) {
305
- borderWidth = Number(match[1]);
306
- }
307
- else {
308
- /**
309
- * this means `border` was set without `width`, or non-px value
310
- */
311
- borderWidth = 1;
312
- }
313
- }
314
- const arrowStyle = {
315
- left: arrowX != null ? `${arrowX}px` : '',
316
- top: arrowY != null ? `${arrowY}px` : '',
317
- right: '',
318
- bottom: '',
319
- ...borderSide,
320
- [staticSide]: `-${4 + borderWidth}px`,
321
- };
322
- return { tooltipStyles: styles, tooltipArrowStyles: arrowStyle, place: placement };
323
- });
324
- }
325
- return computePosition(elementReference, tooltipReference, {
326
- placement: 'bottom',
327
- strategy,
328
- middleware,
329
- }).then(({ x, y, placement }) => {
330
- const styles = { left: `${x}px`, top: `${y}px` };
331
- return { tooltipStyles: styles, tooltipArrowStyles: {}, place: placement };
332
- });
333
- };
334
-
335
- const cssTimeToMs = (time) => {
336
- const match = time.match(/^([\d.]+)(m?s?)$/);
337
- if (!match) {
338
- return 0;
339
- }
340
- const [, amount, unit] = match;
341
- if (unit !== 's' && unit !== 'ms') {
342
- return 0;
343
- }
344
- return Number(amount) * (unit === 'ms' ? 1 : 1000);
345
- };
346
-
347
383
  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"};
348
384
 
349
385
  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"};
@@ -359,9 +395,11 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
359
395
  const tooltipShowDelayTimerRef = useRef(null);
360
396
  const tooltipHideDelayTimerRef = useRef(null);
361
397
  const missedTransitionTimerRef = useRef(null);
362
- const [actualPlacement, setActualPlacement] = useState(place);
363
- const [inlineStyles, setInlineStyles] = useState({});
364
- const [inlineArrowStyles, setInlineArrowStyles] = useState({});
398
+ const [computedPosition, setComputedPosition] = useState({
399
+ tooltipStyles: {},
400
+ tooltipArrowStyles: {},
401
+ place,
402
+ });
365
403
  const [show, setShow] = useState(false);
366
404
  const [rendered, setRendered] = useState(false);
367
405
  const [imperativeOptions, setImperativeOptions] = useState(null);
@@ -519,6 +557,11 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
519
557
  }, transitionShowDelay + 25);
520
558
  }
521
559
  }, [show]);
560
+ const handleComputedPosition = (newComputedPosition) => {
561
+ setComputedPosition((oldComputedPosition) => deepEqual(oldComputedPosition, newComputedPosition)
562
+ ? oldComputedPosition
563
+ : newComputedPosition);
564
+ };
522
565
  const handleShowTooltipDelayed = (delay = delayShow) => {
523
566
  if (tooltipShowDelayTimerRef.current) {
524
567
  clearTimeout(tooltipShowDelayTimerRef.current);
@@ -611,13 +654,7 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
611
654
  middlewares,
612
655
  border,
613
656
  }).then((computedStylesData) => {
614
- if (Object.keys(computedStylesData.tooltipStyles).length) {
615
- setInlineStyles(computedStylesData.tooltipStyles);
616
- }
617
- if (Object.keys(computedStylesData.tooltipArrowStyles).length) {
618
- setInlineArrowStyles(computedStylesData.tooltipArrowStyles);
619
- }
620
- setActualPlacement(computedStylesData.place);
657
+ handleComputedPosition(computedStylesData);
621
658
  });
622
659
  };
623
660
  const handlePointerMove = (event) => {
@@ -710,13 +747,7 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
710
747
  // invalidate computed positions after remount
711
748
  return;
712
749
  }
713
- if (Object.keys(computedStylesData.tooltipStyles).length) {
714
- setInlineStyles(computedStylesData.tooltipStyles);
715
- }
716
- if (Object.keys(computedStylesData.tooltipArrowStyles).length) {
717
- setInlineArrowStyles(computedStylesData.tooltipArrowStyles);
718
- }
719
- setActualPlacement(computedStylesData.place);
750
+ handleComputedPosition(computedStylesData);
720
751
  });
721
752
  }, [
722
753
  show,
@@ -1059,7 +1090,7 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
1059
1090
  }
1060
1091
  }, [delayShow]);
1061
1092
  const actualContent = (_a = imperativeOptions === null || imperativeOptions === void 0 ? void 0 : imperativeOptions.content) !== null && _a !== void 0 ? _a : content;
1062
- const canShow = show && Object.keys(inlineStyles).length > 0;
1093
+ const canShow = show && Object.keys(computedPosition.tooltipStyles).length > 0;
1063
1094
  useImperativeHandle(forwardRef, () => ({
1064
1095
  open: (options) => {
1065
1096
  if (options === null || options === void 0 ? void 0 : options.anchorSelect) {
@@ -1091,10 +1122,10 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
1091
1122
  }
1092
1123
  },
1093
1124
  activeAnchor,
1094
- place: actualPlacement,
1125
+ place: computedPosition.place,
1095
1126
  isOpen: Boolean(rendered && !hidden && actualContent && canShow),
1096
1127
  }));
1097
- return rendered && !hidden && actualContent ? (React.createElement(WrapperElement, { id: id, role: role, className: classNames('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) => {
1128
+ return rendered && !hidden && actualContent ? (React.createElement(WrapperElement, { id: id, role: role, className: classNames('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) => {
1098
1129
  if (missedTransitionTimerRef.current) {
1099
1130
  clearTimeout(missedTransitionTimerRef.current);
1100
1131
  }
@@ -1106,12 +1137,12 @@ content, contentWrapperRef, isOpen, defaultIsOpen = false, setIsOpen, activeAnch
1106
1137
  afterHide === null || afterHide === void 0 ? void 0 : afterHide();
1107
1138
  }, style: {
1108
1139
  ...externalStyles,
1109
- ...inlineStyles,
1140
+ ...computedPosition.tooltipStyles,
1110
1141
  opacity: opacity !== undefined && canShow ? opacity : undefined,
1111
1142
  }, ref: tooltipRef },
1112
1143
  actualContent,
1113
1144
  React.createElement(WrapperElement, { className: classNames('react-tooltip-arrow', coreStyles['arrow'], styles['arrow'], classNameArrow, noArrow && coreStyles['noArrow']), style: {
1114
- ...inlineArrowStyles,
1145
+ ...computedPosition.tooltipArrowStyles,
1115
1146
  background: arrowColor
1116
1147
  ? `linear-gradient(to right bottom, transparent 50%, ${arrowColor} 50%)`
1117
1148
  : undefined,
@@ -1123,11 +1154,6 @@ const TooltipContent = ({ content }) => {
1123
1154
  return React.createElement("span", { dangerouslySetInnerHTML: { __html: content } });
1124
1155
  };
1125
1156
 
1126
- const cssSupports = (property, value) => {
1127
- const hasCssSupports = 'CSS' in window && 'supports' in window.CSS;
1128
- return hasCssSupports ? window.CSS.supports(property, value) : true;
1129
- };
1130
-
1131
1157
  const TooltipController = React.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) => {
1132
1158
  const [tooltipContent, setTooltipContent] = useState(content);
1133
1159
  const [tooltipHtml, setTooltipHtml] = useState(html);
@@ -1248,10 +1274,12 @@ const TooltipController = React.forwardRef(({ id, anchorId, anchorSelect, conten
1248
1274
  if (styleInjectionRef.current === disableStyleInjection) {
1249
1275
  return;
1250
1276
  }
1277
+ /* c8 ignore start */
1251
1278
  {
1252
1279
  // eslint-disable-next-line no-console
1253
1280
  console.warn('[react-tooltip] Do not change `disableStyleInjection` dynamically.');
1254
1281
  }
1282
+ /* c8 ignore end */
1255
1283
  }, [disableStyleInjection]);
1256
1284
  useEffect(() => {
1257
1285
  if (typeof window !== 'undefined') {
@@ -1278,10 +1306,12 @@ const TooltipController = React.forwardRef(({ id, anchorId, anchorSelect, conten
1278
1306
  });
1279
1307
  }
1280
1308
  catch (_b) {
1309
+ /* c8 ignore start */
1281
1310
  {
1282
1311
  // eslint-disable-next-line no-console
1283
1312
  console.warn(`[react-tooltip] "${selector}" is not a valid CSS selector`);
1284
1313
  }
1314
+ /* c8 ignore end */
1285
1315
  }
1286
1316
  }
1287
1317
  const anchorById = document.querySelector(`[id='${anchorId}']`);
@@ -1322,6 +1352,7 @@ const TooltipController = React.forwardRef(({ id, anchorId, anchorSelect, conten
1322
1352
  };
1323
1353
  }, [anchorRefs, providerActiveAnchor, activeAnchor, anchorId, anchorSelect]);
1324
1354
  useEffect(() => {
1355
+ /* c8 ignore end */
1325
1356
  if (style === null || style === void 0 ? void 0 : style.border) {
1326
1357
  // eslint-disable-next-line no-console
1327
1358
  console.warn('[react-tooltip] Do not set `style.border`. Use `border` prop instead.');