BookStackApp/BookStack

View on GitHub
resources/js/wysiwyg/services/auto-links.ts

Summary

Maintainability
A
0 mins
Test Coverage
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();
    };
}