feat: drag and resize lifecycle callbacks - #49
Merged
Conversation
Closes #47. Adds onDragStart, onDragEnd, onResizeStart and onResizeEnd, completing the set that Mantine core FloatingWindow exposes: start, change and end for both gestures. Window had only the two change callbacks, and those fire on every frame — so persisting a layout meant debouncing by hand, and there was no moment to pause an expensive child, show a snap guide, or record one undo entry per gesture. Additive: four optional props, no default or behaviour change. WindowProps extends BoxProps and WindowBaseProps but NOT ElementProps<'div'>, so the DOM event names onDragStart/onDragEnd were never part of the public type and nothing is being shadowed. Both are destructured out of the props spread, which is what keeps them off the root element. Two things the implementation had to get right: - The global mouseup/touchend listener calls handleDragEnd() AND handleResizeEnd() whenever either gesture is active, so a naive end callback would emit an unpaired onDragEnd during a plain resize. Each hook now checks its own in-flight ref before firing. A regression test covers it, and removing the guard makes that test fail with "Expected 0, Received 1". - onDragStart is emitted only past the existing bail-outs: a press on a resize handle, on an interactive child, or on a data-no-window-drag region does not open a gesture that would never close. Unmounting mid-gesture fires the matching end, so a consumer that paused something on start is never left paused. There is no keyboard drag or resize in this component, so core's caveat about its resize callbacks not firing for keyboard resize has no equivalent here. Verified with real pointer events in Chrome, not only jsdom: across one drag and one resize the continuous callbacks fired 20 times and the lifecycle ones twice, with state going idle → dragging → idle → resizing → idle and no unpaired end.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughThe ChangesGesture lifecycle callbacks
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Window
participant useMantineWindow
participant useWindowDrag
participant useWindowResize
participant Consumer
Window->>useMantineWindow: Pass lifecycle callback props
useMantineWindow->>useWindowDrag: Configure drag callbacks
useMantineWindow->>useWindowResize: Configure resize callbacks
useWindowDrag->>Consumer: Emit onDragStart and onDragEnd
useWindowResize->>Consumer: Emit onResizeStart and onResizeEnd
useMantineWindow->>useWindowDrag: End active drag on unmount
useMantineWindow->>useWindowResize: End active resize on unmount
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #47.
Adds
onDragStart,onDragEnd,onResizeStartandonResizeEnd, completing the set that Mantine coreFloatingWindowexposes — start, change and end for both gestures.Windowhad only the two change callbacks, and those fire on every frame of a gesture, so persisting a layout meant debouncing by hand and there was no moment to pause an expensive child, show a snap guide, or record a single undo entry per gesture.onDragStart(new)onPositionChangeonDragEnd(new)onResizeStart(new)onSizeChangeonResizeEnd(new)Why this is additive, checked rather than assumed
Four optional props, no default or behaviour change → minor.
onDragStart/onDragEndare real React DOM event names, so shadowing was the thing to check.WindowProps extends BoxProps, WindowBaseProps, StylesApiProps— notElementProps<div>— so those names were never in the public type and no existing usage changes meaning. They are destructured out of the props spread, which is what keeps them off the root element (leaking them would have wired the native HTML5 drag events).Two things the implementation had to get right
No cross-talk. The global
mouseup/touchendlistener callshandleDragEnd()andhandleResizeEnd()whenever either gesture is active. A naive end callback would therefore emit an unpairedonDragEndduring a plain resize. Each hook now checks its own in-flight ref before firing.I confirmed the test for this actually tests it: removing the guard makes it fail with
Expected number of calls: 0 / Received number of calls: 1. Restored, it passes.No start without a real gesture.
onDragStartis emitted only past the existing bail-outs — a press on a resize handle, on an interactive child (input,button,a[href], …) or on adata-no-window-dragregion does not open a gesture that would never close. Unmounting mid-gesture fires the matching end, so a consumer that paused something on start is never left paused.There is no keyboard drag or resize in this component, so core's caveat about its resize callbacks not firing for keyboard resize has no equivalent here. Stated in the docs so the difference is deliberate rather than silent.
Verified with real pointer events, not only jsdom
Driven in Chrome with
Input.dispatchMouseEvent:idledraggingdraggingidleresizingidleOne drag and one resize: 20 continuous calls against 2 lifecycle calls, every start paired with exactly one end.
What is in the diff
Window, threaded throughuseMantineWindowinto the drag and resize hooks, held in refs so an inline arrow from the consumer does not re-create the memoized pointer handlersonDragStarton an interactive child, end fired on unmount mid-gesture, and nothing fired on a clean unmountDrag and Resize Lifecycleplus adocs.mdxsection; the demo counts the two kinds of write side by side, which makes the point faster than proseTest plan
yarn test— 160 passingyarn build,yarn docgen(the 4 props appear in the Props table),yarn docs:buildwithdocs/.nextclearedSummary by CodeRabbit
New Features
onDragStart,onDragEnd,onResizeStart, andonResizeEnd.Bug Fixes