Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implement custom editor for Quill blot

Tags:

quill

I'm trying to customize the Quill editor for my needs. I managed to implement and insert custom blots, as described in https://quilljs.com/guides/cloning-medium-with-parchment/ But I need to edit data, which is attached to my blots, like the URL of a link for example. The default implementation of Quill displays a small "inline" edit box for links. I want to implement something like that myself, but just don't get it. I did not find any hints in the docs and guides. Reading the source code of Quill, I was not able to figure out where the editing dialog for links is implemented. Any starting point would be very appreciated.

like image 765
Achim Avatar asked Jul 01 '18 17:07

Achim


2 Answers

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
like image 159
Kamil Niski Avatar answered Oct 22 '22 20:10

Kamil Niski


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();
  },
);
like image 3
גלעד ברקן Avatar answered Oct 22 '22 20:10

גלעד ברקן