Skip to content

Basic implementation of react-moveable npm package for dragging, resizing, rotating#3798

Merged
miina merged 14 commits intodevelop-storiesfrom
try/test-moveable
Nov 26, 2019
Merged

Basic implementation of react-moveable npm package for dragging, resizing, rotating#3798
miina merged 14 commits intodevelop-storiesfrom
try/test-moveable

Conversation

@miina
Copy link
Copy Markdown
Contributor

@miina miina commented Nov 20, 2019

Summary

See #3783

Checklist

  • My pull request is addressing an open issue (please create one otherwise).
  • My code is tested and passes existing tests.
  • My code follows the Engineering Guidelines (updates are often made to the guidelines, check it out periodically).

@miina miina added the Stories Stories Editor label Nov 20, 2019
@googlebot googlebot added the cla: yes Signed the Google CLA label Nov 20, 2019
@miina miina changed the title [TEST] Test out react-moveable npm package [WIP] Test out react-moveable npm package for dragging, resizing, rotating Nov 22, 2019
Copy link
Copy Markdown

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks really good!

{ hasSelection && (
<Selection { ...selectionProps } />
) }
{ displayMoveable && (
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it make sense to move selection + movable into a separate component that will be placed on top of the page?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, definitely makes sense to move it separately 👍

@miina
Copy link
Copy Markdown
Contributor Author

miina commented Nov 25, 2019

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:
resizing-sliding

@miina
Copy link
Copy Markdown
Contributor Author

miina commented Nov 25, 2019

Looks like the obvious thing might have been missing pinchable which allows using drag events while resizing. However, there's still some odd sliding happening -- see the bottom left corner continuously sliding:
odd-behaviour

Might create a support ticket to the repo unless I figure it out tomorrow quickly enough, the developer seems to be very responsive and active.

@dvoytenko
Copy link
Copy Markdown

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.

I believe drag.beforeTranslate is used for this. It'd look something like this:

target.style.top = `${oldTop + drag.beforeTranslate[0]}px`;
target.style.left = `${oldLeft + drag.beforeTranslate[1]}px`;

@miina
Copy link
Copy Markdown
Contributor Author

miina commented Nov 26, 2019

I believe drag.beforeTranslate is used for this. It'd look something like this:

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.

@miina miina changed the base branch from try/edit-story-clean-slate to develop-stories November 26, 2019 17:40
Copy link
Copy Markdown

@dvoytenko dvoytenko left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple nits, but otherwise this is good to merge and iterate on.

const [ targetEl, setTargetEl ] = useState( null );

useEffect( () => {
setTargetEl( selectionRef.current );
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed this in couple of places: is there something bad from just doing call ref-callback? E.g.

<X ref={current => setTargetEl(current)}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I'm not sure if there's a difference/downside doing that. ^ @barklund Any thoughts here?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Will update this and then merge this PR.


useEffect( () => {
setTargetEl( selectionRef.current );
}, [ selectionRef ] );
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

selectionRef is guaranteed to never change. Should probably be [selectionRef.current] instead? Or ref-callback I mentioned above?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, just realized that the code is using createRef instead of useRef which seems to be creating a new object on each render.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! I'm just trying to learn codebase this way :)

@miina
Copy link
Copy Markdown
Contributor Author

miina commented Nov 26, 2019

Here are some of the initial follow-up tasks for this issue:

  • Move Moveable into a separate component
  • Hide handles while dragging / resizing, only display outline
  • Minimum limits for resizing
  • Limit dragging out of the Page (at least to some extent for now)
  • Allow group dragging
  • Allow group rotation
  • Allow group resizing

Perhaps creating two separate issues for these would be good, one for general/UX improvements:

  • Move Moveable into a separate component
  • Hide handles while dragging / resizing, only display the outline
  • Minimum limits for resizing (not allowing resizing to non-existing)
  • Limit dragging out of the Page (at least to some extent for now)

The other for enabling group actions:

  • Allow group dragging
  • Allow group rotation
  • Allow group resizing

Does this sound reasonable?

@miina miina marked this pull request as ready for review November 26, 2019 18:25
@miina miina changed the title [WIP] Test out react-moveable npm package for dragging, resizing, rotating Basic implementation of react-moveable npm package for dragging, resizing, rotating Nov 26, 2019
@dvoytenko
Copy link
Copy Markdown

@miina this all sounds very reasonable. I'd only add: "display of guidelines" item.

@miina
Copy link
Copy Markdown
Contributor Author

miina commented Nov 26, 2019

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.)

@dvoytenko
Copy link
Copy Markdown

Currently "sliding" around due to missing position compensating:

Definitely seeing some odd offsetting. But I think this is good to merge - we'll have to figure it out later.

@miina miina merged commit 609ee2b into develop-stories Nov 26, 2019
@miina miina deleted the try/test-moveable branch November 26, 2019 19:30
@miina
Copy link
Copy Markdown
Contributor Author

miina commented Nov 26, 2019

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

@dvoytenko
Copy link
Copy Markdown

I'd only add: "display of guidelines" item.

By guidelines, do you mean the snapping lines? We have a separate issue for that currently: #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.)

Great. Didn't know it was already there. Let's keep separately.

@dvoytenko
Copy link
Copy Markdown

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

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.

@miina
Copy link
Copy Markdown
Contributor Author

miina commented Nov 27, 2019

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 react-moveable package -- apparently it was sliding a little bit in earlier versions but got fixed last Friday (version 14.5) (unless you already had that version before).

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?

@dvoytenko
Copy link
Copy Markdown

@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.

@miina
Copy link
Copy Markdown
Contributor Author

miina commented Dec 3, 2019

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cla: yes Signed the Google CLA Stories Stories Editor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants