quill: TypeScript classes not compatible with quill dist

I’m having issues extending Parchment blots (specifically Inline, and Block) using TypeScript’s class definition transpilation to ES5.

When TypeScript is configured to output ES6 code (ie, does not transpile class definitions) the following code works as expected (should be called when formatting is logged to the console).

But when TypeScript is configured to output ES5 code (and does transpile class definitions), the formats method in SampleBlot is not called.

Steps for Reproduction


// editor.ts (TypeScript file)
import * as Quill from 'quill';

let Inline = Quill.import('blots/inline');

class SampleBlot extends Inline {
  static formats(node) {
    console.log('should be called when formatting');
    return 'H1'; // just returning an h1 tag for demonstration purposes
  }
}
HeaderBlot.blotName = 'sample';
HeaderBlot.tagName = 'H1';

setTimeout(() => {
  quill.format('sample', 1);
}, 1000); // arbitrarily update format after 1 second (for demonstration)

Quill.register('formats/sample', SampleBlot); // have also tried Quill.register(SampleBlot);
const quill = new Quill(document.querySelector('#editor');

Expected behavior: formats method is called and should be called when formatting is logged to the console.

Actual behavior: formats method is not called and the console is empty.

Platforms: Quill 1.1.5 TypeScript 2.1.4 Chrome 55

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 16 (1 by maintainers)

Most upvoted comments

Hi we today got it. Let me show you:

const ReactQuill = require('react-quill');
const Quill = ReactQuill.Quill;
import * as Parchment from "parchment";

const QuillBlockInline = Quill.import('blots/inline');

class BlockInline extends Parchment.default.Inline {}
BlockInline.prototype = QuillBlockInline.prototype;

...

class Link extends BlockInline {

  static blotName = 'link';
  static tagName = 'A';
  static SANITIZED_URL = 'about:blank';

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

...

    return node;
  }

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

  static sanitize(url: string) {
    return sanitize(url, ['http', 'https', 'mailto', 'ftp', 'tel']) ? url : this.SANITIZED_URL;
  }

  static value(node: any) {
    return node.getAttribute('href');
  }

  format(name: string, value: string) {
    if (name !== this.statics.blotName || !value) return super.format(name, value);
    value = Link.sanitize(value);
    this.domNode.setAttribute('href', value);
  }
}

function sanitize(url: string, protocols: string[]) {
  let anchor = document.createElement('a');
  anchor.href = url;
  let protocol = anchor.href.slice(0, anchor.href.indexOf(':'));
  return protocols.indexOf(protocol) > -1;
}

export default Link;

@arahansen The workaround does not work when I try to extend Container for example (actually when I try to use this or super on the not static methods). @jhchen Any plans to rewrite quill to TS? 😃

Those interfaces are stubbed out as

interface IBlock extends ObjectConstructor {
	create(...any): HTMLElement;
}

I was able to get TypeScript compilation to work correctly with the following example:

const Block: IBlock = Quill.import('blots/block');
const Parchment: IParchment = Quill.import('parchment');

class LineHeightBlot extends Block {
	static blotName = 'lineHeight';
	static tagName = 'SPAN';
	static scope = Parchment.Scope.BLOCK;

	static create(value) {
		const node = super.create();
		node.style['line-height'] = value;
		return node;
	}

	static formats(node) {
		return $(node).css('line-height');
	}
}

What’s strange to me is that I had to include the scope as well as the create method.