Implement custom editor for Quill blot

I've tried something similar. Proper way of doing it should be creating a module. Unfortunately as you already know it is not as easy as it seems.

Let me point you to some useful resources that helped me a lot with understanding how to create extensions for quill. Quills maintainer is curating Awesome quill list.

I recommend looking especially into

  • quill-emoji it contains code to display tooltip emoji while writing
  • quill-form maybe some form extension has some code that will point you in the right direction

Here is my try on to it using custom quill module.

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

class NamedLinkBlot extends InlineBlot {
  static create(value) {
    const node = super.create(value);

    node.setAttribute('href', value);
    node.setAttribute('target', '_blank');
    return node;
  }
}
NamedLinkBlot.blotName = 'namedlink';
NamedLinkBlot.tagName = 'A';

Quill.register('formats/namedlink', NamedLinkBlot);

const Tooltip = Quill.import('ui/tooltip');


class NamedLinkTooltip extends Tooltip {
  show() {
    super.show();
    this.root.classList.add('ql-editing');
  }


}

NamedLinkTooltip.TEMPLATE = [
  '<a class="ql-preview" target="_blank" href="about:blank"></a>',
  '<input type="text" data-link="https://quilljs.com">',
  'Url displayed',
  '<input type="text" data-name="Link name">',
  '<a class="ql-action"></a>',
  '<a class="ql-remove"></a>',
].join('');


const QuillModule = Quill.import('core/module');

class NamedLinkModule extends QuillModule {
  constructor(quill, options) {
    super(quill, options);
    this.tooltip = new NamedLinkTooltip(this.quill, options.bounds);
    this.quill.getModule('toolbar').addHandler('namedlink', this.namedLinkHandler.bind(this));
  }

  namedLinkHandler(value) {
    if (value) {
      var range = this.quill.getSelection();
      if (range == null || range.length === 0) return;
      var preview = this.quill.getText(range);
      this.tooltip.show();
    }
  }
}

Quill.register('modules/namedlink', NamedLinkModule);

const quill = new Quill('#editor', {
    theme: 'snow',
    modules: {
      namedlink: {},
      toolbar: {
        container: [
          'bold',
          'link',
          'namedlink'
        ]
      }
    }
  });

CodePen Demo

To see the tooltip:

  1. Select any word
  2. Click invisible button on the right of link button, your cursor will turn to hand.

Main issues that need to be addressed:

  • in order to customize the tooltip you will need to copy majority of the code from SnowTooltip Main pain point is that you cannot easily extend That tooltip.
  • you need to also adapt the code of event listeners but it should be straightforward

Here's a partial answer. Please see lines 41-64 in file "https://github.com/quilljs/quill/blob/08107187eb039eababf925c8216ee2b7d5166d41/themes/snow.js" (Please note, that the authors have since moved to TypeScript, lines 45-70?, and possibly made other changes.)

I haven't tried implementing something similar but it looks like Quill is watching a "selection-change" event and checks if the selection is on a LinkBlot with a defined link.

The SnowTooltip class includes references to the selectors, 'a.ql-preview', 'ql-editing', 'a.ql-action', and 'a.ql-remove', which we find in the link-editing tooltip.

this.quill.on(
  Emitter.events.SELECTION_CHANGE,
  (range, oldRange, source) => {
    if (range == null) return;
    if (range.length === 0 && source === Emitter.sources.USER) {
      const [link, offset] = this.quill.scroll.descendant(
        LinkBlot,
        range.index,
      );
      if (link != null) {
        this.linkRange = new Range(range.index - offset, link.length());
        const preview = LinkBlot.formats(link.domNode);
        this.preview.textContent = preview;
        this.preview.setAttribute('href', preview);
        this.show();
        this.position(this.quill.getBounds(this.linkRange));
        return;
      }
    } else {
      delete this.linkRange;
    }
    this.hide();
  },
);

Tags:

Quill