-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[charts-pro] Zoom pointer improvements #17000
base: master
Are you sure you want to change the base?
Conversation
…events-improvement
…events-improvement
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.
The new implementation seems to simplify the logic a lot! Great job!
I still don't understand some parts of the interaction listener hook, so left a few questions
...src/internals/plugins/corePlugins/useChartInteractionListener/useChartInteractionListener.ts
Outdated
Show resolved
Hide resolved
...src/internals/plugins/corePlugins/useChartInteractionListener/useChartInteractionListener.ts
Outdated
Show resolved
Hide resolved
...src/internals/plugins/corePlugins/useChartInteractionListener/useChartInteractionListener.ts
Show resolved
Hide resolved
const addMultipleInteractionListeners: AddMultipleInteractionListeners = React.useCallback( | ||
(interactions, callback) => { | ||
const cleanups = interactions.map((interaction) => | ||
// @ts-expect-error Overriding the type because the type of the callback is not inferred | ||
addInteractionListener(interaction, callback), | ||
); | ||
return { | ||
cleanup: () => cleanups.forEach((cleanup) => cleanup.cleanup()), | ||
}; | ||
}, | ||
[addInteractionListener], | ||
); |
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.
Not sure if this would work, but probably worth a shot:
const addMultipleInteractionListeners: AddMultipleInteractionListeners = React.useCallback( | |
(interactions, callback) => { | |
const cleanups = interactions.map((interaction) => | |
// @ts-expect-error Overriding the type because the type of the callback is not inferred | |
addInteractionListener(interaction, callback), | |
); | |
return { | |
cleanup: () => cleanups.forEach((cleanup) => cleanup.cleanup()), | |
}; | |
}, | |
[addInteractionListener], | |
); | |
const addMultipleInteractionListeners = React.useCallback<AddMultipleInteractionListeners>( | |
(interactions, callback) => { | |
const cleanups = interactions.map((interaction) => | |
addInteractionListener(interaction, callback), | |
); | |
return { | |
cleanup: () => cleanups.forEach((cleanup) => cleanup.cleanup()), | |
}; | |
}, | |
[addInteractionListener], | |
); |
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.
That resolves to the same type, so it doesn't work.
packages/x-charts/src/internals/plugins/featurePlugins/useChartVoronoi/useChartVoronoi.ts
Outdated
Show resolved
Hide resolved
Did a small test on iOS Safari and it seems to me that zooming in is too slow. Not in the sense of performance, but we might need to make it zoom a bit faster. In desktop it's working perfectly 👍 |
Thanks for checking, will debug |
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.
You have a bug with controlled zoom. The dragging get accelerated.
I could not figure why by just reading the code
Capture.video.du.2025-03-20.10-54-05.mp4
// scroll, we scroll exactly in the center of the svg | ||
// And we do it 200 times which is the lowest number to trigger a zoom where both A and D are not visible | ||
for (let i = 0; i < 200; i += 1) { | ||
fireEvent.wheel(svg, { deltaY: -1, clientX: 50, clientY: 50 }); | ||
} |
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.
It's surprising that it can not be done with delatY: -200
// scroll back | ||
for (let i = 0; i < 200; i += 1) { | ||
fireEvent.wheel(svg, { deltaY: 1, clientX: 50, clientY: 50 }); | ||
} |
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.
If as you said 200 is the limit to only see B and C, then just a few scroll back should be enough
const options = { | ||
wrapper: ({ children }: { children?: React.ReactNode }) => ( | ||
<div style={{ width: 100, height: 130 }}>{children}</div> | ||
), | ||
}; |
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.
What is the advantage of the wrapper compared to just providing the width/height to the chart props?
packages/x-charts-pro/src/internals/plugins/useChartProZoom/gestureHooks/usePanOnDrag.ts
Outdated
Show resolved
Hide resolved
// Drag event is triggered by mobile touch or mouse drag. | ||
const positionOnDragHandler = instance.addMultipleInteractionListeners( | ||
['move', 'drag'], |
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.
It looks like a hack to have the use-gesture
whereas the pointermove
event is exactly what we are looking for
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.
Seems like useGesture also support native events
https://use-gesture.netlify.app/docs/gestures/#native-event-handlers-in-react
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.
In this case it is probably better to use pointerMove
indeed
...ternals/plugins/corePlugins/useChartInteractionListener/useChartInteractionListener.types.ts
Show resolved
Hide resolved
cleanInteractionHandler.cleanup(); | ||
setInteractionHandler.cleanup(); |
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.
Effectively cleaner 👍
@@ -155,13 +158,15 @@ export const useChartCartesianAxis: ChartPlugin<UseChartCartesianAxisSignature<a | |||
return () => {}; | |||
} | |||
|
|||
const handleMouseClick = (event: MouseEvent) => { | |||
event.preventDefault(); | |||
const axisClickHandler = instance.addInteractionListener('drag', (state) => { |
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.
Same feeling about forcing the usage of use-gesture events for stuff they are not planned for
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.
Drag is meant to detect tap/click
as well.
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
76ab4f5
to
0edea79
Compare
xAxis={[ | ||
{ | ||
zoom: true, |
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.
If you'r lookig for what triggers the re-render. I'ts probably the xAxis
This pull request has conflicts, please resolve those before we can evaluate the pull request. |
Main change
Use
use-gesture
to recognise the gestures. Right now the lib is a bit slow to have updates, but it has some solid behaviour detection.Create a
interaction listener
plugin. This allows us to register events directly in the SVG.The current implementation migrates most of the interactions to the schema below.
Other changes
BarChartPro
,LineChartPro
, andScatterChartPro
fireEvent
/element.dispatchEvent
), but couldn't figure out a way to make it work.usePanOnDrag
,useZoomOnPinch
anduseZoomOnWheel
hooks