I would use a custom hook
(let [[ref show hide] (use-tooltip {:msg "My tooltip"})]
(ui/ButtonIcon
{:ref ref
:on-mouse-enter show
:on-mouse-leave hide}
"Click here"))
if you wanted to add the tooltip to the react tree as well rather than do some mutable DOM ops (not sure what kind of library you're using), you could return the element:
(let [[ref Tooltip show hide] (use-tooltip)]
(div
(ui/ButtonIcon
{:ref ref
:on-mouse-enter show
:on-mouse-leave hide}
"Click here")
(Tooltip {:msg "My Tooltip"})))
One thing about tooltips to keep in mind, that most likely you'll want to move them to the end of the DOM into a separate layer that sits above application root, so that tooltips are displayed above everything else, similar to popups. While it's not as frequently needed as for popups, tooltips could be cut off when displayed in elements with overflow: hidden, thus moving them to a separate layer would help.
In case of a separate layer a custom hook makes more sense. But since tooltip live in a diff part of the UI tree you'll need some kind of shared state (context or global state, etc)
This is a very good point about the separate layer. What trick do you use to position them relative to the “originating” element that triggered the tooltip? Somehow measure the absolute positioning of the “originating” element and position the tooltip relative to that?
Correct. Layout effect hook is a good fit for measuring position of an element.
We are using popper.js and its React integration with good results. A bit fiddly to setup initially but works like a charm.
This, plus a Portal to deal with adding the popups to their own div at the end of the body -- actually we have an OverlayContext so that nested popups work as expected.
Also using popper.js
and it’s userPopper
hook. Currently we don’t use portals yet, so far I’ve been able to fix overflow hidden issues with the different positioning strategy https://popper.js.org/docs/v2/constructors/#strategy
The Portal approach sounds nice in a way because it would mean the consumer does not need to place the popover element inside the DOM. EDIT: actually it seems like calls to createPortal should go into render
so I think the Portal approach would still require that.
@orestis I’m not quite sure what the OverlayContext
helps you with, do you mind expanding on that a bit?
Imagine you have a scrollable modal. You want the popup to be attached as the last child of that, so that it would naturally scroll when the content scrolls (otherwise it can be janky). Similar if you have a modal and a popup inside the modal, you want to use the same dom hierarchy so you can easily implement things like "on-click-outside, close the popup".
In case of scrolling, that depends. I think a common behaviour in OSs is to close tooltips when scrolling and block scrolling when a context menu is open
I was investigating native select components the other day. Native popups can even breakout of the browser chrome. Shame we have to reimplement a worse version :(
Worse in the ui integration sense I mean.