my goal is to type anything with # beforehand and CKEditor to highlight it as mention.
It currently works with predefined mentions but that’s not sufficient for me:
Further thing I am trying to achieve it to hyperlink it to a webpage based on the mention.
Example user types: #test
This gets mentioned and hyperlink to https://example.com/test
I have spent hours trying to work it out but can’t come to mind how could I Implement that, so even ideas would be very beneficial.
Below is the code I currently have:
Thank you in advance even if you at least give me an idea how could I move forward..
I would ideally want to use CKEditor, further alternative I thought of is to use JS to look input field change and go from there
ClassicEditor
.create( document.querySelector( '#snippet-mention-customization' ), {
plugins: [ Mention, MentionCustomization, /* ... */ ],
mention: {
dropdownLimit: 4,
feeds: [
{
marker: '#',
feed: getFeedItems,
itemRenderer: customItemRenderer,
},
]
}
} )
.then( editor => {
window.editor = editor;
} )
.catch( err => {
console.error( err.stack );
} );
.
const items = [
{
id: "@swarley",
userId: "1",
name: "Barney Stinson",
link: "https://www.imdb.com/title/tt0460649/characters/nm0000439",
},
];
function getFeedItems(queryText) {
return new Promise((resolve) => {
setTimeout(() => {
console.log("Items:", items, Array.isArray(items)); // Check if items is an array
const itemsToDisplay = items
.filter((item) => isItemMatching(item, queryText))
.map((item) => ({
...item,
link: "https://example.com/search?q=" + encodeURIComponent(queryText),
}))
.slice(0, 10);
resolve(itemsToDisplay);
}, 100);
});
}
function isItemMatching(item, queryText) {
if (typeof queryText === "string") {
const searchString = queryText.toLowerCase();
return (
item.name.toLowerCase().includes(searchString) ||
item.id.toLowerCase().includes(searchString)
);
} else {
return true;
}
}
function customItemRenderer(item) {
const itemElement = document.createElement("span");
itemElement.classList.add("custom-item");
itemElement.id = `mention-list-item-id-${item.userId}`;
itemElement.textContent = `${item.name} (${item.link}) `;
const linkElement = document.createElement("span");
linkElement.classList.add("custom-item-link");
linkElement.textContent = `[Link]`;
itemElement.appendChild(linkElement);
return itemElement;
}
function MentionCustomization(editor) {
// The upcast converter will convert <a class="mention" href="" data-user-id="">
// elements to the model 'mention' attribute.
editor.conversion.for("upcast").elementToAttribute({
view: {
name: "a",
key: "data-mention",
classes: "mention",
attributes: {
href: true,
"data-user-id": true,
},
},
model: {
key: "mention",
value: (viewItem) => {
// The mention feature expects that the mention attribute value
// in the model is a plain object with a set of additional attributes.
// In order to create a proper object, use the toMentionAttribute helper method:
const mentionAttribute = editor.plugins
.get("Mention")
.toMentionAttribute(viewItem, {
// Add any other properties that you need.
link: viewItem.getAttribute("href"),
userId: viewItem.getAttribute("data-user-id"),
});
return mentionAttribute;
},
},
converterPriority: "high",
});
// Downcast the model 'mention' text attribute to a view <a> element.
editor.conversion.for("downcast").attributeToElement({
model: "mention",
view: (modelAttributeValue, { writer }) => {
// Do not convert empty attributes (lack of value means no mention).
if (!modelAttributeValue) {
return;
}
return writer.createAttributeElement(
"a",
{
class: "mention",
"data-mention": modelAttributeValue.id,
"data-user-id": modelAttributeValue.userId,
href: modelAttributeValue.link,
},
{
// Make mention attribute to be wrapped by other attribute elements.
priority: 20,
// Prevent merging mentions together.
id: modelAttributeValue.uid,
}
);
},
converterPriority: "high",
});
}