-
Notifications
You must be signed in to change notification settings - Fork 185
Tooltips! #1304
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
Tooltips! #1304
Conversation
Another point I forgot to mention: the tooltip mark will need a way to access the unscaled channel values, too. Currently marks only receive the scaled values during render. |
2e13e18
to
ce6b5e9
Compare
Thank you @mkfreeman @Fil @mbostock for the work on this in the posted notebooks. Today I ran into an issue where it was difficult to build a on:hover interaction and was wondering if the Plot API was a bit friendlier when they will be developed/built. The design feedback that I wanted to pass on: With this Pull request, in a histogram on hover, is it possible to reduce the opacity of all the bars except the one on which is mouse pointer is over? Wanted to seek advise on how would one go about doing this. |
55f7ca2
to
0bcd9f1
Compare
This has been rebased against the latest main, and now requires even fewer core changes—just the new ownerSVGElement property on the context. 😌 Remaining work centers around the contents and appearance of the tooltip, the various anchor modes, and shorthand syntax for deriving tooltips from an existing mark. |
f9087ff
to
371dcfa
Compare
a8b6a7c
to
b1e03e7
Compare
@@ -61,7 +61,7 @@ export type ChannelName = | |||
* An object literal of channel definitions. This is also used to represent | |||
* materialized channel states after mark initialization. | |||
*/ | |||
export type Channels = {[key in ChannelName]?: Channel}; | |||
export type Channels = Record<string, Channel>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve changed this here to allow specifying custom channels for tooltips, but we might want to make this change in more places… and we might even want to change the ChannelName type to include string so that it’s always allowed, but use the never hack to still let VS Code autocomplete known channel names. Although, that makes it less likely we will detect typos.
@Fil Can you do an initial review here? I want to know if we have sufficiently captured the requirements for the first version or if there are unanticipated requirements that could delay this landing. |
We’ll need to figure out some hint so that the tooltip can be smarter with x and y when x1 and x2 or y1 and y2 are available. Sometimes we want to express an extent (for example with binX, it’d be nice to see the upper and lower bound of the bin in x rather than the midpoint) and other times we want a length (for example with stackY, we want to see the difference between y2 and y1, not the midpoint y). The latter—fixing the tooltip with the stack transform—is especially important because it is misleading to show the midpoint, especially in the streamgraph case. I wonder whether the stack transform can populate a hint that y1 and y2 represent lengths (say using the channel.source hint, and suppressing y2)? |
Screen.Recording.2023-05-05.at.5.12.34.PM.mov |
* mobile tooltip on tap test: https://observablehq.com/@fil/tt-tests * pointermove on pointerenter * ignore pointerleave for non-mouse --------- Co-authored-by: Mike Bostock <[email protected]>
Given the numerous requests we have received related to this feature, and knowing that there is a lot of demand for customization here, I do wonder whether the initial implementation of this feature could be decoupled a bit (a bit like how axes were initially built-in, but then later we redesigned them to be explicit marks affording greater flexibility). In particular I’d like to try implementing the (non-interactive) display of the tooltip as a standard mark. I imagine using it to annotate a specific point in the plot (statically and manually), or perhaps multiple points at the same time (say the extrema). If we had such a mark, perhaps we could have a separate “point” interaction that picks a specific point of interest using pointer events. And then lastly some way to tie that pointing interaction with the tooltip display mark (having Plot re-render the mark automatically in response to the interaction saying that the focused point has changed). A lot of details to figure out, but since we’re going to need to do some of this for interaction anyway, it might be worth experimenting even if it means delaying the initial release of tooltips? |
Closing in favor of #1527, which does almost everything this PR does, but is capable of much more, too. |
This is a first pass at the foundation for tooltips (hover-based assistance for reading values out of plots). I’m focusing to start on the semantics with the expectation that we will devise better shorthand syntax for declaring tooltips in the future (e.g., adding tooltip: true to an existing mark rather than having to repeat everything on a separate tooltip mark).
The goal in this prototype is to demonstrate that we can use a pointermove event listener to determine the closest data point to the pointer based on the scaled position channels (a subset of x, y, fx, and fy).
There are a few tricky things here. First, the tooltip mark needs to know the owner SVG element so that it can register an event listener at the SVG level, rather than within each facet, and without obscuring other pointer-based interaction such as text selection; this is done by adding a context.ownerSVGElement property. Second, the tooltip mark needs to “multiplex”: like other marks, it gets rendered separately for each facet, but we only want to show a single tooltip across facets. This is done using a mark-scoped WeakMap, but perhaps with additional support from Plot we could make this more elegant. In a sense the tooltip mark isn’t a mark because it has no (conventional) visual representation, but it does have channels that are accessed by the event listener.
To-do:
AutomaticallyClick to switch between “sticky” and “free” modeor after a delay?Optional:
Above, the idea of peruse mode is when you’re moving the pointer around and you want the tooltip to update instantly, whereas focus mode is when the pointer seems to be pointing at a particular point of interest, and you want to be able to move the pointer into the tooltip to select text. I’m thinking that you start in focus mode (where the tooltip is stable), but during the first 250ms (or whatever), you can either move the pointer into the tooltip, or you move it elsewhere, and in the latter case you switch to peruse mode and the tooltip will not receive pointer events.
Fixes #4 (eventually).