@selfcommunity/react-ui
Version:
React UI Components to integrate a Community created with SelfCommunity Platform.
277 lines (276 loc) • 16.1 kB
JavaScript
"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;