UNPKG

@lexical/react

Version:

This package provides Lexical components and hooks for React applications.

104 lines (97 loc) 3.2 kB
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ 'use strict'; var link = require('@lexical/link'); var LexicalComposerContext = require('@lexical/react/LexicalComposerContext'); var utils = require('@lexical/utils'); var lexical = require('lexical'); var react = require('react'); /** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ function findMatchingDOM(startNode, predicate) { let node = startNode; while (node != null) { if (predicate(node)) { return node; } node = node.parentNode; } return null; } function ClickableLinkPlugin({ newTab = true, disabled = false }) { const [editor] = LexicalComposerContext.useLexicalComposerContext(); react.useEffect(() => { const onClick = event => { const target = event.target; if (!lexical.isDOMNode(target)) { return; } const nearestEditor = lexical.getNearestEditorFromDOMNode(target); if (nearestEditor === null) { return; } let url = null; let urlTarget = null; nearestEditor.update(() => { const clickedNode = lexical.$getNearestNodeFromDOMNode(target); if (clickedNode !== null) { const maybeLinkNode = utils.$findMatchingParent(clickedNode, lexical.$isElementNode); if (!disabled) { if (link.$isLinkNode(maybeLinkNode)) { url = maybeLinkNode.sanitizeUrl(maybeLinkNode.getURL()); urlTarget = maybeLinkNode.getTarget(); } else { const a = findMatchingDOM(target, utils.isHTMLAnchorElement); if (a !== null) { url = a.href; urlTarget = a.target; } } } } }); if (url === null || url === '') { return; } // Allow user to select link text without following url const selection = editor.getEditorState().read(lexical.$getSelection); if (lexical.$isRangeSelection(selection) && !selection.isCollapsed()) { event.preventDefault(); return; } const isMiddle = event.type === 'auxclick' && event.button === 1; window.open(url, newTab || isMiddle || event.metaKey || event.ctrlKey || urlTarget === '_blank' ? '_blank' : '_self'); event.preventDefault(); }; const onMouseUp = event => { if (event.button === 1) { onClick(event); } }; return editor.registerRootListener((rootElement, prevRootElement) => { if (prevRootElement !== null) { prevRootElement.removeEventListener('click', onClick); prevRootElement.removeEventListener('mouseup', onMouseUp); } if (rootElement !== null) { rootElement.addEventListener('click', onClick); rootElement.addEventListener('mouseup', onMouseUp); } }); }, [editor, newTab, disabled]); return null; } exports.ClickableLinkPlugin = ClickableLinkPlugin;