Skip to content
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: autoscroll when block is bigger than viewport #3746

Conversation

gztomas
Copy link
Contributor

@gztomas gztomas commented Jun 20, 2020

Is this adding or improving a feature or fixing a bug?

This is fixing an issue #3146

What's the new behavior?

Before:
before mov
After:
after mov

How does this change work?

scroll-into-view-if-needed can only scroll elements into view. In this case, the element is bigger than the viewport, and we actually want to scroll the range of the selection into the viewport. Range already implements getBoundingClientRect. That should be all what scroll-into-view-if-needed needs to do its thing. But scroll-into-view-if-needed won't just work if we pass a range instead of an element. While being a bit hacky, overriding getBoundingClientRect of the block with the getBoundingClientRect from the range works wonderfully.

Have you checked that...?

  • The new code matches the existing patterns and styles.
  • The tests pass with yarn test.
  • The linter passes with yarn lint. (Fix errors with yarn fix.)
  • The relevant examples still work. (Run examples with yarn start.)

Does this fix any issues or need any specific reviewers?

Fixes: #3146
Reviewers: @CameronAckermanSEL @ianstormtaylor

Copy link

@rglusk rglusk left a comment

Choose a reason for hiding this comment

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

@ianstormtaylor can you please take a look at adding in this fix? getting this improvement out would be a big win for a lot of projects facing this bug :)

@rglusk
Copy link

rglusk commented Jun 30, 2020

@gztomas can you please tag @ianstormtaylor as a reviewer? maybe that will help to get this reviewed and merged sooner

@@ -186,10 +186,14 @@ export const Editable = (props: EditableProps) => {
if (newDomRange) {
domSelection.addRange(newDomRange!)
const leafEl = newDomRange.startContainer.parentElement!
leafEl.getBoundingClientRect = newDomRange.getBoundingClientRect.bind(
newDomRange
)
Copy link

Choose a reason for hiding this comment

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

when testing this fix, did you scroll around a lot? Worried about performance issues when building and destroying this every time you scroll

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I haven't found any performance issues so far. If that was the case however I think we would see the issue on typing rather than on scrolling given this piece of code is only called when the selection changes, not the scroll. I believe this should be a cheap operation either way. Or at least probably cheaper than the DOM access operations that happen on toDOMRange sequential to this.

@cameracker
Copy link
Collaborator

Hi @gztomas , thank you so much for the contribution.

I went to go test this out on the deployed preview examples and it didn't seem to have any effect. What I tried was going to the rich text example, adding enough text for there to be a scollbar, and then I typed while the cursor was in and out of the view port. In both cases, the behavior was identical to what is now on latest. I also tried this by ensuring the text was in a single block and still no improvement.

Is there something I'm missing in the steps to reproduce?

@gztomas
Copy link
Contributor Author

gztomas commented Jul 2, 2020

Hi @CameronAckermanSEL
Thanks for looking into this. What I have seen this change fixes happens when the slate editor height has a limit. I can repro the issue in the preview examples only after some css changes:
scroll-issue mov
Please let me know if that makes sense

@rglusk
Copy link

rglusk commented Jul 8, 2020

@CameronAckermanSEL have you had a chance to re-review this? Is there anything else you need to repro?

@rglusk
Copy link

rglusk commented Aug 31, 2020

@gztomas can we push this change out? its been a few months and there's been no comments around this change causing issues.

cc: @CameronAckermanSEL @ianstormtaylor

@gztomas
Copy link
Contributor Author

gztomas commented Aug 31, 2020

@rglusk I don't think there is anything to be done on our side. There are a lot of PRs unattended fixing really important stuff.

@rglusk
Copy link

rglusk commented Sep 1, 2020

@timbuckley, i see you recently merged new code. Can you please either merge this for us, or give @gztomas and I write access to this repo? Thank you!!

@anhphung97
Copy link

This is a problem for me as well, would like to see it gets fixed soon 👍 @timbuckley

@gztomas
Copy link
Contributor Author

gztomas commented Sep 1, 2020

In case it was useful for you, this is the hack I'm using to fix this on my side

// HACK for fixing Slate auto-scroll issue
// Overriding the rect for the Slate block with the selection block so Slate
// auto-scrolls to where the cursor is rather than auto-scrolling the whole
// block. If the block is big enough then the auto-scroll can make the cursor be
// outside of the viewport.
const { toDOMRange } = ReactEditor;
ReactEditor.toDOMRange = (editor, selection) => {
  const range = toDOMRange(editor, selection);
  range.startContainer.parentElement?.getBoundingClientRect = range.getBoundingClientRect.bind(
    range
  );
  return range;
};

@rglusk
Copy link

rglusk commented Sep 1, 2020

thanks @gztomas, that works :) hopefully this change will get merged at some point so other people can use your fix too!

@changeset-bot
Copy link

changeset-bot bot commented Mar 31, 2021

🦋 Changeset detected

Latest commit: 95a235d

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
slate-react Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@ianstormtaylor
Copy link
Owner

Thanks @gztomas!

@ianstormtaylor ianstormtaylor merged commit f8be509 into ianstormtaylor:master Mar 31, 2021
This was referenced Mar 31, 2021
scrollIntoView(leafEl, {
scrollMode: 'if-needed',
boundary: el,
})
delete leafEl.getBoundingClientRect
Copy link
Collaborator

Choose a reason for hiding this comment

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

🤔 this throws a typescript error could you elaborate why this is needed @gztomas?

Copy link
Contributor Author

@gztomas gztomas Apr 22, 2021

Choose a reason for hiding this comment

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

@juliankrispel this restores the override happening at line 208, so any future call to leafEl.getBoundingClientRect will get the native getBoundingClientRect implementation from leafEl prototype chain. This is for cleanup purposes, as this code doesn't know who else might need to call getBoundingClientRect for this element. I guess TS can't know that even deleting this, getBoundingClientRect will still be available, from the prototype.

Copy link
Collaborator

Choose a reason for hiding this comment

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

thanks for explaining @gztomas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Unexpected scroll when start/end of a block is outside the viewport
6 participants