Hi,
it is a pleasure to contribute, after all what all forum-active Bubblers gave.
A possible solution to inject a desired text at the cursor position in the Bubble’s Rich Text Editor (RTE), is to leverage on the magical Toolbox Plugin.
In particular, in our specific case we needed to allow our Users to tag / mention other User in the comments, which we absolutely needed to be RTE.
The process from the User point of view is:
- In the comment section of the App, the User add “@” in the RTE
- A popup shows up where the User can select the User to tag
- The tagged-User’s name is added in the RTE with a specific style (blue and underlined for ex.) - naturally, it is injected where the cursor was before.
Mirroring this logic and implementing it in the App, we have divided the process the same way as it is structured the from User point of view point:
-
On the page where there is the RTE, the below “First Part JS” adds a listener through the Toolbox Plugin so that, when the user add “@” to the RTE:
A) the opening of the popup got triggered - for ex. in the below “First Part JS” we have used a button as a bridge $(‘#ButtonMentionPeople’).click();
B) the anchorNodeID referencing the “@” the User has added in the RTE, is passed to a JavascriptToBubble element, in order to be used in the “Second Part JS below”
-
When the ButtonMentionPeople is clicked, the popup shows up with the RG of other Users inside the App. This button can also be made 0x0px in order to don’t influence the App UI.
-
In the popup, when a User from the RG get clicked, the “Second Part JS” injects the tagged-User’s name thanks to the 2 piece of information that are passed to this JS as input: the tagged-User’s name, and the anchorNodeID that the “First Part JS” has created.
UI ELEMENTS AND WORKFLOW ACTION TO ADD TO THE APP
A) The Rich Text Editor to inject the desired string into - NB: important to put the ID Attribute used in the “Second Part JS”
B) The “JavascriptToBubble” element to place in the Frontend Design UI of the App
C) The “Run Javascript” Action to add in the Workflow section for the “First Part JS” as a listener - for ex. in a “When the page is loaded…” WF
D) The Button to place in the Frontend Design UI of the App (in this ex., the clicked element is even a text, the important thing is to put its ID Attribute = ButtonMentionPeople, or whatever id you use in the “First Part JS”)
E) The Popup with the RG User, so the Current User can choose the User to tag
F) The “Run Javascript” Action to add in the Workflow section for the “Second Part JS” triggered when the User select the User to tag from the RG inside the Popup
FIRST PART JS
$(window).keydown(function(e) {
if (e.key === '@') {
e.preventDefault(); // the "@" here is blocked before it is written down, it will be added just couple of lines below
var richTextEditor = document.getElementById('CommentRichText');
var selection = window.getSelection();
var anchorNode = selection.anchorNode;
if (anchorNode && richTextEditor.contains(anchorNode)) {
// Compose the string the user has inject (in this case "@") with a timestamp in millisecond as id
var timestamp = new Date().getTime();
var stringToInsert = '<span style="color: blue; text-decoration: underline;" id="' + timestamp + '">@</span>';
// Create HTML element from markup
var parser = new DOMParser();
var doc = parser.parseFromString(stringToInsert, 'text/html');
var newNode = doc.body.firstChild;
// Inject the element in the Rich Text Editor (in particular, where the user have cursor in that moment)
var range = selection.getRangeAt(0);
range.deleteContents();
var startContainer = range.startContainer;
var startOffset = range.startOffset;
startContainer.splitText(startOffset);
startContainer.parentNode.insertBefore(newNode, startContainer.nextSibling);
range.selectNodeContents(newNode.nextSibling);
// Save the ID in order to utilize it in the Second Part of the Process
var anchorNodeId = timestamp.toString();
}
console.log(anchorNodeId);
bubble_fn_outputAnchorID(anchorNodeId);
$('#ButtonMentionPeople').click(); // trigger the click of the button that opens up the popup with the RG Users inside it
}
});
SECOND PART JS
var anchorNodeId = properties.param1; // parameter 1 passed to the second WF Action (see screenshot E above)
var userName = properties.param2; // parameter 2 passed to the second WF Action (see screenshot E above)
if (anchorNodeId) {
console.log("anchorNodeId found");
var richTextEditor = document.getElementById('CommentRichText'); // Attribute ID of your Rich Text Editor
var anchorNode = document.getElementById(anchorNodeId); // Select the desired element through the ID
if (anchorNode) {
console.log("anchorNode element found");
// Compose the string to inject and style it as you prefere (in this case it is styled as blue and underlined)
var stringToInsert = '<span style="color: blue; text-decoration: underline;">@' + userName + '</span>';
// Create HTML element from markup
var parser = new DOMParser();
var doc = parser.parseFromString(stringToInsert, 'text/html');
var newNode = doc.body.firstChild;
// Replace the element injected in the First Part of the Process with the new desired element (in this case "@UserName")
anchorNode.replaceWith(newNode);
// Update the Selection
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(newNode.nextSibling);
// Add a space in the node after the injected one (so, when the user keep writing after the mention, the style of the text should be the one as before the injection)
newNode.nextSibling.textContent = ' ' + newNode.nextSibling.textContent;
// Set the cursor in the node after the injected one, in particular after the just-added space after the injected string
range.setStart(newNode.nextSibling, 1);
range.setEnd(newNode.nextSibling, 1);
// Update the selection
selection.removeAllRanges();
selection.addRange(range);
}
}