UNPKG

@selfcommunity/react-ui

Version:

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

277 lines (276 loc) • 16.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const jsx_runtime_1 = require("react/jsx-runtime"); const lexical_1 = require("lexical"); const link_1 = require("@lexical/link"); const list_1 = require("@lexical/list"); const LexicalComposerContext_1 = require("@lexical/react/LexicalComposerContext"); const LexicalDecoratorBlockNode_1 = require("@lexical/react/LexicalDecoratorBlockNode"); const rich_text_1 = require("@lexical/rich-text"); const selection_1 = require("@lexical/selection"); const table_1 = require("@lexical/table"); const utils_1 = require("@lexical/utils"); const React = tslib_1.__importStar(require("react")); const react_1 = require("react"); const editor_1 = require("../../../utils/editor"); const material_1 = require("@mui/material"); const react_intl_1 = require("react-intl"); const styles_1 = require("@mui/material/styles"); const system_1 = require("@mui/system"); const ImagePlugin_1 = tslib_1.__importDefault(require("./ImagePlugin")); const EmojiPlugin_1 = tslib_1.__importDefault(require("./EmojiPlugin")); const LexicalHorizontalRuleNode_1 = require("@lexical/react/LexicalHorizontalRuleNode"); const constants_1 = require("../constants"); const blockTypeToBlockIcon = { h1: 'format_heading_1', h2: 'format_heading_2', h3: 'format_heading_3', bullet: 'format_list_bulleted', number: 'format_list_numbered', quote: 'format_quote', paragraph: 'format_paragraph' }; const rootTypeToRootName = { root: 'Root', table: 'Table' }; const FORMATS = ['bold', 'underline', 'italic', 'strikethrough', 'subscript', 'superscript']; const ALIGNMENTS = ['left', 'right', 'center', 'justify']; function BlockFormatIconButton({ className = '', editor, blockType, disabled = false }) { // STATE const [anchorEl, setAnchorEl] = React.useState(null); // FORMAT METHODS const formatParagraph = () => { editor.update(() => { const selection = (0, lexical_1.$getSelection)(); if ((0, lexical_1.$isRangeSelection)(selection) || (0, table_1.$isTableSelection)(selection)) { (0, selection_1.$setBlocksType)(selection, () => (0, lexical_1.$createParagraphNode)()); } }); }; const formatHeading = (headingSize) => { if (blockType !== headingSize) { editor.update(() => { const selection = (0, lexical_1.$getSelection)(); if ((0, lexical_1.$isRangeSelection)(selection) || (0, table_1.$isTableSelection)(selection)) { (0, selection_1.$setBlocksType)(selection, () => (0, rich_text_1.$createHeadingNode)(headingSize)); } }); } }; const formatBulletList = () => { if (blockType !== 'bullet') { editor.dispatchCommand(list_1.INSERT_UNORDERED_LIST_COMMAND, undefined); } else { editor.dispatchCommand(list_1.REMOVE_LIST_COMMAND, undefined); } }; const formatNumberedList = () => { if (blockType !== 'number') { editor.dispatchCommand(list_1.INSERT_ORDERED_LIST_COMMAND, undefined); } else { editor.dispatchCommand(list_1.REMOVE_LIST_COMMAND, undefined); } }; const formatQuote = () => { if (blockType !== 'quote') { editor.update(() => { const selection = (0, lexical_1.$getSelection)(); if ((0, lexical_1.$isRangeSelection)(selection) || (0, table_1.$isTableSelection)(selection)) { (0, selection_1.$setBlocksType)(selection, () => (0, rich_text_1.$createQuoteNode)()); } }); } }; // HANDLERS const handleSelect = (block) => (event) => { switch (block) { case 'bullet': formatBulletList(); break; case 'number': formatNumberedList(); break; case 'paragraph': formatParagraph(); break; case 'quote': formatQuote(); break; default: formatHeading(block); break; } setAnchorEl(null); }; const handleOpen = (event) => { setAnchorEl(event.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ className: className, disabled: disabled, onClick: handleOpen }, { children: (0, jsx_runtime_1.jsx)(material_1.Tooltip, Object.assign({ title: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.editor.toolbarPlugin.blockType", defaultMessage: "ui.editor.toolbarPlugin.blockType" }) }, { children: (0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(material_1.Icon, { children: blockTypeToBlockIcon[blockType] }), (0, jsx_runtime_1.jsx)(material_1.Icon, { children: anchorEl ? 'expand_less' : 'expand_more' })] }) })) })), (0, jsx_runtime_1.jsx)(material_1.Menu, Object.assign({ anchorEl: anchorEl, open: Boolean(anchorEl), onClose: handleClose }, { children: Object.keys(blockTypeToBlockIcon).map((block) => ((0, jsx_runtime_1.jsx)(material_1.MenuItem, Object.assign({ onClick: handleSelect(block) }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: blockTypeToBlockIcon[block] }) }), block))) }))] })); } const classes = { root: `${constants_1.PREFIX}-toolbar-plugin-root`, blockFormat: `${constants_1.PREFIX}-block-format` }; const Root = (0, styles_1.styled)(material_1.Box, { name: constants_1.PREFIX, slot: 'ToolbarPluginRoot' })(() => ({})); function ToolbarPlugin(inProps) { // PROPS const props = (0, system_1.useThemeProps)({ props: inProps, name: constants_1.PREFIX }); const { uploadImage = false } = props; // STATE const [editor] = (0, LexicalComposerContext_1.useLexicalComposerContext)(); const [activeEditor, setActiveEditor] = (0, react_1.useState)(editor); const [blockType, setBlockType] = (0, react_1.useState)('paragraph'); const [rootType, setRootType] = (0, react_1.useState)('root'); const [selectedElementKey, setSelectedElementKey] = (0, react_1.useState)(null); const [isLink, setIsLink] = (0, react_1.useState)(false); const [isEditable, setIsEditable] = (0, react_1.useState)(() => editor.isEditable()); const [formats, setFormats] = (0, react_1.useState)([]); const [alignment, setAlignment] = (0, react_1.useState)(ALIGNMENTS[0]); const $updateToolbar = (0, react_1.useCallback)(() => { const selection = (0, lexical_1.$getSelection)(); if ((0, lexical_1.$isRangeSelection)(selection)) { const anchorNode = selection.anchor.getNode(); let element = anchorNode.getKey() === 'root' ? anchorNode : (0, utils_1.$findMatchingParent)(anchorNode, (e) => { const parent = e.getParent(); return parent !== null && (0, lexical_1.$isRootOrShadowRoot)(parent); }); if (element === null) { element = anchorNode.getTopLevelElementOrThrow(); } const elementKey = element.getKey(); const elementDOM = activeEditor.getElementByKey(elementKey); // Update text format setFormats(FORMATS.filter((f) => selection.hasFormat(f))); // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore setAlignment(ALIGNMENTS.find((a) => { var _a; return ((_a = element.getFormatType) === null || _a === void 0 ? void 0 : _a.call(element)) === a; }) || ALIGNMENTS[0]); // Update links const node = (0, editor_1.getSelectedNode)(selection); const parent = node.getParent(); if ((0, link_1.$isLinkNode)(parent) || (0, link_1.$isLinkNode)(node)) { setIsLink(true); } else { setIsLink(false); } const tableNode = (0, utils_1.$findMatchingParent)(node, table_1.$isTableNode); if ((0, table_1.$isTableNode)(tableNode)) { setRootType('table'); } else { setRootType('root'); } if (elementDOM !== null) { setSelectedElementKey(elementKey); if ((0, list_1.$isListNode)(element)) { const parentList = (0, utils_1.$getNearestNodeOfType)(anchorNode, list_1.ListNode); const type = parentList ? parentList.getListType() : element.getListType(); setBlockType(type); } else { // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore const type = (0, rich_text_1.$isHeadingNode)(element) ? element.getTag() : element.getType(); if (type in blockTypeToBlockIcon) { setBlockType(type); } } } } }, [activeEditor]); (0, react_1.useEffect)(() => { return editor.registerCommand(lexical_1.SELECTION_CHANGE_COMMAND, (_payload, newEditor) => { $updateToolbar(); setActiveEditor(newEditor); return false; }, lexical_1.COMMAND_PRIORITY_CRITICAL); }, [editor, $updateToolbar]); (0, react_1.useEffect)(() => { return (0, utils_1.mergeRegister)(editor.registerEditableListener((editable) => { setIsEditable(editable); }), activeEditor.registerUpdateListener(({ editorState }) => { editorState.read(() => { $updateToolbar(); }); })); }, [$updateToolbar, activeEditor, editor]); const clearFormatting = (0, react_1.useCallback)(() => { activeEditor.update(() => { const selection = (0, lexical_1.$getSelection)(); if ((0, lexical_1.$isRangeSelection)(selection)) { const anchor = selection.anchor; const focus = selection.focus; const nodes = selection.getNodes(); if (anchor.key === focus.key && anchor.offset === focus.offset) { return; } nodes.forEach((node, idx) => { // We split the first and last node by the selection // So that we don't format unselected text inside those nodes if ((0, lexical_1.$isTextNode)(node)) { if (idx === 0 && anchor.offset !== 0) { node = node.splitText(anchor.offset)[1] || node; } if (idx === nodes.length - 1) { // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore node = node.splitText(focus.offset)[0] || node; } // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore if (node.__style !== '') { // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore node.setStyle(''); } // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore if (node.__format !== 0) { // eslint-disable-next-line @typescript-eslint/ban-ts-ignore // @ts-ignore node.setFormat(0); (0, utils_1.$getNearestBlockElementAncestorOrThrow)(node).setFormat(''); } } else if ((0, rich_text_1.$isHeadingNode)(node) || (0, rich_text_1.$isQuoteNode)(node)) { node.replace((0, lexical_1.$createParagraphNode)(), true); } else if ((0, LexicalDecoratorBlockNode_1.$isDecoratorBlockNode)(node)) { node.setFormat(''); } }); } }); }, [activeEditor]); const insertLink = (0, react_1.useCallback)(() => { if (!isLink) { editor.dispatchCommand(link_1.TOGGLE_LINK_COMMAND, 'https://'); } else { editor.dispatchCommand(link_1.TOGGLE_LINK_COMMAND, null); } }, [editor, isLink]); return ((0, jsx_runtime_1.jsxs)(Root, Object.assign({ className: classes.root }, { children: [blockType in blockTypeToBlockIcon && activeEditor === editor && ((0, jsx_runtime_1.jsx)(BlockFormatIconButton, { className: classes.blockFormat, disabled: !isEditable, blockType: blockType, editor: editor })), (0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ disabled: !isEditable, onClick: () => { activeEditor.dispatchCommand(lexical_1.FORMAT_ELEMENT_COMMAND, ALIGNMENTS[(ALIGNMENTS.findIndex((a) => alignment === a) + 1) % ALIGNMENTS.length]); } }, { children: (0, jsx_runtime_1.jsx)(material_1.Tooltip, Object.assign({ title: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: `ui.editor.toolbarPlugin.${alignment}`, defaultMessage: `ui.editor.toolbarPlugin.${alignment}` }) }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: `format_align_${alignment}` }) })) })), (0, jsx_runtime_1.jsx)(material_1.ToggleButtonGroup, Object.assign({ value: formats }, { children: FORMATS.map((format) => ((0, jsx_runtime_1.jsx)(material_1.ToggleButton, Object.assign({ value: format, disabled: !isEditable, onClick: () => { activeEditor.dispatchCommand(lexical_1.FORMAT_TEXT_COMMAND, format); } }, { children: (0, jsx_runtime_1.jsx)(material_1.Tooltip, Object.assign({ title: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: `ui.editor.toolbarPlugin.${format}`, defaultMessage: `ui.editor.toolbarPlugin.${format}` }) }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: `format_${format}` }) })) }), format))) })), (0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ disabled: !isEditable, onClick: clearFormatting }, { children: (0, jsx_runtime_1.jsx)(material_1.Tooltip, Object.assign({ title: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.editor.toolbarPlugin.clear", defaultMessage: "ui.editor.toolbarPlugin.clear" }) }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "format_clear" }) })) })), (0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ disabled: !isEditable, onClick: () => { activeEditor.dispatchCommand(LexicalHorizontalRuleNode_1.INSERT_HORIZONTAL_RULE_COMMAND, undefined); } }, { children: (0, jsx_runtime_1.jsx)(material_1.Tooltip, Object.assign({ title: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.editor.toolbarPlugin.horizontalRule", defaultMessage: "ui.editor.toolbarPlugin.horizontalRule" }) }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "format_horizontal_rule" }) })) })), uploadImage && (0, jsx_runtime_1.jsx)(ImagePlugin_1.default, {}), (0, jsx_runtime_1.jsx)(material_1.IconButton, Object.assign({ disabled: !isEditable, onClick: insertLink }, { children: (0, jsx_runtime_1.jsx)(material_1.Tooltip, Object.assign({ title: (0, jsx_runtime_1.jsx)(react_intl_1.FormattedMessage, { id: "ui.editor.toolbarPlugin.link", defaultMessage: "ui.editor.toolbarPlugin.link" }) }, { children: (0, jsx_runtime_1.jsx)(material_1.Icon, { children: "format_link" }) })) })), (0, jsx_runtime_1.jsx)(EmojiPlugin_1.default, {})] }))); } exports.default = ToolbarPlugin;