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

Edit Link modal fix #5551

Merged
merged 12 commits into from
Jan 30, 2024
Merged

Edit Link modal fix #5551

merged 12 commits into from
Jan 30, 2024

Conversation

HarrySIV
Copy link
Contributor

@HarrySIV HarrySIV commented Jan 26, 2024

This PR fixes #4064 by searching the selected nodes for a node whose parent is not a auto/link node and sets isLink accordingly.
Edit: I forgot to mention the css needs updating with this fix as the text toolbar still makes space for the link toolbar, but I couldn't figure out how the styling was being added.

Copy link

vercel bot commented Jan 26, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
lexical ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 29, 2024 6:16pm
lexical-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jan 29, 2024 6:16pm

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Jan 26, 2024
@HarrySIV
Copy link
Contributor Author

HarrySIV commented Jan 27, 2024

I'm not sure how to solve the failed test, essentially it looks like assertSelection is failing, but I'm not exactly sure what the Array is saying.

` - Expected - 2
+ Received + 1

  Array [
    0,
-   0,
-   0,
+   1,
  ]`

I've tested it and the only change I see is when you click the hyperlink icon the focus offset is 0 instead of 6, but the selection is essentially the same otherwise. I'm not sure how the change alters the selection since all it does is add additional checks for determining when isLink should be true.

Copy link
Member

@zurfyx zurfyx left a comment

Choose a reason for hiding this comment

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

Thank you! I wonder if it would be easier and more performant to look at the selection anchor and focus instead to determine whether the link is in between boundaries.

const linkNodes = selection.getNodes().filter((selectedNode) => {
if (
$isLinkNode(selectedNode.getParent()) ||
$isAutoLinkNode(selectedNode.getParent()) ||
Copy link
Member

Choose a reason for hiding this comment

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

Be careful when using .getParent directly, the immediate parent doesn't necessarily have to be the Link. For example, when using CharacterLimit ?isCharLimit=true

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, I will look into this.

@HarrySIV
Copy link
Contributor Author

I thought about that, but I figured if you select a lone text node in between two link nodes the modal would still pop up.
I also considered adding an out-of-bounds state that prevented further rendering of the modal if you selected a lone text node and were still selecting, but that turned out to be convoluted and I wasn't sure it would work when using a "select all" command.
Let me know if you still think that's the best course of action.

@zurfyx
Copy link
Member

zurfyx commented Jan 29, 2024

@HarrySIV You're right, what about, we loop all the nodes, find $findMatchingParent(LinkNode) for each of them. If any returns null, we close the loop (there's no link), if there's a link we continue matching as long as they all match to the same link. Exception is LineBreakNode which gets excluded. For the majority of the cases, the cost will be O(d) where d is the depth of the tree, when a link is matched it will be O(n * d)

@HarrySIV
Copy link
Contributor Author

@zurfyx I'll try that

@HarrySIV
Copy link
Contributor Author

Oh, @zurfyx , there was the problem that if you select all, you select the top paragraph node which doesn't have a link node ancestor, which is why I made the search for any node that wasn't the parent of a link node, or have a link node as a parent. I'll try out your suggestion and see what I can do to catch the paragraph node.

@zurfyx
Copy link
Member

zurfyx commented Jan 29, 2024

#4064

Good point, I think that what you're referring to is a confusing behavior from the browser. The selection is moved to the previous ElementNode even when it's selected at the very end. What do you think of our own custom getNodes() that looks at leaf nodes only? Otherwise a custom DFS implementation from anchor to focus but seems like an overkill for this case. I can give it another thought tomorrow, at first glance, it seems like we lack the tooling for a use case that shouldn't be too complicated to address on the product side...

@HarrySIV
Copy link
Contributor Author

I think I fixed the issue.
Basically updateToolbar searches for the first "badNode" it can find, either a non-LinkNode that isn't a LineBreakNode or another LinkNode that isn't the focus node from selection.
For some reason, "select all" only selects the top ParagraphNode or ListItemNode when there are multiple lines, which would select a non-LinkNode or another LinkNode anyway, so the modal doesn't open, which is fine. That could change if the way selection works changes though.
I will say, something that currently bothers me is if you have a space after the link and double click it, the modal will close because it selects the text node. I'm not exactly sure how I would write that out if the proceeding TextNode contains other text, as you'd have to check that the first character is an empty space.

@HarrySIV
Copy link
Contributor Author

Would it make sense to move the selection and focusNode declarations out of updateToolbar, so that if the focus is not on a LinkNode, we can return out of the useEffect hook so as not to go through the .find method every time selection changes?

@zurfyx
Copy link
Member

zurfyx commented Jan 30, 2024

Would it make sense to move the selection and focusNode declarations out of updateToolbar, so that if the focus is not on a LinkNode, we can return out of the useEffect hook so as not to go through the .find method every time selection changes?

The React re-renderering involved in useEffect might make it slower and harder to synchronize with the rest of the logic.

I still think that this complex problem might help from some utility function to optimize for the best performance without making the product code complicated but happy to merge this PR as is right now. Thank you for working on this!

@zurfyx zurfyx merged commit 3177f77 into facebook:main Jan 30, 2024
41 of 45 checks passed
@HarrySIV HarrySIV deleted the linkModalFix branch January 30, 2024 05:41
@HarrySIV
Copy link
Contributor Author

That's certainly something I could take a crack at, like the custom getNodes() function you were mentioning? Where would it live, in selection, utils, link?

@agriffis
Copy link
Contributor

agriffis commented Apr 2, 2024

@HarrySIV I'm looking at the code in this PR, and I'm struggling to understand this condition:

          if (
            !linkNode?.is(focusLinkNode) &&
            !autoLinkNode?.is(focusAutoLinkNode) &&
            !linkNode &&
            !autoLinkNode &&
            !$isLineBreakNode(node)
          ) {
            return node;
          }

First it tests !linkNode?.is(focusLinkNode) and then it tests !linkNode. It seems like the earlier condition can't possibly match if linkNode is falsy?

It seems like this wants to be either

          if (
            !linkNode?.is(focusLinkNode) &&
            !autoLinkNode?.is(focusAutoLinkNode) &&
            !$isLineBreakNode(node)
          ) {
            return node;
          }

or

          if (
            !linkNode &&
            !autoLinkNode &&
            !$isLineBreakNode(node)
          ) {
            return node;
          }

but it's also possibly I'm misunderstanding entirely 🤷‍♂️

@HarrySIV
Copy link
Contributor Author

HarrySIV commented Apr 2, 2024

Hi @agriffis ,
That looks to be an error on my part, the first replacement you suggested should work.
Cheers!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Edit link modal is shown when the selection spans further than the link
4 participants