Add dragSnapToCursor prop to motion components#3723
Conversation
Adds a new `dragSnapToCursor` prop on drag-enabled motion components so
the snap-to-cursor behavior previously only reachable via
`dragControls.start(event, { snapToCursor: true })` is also available
declaratively. This lets `dragSnapToCursor` work alongside
`dragSnapToOrigin` on the same component.
Fixes #2677
Greptile SummaryThis PR exposes the existing
Confidence Score: 4/5Safe to merge — the change is small, additive, and builds on existing well-tested infrastructure. The implementation is functionally correct: passing The Important Files Changed
Sequence DiagramsequenceDiagram
participant User
participant DOM
participant VisualElementDragControls
participant PanSession
User->>DOM: pointerdown
DOM->>VisualElementDragControls: pointerdown handler fires
VisualElementDragControls->>VisualElementDragControls: "getProps() → { drag, dragListener, dragSnapToCursor }"
alt "drag && dragListener && !isClickingTextInputChild"
VisualElementDragControls->>VisualElementDragControls: "start(event, { snapToCursor: dragSnapToCursor })"
VisualElementDragControls->>PanSession: new PanSession(originEvent, handlers)
alt "snapToCursor === true"
PanSession->>VisualElementDragControls: onSessionStart → snapToCursor(point)
end
end
Reviews (1): Last reviewed commit: "Add dragSnapToCursor prop to motion comp..." | Re-trigger Greptile |
| /** | ||
| * If `true`, the element will snap so that the cursor is centered on it | ||
| * when a drag gesture starts. This is the equivalent of passing | ||
| * `{ snapToCursor: true }` to a `dragControls.start()` call but works | ||
| * for any drag-enabled motion component. | ||
| * | ||
| * ```jsx | ||
| * <motion.div drag dragSnapToCursor /> | ||
| * ``` | ||
| */ | ||
| dragSnapToCursor?: boolean |
There was a problem hiding this comment.
The JSDoc states this prop "works for any drag-enabled motion component," but it is silently ignored when
dragListener={false} is set — a pattern commonly used together with dragControls. A user who combines dragSnapToCursor with dragControls + dragListener={false} will see no snap behaviour and get no warning.
| /** | |
| * If `true`, the element will snap so that the cursor is centered on it | |
| * when a drag gesture starts. This is the equivalent of passing | |
| * `{ snapToCursor: true }` to a `dragControls.start()` call but works | |
| * for any drag-enabled motion component. | |
| * | |
| * ```jsx | |
| * <motion.div drag dragSnapToCursor /> | |
| * ``` | |
| */ | |
| dragSnapToCursor?: boolean | |
| /** | |
| * If `true`, the element will snap so that the cursor is centered on it | |
| * when a drag gesture starts. This is the equivalent of passing | |
| * `{ snapToCursor: true }` to a `dragControls.start()` call but works | |
| * for any drag-enabled motion component. | |
| * | |
| * Note: has no effect when `dragListener` is `false`, in which case pass | |
| * `{ snapToCursor: true }` directly to `dragControls.start()`. | |
| * | |
| * ```jsx | |
| * <motion.div drag dragSnapToCursor /> | |
| * ``` | |
| */ | |
| dragSnapToCursor?: boolean |
Summary
dragSnapToCursorprop to drag-enabled motion components, exposing the snap-to-cursor behavior previously only available viadragControls.start(event, { snapToCursor: true }).dragSnapToCursoranddragSnapToOriginbe declared together on the same component (the previousdragControls-only path forced you to forgo declarativedragSnapToOrigin).Cause
VisualElementDragControls.addListenersattached apointerdownhandler that calledthis.start(event)with no options, so the prop had no way to influence behavior —snapToCursorwas only ever set when the user passed it explicitly todragControls.start.Fix
Read
dragSnapToCursorfrom props in thepointerdownhandler and forward it as{ snapToCursor: dragSnapToCursor }tothis.start(event, ...). Added a public type field onMotionNodeDraggableOptionsso the prop is accepted.Test plan
startis invoked with{ snapToCursor: true }when the prop is set, and without the option when it isn't.yarn buildsucceeds.yarn test— all 778 unit tests pass.Fixes #2677