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

Pasting multiple lines breaks the table #33

Open
UnivocalHoneyBadger opened this issue Dec 5, 2019 · 5 comments
Open

Pasting multiple lines breaks the table #33

UnivocalHoneyBadger opened this issue Dec 5, 2019 · 5 comments

Comments

@UnivocalHoneyBadger
Copy link

UnivocalHoneyBadger commented Dec 5, 2019

When copying multiple lines into the table it breaks. I think that due to the clipboard module, each line is seen as a delta insert which does not check against the table and simply inserts it in between.

Here's a GIF that shows what happened:
0VcWUYUNUu

Here's what the clipboard text/html data returned:

<html><body>
<!--StartFragment--><p>test</p><p>test</p><!--EndFragment-->
</body>
</html>

Here's what the clipboard text/plain data returned: test\ntest

Edit:
This also breaks the showTableTools function

@soccerloway
Copy link
Owner

Yes, pasting in table should be processed specially.
In my product, I forked quilljs and processed this in clipboard module(onCapturePaste).
But there, I can not find a proper way to process.

@UnivocalHoneyBadger
Copy link
Author

I agree it's quite tricky. What could work is adding a paste eventListener where you access the text\html and find all instances of <p> and replace them with something else. Maybe with the help of a clipboard matcher we could properly turn each line into cell divs.

How does this suggestion sound, and please correct me if I'm wrong:
Find all instances of <p> and </p> in text/html data of the paste event, replace it with a tag like (for example) <celldiv>.

Then by using a matcher you let the clipboard itself process it for you into the element you want. Here's the documentation about matchers: https://quilljs.com/docs/modules/clipboard/#addmatcher

@soccerloway
Copy link
Owner

As far as I know, I think the matchers is used to handle that how quill convert DOM element to content, they are always some pure functions, it may not be suitable for this.
I have already fixed this issue in my company project. As you said, adding a paste eventListener, processing pasted delta if current selection is an instance of TableCellLine.

Here is the code:

onCapturePaste(e) {
    if (e.defaultPrevented || !this.quill.isEnabled()) return;
    const range = this.quill.getSelection(true);

    // Process pasting in table-cell-line before
    const [thisLeaf] = this.quill.getLine(range.index)
    if (thisLeaf && thisLeaf.constructor.name === 'TableCellLine') {
      e.preventDefault();
      const html = e.clipboardData.getData('text/html');
      const text = e.clipboardData.getData('text/plain');
      const files = Array.from(e.clipboardData.files || []);
      if (!html && files.length > 0) {
        this.quill.uploader.upload(range, files);
      } else {
        this.onTableCellPaste(range, { html, text });
      }
      return;
    }

    // ............bypass quill origin code
}

onTableCellPaste is the function processing default converted delta:

onTableCellPaste (range, {text, html}) {
    const [line] = this.quill.getLine(range.index)
    const lineFormats = line.formats()
    const formats = this.quill.getFormat(range.index);
    let pastedDelta = this.convert({ text, html }, formats);

    pastedDelta = pastedDelta.reduce((newDelta, op) => {
      if (op.insert && typeof op.insert === 'string') {
        const lines = []
        let insertStr = op.insert
        let start = 0
        for (let i = 0; i < op.insert.length; i++) {
          if (insertStr.charAt(i) === '\n') {
            if (i === 0) {
              lines.push('\n')
            } else {
              lines.push(insertStr.substring(start, i))
              lines.push('\n')
            }
            start = i + 1
          }
        }

        const tailStr = insertStr.substring(start)
        if (tailStr) lines.push(tailStr)

        lines.forEach(text => {
          text === '\n'
          ? newDelta.insert('\n', extend(
              {},
              op.attributes,
              lineFormats
            ))
          : newDelta.insert(text, op.attributes)
        })
      } else {
        newDelta.insert(op.insert, op.attributes)
      }

      return newDelta
    }, new Delta())

    debug.log('onTableCellPaste', pastedDelta, { text, html });
    const delta = new Delta()
      .retain(range.index)
      .delete(range.length)
      .concat(pastedDelta);
    this.quill.updateContents(delta, Quill.sources.USER);
    this.quill.setSelection(
      delta.length() - range.length,
      Quill.sources.SILENT,
    );
    this.quill.scrollIntoView();
  }

@soccerloway
Copy link
Owner

The delta of multiple lines is merged in only one insertion:

{
  insert: first line \n second line \n third line,
  attributes: { foo: foo }
}

It caused the problem.
Back to quill-better-table module, I have not found a proper way to fix it.
If I add a new paste eventListener, I need to make sure that the new listener must execute before the old one.
Maybe there is a better way to fix it? I will try adding a new paste eventListener first, if you have some other better way, help me please~:)

@NasirTaha
Copy link

Does any body has any fix or work-around for this issue?

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

No branches or pull requests

3 participants