patleeman/quill-markdown-shortcuts

Inline formats such as bold, italic, bold-italic, code does not work well with inline-embeds.

wooolfgang opened this issue · 0 comments

For my particular use-case, using quill-markdown-shortcuts with quill-mentions causes a bug. The bug will also happen if you have custom inline-embeds, or default ones such as images, etc.

Steps to reproduce:

  • Open code-sandbox -- the editor here uses quill-markdown-shortcuts and quill-mentions
  • Create a mention blot (use @{USER_HERE}) and then after the blot and within the current line, use markdown shortcuts such as bold ({WORD}), italics ({WORD}), etc.
  • As you can see, there's an issue with the deleting/replacing of the text with the correct format.

The issue is caused by this line in particular

    const startIndex = lineStart + match.index

    if (text.match(/^([*_ \n]+)$/g)) return

     setTimeout(() => {
         this.quill.deleteText(startIndex, annotatedText.length)
         this.quill.insertText(startIndex, matchedText, {bold: true, italic: true})
         this.quill.format('bold', false)
    }, 0)

The calculations for startIndex is not correct when there are inline-embed blots before the matchedText and within the current line. Inline-embeds will only have length === 1, causing an issue with the markdown-shortcut plugin once the editor has non-text contents.

I have a solution that fixes this issue, but I'm not sure if this is best one. I would like to know anyone's thoughts, or if they came up with more elegant solutions. Thanks!

 getStartIndex (annotatedText) {
    const wholeText = this.quill.getText()
    const embedsCountBeforeText = this.getInlineEmbedsBeforeText(annotatedText)
    const startIndex = wholeText.indexOf(annotatedText) + embedsCountBeforeText
    return startIndex
  }

  /**
   * @description -- This counts the number of inline embeds (image|imageLoading|mention)
   * before a matched text within the current line
   * @param {String} annotatedText
   * @returns {Number}
   */
  getInlineEmbedsBeforeText (annotatedText) {
    const contents = this.quill.getContents()
    let count = 0
    let startCount = false
    let matchedIndex = 0

    if (contents.ops.length <= 1) {
      return count
    }

    for (let i = contents.ops.length - 1; i >= 0; i--) {
      const content = contents.ops[i]

      // If the annotatedText matches within the text, start the count
      if (typeof content.insert === 'string' && content.insert.includes(annotatedText)) {
        startCount = true
        matchedIndex = i
      }
      // If an inline embed is matched before the annotatedText and within the current line, increment
      if (startCount &&
        (content.insert.mention || content.insert.image || content.insert.imageLoading)) {
        count += 1
      }
      // If the content is not within the current line, end the count
      if (
        startCount &&
        typeof content.insert === 'string' &&
        content.insert.includes('↵') &&
        matchedIndex !== i
      ) {
        startCount = false
        break
      }
    }

    return count
  }