UNPKG

@selfcommunity/react-ui

Version:

React UI Components to integrate a Community created with SelfCommunity Platform.

139 lines (138 loc) 6.07 kB
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime"; import { $isAutoLinkNode, $isLinkNode, TOGGLE_LINK_COMMAND } from '@lexical/link'; import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext'; import { $findMatchingParent, mergeRegister } from '@lexical/utils'; import { $getSelection, $isRangeSelection, COMMAND_PRIORITY_CRITICAL, COMMAND_PRIORITY_HIGH, COMMAND_PRIORITY_LOW, KEY_ESCAPE_COMMAND, SELECTION_CHANGE_COMMAND } from 'lexical'; import { useCallback, useEffect, useState } from 'react'; import { getSelectedNode } from '../../../utils/editor'; import { isValidUrl } from '@selfcommunity/utils'; import { styled } from '@mui/material/styles'; import { IconButton, InputAdornment, Paper, Popper, TextField } from '@mui/material'; import Icon from '@mui/material/Icon'; import { PREFIX } from '../constants'; const classes = { root: `${PREFIX}-floating-link-plugin-root` }; const Root = styled(Popper, { name: PREFIX, slot: 'FloatingLinkPluginRoot' })(() => ({})); function FloatingLinkPlugin({ editor, isLink, setIsLink }) { const [anchorEl, setAnchorEl] = useState(null); const [linkUrl, setLinkUrl] = useState(''); const [hideForUrl, setHideForUrl] = useState(null); const [lastSelection, setLastSelection] = useState(null); const updateLinkEditor = useCallback(() => { var _a; const selection = $getSelection(); if ($isRangeSelection(selection)) { const node = getSelectedNode(selection); const parent = node.getParent(); if ($isLinkNode(parent)) { setLinkUrl(parent.getURL()); } else if ($isLinkNode(node)) { setLinkUrl(node.getURL()); } else { setLinkUrl(''); } } const nativeSelection = window.getSelection(); const activeElement = document.activeElement; const rootElement = editor.getRootElement(); if (selection !== null && nativeSelection !== null && rootElement !== null && rootElement.contains(nativeSelection.anchorNode) && editor.isEditable()) { setAnchorEl((_a = nativeSelection.focusNode) === null || _a === void 0 ? void 0 : _a.parentElement); setLastSelection(selection); } else if (!activeElement) { setAnchorEl(null); setLastSelection(null); setLinkUrl(''); } return true; }, [editor]); useEffect(() => { if (linkUrl != hideForUrl) { setHideForUrl(null); } }, [linkUrl]); useEffect(() => { return mergeRegister(editor.registerUpdateListener(({ editorState }) => { editorState.read(() => { updateLinkEditor(); }); }), editor.registerCommand(SELECTION_CHANGE_COMMAND, () => { updateLinkEditor(); return true; }, COMMAND_PRIORITY_LOW), editor.registerCommand(KEY_ESCAPE_COMMAND, () => { if (isLink) { setIsLink(false); return true; } return false; }, COMMAND_PRIORITY_HIGH)); }, [editor, updateLinkEditor, setIsLink, isLink]); useEffect(() => { editor.getEditorState().read(() => { updateLinkEditor(); }); }, [editor, updateLinkEditor]); const handleLinkSubmission = () => { if (lastSelection !== null) { if (linkUrl !== '') { setHideForUrl(linkUrl); editor.dispatchCommand(TOGGLE_LINK_COMMAND, isValidUrl(linkUrl) ? linkUrl : 'https://'); } } }; if (!isLink || linkUrl === hideForUrl) { return null; } return (_jsx(Root, Object.assign({ className: classes.root, open: Boolean(anchorEl), anchorEl: anchorEl, placement: "right" }, { children: _jsx(Paper, { children: _jsx(TextField, { size: "small", value: linkUrl, variant: "outlined", onChange: (event) => { setLinkUrl(event.target.value); }, InputProps: { endAdornment: (_jsxs(InputAdornment, Object.assign({ position: "end" }, { children: [_jsx(IconButton, Object.assign({ size: "small", tabIndex: 0, onClick: () => { setIsLink(false); } }, { children: _jsx(Icon, { children: "close" }) })), _jsx(IconButton, Object.assign({ size: "small", tabIndex: 1, onClick: handleLinkSubmission }, { children: _jsx(Icon, { children: "check" }) }))] }))) } }) }) }))); } function useFloatingLinkEditorToolbar(editor) { const [activeEditor, setActiveEditor] = useState(editor); const [isLink, setIsLink] = useState(false); const updateToolbar = useCallback(() => { const selection = $getSelection(); if ($isRangeSelection(selection)) { const node = getSelectedNode(selection); const linkParent = $findMatchingParent(node, $isLinkNode); const autoLinkParent = $findMatchingParent(node, $isAutoLinkNode); // We don't want this menu to open for auto links. if (linkParent != null && autoLinkParent == null) { setIsLink(true); } else { setIsLink(false); } } }, []); useEffect(() => { return mergeRegister(editor.registerUpdateListener(({ editorState }) => { editorState.read(() => { updateToolbar(); }); }), editor.registerCommand(SELECTION_CHANGE_COMMAND, (_payload, newEditor) => { updateToolbar(); setActiveEditor(newEditor); return false; }, COMMAND_PRIORITY_CRITICAL)); }, [editor, updateToolbar]); return _jsx(FloatingLinkPlugin, { editor: activeEditor, isLink: isLink, setIsLink: setIsLink }); } export default () => { const [editor] = useLexicalComposerContext(); return useFloatingLinkEditorToolbar(editor); };