When user pastes a link into editor, the link should be converted to preview (title, image, description, etc.)
First created a PreviewBlot
const REGEXP_URL = /https?://[^s]+/g;
export class PreviewBlot extends Link {
static blotName = 'preview';
static className = 'ql-preview';
static tagName = 'div';
static create(value: string): HTMLElement {
console.log('should create preview'); // not called on 2nd approach
const node = super.create(value);
// Fetch metadata and append to node
fetch(`http://localhost:3000/opengraph?url=${encodeURIComponent(value)}`)
.then(response => response.json())
.then(metadata => {
const preview = document.createElement('div');
preview.innerHTML = `
<img src="${metadata.image}" alt="${metadata.title}" />
<h3>${metadata.title}</h3>
<p>${metadata.description}</p>
`;
node.appendChild(preview);
});
return node;
}
}
Next you need to intercept that user pastes a link:
quill.clipboard.addMatcher(Node.TEXT_NODE, (node, delta) => {
if (node.nodeName !== '#text') {
return delta;
}
const textNode = node as Text;
const matches = textNode.data.match(REGEXP_URL);
if (matches && matches.length > 0) {
console.log('USING MATCHER');
const ops = [];
let str = textNode.data;
matches.forEach(match => {
const split = str.split(match);
const beforeLink = split.shift();
ops.push({insert: beforeLink});
console.log('should push preview');
ops.push({insert: match, attributes: {preview: match}});
str = split.join(match);
});
ops.push({insert: str});
delta.ops = ops;
}
return delta;
});
The matcher is called when you cut text from editor and paste in back.
The matcher is not called when you copy a link from browser’s address bar and paste the link into editor.
I consider this may be a bug in Quill.
So I tried 2nd approach:
quill.on('text-change', delta => {
const ops = delta.ops;
const insert = ops?.[ops.length - 1]?.insert;
const matches = typeof insert === 'string' && insert.match(REGEXP_URL);
if (matches && matches.length > 0) {
console.log('USING EVENT');
const ops = [];
let str = insert;
for (const match of matches) {
const split = str.split(match);
const beforeLink = split.shift();
ops.push({insert: beforeLink});
ops.push({insert: match, attributes: {preview: match}});
str = split.join(match);
}
ops.push({insert: str});
delta.ops = ops;
}
});
But this time the PreviewBlot
is not inserted despite we change ops
in delta
to create the preview
.
Working example: https://stackblitz.com/edit/stackblitz-starters-ctmwke?file=src%2Findex.html
Case 1: copy a link from browser’s address bar and paste into editor – doesn’t work
Case 2: cut link from editor and paste it back – the proper PrewiewBolt is inserted