-
Notifications
You must be signed in to change notification settings - Fork 187
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
tip mark + pointer interaction #1527
Conversation
Here’s an example of the facet z-order problem with this approach: Another interesting problem is that each facet now listens independently for pointer events, so it’s possible to get multiple tooltips from adjacent facets! I’m not sure if this is an urgent problem, but we could certainly add some cross-facet coordination to prevent this from happening. (It can be rare depending on the chosen maxRadius, and it might be useful in some cases.) There’s also a funny problem with the sticky implementation now, in that you can have multiple marks driven by the pointer interaction, and the sticky modality is independent for each mark (based on |
Crosshairs! Screen.Recording.2023-05-07.at.10.01.41.AM.mov |
I reorganized the to-dos and would like to proceed with this direction instead of #1304. There are a few things still to figure out here, but I think the biggest two are (1) more coordination of the pointer interaction, such that (1a) only one point can be focused at a time across facets, and (1b) if there are multiple pointer interactions they are required to be defined consistently; and (2) some mechanism that lets the tip mark draw atop other facets. One possibility for (2) is that we transpose the hierarchy of marks and facets, such that z-order is preserved across facets, rather than only within facets. And it’s possible that (1b) isn’t a requirement at all… |
Fun experiment using color-mix to apply the stroke channel to the drop-shadow filter: Screen.Recording.2023-05-08.at.9.16.50.AM.mov |
There are a couple things still to-do above, but I think this is ready for an earnest look. |
easy to add to auto 😅 #1532 Screen.Recording.2023-05-09.at.6.50.41.PM.mov |
I would like to get this released soon. I feel the biggest blocking issue is the shorthand syntax, and more specifically, how we might drive the tip mark from an existing mark’s channels rather than duplicating a mark’s definition for the purpose of applying tips. Repeating the channel specifications definitely works, as the tests demonstrate, but it can be difficult to use particularly in conjunction with Plot’s many implicit behaviors (e.g., implicit stack transform, implicit tuples). My hope is we can somehow describe that the tip mark is derived from another mark, and that, somehow, the tip mark can therefore “inherit” the channels from this other mark (including even channels created by an initializer). Related to this, the tip mark is unusual in that channels control the contents of the tip and also affect the appearance of the tip. For example, if you’re applying the tip to a mark that uses the fill channel, you might want to display the value in the tip; but, you probably don’t want to apply that color as the fill of the tip itself. We ideally want these channels to be independent: the values that we display in the tip should be configurable separately from the aesthetics of the tip. So maybe the tip mark doesn’t inherit the channels of some other mark, but instead has a way of reading the channels of another mark (and maybe there’s a restriction that the two marks have the same data and faceting, so they’re parallel)? Perhaps the context that is passed to mark.render could expose a method for reading the channels of another mark? I’ll experiment with this tomorrow. |
Color swatches! 🟦 🟧 🟥 Screen.Recording.2023-05-10.at.12.14.00.PM.mov |
Looks good to me! LGTY, @Fil? 🚢 I suggest we followup with documentation (including the TypeScript interfaces). |
Playing with it, and everything's working great. Still unclear to me:
|
Are you saying those are blockers? I suggest we figure those out later as there are many possible solutions and I don’t think we’ll get stuck. (The tipDotFilter test demonstrates one strategy for putting the tips last even when there are multiple tips.) |
Definitely not blockers. Let's rock :) |
Ooo la la 😍 testing asap |
Thank you for not binding the tooltip position to the mouse position. I've always hated the aesthetic of tooltips moving around with the mouse. No other UI tooltips follow the mouse, and plots' shouldn't either. ❤️ |
I am unreasonably excited about these! Great work. Just tried them out and noticed an interesting consequence of the current rules, mentioning it in case it helps decide whether to keep this behavior or maybe change it: ![]() You can cause the same data field to appear multiple times in the tooltip if more than one channel is driven by it. The above picture is the result of evaluating Plot.dot([{ x: 1 }], {
x: "x",
y: "x",
tip: true
}).plot({ width: 100, height: 100 }) using the tip-option branch. |
Yes, @yurivish. The tip can’t currently tell which channels are redundant. The tip has access to the materialized channel’s array of values, but not the channel’s definition. Even if it had access to the definition, if in your example you wrote the definitions as Instead of trying to detect such a case, the current plan is to give the user control over which channels appear in the tip; by default we’ll try to show (almost) everything. If the problem of redundant channels occurs more often than expected then we can try to detect the common forms, but I’d rather not add the complexity if possible. |
Ah, yeah, that's a very good point. Makes sense – thanks for explaining! |
Any way we could get an |
* tip mark * render transform! * pointer interaction * port mbostock/tooltip fixes * improved tip & pointer * simplify * better facets; stable anchor * px, py; crosshairs * crosshairs composite mark * crosshair singular * transpose facets and marks * prefer top-left * only pass index.f[xyi] if faceted * [xy][12]; don’t apply stroke as text fill * tip + hexbin test * optimize faceting by swapping transforms * renderTransform instead of _render * only one pointer across facets * suppress other facets when sticky * prevent duplicate ARIA when faceting * isolate state per-pointer * fix crash with one-dimensional tip * if px, default x to null; same for py * tidier crosshair options * use channel label if available * only separating space if named * crosshair initializer fixes * tidier crosshair options * remove to-do * tip + dodge test * cleaner facet translate * crosshair text using channel alias * preTtier * fix transform for [xy][12] * p[xy] precedence * pointer comments * tip textAnchor * more tip options * more tip options, comments * bandwidth offset * fix for multi-facet, multi-pointer * fix dimensions * tip side anchors * tipped helper * raster nearest * color swatch; fix f[xy]; no tip aesthetic channels * multi-line, summary ariaLabel * tidier formatting * tidier crosshair * project p[xy], too * centroid test * geoCentroid test * shorthand extra channels * no pointer-specific state * revert Mark interface change * remove dead code
This is an alternative to #1304 that decouples the display mark (tip) from interaction (pointer). The tip mark no longer has any built-in interaction and can be used to display static tips. The pointer interaction, meanwhile, handles pointer events and re-renders a downstream mark (often but not always the tip mark) similar to a dynamic select or filter transform. Re-rendering is accomplished using a new render transform mechanism: a render option which allows the specified function to wrap mark.render.
As with the tooltip mark in the previous PR, the tip mark here uses getBBox to compute text metrics so that the path surrounding the tip text exactly fits the text. This requires the tip mark to render (partially) asynchronously, since plots and marks are initially rendered detached; the tip mark using a microtask (Promise.resolve) or requestAnimationFrame to defer rendering. I would prefer to render the tooltip synchronously, but this would require either approximating text metrics or synchronously inserting the Plot into the document at an arbitrary location, so partial asynchronous rendering feels like the least worst option.
To-do, pointer interaction:
(pass a next option)To-do, tip mark:
or HTML, anchored to a pointTo-do, crosshairs:
To-do, other:
Optional:
Fixes #4.
Fixes #443.