I'm attempting to implement the case change feature available in Microsoft Word with Shift + F3 into a TinyMCE React editor. The problem I'm running into is the last part where it should keep the same text selected/highlighted. The below works fine, as long as I haven't highlighted the last character of a node. If I have selected the end of a line, I get an error: Uncaught DOMException: Index or size is negative or greater than the allowed amount
So far I have the following:
const handleCaseChange = (ed: Editor) => {
ed.on("keydown", (event: KeyboardEvent) => {
if (event.shiftKey && event.key === "F3") {
event.preventDefault();
const selection = ed.selection.getSel();
const selectedText = selection?.toString();
const startOffset = selection?.getRangeAt(0).startOffset;
const endOffset = selection?.getRangeAt(0).endOffset;
if (selectedText !== undefined && selectedText.length > 0) {
let transformedText;
if (selectedText === selectedText.toUpperCase()) {
transformedText = selectedText.toLowerCase();
} else if (selectedText === selectedText.toLowerCase()) {
transformedText = capitalizeEachWord(selectedText);
} else {
transformedText = selectedText.toUpperCase();
}
ed.selection.setContent(transformedText);
const range = ed.getDoc().createRange();
// This is what's currently erroring
range.setStart(selection.anchorNode, startOffset);
if (endOffset === selection?.anchorNode?.textContent?.length) {
range.setEndAfter(selection.anchorNode);
} else {
range.setEnd(selection.anchorNode, endOffset);
}
selection.removeAllRanges();
selection.addRange(range);
}
}
}
}
const capitalizeEachWord = (str: string) => str.replace(/\b\w/g, (char: string) => char.toUpperCase());
What else could I try in the range.setStart
to get this to work correctly?
I found a simple solution from the TinyMCE docs - selection.getBookmark().
getBookmark
Returns a bookmark location for the current selection. This bookmark object can then be used to restore the selection after some content modification to the document.
I tried the below and it works to change the case and then mark the new content as selected.
const handleCaseChange = (ed: Editor) => {
ed.on("keydown", (event: KeyboardEvent) => {
if (event.shiftKey && event.key === "F3") {
event.preventDefault();
const selection = ed.selection.getSel();
const selectedText = selection?.toString();
if (selectedText !== undefined && selectedText.length > 0) {
let transformedText;
if (selectedText === selectedText.toUpperCase()) {
transformedText = selectedText.toLowerCase();
} else if (selectedText === selectedText.toLowerCase()) {
transformedText = capitalizeEachWord(selectedText);
} else {
transformedText = selectedText.toUpperCase();
}
const bookmark = ed.selection.getBookmark();
ed.selection.setContent(transformedText);
ed.selection.moveToBookmark(bookmark);
}
}
}
}
const capitalizeEachWord = (str: string) => str.replace(/\b\w/g, (char: string) => char.toUpperCase());