resources/js/wysiwyg/services/auto-links.ts
import {
$getSelection, BaseSelection,
COMMAND_PRIORITY_NORMAL,
KEY_ENTER_COMMAND,
KEY_SPACE_COMMAND,
LexicalEditor,
TextNode
} from "lexical";
import {$getTextNodeFromSelection} from "../utils/selection";
import {$createLinkNode, LinkNode} from "@lexical/link";
function isLinkText(text: string): boolean {
const lower = text.toLowerCase();
if (!lower.startsWith('http')) {
return false;
}
const linkRegex = /(http|https):\/\/(\S+)\.\S+$/;
return linkRegex.test(text);
}
function handlePotentialLinkEvent(node: TextNode, selection: BaseSelection, editor: LexicalEditor) {
const selectionRange = selection.getStartEndPoints();
if (!selectionRange) {
return;
}
const cursorPoint = selectionRange[0].offset;
const nodeText = node.getTextContent();
const rTrimText = nodeText.slice(0, cursorPoint);
const priorSpaceIndex = rTrimText.lastIndexOf(' ');
const startIndex = priorSpaceIndex + 1;
const textSegment = nodeText.slice(startIndex, cursorPoint);
if (!isLinkText(textSegment)) {
return;
}
editor.update(() => {
const linkNode: LinkNode = $createLinkNode(textSegment);
linkNode.append(new TextNode(textSegment));
const splits = node.splitText(startIndex, cursorPoint);
const targetIndex = splits.length === 3 ? 1 : 0;
const targetText = splits[targetIndex];
if (targetText) {
targetText.replace(linkNode);
}
});
}
export function registerAutoLinks(editor: LexicalEditor): () => void {
const handler = (payload: KeyboardEvent): boolean => {
const selection = $getSelection();
const textNode = $getTextNodeFromSelection(selection);
if (textNode && selection) {
handlePotentialLinkEvent(textNode, selection, editor);
}
return false;
};
const unregisterSpace = editor.registerCommand(KEY_SPACE_COMMAND, handler, COMMAND_PRIORITY_NORMAL);
const unregisterEnter = editor.registerCommand(KEY_ENTER_COMMAND, handler, COMMAND_PRIORITY_NORMAL);
return (): void => {
unregisterSpace();
unregisterEnter();
};
}