-
Notifications
You must be signed in to change notification settings - Fork 367
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
Fix top-left resizing - account for new position when calling back onResize #136
Conversation
Hi @conor-kelleher. Thanks a lot for this contribution! I'd like for this to make it into 2.0. #133 contributed a test suite into the application, it would be really great if you could rebase on |
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.
Implementation works, just some minor bits on the code & comments. Thanks!
@@ -182,6 +186,27 @@ export default class Resizable extends React.Component<Props, State> { | |||
const canDragX = (this.props.axis === 'both' || this.props.axis === 'x') && ['n', 's'].indexOf(axis) === -1; | |||
const canDragY = (this.props.axis === 'both' || this.props.axis === 'y') && ['e', 'w'].indexOf(axis) === -1; | |||
|
|||
// Check if handle's position has moved since last resizeHandler call - adjust deltas accordingly | |||
if (this.draggingNode == null && e.target instanceof HTMLElement) { |
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.
Is saving this ref necessary (rather than simply using e.target
)?
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.
I originally had it just using e.target
on each callback but because the listener is calling back to any movement in the document body, e.target
will not always be the same value during a drag. It will be whatever element the mouse is currently over. As you can see, using this value directly causes jumpy behaviour when the mouse enters different elements:
,
Hence the need to persist a reference to the same element through multiple callbacks
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.
lib/Resizable.js
Outdated
deltaY += deltaTopSinceLast / this.props.transformScale; | ||
} | ||
} | ||
this.lastTop = handleRect.top; |
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.
Can this be a single property (this.lastHandleRect = handleRect
)?
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.
Yep, it can. I've rolled that in now
lib/Resizable.js
Outdated
const deltaLeftSinceLast = handleRect.left - this.lastLeft; | ||
const deltaTopSinceLast = handleRect.top - this.lastTop; | ||
|
||
if (canDragX && axis.indexOf('w') > -1) { |
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 allowable values for axis are:
['s', 'w', 'e', 'n', 'sw', 'nw', 'se', 'ne']
So this could be more clear as axis[axis.length - 1] === 'w'
, similar to https://github.com/STRML/react-resizable/blob/a1fff416339df13b15b23044b026ebbe376a2b9d/lib/Resizable.js#L97.
This of course would be a great use case for String#endsWith()
but we are trying to avoid prototype methods that aren't supported in IE.
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.
Good point, I've made this change now
lib/Resizable.js
Outdated
if (canDragX && axis.indexOf('w') > -1) { | ||
deltaX += deltaLeftSinceLast / this.props.transformScale; | ||
} | ||
if(canDragY && axis.indexOf('n') > -1) { |
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.
See above comment, axis[0] === 'n'
will work here.
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.
Very true, much cleaner this way 👌
lib/Resizable.js
Outdated
@@ -182,6 +186,27 @@ export default class Resizable extends React.Component<Props, State> { | |||
const canDragX = (this.props.axis === 'both' || this.props.axis === 'x') && ['n', 's'].indexOf(axis) === -1; | |||
const canDragY = (this.props.axis === 'both' || this.props.axis === 'y') && ['e', 'w'].indexOf(axis) === -1; | |||
|
|||
// Check if handle's position has moved since last resizeHandler call - adjust deltas accordingly |
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'd be great if you could expand upon this comment with some of the detail in this PR: that is, what happens, why it only affects n
and w
directions, and how this fixes it.
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.
Fleshed these comments out a good bit. Hopefully they're clear enough now 🤞
I've made the requested changes to the code. Need to add some tests before re-requesting a review |
Hey @conor-kelleher Great PR 🎉 . Really looking forward to its release. |
Hey @STRML, sorry for the delay getting this fixed up. I've made the suggested changes, added a suite of tests for the new functionality and added a new set of examples to the demo, keeping the absolutely positioned ones away from the inline ones: Just note: after pulling the changes you pushed to master preparing for v2, I couldn't get the example to run since the webpack config was referencing a non-existent file |
lib/Resizable.js
Outdated
@@ -119,7 +119,10 @@ export default class Resizable extends React.Component<Props, ResizableState> { | |||
deltaY += deltaTopSinceLast / this.props.transformScale; | |||
} | |||
} | |||
this.lastHandleRect = handleRect; |
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 that it matters much, but why not just save the whole rect?
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.
I hit an issue with flow upon trying to cast an exact object to an inexact type. I'm new to flow so couldnt find a proper way around this, just got it working by taking the exact data I needed. I'm open to changes though
Any updates on this fix? Ran into this same issue! |
I have the same issue. @STRML would this fix available soon? |
This works great. Thanks for the contribution! |
Is there some code sandbox for the final solution ? |
Any plans on updating the official demo to demonstrate this. Anyone have any codesandbox for this, I tried the |
Can someone help me? Why when positioning my resize handles on top/left/top-left the behavior is so weird like i show at the GIF? Is something im doing wrong or i need to implement the right behavior by my self? I expected that the grid would follow my mouse but it expand to the other side... Thanks for the attention |
What's wrong?
Hey, love the library. Works really well for the most part but, as a few different issues have mentioned, it has some serious issues when resizing from the left or the top, using anything other than top-left positioning. The following open issues are just some that have documented the issue:
I've put together a minimal codesandbox to demonstrate the issue as well:
https://codesandbox.io/s/react-playground-vwlpk?file=/index.js
Samples from the above codesandbox:
Dragging to the right: smooth
Dragging to the left: jumpy/flickering
The Cause?
The problem is that on subsequent calls of the
resizeHandler
function, it's reporting a resize based on thedeltaX
anddeltaY
of the movement. However, if calling back through theonResize
prop results in a position change (for instance, if you are using right-based absolute positioning as in my example above, or if you are manually updating the top/left positions based on the resize), then the drag handle itself might be moved by the re-render, so the next call toresizeHandler
will report that the handle has moved, even though this is a prop-driven movement rather than a mouse-event driven one.The solution
I've added a few lines to track a reference to the drag handle element in the document and in every call of
resizeHandler
, I'm comparing the new top and left of the element on the page with the previous values. A difference in these values will be a result of a positioning change, and so we want to account for this in ourdeltaX
/deltaY
values.If dragging any handle with an
'n'
in it and its position has changed, then our event's reporteddeltaY
value will either be larger or smaller than we expect it to be (because this value will include the position change). Adding this y-position differential to the delta value accounts for this and keeps the callback only reporting on movement that was caused by the mouse event. The same goes for the x-axis and any handle with a'w'
in it.Demo
I've added a new example to the examples demo page which features a new box with omni-directional movement. This can be used to demonstrate a before and after and also shows much more power behind this library.
e.g.
Old - omni-directional box stuttering dragging top and left
New - omni-directional box smoothly resizing in all directions
Let me know of any issues you come across with it and have a good day!