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.
问题:
回答1:
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:
- Select any word
- 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
回答2:
Here's a partial answer. Please see lines 41-64 in file "https://github.com/quilljs/quill/blob/develop/themes/snow.js"
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();
},
);