diff --git a/docs/docs/examples/events.mdx b/docs/docs/examples/events.mdx
index 037c72a9..0d4d0a4b 100644
--- a/docs/docs/examples/events.mdx
+++ b/docs/docs/examples/events.mdx
@@ -8,7 +8,9 @@ Events available in ReactTooltip component.
:::danger
-This has been deprecated. Use the `openOnClick` tooltip prop instead.
+This has been deprecated. Use `openOnClick`, or `openEvents`, `closeEvents`, and `globalCloseEvents` instead.
+
+See the [options page](../options.mdx#available-props) for more details.
:::
diff --git a/docs/docs/options.mdx b/docs/docs/options.mdx
index d91b7d5c..84415a68 100644
--- a/docs/docs/options.mdx
+++ b/docs/docs/options.mdx
@@ -91,8 +91,8 @@ import { Tooltip } from 'react-tooltip';
| ----------------------- | -------------------------------------- | -------- | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `className` | `string` | no | | | Class name to customize tooltip element. You can also use the default class `react-tooltip` which is set internally |
| `classNameArrow` | `string` | no | | | Class name to customize tooltip arrow element. You can also use the default class `react-tooltip-arrow` which is set internally |
-| `content` | `string` | no | | | Content to de displayed in tooltip (`html` prop is priorized over `content`) |
-| ~~`html`~~ | ~~`string`~~ | ~~no~~ | | | ~~HTML content to de displayed in tooltip~~
**DEPRECATED**
Use `children` or `render` instead |
+| `content` | `string` | no | | | Content to be displayed in tooltip (`html` prop is priorized over `content`) |
+| ~~`html`~~ | ~~`string`~~ | ~~no~~ | | | ~~HTML content to be displayed in tooltip~~
**DEPRECATED**
Use `children` or `render` instead |
| `render` | `function` | no | | | A function which receives a ref to the currently active anchor element and returns the content for the tooltip. Check the [examples](./examples/render.mdx) |
| `place` | `string` | no | `top` | `top` `top-start` `top-end` `right` `right-start` `right-end` `bottom` `bottom-start` `bottom-end` `left` `left-start` `left-end` | Position relative to the anchor element where the tooltip will be rendered (if possible) |
| `offset` | `number` | no | `10` | any `number` | Space between the tooltip element and anchor element (arrow not included in calculation) |
@@ -103,7 +103,7 @@ import { Tooltip } from 'react-tooltip';
| `wrapper` | HTML tag | no | `div` | `div` `span` `p` ... | Element wrapper for the tooltip container, can be `div`, `span`, `p` or any valid HTML tag |
| `children` | React node | no | `undefined` | valid React children | The tooltip children have lower priority compared to the `content` prop and the `data-tooltip-content` attribute. Useful for setting default content |
| ~~`events`~~ | ~~`string[]`~~ | ~~no~~ | ~~`hover`~~ | ~~`hover` `click`~~ | ~~Events to watch for when handling the tooltip state~~
**DEPRECATED**
Use `openOnClick` tooltip prop instead |
-| `openOnClick` | `boolean` | no | `false` | `true` `false` | Controls whether the tooltip should open when clicking (`true`) or hovering (`false`) the anchor element |
+| `openOnClick` | `boolean` | no | `false` | `true` `false` | When enabled, the tooltip will open on click instead of on hover. Use `openEvents`, `closeEvents`, and `globalCloseEvents` for more customization |
| `positionStrategy` | `string` | no | `absolute` | `absolute` `fixed` | The position strategy used for the tooltip. Set to `fixed` if you run into issues with `overflow: hidden` on the tooltip parent container |
| `delayShow` | `number` | no | | any `number` | The delay (in ms) before showing the tooltip |
| `delayHide` | `number` | no | | any `number` | The delay (in ms) before hiding the tooltip |
@@ -111,17 +111,20 @@ import { Tooltip } from 'react-tooltip';
| `hidden` | `boolean` | no | `false` | `true` `false` | Tooltip will not be shown |
| `noArrow` | `boolean` | no | `false` | `true` `false` | Tooltip arrow will not be shown |
| `clickable` | `boolean` | no | `false` | `true` `false` | Allow interaction with elements inside the tooltip. Useful when using buttons and inputs |
-| `closeOnEsc` | `boolean` | no | `false` | `true` `false` | Pressing escape key will close the tooltip |
-| `closeOnScroll` | `boolean` | no | `false` | `true` `false` | Scrolling will close the tooltip (for this to work, scroll element must be either the root html tag, the tooltip parent, or the anchor parent) |
-| `closeOnResize` | `boolean` | no | `false` | `true` `false` | Resizing the window will close the tooltip |
+| ~~`closeOnEsc`~~ | ~~`boolean`~~ | ~~no~~ | ~~`false`~~ | ~~`true` `false`~~ | ~~Pressing escape key will close the tooltip~~
**DEPRECATED**
Use `globalCloseEvents` instead. |
+| ~~`closeOnScroll`~~ | ~~`boolean`~~ | ~~no~~ | ~~`false`~~ | ~~`true` `false`~~ | ~~Scrolling will close the tooltip~~
**DEPRECATED**
Use `globalCloseEvents` instead. |
+| ~~`closeOnResize`~~ | ~~`boolean`~~ | ~~no~~ | ~~`false`~~ | ~~`true` `false`~~ | ~~Resizing the window will close the tooltip~~
**DEPRECATED**
Use `globalCloseEvents` instead. |
+| `openEvents` | `Record` | no | `mouseenter` `focus` | `mouseenter` `focus` `click` `dblclick` `mousedown` | Events to be listened on the anchor elements to open the tooltip |
+| `closeEvents` | `Record` | no | `mouseleave` `blur` | `mouseleave` `blur` `click` `dblclick` `mouseup` | Events to be listened on the anchor elements to close the tooltip |
+| `globalCloseEvents` | `Record` | no | | `escape` `scroll` `resize` `clickOutsideAnchor` | Global events to be listened to close the tooltip (`escape` closes on pressing `ESC`, `clickOutsideAnchor` is useful with click events on `openEvents`) |
| `style` | `CSSProperties` | no | | a CSS style object | Add inline styles directly to the tooltip |
| `position` | `{ x: number; y: number }` | no | | any `number` value for both `x` and `y` | Override the tooltip position on the DOM |
-| `isOpen` | `boolean` | no | handled by internal state | `true` `false` | The tooltip can be controlled or uncontrolled, this attribute can be used to handle show and hide tooltip outside tooltip (can be used **without** `setIsOpen`) |
+| `isOpen` | `boolean` | no | | `true` `false` | The tooltip can be controlled or uncontrolled, this attribute can be used to handle show and hide tooltip outside tooltip (can be used **without** `setIsOpen`) |
| `setIsOpen` | `function` | no | | | The tooltip can be controlled or uncontrolled, this attribute can be used to handle show and hide tooltip outside tooltip |
| `afterShow` | `function` | no | | | A function to be called after the tooltip is shown |
| `afterHide` | `function` | no | | | A function to be called after the tooltip is hidden |
| `middlewares` | `Middleware[]` | no | | array of valid `floating-ui` middlewares | Allows for advanced customization. Check the [`floating-ui` docs](https://floating-ui.com/docs/middleware) for more information |
-| `border` | `CSSProperties['border']` | no | | a CSS border style | Change the style of the tooltip border (including the arrow) |
-| `opacity` | `CSSProperties['opacity']` | no | `0.9` | a CSS opacity value | Change the opacity of the tooltip |
-| `arrowColor` | `CSSProperties['backgroundColor']` | no | | a CSS background color | Change color of the tooltip arrow |
-| `disableStyleInjection` | `boolean` | `'core'`
| no | `false` | `true` `false` `'core'` | Whether to disable automatic style injection. Do not set dynamically. Check the [styling page](./examples/styling#disabling-reacttooltip-css) for more details |
+| `border` | CSS border | no | | a CSS border style | Change the style of the tooltip border (including the arrow) |
+| `opacity` | CSS opacity | no | `0.9` | a CSS opacity value | Change the opacity of the tooltip |
+| `arrowColor` | CSS color | no | | a CSS background color | Change color of the tooltip arrow |
+| `disableStyleInjection` | `boolean` or `'core'` | no | `false` | `true` `false` `'core'` | Whether to disable automatic style injection. Do not set dynamically. Check the [styling page](./examples/styling#disabling-reacttooltip-css) for more details |
diff --git a/docs/docs/upgrade-guide/changelog-v4-v5.md b/docs/docs/upgrade-guide/changelog-v4-v5.md
index a63a48f3..599bbd61 100644
--- a/docs/docs/upgrade-guide/changelog-v4-v5.md
+++ b/docs/docs/upgrade-guide/changelog-v4-v5.md
@@ -50,9 +50,15 @@ If you run into any problems with the tooltip not updating after changes are mad
- [x] `float` - `boolean` - used to achieve V4's `effect="float"`
- [x] `hidden` - `boolean` - when set, the tooltip will not show
- [x] `render` - `function` - can be used to render dynamic content based on the active anchor element (check [the examples](../examples/render.mdx) for more details)
-- [x] `closeOnEsc` - `boolean` - when set, the tooltip will close after pressing the escape key
-- [x] `closeOnScroll` - `boolean` - when set, the tooltip will close when scrolling (similar to V4's `scrollHide`)
-- [x] `closeOnResize` - `boolean` - when set, the tooltip will close when resizing the window (same as V4's `resizeHide`)
+- [x] `closeOnEsc` - **DEPRECATED** - ~~`boolean` - when set, the tooltip will close after pressing the escape key~~
+- [x] `closeOnScroll` - **DEPRECATED** - ~~`boolean` - when set, the tooltip will close when scrolling (similar to V4's `scrollHide`)~~
+- [x] `closeOnResize` - **DEPRECATED** - ~~`boolean` - when set, the tooltip will close when resizing the window (same as V4's `resizeHide`)~~
+
+:::note
+
+Use `globalCloseEvents` instead of `closeOnEsc`, `closeOnScroll`, and `closeOnResize`. See the [options page](../options.mdx#available-props) for more details.
+
+:::
## `V4` props available in `V5`
@@ -78,10 +84,10 @@ If you run into any problems with the tooltip not updating after changes are mad
- [x] `delayHide` - also available on anchor element as `data-delay-hide`
- [ ] `delayUpdate` - can be implemented if requested
- [x] `delayShow` - also available on anchor element as `data-delay-show`
-- [ ] `event`
-- [ ] `eventOff`
+- [x] `event` - functionality changed and renamed to `openEvents`
+- [x] `eventOff` - functionality changed and renamed to `closeEvents`
- [ ] `isCapture`
-- [ ] `globalEventOff`
+- [x] `globalEventOff` - functionality changed and renamed to `globalCloseEvents`
- [ ] `getContent` - pass dynamic values to `content` instead
- [x] `afterShow`
- [x] `afterHide`
@@ -92,7 +98,7 @@ If you run into any problems with the tooltip not updating after changes are mad
- [x] `wrapper` - also available on anchor element as `data-tooltip-wrapper`
- [ ] `bodyMode`
- [x] `clickable`
-- [ ] `disableInternalStyle`
+- [x] `disableInternalStyle` - renamed to `disableStyleInjection`
### Detailed informations
diff --git a/src/App.tsx b/src/App.tsx
index fd85b9ad..c65155ac 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -88,7 +88,9 @@ function App() {
Tooltip content
diff --git a/src/components/Tooltip/Tooltip.tsx b/src/components/Tooltip/Tooltip.tsx
index 5413a652..77a471c6 100644
--- a/src/components/Tooltip/Tooltip.tsx
+++ b/src/components/Tooltip/Tooltip.tsx
@@ -8,7 +8,14 @@ import { getScrollParent } from 'utils/get-scroll-parent'
import { computeTooltipPosition } from 'utils/compute-positions'
import coreStyles from './core-styles.module.css'
import styles from './styles.module.css'
-import type { IPosition, ITooltip, PlacesType } from './TooltipTypes'
+import type {
+ AnchorCloseEvents,
+ AnchorOpenEvents,
+ GlobalCloseEvents,
+ IPosition,
+ ITooltip,
+ PlacesType,
+} from './TooltipTypes'
const Tooltip = ({
// props
@@ -34,6 +41,9 @@ const Tooltip = ({
closeOnEsc = false,
closeOnScroll = false,
closeOnResize = false,
+ openEvents,
+ closeEvents,
+ globalCloseEvents,
style: externalStyles,
position,
afterShow,
@@ -68,7 +78,49 @@ const Tooltip = ({
const [anchorsBySelect, setAnchorsBySelect] = useState([])
const mounted = useRef(false)
+ /**
+ * @todo Update when deprecated stuff gets removed.
+ */
const shouldOpenOnClick = openOnClick || events.includes('click')
+ const hasClickEvent =
+ shouldOpenOnClick || openEvents?.click || openEvents?.dblclick || openEvents?.mousedown
+ const actualOpenEvents: AnchorOpenEvents = openEvents
+ ? { ...openEvents }
+ : {
+ mouseenter: true,
+ focus: true,
+ click: false,
+ dblclick: false,
+ mousedown: false,
+ }
+ if (!openEvents && shouldOpenOnClick) {
+ Object.assign(actualOpenEvents, {
+ mouseenter: false,
+ focus: false,
+ click: true,
+ })
+ }
+ const actualCloseEvents: AnchorCloseEvents = closeEvents
+ ? { ...closeEvents }
+ : {
+ mouseleave: true,
+ blur: true,
+ click: false,
+ }
+ if (!closeEvents && shouldOpenOnClick) {
+ Object.assign(actualCloseEvents, {
+ mouseleave: false,
+ blur: false,
+ })
+ }
+ const actualGlobalCloseEvents: GlobalCloseEvents = globalCloseEvents
+ ? { ...globalCloseEvents }
+ : {
+ escape: closeOnEsc || false,
+ scroll: closeOnScroll || false,
+ resize: closeOnResize || false,
+ clickOutsideAnchor: hasClickEvent || false,
+ }
/**
* useLayoutEffect runs before useEffect,
@@ -266,13 +318,6 @@ const Tooltip = ({
lastFloatPosition.current = mousePosition
}
- const handleClickTooltipAnchor = (event?: Event) => {
- handleShowTooltip(event)
- if (delayHide) {
- handleHideTooltipDelayed()
- }
- }
-
const handleClickOutsideAnchors = (event: MouseEvent) => {
const anchorById = document.querySelector(`[id='${anchorId}']`)
const anchors = [anchorById, ...anchorsBySelect]
@@ -371,13 +416,13 @@ const Tooltip = ({
const anchorScrollParent = getScrollParent(activeAnchor)
const tooltipScrollParent = getScrollParent(tooltipRef.current)
- if (closeOnScroll) {
+ if (actualGlobalCloseEvents.scroll) {
window.addEventListener('scroll', handleScrollResize)
anchorScrollParent?.addEventListener('scroll', handleScrollResize)
tooltipScrollParent?.addEventListener('scroll', handleScrollResize)
}
let updateTooltipCleanup: null | (() => void) = null
- if (closeOnResize) {
+ if (actualGlobalCloseEvents.resize) {
window.addEventListener('resize', handleScrollResize)
} else if (activeAnchor && tooltipRef.current) {
updateTooltipCleanup = autoUpdate(
@@ -398,29 +443,63 @@ const Tooltip = ({
}
handleShow(false)
}
-
- if (closeOnEsc) {
+ if (actualGlobalCloseEvents.escape) {
window.addEventListener('keydown', handleEsc)
}
+ if (actualGlobalCloseEvents.clickOutsideAnchor) {
+ window.addEventListener('click', handleClickOutsideAnchors)
+ }
+
const enabledEvents: { event: string; listener: (event?: Event) => void }[] = []
- if (shouldOpenOnClick) {
- window.addEventListener('click', handleClickOutsideAnchors)
- enabledEvents.push({ event: 'click', listener: handleClickTooltipAnchor })
- } else {
- enabledEvents.push(
- { event: 'mouseenter', listener: debouncedHandleShowTooltip },
- { event: 'mouseleave', listener: debouncedHandleHideTooltip },
- { event: 'focus', listener: debouncedHandleShowTooltip },
- { event: 'blur', listener: debouncedHandleHideTooltip },
- )
- if (float) {
- enabledEvents.push({
- event: 'mousemove',
- listener: handleMouseMove,
- })
+ const handleClickOpenTooltipAnchor = (event?: Event) => {
+ if (show) {
+ return
}
+ handleShowTooltip(event)
+ }
+ const handleClickCloseTooltipAnchor = () => {
+ if (!show) {
+ return
+ }
+ handleHideTooltip()
+ }
+
+ const regularEvents = ['mouseenter', 'mouseleave', 'focus', 'blur']
+ const clickEvents = ['click', 'dblclick', 'mousedown', 'mouseup']
+
+ Object.entries(actualOpenEvents).forEach(([event, enabled]) => {
+ if (!enabled) {
+ return
+ }
+ if (regularEvents.includes(event)) {
+ enabledEvents.push({ event, listener: debouncedHandleShowTooltip })
+ } else if (clickEvents.includes(event)) {
+ enabledEvents.push({ event, listener: handleClickOpenTooltipAnchor })
+ } else {
+ // never happens
+ }
+ })
+
+ Object.entries(actualCloseEvents).forEach(([event, enabled]) => {
+ if (!enabled) {
+ return
+ }
+ if (regularEvents.includes(event)) {
+ enabledEvents.push({ event, listener: debouncedHandleHideTooltip })
+ } else if (clickEvents.includes(event)) {
+ enabledEvents.push({ event, listener: handleClickCloseTooltipAnchor })
+ } else {
+ // never happens
+ }
+ })
+
+ if (float) {
+ enabledEvents.push({
+ event: 'mousemove',
+ listener: handleMouseMove,
+ })
}
const handleMouseEnterTooltip = () => {
@@ -431,7 +510,9 @@ const Tooltip = ({
handleHideTooltip()
}
- if (clickable && !shouldOpenOnClick) {
+ if (clickable && !hasClickEvent) {
+ // used to keep the tooltip open when hovering content.
+ // not needed if using click events.
tooltipRef.current?.addEventListener('mouseenter', handleMouseEnterTooltip)
tooltipRef.current?.addEventListener('mouseleave', handleMouseLeaveTooltip)
}
@@ -443,23 +524,23 @@ const Tooltip = ({
})
return () => {
- if (closeOnScroll) {
+ if (actualGlobalCloseEvents.scroll) {
window.removeEventListener('scroll', handleScrollResize)
anchorScrollParent?.removeEventListener('scroll', handleScrollResize)
tooltipScrollParent?.removeEventListener('scroll', handleScrollResize)
}
- if (closeOnResize) {
+ if (actualGlobalCloseEvents.resize) {
window.removeEventListener('resize', handleScrollResize)
} else {
updateTooltipCleanup?.()
}
- if (shouldOpenOnClick) {
+ if (actualGlobalCloseEvents.clickOutsideAnchor) {
window.removeEventListener('click', handleClickOutsideAnchors)
}
- if (closeOnEsc) {
+ if (actualGlobalCloseEvents.escape) {
window.removeEventListener('keydown', handleEsc)
}
- if (clickable && !shouldOpenOnClick) {
+ if (clickable && !hasClickEvent) {
tooltipRef.current?.removeEventListener('mouseenter', handleMouseEnterTooltip)
tooltipRef.current?.removeEventListener('mouseleave', handleMouseLeaveTooltip)
}
@@ -479,8 +560,11 @@ const Tooltip = ({
rendered,
anchorRefs,
anchorsBySelect,
- closeOnEsc,
- events,
+ // the effect uses the `actual*Events` objects, but this should work
+ openEvents,
+ closeEvents,
+ globalCloseEvents,
+ shouldOpenOnClick,
])
useEffect(() => {
diff --git a/src/components/Tooltip/TooltipTypes.d.ts b/src/components/Tooltip/TooltipTypes.d.ts
index 41760079..5084b38c 100644
--- a/src/components/Tooltip/TooltipTypes.d.ts
+++ b/src/components/Tooltip/TooltipTypes.d.ts
@@ -49,6 +49,27 @@ export interface IPosition {
y: number
}
+export type AnchorOpenEvents = {
+ mouseenter?: boolean
+ focus?: boolean
+ click?: boolean
+ dblclick?: boolean
+ mousedown?: boolean
+}
+export type AnchorCloseEvents = {
+ mouseleave?: boolean
+ blur?: boolean
+ click?: boolean
+ dblclick?: boolean
+ mouseup?: boolean
+}
+export type GlobalCloseEvents = {
+ escape?: boolean
+ scroll?: boolean
+ resize?: boolean
+ clickOutsideAnchor?: boolean
+}
+
export interface ITooltip {
className?: string
classNameArrow?: string
@@ -81,6 +102,9 @@ export interface ITooltip {
closeOnEsc?: boolean
closeOnScroll?: boolean
closeOnResize?: boolean
+ openEvents?: AnchorOpenEvents
+ closeEvents?: AnchorCloseEvents
+ globalCloseEvents?: GlobalCloseEvents
style?: CSSProperties
position?: IPosition
isOpen?: boolean
diff --git a/src/components/TooltipController/TooltipController.tsx b/src/components/TooltipController/TooltipController.tsx
index 729e77b8..2176df9b 100644
--- a/src/components/TooltipController/TooltipController.tsx
+++ b/src/components/TooltipController/TooltipController.tsx
@@ -41,6 +41,9 @@ const TooltipController = ({
closeOnEsc = false,
closeOnScroll = false,
closeOnResize = false,
+ openEvents,
+ closeEvents,
+ globalCloseEvents,
style,
position,
isOpen,
@@ -330,6 +333,9 @@ const TooltipController = ({
closeOnEsc,
closeOnScroll,
closeOnResize,
+ openEvents,
+ closeEvents,
+ globalCloseEvents,
style,
position,
isOpen,
diff --git a/src/components/TooltipController/TooltipControllerTypes.d.ts b/src/components/TooltipController/TooltipControllerTypes.d.ts
index e0568907..deeb2afc 100644
--- a/src/components/TooltipController/TooltipControllerTypes.d.ts
+++ b/src/components/TooltipController/TooltipControllerTypes.d.ts
@@ -9,6 +9,9 @@ import type {
PositionStrategy,
IPosition,
Middleware,
+ AnchorOpenEvents,
+ AnchorCloseEvents,
+ GlobalCloseEvents,
} from 'components/Tooltip/TooltipTypes'
export interface ITooltipController {
@@ -33,7 +36,7 @@ export interface ITooltipController {
wrapper?: WrapperType
children?: ChildrenType
/**
- * @deprecated Use `openOnClick` instead.
+ * @deprecated Use `openOnClick` or `openEvents`/`closeEvents` instead.
*/
events?: EventsType[]
openOnClick?: boolean
@@ -46,17 +49,29 @@ export interface ITooltipController {
noArrow?: boolean
clickable?: boolean
/**
- * @todo refactor to `hideOnEsc` for naming consistency
+ * @deprecated Use `globalCloseEvents={{ escape: true }}` instead.
*/
closeOnEsc?: boolean
/**
- * @todo refactor to `hideOnScroll` for naming consistency
+ * @deprecated Use `globalCloseEvents={{ scroll: true }}` instead.
*/
closeOnScroll?: boolean
/**
- * @todo refactor to `hideOnResize` for naming consistency
+ * @deprecated Use `globalCloseEvents={{ resize: true }}` instead.
*/
closeOnResize?: boolean
+ /**
+ * @description The events to be listened on anchor elements to open the tooltip.
+ */
+ openEvents?: AnchorOpenEvents
+ /**
+ * @description The events to be listened on anchor elements to close the tooltip.
+ */
+ closeEvents?: AnchorCloseEvents
+ /**
+ * @description The global events listened to close the tooltip.
+ */
+ globalCloseEvents?: GlobalCloseEvents
style?: CSSProperties
position?: IPosition
isOpen?: boolean