@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
142 lines (141 loc) • 6.67 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const jsx_runtime_1 = require("react/jsx-runtime");
const link_1 = require("@lexical/link");
const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext");
const utils_1 = require("@lexical/utils");
const lexical_1 = require("lexical");
const react_1 = require("react");
const editor_1 = require("../../../utils/editor");
const utils_2 = require("@selfcommunity/utils");
const styles_1 = require("@mui/material/styles");
const material_1 = require("@mui/material");
const Icon_1 = tslib_1.__importDefault(require("@mui/material/Icon"));
const constants_1 = require("../constants");
const classes = {
root: `${constants_1.PREFIX}-floating-link-plugin-root`
};
const Root = (0, styles_1.styled)(material_1.Popper, {
name: constants_1.PREFIX,
slot: 'FloatingLinkPluginRoot'
})(() => ({}));
function FloatingLinkPlugin({ editor, isLink, setIsLink }) {
const [anchorEl, setAnchorEl] = (0, react_1.useState)(null);
const [linkUrl, setLinkUrl] = (0, react_1.useState)('');
const [hideForUrl, setHideForUrl] = (0, react_1.useState)(null);
const [lastSelection, setLastSelection] = (0, react_1.useState)(null);
const updateLinkEditor = (0, react_1.useCallback)(() => {
var _a;
const selection = (0, lexical_1.$getSelection)();
if ((0, lexical_1.$isRangeSelection)(selection)) {
const node = (0, editor_1.getSelectedNode)(selection);
const parent = node.getParent();
if ((0, link_1.$isLinkNode)(parent)) {
setLinkUrl(parent.getURL());
}
else if ((0, link_1.$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]);
(0, react_1.useEffect)(() => {
if (linkUrl != hideForUrl) {
setHideForUrl(null);
}
}, [linkUrl]);
(0, react_1.useEffect)(() => {
return (0, utils_1.mergeRegister)(editor.registerUpdateListener(({ editorState }) => {
editorState.read(() => {
updateLinkEditor();
});
}), editor.registerCommand(lexical_1.SELECTION_CHANGE_COMMAND, () => {
updateLinkEditor();
return true;
}, lexical_1.COMMAND_PRIORITY_LOW), editor.registerCommand(lexical_1.KEY_ESCAPE_COMMAND, () => {
if (isLink) {
setIsLink(false);
return true;
}
return false;
}, lexical_1.COMMAND_PRIORITY_HIGH));
}, [editor, updateLinkEditor, setIsLink, isLink]);
(0, react_1.useEffect)(() => {
editor.getEditorState().read(() => {
updateLinkEditor();
});
}, [editor, updateLinkEditor]);
const handleLinkSubmission = () => {
if (lastSelection !== null) {
if (linkUrl !== '') {
setHideForUrl(linkUrl);
editor.dispatchCommand(link_1.TOGGLE_LINK_COMMAND, (0, utils_2.isValidUrl)(linkUrl) ? linkUrl : 'https://');
}
}
};
if (!isLink || linkUrl === hideForUrl) {
return null;
}
return ((0, jsx_runtime_1.jsx)(Root, Object.assign({ className: classes.root, open: Boolean(anchorEl), anchorEl: anchorEl, placement: "right" }, { children: (0, jsx_runtime_1.jsx)(material_1.Paper, { children: (0, jsx_runtime_1.jsx)(material_1.TextField, { size: "small", value: linkUrl, variant: "outlined", onChange: (event) => {
setLinkUrl(event.target.value);
}, InputProps: {
endAdornment: ((0, jsx_runtime_1.jsxs)(material_1.InputAdornment, Object.assign({ position: "end" }, { children: [(0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ size: "small", tabIndex: 0, onClick: () => {
setIsLink(false);
} }, { children: (0, jsx_runtime_1.jsx)(Icon_1.default, { children: "close" }) })), (0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ size: "small", tabIndex: 1, onClick: handleLinkSubmission }, { children: (0, jsx_runtime_1.jsx)(Icon_1.default, { children: "check" }) }))] })))
} }) }) })));
}
function useFloatingLinkEditorToolbar(editor) {
const [activeEditor, setActiveEditor] = (0, react_1.useState)(editor);
const [isLink, setIsLink] = (0, react_1.useState)(false);
const updateToolbar = (0, react_1.useCallback)(() => {
const selection = (0, lexical_1.$getSelection)();
if ((0, lexical_1.$isRangeSelection)(selection)) {
const node = (0, editor_1.getSelectedNode)(selection);
const linkParent = (0, utils_1.$findMatchingParent)(node, link_1.$isLinkNode);
const autoLinkParent = (0, utils_1.$findMatchingParent)(node, link_1.$isAutoLinkNode);
// We don't want this menu to open for auto links.
if (linkParent != null && autoLinkParent == null) {
setIsLink(true);
}
else {
setIsLink(false);
}
}
}, []);
(0, react_1.useEffect)(() => {
return (0, utils_1.mergeRegister)(editor.registerUpdateListener(({ editorState }) => {
editorState.read(() => {
updateToolbar();
});
}), editor.registerCommand(lexical_1.SELECTION_CHANGE_COMMAND, (_payload, newEditor) => {
updateToolbar();
setActiveEditor(newEditor);
return false;
}, lexical_1.COMMAND_PRIORITY_CRITICAL));
}, [editor, updateToolbar]);
return (0, jsx_runtime_1.jsx)(FloatingLinkPlugin, { editor: activeEditor, isLink: isLink, setIsLink: setIsLink });
}
exports.default = () => {
const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)();
return useFloatingLinkEditorToolbar(editor);
};