leeoniya / uPlot

📈 A small, fast chart for time series, lines, areas, ohlc & bars
MIT License
8.51k stars 371 forks source link

show/hide [unlocked] cursor on plot leave/enter #67

Closed leeoniya closed 4 years ago

leeoniya commented 4 years ago

(and emit a cursor toggle event)

this is a followup to https://github.com/leeoniya/uPlot/issues/63#issuecomment-562868349

will probably need to wait on #66 to minimize API turbulence.

silverwind commented 4 years ago

Also need to think about what to do with the legend when the cursor is hidden. Either hide it outright (resulting in a resize of the .uPlot area) or just making it invisible (visibilty: hidden) to prevent the resize.

leeoniya commented 4 years ago

yeah it'd have to be visibilty: hidden but then with current default legend placement, you'd end up with a bunch of awkward empty space below the chart by default. the only way to avoid this would be to overlay the legend, but that's a non-starter. changing opacity might work as a middle ground 😕

hiding or restyling the legend is also poor UX because it also serves as series toggle controls and focus on hover: https://leeoniya.github.io/uPlot/demos/focus-cursor.html

you'd end up with some pretty crazy, rapidly-changing UI states as you hover, toggle, enter, leave. i would personally hate it.

that's the problem with adding UI features or defaults. with every extra feature, you get an exponential growth in requests to customize every detail.

i'd like to maybe see what would be needed to do this customization externally. there's already a plan to add legend & cursor toggling to the API, as well as enter/leave events, so i think it should be possible to do this outside the core at some point.

silverwind commented 4 years ago

Yeah it's certainly a burden to support features and the interactions between them. I'd understand if you decide to not deal with cursor hiding at all, it's all possible via CSS by the user and they probably know best what to do for each circumstance. So I guess you should better close this 😉

leeoniya commented 4 years ago

in general, i actually do want to have auto cursor toggle on enter/leave behind some default cursor.show: "auto" or cursor.auto: true opt. i never particularly cared for starting it at 50,50, which is only marginally better than the previous 0,0.

since the legend serves to both identify the series as well as display the cursor values, it makes no sense to ever hide it. we just need to display -- for values when the cursor is toggled off (which would be part of the cursor toggle api).

so imo this is still very much on the table.

silverwind commented 4 years ago

Ah yes, I'd actually like to be able to define the starting position of the cursor too. In my case, it would be on the last data point of the time series.

leeoniya commented 4 years ago

how do you envision this working with autohiding? since the initial state will always be off as there's no way to assert if the cursor is over a plot prior to a mousemove.

silverwind commented 4 years ago

Initial cursor position would only make sense with autohiding off, so either

cursor: {initial: {x, y}, autohide: false}

or

cursor: {initial: undefined, autohide: true}
leeoniya commented 4 years ago

either that or just leave this out of opts, in favor of using some .setCursor(x, y, onOff) api inside init().

though there's definitely elegance in doing it declaratively.

silverwind commented 4 years ago

setCursor sounds good to me. I prefer one way of doing something. Maybe have a small example of doing it during init.

leeoniya commented 4 years ago

it looks like .moveCursor(left, top) was all that was necessary. the rest is easily done in app-space.

moving the cursor to negative coordinates has the effect of hiding it, and that heuristic is used internally to clear the legend values. the default cursor position on initial render is now -10,-10.

i've updated the tooltips demo to show/hide the cursor and tips on mouseenter/mouseleave events that are bound in init(): https://leeoniya.github.io/uPlot/demos/tooltips.html

the whole thing is obvious and minimal:

https://github.com/leeoniya/uPlot/commit/0c6a6394c4c6adaa2d5f403744b340efa0e555ae#diff-726893fe0f5471adf7d2372e701101ff

i haven't tested at all how it interacts with sync, so let me know - i probably overlooked something or other.

EDIT: u.cursor is now exposed so that u.cursor.locked can be checked before hiding/showing the cursor and tooltips. i've moved all the relevant live state into u.cursor and as a result it's possible to now pass cursor.left and cursor.top via opts for initial cursor positioning.

leeoniya commented 4 years ago

as i'm exposing more and more state, it's becoming less clear what params to pass to event handlers.

now that the live internal top/left can be read from u.cursor, i can simply expose the internal .idx, too and then cursormove would only take a single self param.

i'm thinking of doing the same for legend and exposing u.legend and all current values in it.

any extra params in the handlers would be used for transient things or internal vars not exposed by the instance.

this way the param list does not need to grow and i don't need to think hard about argument order or duplication of what's already readable.

in contrast with thr dom event model, which creates event objects and fires the handlers before the dom is touched, uplots events fire after things have happened and live state is already updated. this has the advantage of being concise and performant but the drawback of non-cancelable events, so things like cursormove can only implement additional effects.

i pushed this to another branch. i gotta say it's starting to grow on me: https://github.com/leeoniya/uPlot/commit/5716304548965ad443dd618d30c2c2e7e24072d6

leeoniya commented 4 years ago

i added another off-by-default series to https://leeoniya.github.io/uPlot/demos/tooltips.html and i think it now addresses all concerns in this issue.

leeoniya commented 4 years ago

@silverwind cursor hiding on mouseleave is now the default behavior. i've dropped the explicit .setCursor() call from the demo.

https://github.com/leeoniya/uPlot/commit/e4da0ba214a5f6c6e0580aba826f535ee78659ec