Skip to content

Add the clickable prop, similar to v4 #895

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/docs/getting-started.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,32 @@ import { Tooltip } from 'react-tooltip'
<Tooltip anchorId="my-anchor-element" />
```

### Clickable tooltip

By default, you can't interact with elements inside the tooltip. To allow for proper usage of elements such as buttons and inputs, use the `clickable` prop.

```jsx
<TooltipAnchor id="not-clickable">◕‿‿◕</TooltipAnchor>
<Tooltip anchorId="not-clickable">
<button>You can't click me :(</button>
</Tooltip>
<TooltipAnchor id="clickable">◕‿‿◕</TooltipAnchor>
<Tooltip anchorId="clickable" clickable>
<button>You can click me!</button>
</Tooltip>
```

<div style={{ display: 'flex', columnGap: '8px', justifyContent: 'center', paddingTop: '36px' }}>
<TooltipAnchor id="not-clickable">◕‿‿◕</TooltipAnchor>
<Tooltip anchorId="not-clickable">
<button>You can't click me :(</button>
</Tooltip>
<TooltipAnchor id="clickable">◕‿‿◕</TooltipAnchor>
<Tooltip anchorId="clickable" clickable>
<button>You can click me!</button>
</Tooltip>
</div>

### Styling

If you want a styled tooltip, don't forget to add the style file `import 'react-tooltip/dist/react-tooltip.css'`.
Expand Down
1 change: 1 addition & 0 deletions docs/docs/options.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import 'react-tooltip/dist/react-tooltip.css'
| delayHide | number | false | | any `number` | tooltip hide will be delayed in miliseconds by the amount of value |
| float | boolean | false | `false` | `true` `false` | tooltip will follow the mouse position when it moves inside the anchor element (same as V4's `effect="float"`) |
| noArrow | boolean | false | `false` | `true` `false` | tooltip arrow will not be shown |
| clickable | boolean | false | `false` | `true` `false` | allow interaction with elements inside the tooltip. useful when using buttons and inputs |
| style | CSSProperties | false | | any React inline style | add styles directly to the component by `style` attribute |
| position | `{ x: number; y: number }` | false | | any `number` value for both `x` and `y` | override the tooltip position on the viewport |
| isOpen | boolen | false | 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`) |
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/upgrade-guide/changelog-v4-v5.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ V4 was a great react tooltip component but was built a few years ago, he was bui
- [ ] `resizeHide` - if requested, can be implemented later
- [x] `wrapper` - also available on anchor element as `data-tooltip-wrapper`
- [ ] `bodyMode`
- [ ] `clickable` - use controlled state to keep tooltip open
- [x] `clickable`
- [ ] `disableInternalStyle` - CSS will be a separate file and can be imported or not

### Detailed informations
Expand Down
5 changes: 4 additions & 1 deletion src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ function WithProviderMinimal() {
<button>Minimal 2</button>
</TooltipWrapper>
</p>
<Tooltip />
<Tooltip clickable>
<button>button</button>
</Tooltip>
</section>
)
}
Expand Down Expand Up @@ -159,6 +161,7 @@ function App() {
content={`This is an on click tooltip (x:${position.x},y:${position.y})`}
events={['click']}
position={position}
positionStrategy="fixed"
/>
</div>
</div>
Expand Down
34 changes: 30 additions & 4 deletions src/components/Tooltip/Tooltip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ const Tooltip = ({
delayShow = 0,
delayHide = 0,
float = false,
noArrow,
noArrow = false,
clickable = false,
style: externalStyles,
position,
// props handled by controller
Expand All @@ -43,6 +44,7 @@ const Tooltip = ({
const lastFloatPosition = useRef<IPosition | null>(null)
const { anchorRefs, setActiveAnchor: setProviderActiveAnchor } = useTooltip()(id)
const [activeAnchor, setActiveAnchor] = useState<React.RefObject<HTMLElement>>({ current: null })
const hoveringTooltip = useRef(false)

const handleShow = (value: boolean) => {
if (setIsOpen) {
Expand All @@ -62,14 +64,17 @@ const Tooltip = ({
}, delayShow)
}

const handleHideTooltipDelayed = () => {
const handleHideTooltipDelayed = (delay = delayHide) => {
if (tooltipHideDelayTimerRef.current) {
clearTimeout(tooltipHideDelayTimerRef.current)
}

tooltipHideDelayTimerRef.current = setTimeout(() => {
if (hoveringTooltip.current) {
return
}
handleShow(false)
}, delayHide)
}, delay)
}

const handleShowTooltip = (event?: Event) => {
Expand All @@ -93,7 +98,10 @@ const Tooltip = ({
}

const handleHideTooltip = () => {
if (delayHide) {
if (clickable) {
// allow time for the mouse to reach the tooltip, in case there's a gap
handleHideTooltipDelayed(delayHide || 50)
} else if (delayHide) {
handleHideTooltipDelayed()
} else {
handleShow(false)
Expand Down Expand Up @@ -207,6 +215,19 @@ const Tooltip = ({
}
}

const handleMouseEnterTooltip = () => {
hoveringTooltip.current = true
}
const handleMouseLeaveTooltip = () => {
hoveringTooltip.current = false
handleHideTooltip()
}

if (clickable) {
tooltipRef.current?.addEventListener('mouseenter', handleMouseEnterTooltip)
tooltipRef.current?.addEventListener('mouseleave', handleMouseLeaveTooltip)
}

enabledEvents.forEach(({ event, listener }) => {
elementRefs.forEach((ref) => {
ref.current?.addEventListener(event, listener)
Expand All @@ -215,6 +236,10 @@ const Tooltip = ({

return () => {
window.removeEventListener('click', handleClickOutsideAnchor)
if (clickable) {
tooltipRef.current?.removeEventListener('mouseenter', handleMouseEnterTooltip)
tooltipRef.current?.removeEventListener('mouseleave', handleMouseLeaveTooltip)
}
enabledEvents.forEach(({ event, listener }) => {
elementRefs.forEach((ref) => {
ref.current?.removeEventListener(event, listener)
Expand Down Expand Up @@ -297,6 +322,7 @@ const Tooltip = ({
className={classNames('react-tooltip', styles['tooltip'], styles[variant], className, {
[styles['show']]: hasContentOrChildren && !calculatingPosition && (isOpen || show),
[styles['fixed']]: positionStrategy === 'fixed',
[styles['clickable']]: clickable,
})}
style={{ ...externalStyles, ...inlineStyles }}
ref={tooltipRef}
Expand Down
1 change: 1 addition & 0 deletions src/components/Tooltip/TooltipTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export interface ITooltip {
delayHide?: number
float?: boolean
noArrow?: boolean
clickable?: boolean
style?: CSSProperties
position?: IPosition
isOpen?: boolean
Expand Down
4 changes: 4 additions & 0 deletions src/components/Tooltip/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
display: none;
}

.clickable {
pointer-events: auto;
}

.show {
visibility: visible;
opacity: var(--rt-opacity);
Expand Down
4 changes: 3 additions & 1 deletion src/components/TooltipController/TooltipController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ const TooltipController = ({
delayShow = 0,
delayHide = 0,
float = false,
noArrow,
noArrow = false,
clickable = false,
style,
position,
isOpen,
Expand Down Expand Up @@ -184,6 +185,7 @@ const TooltipController = ({
delayHide: tooltipDelayHide,
float: tooltipFloat,
noArrow,
clickable,
style,
position,
isOpen,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export interface ITooltipController {
delayHide?: number
float?: boolean
noArrow?: boolean
clickable?: boolean
style?: CSSProperties
position?: IPosition
isOpen?: boolean
Expand Down