UNPKG

@chayns-components/emoji-input

Version:
281 lines (272 loc) • 11.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setChildIndex = exports.saveSelection = exports.restoreSelection = exports.moveSelectionOffset = exports.insertInvisibleCursorMarker = exports.getCharCodeThatWillBeDeleted = exports.findAndSelectText = void 0; var _number = require("./number"); var _text = require("./text"); let childIndex = -1; let endOffset = -1; let startOffset = -1; const saveSelection = (element, { shouldIgnoreEmptyTextNodes } = {}) => { const selection = window.getSelection(); if (!selection) { return; } const { anchorNode } = selection; if (!anchorNode) { return; } const range = selection.getRangeAt(0); let childNodesArray = Array.from(element.childNodes); if (shouldIgnoreEmptyTextNodes) { childNodesArray = childNodesArray.filter(({ nodeType, nodeValue }) => nodeType !== Node.TEXT_NODE || nodeValue !== '' && nodeValue !== '\u200B'); } childIndex = childNodesArray.indexOf(anchorNode); endOffset = range.endOffset; startOffset = range.startOffset; }; exports.saveSelection = saveSelection; const restoreSelection = element => { // Search for \u200C in child nodes. If found, set the childIndex, startOffset, and endOffset to the // position of the \u200C character. Also remove the \u200C character from the child node. If not found, // the childIndex, startOffset, and endOffset will be like before. const childNodesArray = Array.from(element.childNodes); let hasFoundNoJoiner = false; childNodesArray.forEach(node => { if (!hasFoundNoJoiner && node.nodeType === Node.TEXT_NODE && typeof node.nodeValue === 'string') { const noJoinerIndex = node.nodeValue.indexOf('\u200C'); if (noJoinerIndex !== -1) { hasFoundNoJoiner = true; childIndex = childNodesArray.indexOf(node); startOffset = noJoinerIndex; endOffset = noJoinerIndex; } } }); // Remove all no joiner characters from the child nodes if no joiner was found if (hasFoundNoJoiner) { childNodesArray.forEach(node => { if (node.nodeType === Node.TEXT_NODE && typeof node.nodeValue === 'string') { // eslint-disable-next-line no-param-reassign node.nodeValue = node.nodeValue.replace(/\u200C/g, ''); } }); } let childNode = element.childNodes[childIndex]; const selection = window.getSelection(); if (!childNode || !element || !selection) { return; } // noinspection SuspiciousTypeOfGuard if (typeof childNode.nodeValue !== 'string') { const elementTextLength = (0, _text.getElementTextLength)(childNode); if (childNode.nextSibling) { childNode = childNode.nextSibling; if (childNode.nodeType === Node.TEXT_NODE && childNode.nodeValue) { endOffset -= elementTextLength; startOffset -= elementTextLength; if (childNode.nodeValue.charCodeAt(endOffset) === 8203) { endOffset += 1; startOffset += 1; } } else { var _childNode$parentNode; const textNode = document.createTextNode('\u200B'); (_childNode$parentNode = childNode.parentNode) === null || _childNode$parentNode === void 0 || _childNode$parentNode.insertBefore(textNode, childNode.nextSibling); childNode = textNode; endOffset = textNode.length; startOffset = textNode.length; } } else { var _childNode$parentNode2; const textNode = document.createTextNode('\u200B'); (_childNode$parentNode2 = childNode.parentNode) === null || _childNode$parentNode2 === void 0 || _childNode$parentNode2.insertBefore(textNode, childNode.nextSibling); childNode = textNode; endOffset = textNode.length; startOffset = textNode.length; } } else if (childNode.nodeValue && endOffset > childNode.nodeValue.length) { var _childNode$nextSiblin; if ((_childNode$nextSiblin = childNode.nextSibling) !== null && _childNode$nextSiblin !== void 0 && _childNode$nextSiblin.nodeValue) { let elementTextLength = childNode.nodeValue.length; childNode = childNode.nextSibling; // noinspection SuspiciousTypeOfGuard if (typeof childNode.nodeValue !== 'string') { var _childNode$nextSiblin2; elementTextLength += (0, _text.getElementTextLength)(childNode); if ((_childNode$nextSiblin2 = childNode.nextSibling) !== null && _childNode$nextSiblin2 !== void 0 && _childNode$nextSiblin2.nodeValue) { childNode = childNode.nextSibling; if (childNode.nodeType === Node.TEXT_NODE && childNode.nodeValue) { endOffset -= elementTextLength; startOffset -= elementTextLength; if (childNode.nodeValue.charCodeAt(endOffset) === 8203) { endOffset += 1; startOffset += 1; } } else { var _childNode$parentNode3; const textNode = document.createTextNode('\u200B'); (_childNode$parentNode3 = childNode.parentNode) === null || _childNode$parentNode3 === void 0 || _childNode$parentNode3.insertBefore(textNode, childNode.nextSibling); childNode = textNode; endOffset = textNode.length; startOffset = textNode.length; } } else { var _childNode$parentNode4; const textNode = document.createTextNode('\u200B'); (_childNode$parentNode4 = childNode.parentNode) === null || _childNode$parentNode4 === void 0 || _childNode$parentNode4.insertBefore(textNode, childNode.nextSibling); childNode = textNode; endOffset = textNode.length; startOffset = textNode.length; } } } else { endOffset = childNode.nodeValue.length; startOffset = childNode.nodeValue.length; } } const range = document.createRange(); // noinspection SuspiciousTypeOfGuard if (typeof childNode.nodeValue === 'string') { startOffset = (0, _number.clamp)(startOffset, 0, childNode.nodeValue.length); endOffset = (0, _number.clamp)(endOffset, 0, childNode.nodeValue.length); } try { range.setStart(childNode, startOffset); range.setEnd(childNode, endOffset); } catch (error) { // Do nothing } selection.removeAllRanges(); selection.addRange(range); range.collapse(true); }; exports.restoreSelection = restoreSelection; const insertInvisibleCursorMarker = () => { const selection = window.getSelection(); if (!selection || selection.rangeCount === 0) { return; } const range = selection.getRangeAt(0); const textNode = range.startContainer; const offset = range.startOffset; // noinspection JSDeprecatedSymbols document.execCommand('delete', false); const span = document.createElement('span'); span.style.display = 'inline-block'; span.style.width = '0'; span.style.height = '0'; span.className = 'invisible-cursor-marker'; const parent = textNode.parentNode; if (parent) { if (textNode.nodeType === Node.TEXT_NODE) { const textContent = textNode.textContent || ''; const beforeText = textContent.slice(0, offset); const afterText = textContent.slice(offset); textNode.textContent = beforeText; parent.insertBefore(span, textNode.nextSibling); const afterTextNode = document.createTextNode(afterText); parent.insertBefore(afterTextNode, span.nextSibling); setTimeout(() => { // Set cursor to cursor element const newRange = document.createRange(); newRange.setStartAfter(span); newRange.setEndAfter(span); selection.removeAllRanges(); selection.addRange(newRange); // Remove cursor element span.remove(); }, 10); } } }; exports.insertInvisibleCursorMarker = insertInvisibleCursorMarker; const moveSelectionOffset = distance => { endOffset += distance; startOffset += distance; }; exports.moveSelectionOffset = moveSelectionOffset; const setChildIndex = index => { childIndex = index; }; /** * This function returns the code of the character that will be removed by the KeyDown event in the * next step, if the "Backspace" or "Delete" key was pressed and there is no selection of multiple * characters. * * @param event - Keyboard event from "onKeyDown" */ exports.setChildIndex = setChildIndex; const getCharCodeThatWillBeDeleted = event => { var _window$getSelection, _nextSibling$nodeValu; const range = (_window$getSelection = window.getSelection()) === null || _window$getSelection === void 0 ? void 0 : _window$getSelection.getRangeAt(0); /** * At this point the function is aborted if there is no selection range, several characters have * been selected and therefore no single letter is removed or neither the "Backspace" nor the * "Delete" key has been pressed. */ if (!range || range.endOffset !== range.startOffset || event.key !== 'Backspace' && event.key !== 'Delete' && event.key !== 'Unidentified') { return null; } if (event.key === 'Backspace' || event.key === 'Unidentified') { var _previousSibling$node; const { nodeValue, previousSibling } = range.startContainer; if (range.startOffset > 0) { return nodeValue === null || nodeValue === void 0 ? void 0 : nodeValue.charCodeAt(range.startOffset - 1); } return previousSibling === null || previousSibling === void 0 || (_previousSibling$node = previousSibling.nodeValue) === null || _previousSibling$node === void 0 ? void 0 : _previousSibling$node.charCodeAt(previousSibling.nodeValue.length - 1); } const { nextSibling, nodeValue } = range.endContainer; if (range.endOffset < ((nodeValue === null || nodeValue === void 0 ? void 0 : nodeValue.length) ?? 0)) { return nodeValue === null || nodeValue === void 0 ? void 0 : nodeValue.charCodeAt(range.endOffset); } return nextSibling === null || nextSibling === void 0 || (_nextSibling$nodeValu = nextSibling.nodeValue) === null || _nextSibling$nodeValu === void 0 ? void 0 : _nextSibling$nodeValu.charCodeAt(0); }; exports.getCharCodeThatWillBeDeleted = getCharCodeThatWillBeDeleted; const findAndSelectText = ({ editorElement, searchText }) => { var _editorElement$textCo; if (!((_editorElement$textCo = editorElement.textContent) !== null && _editorElement$textCo !== void 0 && _editorElement$textCo.includes(searchText))) { return null; } const range = document.createRange(); let startNode = null; let offset = -1; const searchNodesForText = node => { if (node.nodeType === Node.TEXT_NODE) { var _node$textContent; const index = (_node$textContent = node.textContent) === null || _node$textContent === void 0 ? void 0 : _node$textContent.indexOf(searchText); if (typeof index === 'number' && index !== -1) { startNode = node; offset = index; range.setStart(node, index); range.setEnd(node, index + searchText.length); return true; } } else if (node.nodeName !== 'LC_MENTION') { return Array.from(node.childNodes).some(searchNodesForText); } return false; }; searchNodesForText(editorElement); if (startNode && offset !== -1) return range; return null; }; exports.findAndSelectText = findAndSelectText; //# sourceMappingURL=selection.js.map