Quill: Cannot insert custom blot into block

Created on 23 Oct 2018  Â·  3Comments  Â·  Source: quilljs/quill

I just change the name of the linkBlot exemple to create a "custom" blot. When I try to use my new button I get this error:
Error: [Parchment] Cannot insert tone-inline into block
Any idea?

class ToneInlineBlot extends Parchment.Inline {
      static create(url) {
        let node = super.create();
        node.setAttribute('href', url);
        node.setAttribute('target', '_blank');
        node.setAttribute('title', node.textContent);
        return node;
      }

      static formats(domNode) {
        return domNode.getAttribute('href') || true;
      }

      format(name, value) {
        if (name === 'tone-inline' && value) {
          this.domNode.setAttribute('href', value);
        } else {
          super.format(name, value);
        }
      }

      formats() {
        let formats = super.formats();
        formats['tone-inline'] = ToneInlineBlot.formats(this.domNode);
        return formats;
      }
    }
    ToneInlineBlot.blotName = 'tone-inline';
    ToneInlineBlot.tagName = 'A';
    Parchment.register(ToneInlineBlot);

Most helpful comment

Having wasted a day of work struggling with this exact same problem, I wanted to share the solution I found. Bear in mind I wrote this in Typescript, but there is little difference other than the type qualifications and the way the class is exported.

Here is my class extending an inline element:

import Quill from 'quill';
const Link = Quill.import('formats/link');

export default class ExtendedLink extends Link {
  private static sanitizeUrl(url: string, protocols: any[]) {
    const anchor = document.createElement('a');
    anchor.href = url;
    const protocol = anchor.href.slice(0, anchor.href.indexOf(':'));
    return protocols.indexOf(protocol) > -1;
  }

  static create(value: string) {
    const node = super.create(value);
    node.setAttribute('href', this.sanitize(value));

    for (const protocol of ExtendedLink.PROTOCOL_WHITELIST) {
      if (value.startsWith(protocol)) {
        node.setAttribute('rel', 'noopener');
        node.setAttribute('target', '_blank');
        break;
      }
    }

    return node;
  }

  static formats(domNode: HTMLElement) {
    return domNode.getAttribute('href');
  }

  static sanitize(url: string) {
    return this.sanitizeUrl(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL;
  }
}

ExtendedLink.blotName = 'link';
ExtendedLink.tagName = 'A';
ExtendedLink.SANITIZED_URL = 'about:blank';
ExtendedLink.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel'];

For the purposes of your question, you can replace 'Link' with 'Inline'. The key thing for me here was realising I needed to use const Link = Quill.import('formats/link'); instead of a typical ES5/6 import directive (this holds true in Typescript also). The error you describe is thrown if you don't call Quill.import() — for some reason failing to do so means that Quill doesn't recognise your new class as being derived from the parent class (be it Inline, Link, Block or whatever).

Then, in another file, I imported this class and registered it, prior to calling a new instance of the quill editor:

import ExtendedLink from './extended-link.js';

[...]

Quill.register({ 'formats/extended-link': ExtendedLink }, true);

[...]

const editor = new Quill(this.editorElement, options);

[...]

All 3 comments

Having wasted a day of work struggling with this exact same problem, I wanted to share the solution I found. Bear in mind I wrote this in Typescript, but there is little difference other than the type qualifications and the way the class is exported.

Here is my class extending an inline element:

import Quill from 'quill';
const Link = Quill.import('formats/link');

export default class ExtendedLink extends Link {
  private static sanitizeUrl(url: string, protocols: any[]) {
    const anchor = document.createElement('a');
    anchor.href = url;
    const protocol = anchor.href.slice(0, anchor.href.indexOf(':'));
    return protocols.indexOf(protocol) > -1;
  }

  static create(value: string) {
    const node = super.create(value);
    node.setAttribute('href', this.sanitize(value));

    for (const protocol of ExtendedLink.PROTOCOL_WHITELIST) {
      if (value.startsWith(protocol)) {
        node.setAttribute('rel', 'noopener');
        node.setAttribute('target', '_blank');
        break;
      }
    }

    return node;
  }

  static formats(domNode: HTMLElement) {
    return domNode.getAttribute('href');
  }

  static sanitize(url: string) {
    return this.sanitizeUrl(url, this.PROTOCOL_WHITELIST) ? url : this.SANITIZED_URL;
  }
}

ExtendedLink.blotName = 'link';
ExtendedLink.tagName = 'A';
ExtendedLink.SANITIZED_URL = 'about:blank';
ExtendedLink.PROTOCOL_WHITELIST = ['http', 'https', 'mailto', 'tel'];

For the purposes of your question, you can replace 'Link' with 'Inline'. The key thing for me here was realising I needed to use const Link = Quill.import('formats/link'); instead of a typical ES5/6 import directive (this holds true in Typescript also). The error you describe is thrown if you don't call Quill.import() — for some reason failing to do so means that Quill doesn't recognise your new class as being derived from the parent class (be it Inline, Link, Block or whatever).

Then, in another file, I imported this class and registered it, prior to calling a new instance of the quill editor:

import ExtendedLink from './extended-link.js';

[...]

Quill.register({ 'formats/extended-link': ExtendedLink }, true);

[...]

const editor = new Quill(this.editorElement, options);

[...]

Yikes. Apparently this still exists. Thanks for your detailed comment @rodoch . I was able to solve my issue (not exactly the same, ANY custom blot was being rejected by scroll) with your solution. I imported Quill, used .import and no Parchment error.

This would definitely be worth including in the webpack pipeline doc as the method for importing classes there just doesn't work: https://quilljs.com/guides/adding-quill-to-your-build-pipeline/

I tried to follow the various registries and instanceof checks, but could not make sense of it without setting up a more involved build environment.

I still have the same issue even after importing with
const Parchment = Quill.imports.parchment;
https://jsfiddle.net/Imabot/5tsnyLuh/41/

Any idea?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

emanuelbsilva picture emanuelbsilva  Â·  3Comments

visore picture visore  Â·  3Comments

markstewie picture markstewie  Â·  3Comments

aletorrado picture aletorrado  Â·  3Comments

lustoykov picture lustoykov  Â·  3Comments