ProseMirror / prosemirror

The ProseMirror WYSIWYM editor
http://prosemirror.net/
MIT License
7.59k stars 335 forks source link

Cursor and typing doesn't work correctly in Markview #1396

Closed dragonman225 closed 1 year ago

dragonman225 commented 1 year ago

I'd like to create a button after each link mark, when the button is clicked, the browser opens the link. And clicking the text of the link does nothing.

I created a MarkView as below:

export const createLinkMarkView: MarkViewConstructor = (mark, view) => {
  const dom = document.createElement('span')
  const contentDOM = document.createElement('a')
  const openBtn = document.createElement('button')
  dom.append(contentDOM, openBtn)

  contentDOM.href = mark.attrs.href
  contentDOM.title = mark.attrs.title

  openBtn.innerText = '↗'
  openBtn.style.userSelect = 'none'
  openBtn.style.display = 'inline'
  openBtn.onclick = () => {
    openWebUrl(mark.attrs.href)
  }

  return { dom, contentDOM }
}

which would renders

<span>
  <a href="https://prosemirror.net/" title="null">ProseMirror</a>
  <button style="user-select: none; display: inline;">↗</button>
</span>

dom is the outer <span>, and contentDom is <a>.

My toDOM function of the link mark is generic,

toDOM(node) {
  let { href, title } = node.attrs
  return ['a', { href, title }, 0]
},

Then, I encountered two problems:

  1. When the cursor is right after the link, pressing the right arrow key doesn't move one character right.

https://github.com/ProseMirror/prosemirror/assets/25148955/53c03ff2-1930-493d-8fba-0b67f1616ac1

  1. When I type right after the link, new text is inserted to neither the contentDOM nor outside dom, but in between. Therefore I can't move the cursor into the new text or delete it, and it's not captured into the ProseMirror doc, either.

https://github.com/ProseMirror/prosemirror/assets/25148955/23950d60-e820-4855-805a-5a68ac13e81d

Like this:

<span>
  <a href="https://prosemirror.net/" title="null">ProseMirror</a>
  aaa
  <button style="user-select: none; display: inline;">↗</button>
</span>

I searched for previous issues and it looked like there're some limitations for MarkView. I wonder if what I was trying to do involves with the limitations. Maybe it's not supported to have an addition node between dom and contentDOM?

The ProseMirror libraries I'm using:

"prosemirror-commands": "1.5.2",
"prosemirror-history": "1.3.2",
"prosemirror-inputrules": "1.2.1",
"prosemirror-keymap": "1.2.2",
"prosemirror-model": "1.19.2",
"prosemirror-state": "1.4.3",
"prosemirror-transform": "1.7.3",
"prosemirror-view": "1.31.5",

And the browser I'm using is Google Chrome 114.0.5735.198 on macOS.

marijnh commented 1 year ago

Try making openBtn non-editable (openBtn.contentEditable = "false") and see if that helps.

dragonman225 commented 1 year ago

Thank you for the suggestion! Unfortunately the results are the same.

One small difference is that with openBtn.contentEditable = "false", when the cursor is at the end of the link text, it doesn't try to go into the button and be set back, instead, it doesn't move to the right at all.

marijnh commented 1 year ago

Patch https://github.com/ProseMirror/prosemirror-view/commit/39b5664e2438d841b904dd59269351fc8a6fa766 should help with that.

dragonman225 commented 1 year ago

Thank you for the patch! Cursor movement now works.

But typing after the last character of the link text is a bit tricky.

This ↓ will work (inserts new text after </a>)

<a href="https://example.com" title="null"> ← dom
  <span>content</span> ← contentDOM
  <button contenteditable="false" style="user-select: none; display: inline;">↗</button>
</a>

while this ↓ will not work (inserts new text between </a> and <button...)

<span> ← dom
  <a href="https://example.com" title="null">content</a> ← contentDOM
  <button contenteditable="false" style="user-select: none; display: inline;">↗</button>
</span>

I have no particular preference, just point out in case someone encounter the same problem.