Basic implementation of react-moveable npm package for dragging, resizing, rotating#3798
Basic implementation of react-moveable npm package for dragging, resizing, rotating#3798miina merged 14 commits intodevelop-storiesfrom
Conversation
| { hasSelection && ( | ||
| <Selection { ...selectionProps } /> | ||
| ) } | ||
| { displayMoveable && ( |
There was a problem hiding this comment.
Would it make sense to move selection + movable into a separate component that will be placed on top of the page?
There was a problem hiding this comment.
Yes, definitely makes sense to move it separately 👍
Use px for simplicity for now.
Add non-working resizing.
|
Note: Currently looking into getting resizing working properly. Due to absolute positioning, the position also needs an update when resizing, always when the element is rotated and otherwise also for every resizing handle except for the bottom, right, and bottom-right. In the previous version of AMP Stories, we wrote all the logic of adjusting the position while resizing ourselves but it was quite often buggy and influenced by other changes in the codebase. In theory, Moveable should work with absolute positioning as well, however, not currently working here. Will continue looking into this, perhaps I'm missing something obvious. Currently "sliding" around due to missing position compensating: |
I believe |
Seems so, that's currently used in the PR as well, also tried a few variations, still sliding a little bit. Will look more into this. |
dvoytenko
left a comment
There was a problem hiding this comment.
Couple nits, but otherwise this is good to merge and iterate on.
| const [ targetEl, setTargetEl ] = useState( null ); | ||
|
|
||
| useEffect( () => { | ||
| setTargetEl( selectionRef.current ); |
There was a problem hiding this comment.
I noticed this in couple of places: is there something bad from just doing call ref-callback? E.g.
<X ref={current => setTargetEl(current)}
There was a problem hiding this comment.
Hmm, I'm not sure if there's a difference/downside doing that. ^ @barklund Any thoughts here?
There was a problem hiding this comment.
Usually the main downside to using a ref-callback is that you don't have ref.current handy. But since we're writing the value into an easily-available state, we will have it anyway. So I think there's no real downside that I can see at this point. You'd remove an extra re-render this way vs useEffect -> setState approach. Not a huge deal, but still. Also the code itself would actually be simpler: <X ref={setTargetEl}> - so it looks pretty natural, imho.
There was a problem hiding this comment.
👍 Will update this and then merge this PR.
|
|
||
| useEffect( () => { | ||
| setTargetEl( selectionRef.current ); | ||
| }, [ selectionRef ] ); |
There was a problem hiding this comment.
selectionRef is guaranteed to never change. Should probably be [selectionRef.current] instead? Or ref-callback I mentioned above?
There was a problem hiding this comment.
Since selectionRef is an object then it would probably always be considered as not equal and always run. Wouldn't selectionRef.current always be null at this point instead?
There was a problem hiding this comment.
The result of useRef() is guaranteed to always be the same object. E.g. Object.is(old, new) === true. If we use selectionRef.current it will first start as null and then will re-update itself with the actual element value. You'd have one extra render in that case, which is not ideal. A callback-ref could thus be better...
There was a problem hiding this comment.
Doesn't Object.is( old, new ) always result in false since the references are compared here not the actual values of the two objects? E.g. if you do a = createRef(); b = createRef(), then Object.is( a, b ) is always false.
(Note: just tested out with selectionRef.current and it doesn't seem to update -- the targetEl always stays null and useEffect is run just once. Perhaps I'm missing something & you tried it out and it worked for you?)
There was a problem hiding this comment.
With [selectionRef] the useEffect will definitely run only once since it never changes. In a = useRef(); b = useRef(), the Object.is(a, b) would definitely be false. But the a = useRef() in the same position will always return the same a object for any number of render calls. I'll try it now and see what's happening there. Possibly I'm missing something in the code. Does it work for you for the selectionRef?
There was a problem hiding this comment.
I just realized that the code uses createRef. I thought it was useRef hook instead? createRef indeed would create the new reference for each render. But that's not what you want here, is it? With hooks a createRef is no different that just a simple let/const var?
There was a problem hiding this comment.
Okay, just realized that the code is using createRef instead of useRef which seems to be creating a new object on each render.
There was a problem hiding this comment.
I tried with useRef just now and it all seems to work pretty well. Also I tried to just using <Selection ref={setTargetEl}> - that also works well. I guess either way works well.
There was a problem hiding this comment.
The last commit changed the code to use <Selection ref={ setTargetEl }> instead of useEffect. Thank you for going into such detail in reviewing btw!
Will merge this as a basis for future iterations.
There was a problem hiding this comment.
Thanks! I'm just trying to learn codebase this way :)
|
Here are some of the initial follow-up tasks for this issue:
Perhaps creating two separate issues for these would be good, one for general/UX improvements:
The other for enabling group actions:
Does this sound reasonable? |
|
@miina this all sounds very reasonable. I'd only add: "display of guidelines" item. |
By guidelines, do you mean the snapping lines? We have a separate issue for that currently: https://github.com/ampproject/amp-wp/issues/3773, which seems to be P1 as well, others being P0, perhaps good to keep it separately? (Let me know if you meant something else by Guidelines.) |
Definitely seeing some odd offsetting. But I think this is good to merge - we'll have to figure it out later. |
What's odd is that it seems to be sliding with the image element only. Also created a sandbox for testing it out and asking the dev of the library to check what's wrong but it works as expected here: https://codesandbox.io/s/react-moveable-sliding-74s5q |
Great. Didn't know it was already there. Let's keep separately. |
Really strange, but I was just going to check it out, so I pulled the merged version and I'm no longer seeing any drifting effect from the animations above. Not sure if it's the code state or maybe something environmental interfering? Sometimes I see some weirdness when I collapse the outline to 0, but the same can be seen in the code snippet as well and probably not a real issue. |
|
Discovered the issue in my case: Chrome was zoomed out to 75% and this made it slide -- I've also let the developer know. Works as expected when zoomed in and 100%. In your case the "fix" might possibly be due to updating the version of It's still odd that only in an image's case it slides. Does it slide for you when zoomed out and resizing image when rotated? |
|
@miina yes, definitely scales when the page is zoomed-in/zoomed-out. Odd, but I guess not a big issue for us since it's not entirely an expected mode. |
|
@dvoytenko There is a new version of the package installed now which should fix the issue, it did at least for me locally and for the dev of the package. It got merged within #3841, looks like it's still not 100% fixed then. |


Summary
See #3783
Checklist