You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
7.9 KiB
JavaScript
215 lines
7.9 KiB
JavaScript
import { createContext, useEffect, useState } from 'react';
|
|
import ExampleTheme from "./themes/ExampleTheme";
|
|
import { LexicalComposer } from "@lexical/react/LexicalComposer";
|
|
import { RichTextPlugin } from "@lexical/react/LexicalRichTextPlugin";
|
|
import {PlainTextPlugin} from '@lexical/react/LexicalPlainTextPlugin';
|
|
import { ContentEditable } from "@lexical/react/LexicalContentEditable";
|
|
import { HistoryPlugin } from "@lexical/react/LexicalHistoryPlugin";
|
|
import { AutoFocusPlugin } from "@lexical/react/LexicalAutoFocusPlugin";
|
|
import {LexicalErrorBoundary} from "@lexical/react/LexicalErrorBoundary";
|
|
import {TabIndentationPlugin} from '@lexical/react/LexicalTabIndentationPlugin';
|
|
import TreeViewPlugin from "./plugins/TreeViewPlugin";
|
|
import ToolbarPlugin from "./plugins/ToolbarPlugin";
|
|
import { HeadingNode, QuoteNode } from "@lexical/rich-text";
|
|
import { TableCellNode, TableNode, TableRowNode } from "@lexical/table";
|
|
import { ListItemNode, ListNode } from "@lexical/list";
|
|
import { CodeHighlightNode, CodeNode } from "@lexical/code";
|
|
import { AutoLinkNode, LinkNode } from "@lexical/link";
|
|
// import {ClickableLinkPlugin} from '@lexical/react/LexicalClickableLinkPlugin';
|
|
import { LinkPlugin } from "@lexical/react/LexicalLinkPlugin";
|
|
import { ListPlugin } from "@lexical/react/LexicalListPlugin";
|
|
import { MarkdownShortcutPlugin } from "@lexical/react/LexicalMarkdownShortcutPlugin";
|
|
import {HorizontalRulePlugin} from '@lexical/react/LexicalHorizontalRulePlugin';
|
|
import {HorizontalRuleNode} from '@lexical/react/LexicalHorizontalRuleNode';
|
|
import { TRANSFORMERS } from "@lexical/markdown";
|
|
|
|
import ListMaxIndentLevelPlugin from "./plugins/ListMaxIndentLevelPlugin";
|
|
import CodeHighlightPlugin from "./plugins/CodeHighlightPlugin";
|
|
import AutoLinkPlugin from "./plugins/AutoLinkPlugin";
|
|
import TabFocusPlugin from './plugins/TabFocusPlugin';
|
|
import EditorRefPlugin from './plugins/EditorRefPlugin'
|
|
import ImagesPlugin from './plugins/ImagesPlugin';
|
|
import InlineImagePlugin from './plugins/InlineImagePlugin';
|
|
import DragDropPaste from './plugins/DragDropPastePlugin';
|
|
import { ImageNode } from './nodes/ImageNode';
|
|
import {InlineImageNode} from './nodes/InlineImageNode/InlineImageNode';
|
|
import { ExtendedTextNode } from './nodes/ExtendedTextNode';
|
|
|
|
import { useLexicalComposerContext } from '@lexical/react/LexicalComposerContext';
|
|
// import { useLexicalEditable } from '@lexical/react/useLexicalEditable';
|
|
|
|
import {TablePlugin} from '@lexical/react/LexicalTablePlugin';
|
|
import TableCellActionMenuPlugin from './plugins/TableActionMenuPlugin';
|
|
import TableCellResizer from './plugins/TableCellResizer';
|
|
// import TableHoverActionsPlugin from './plugins/TableHoverActionsPlugin';
|
|
|
|
// import {useLexicalEditable} from '@lexical/react/useLexicalEditable';
|
|
|
|
import FormatPaintPlugin from './plugins/FormatPaint';
|
|
|
|
|
|
import { TextNode, $getRoot, $getSelection, $createParagraphNode } from 'lexical';
|
|
import { $generateHtmlFromNodes, $generateNodesFromDOM, } from '@lexical/html';
|
|
// import { } from '@lexical/clipboard';
|
|
import { isEmpty } from '@/utils/commons';
|
|
import {useSettings} from './context/SettingsContext';
|
|
|
|
import './styles.css';
|
|
|
|
function Placeholder() {
|
|
return <div className="editor-placeholder">Enter some rich text...</div>;
|
|
}
|
|
|
|
const editorConfig = {
|
|
// The editor theme
|
|
// theme: {},
|
|
theme: ExampleTheme,
|
|
// Handling of errors during update
|
|
onError(error) {
|
|
console.log(error)
|
|
throw error;
|
|
},
|
|
// Any custom nodes go here
|
|
nodes: [
|
|
ExtendedTextNode,
|
|
{
|
|
replace: TextNode,
|
|
with: (node) => new ExtendedTextNode(node.__text),
|
|
withKlass: ExtendedTextNode,
|
|
},
|
|
HeadingNode,
|
|
ListNode,
|
|
ListItemNode,
|
|
QuoteNode,
|
|
CodeNode,
|
|
CodeHighlightNode,
|
|
TableNode,
|
|
TableCellNode,
|
|
TableRowNode,
|
|
AutoLinkNode,
|
|
LinkNode,
|
|
HorizontalRuleNode,
|
|
ImageNode,InlineImageNode,
|
|
]
|
|
};
|
|
|
|
function LexicalDefaultValuePlugin({ value = '' }= {}) {
|
|
const [editor] = useLexicalComposerContext();
|
|
|
|
const updateHTML = (editor, value, clear) => {
|
|
const root = $getRoot();
|
|
if (clear) {
|
|
root.clear();
|
|
}
|
|
|
|
if (isEmpty(value)) {
|
|
root.clear();
|
|
} else {
|
|
const parser = new DOMParser();
|
|
const dom = parser.parseFromString(value, "text/html");
|
|
const nodes = $generateNodesFromDOM(editor, dom);
|
|
nodes.filter(n => n.__size !== 0).forEach((n) => {
|
|
const paragraphNode = $createParagraphNode();
|
|
paragraphNode.append(n);
|
|
root.append(paragraphNode);
|
|
});
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (editor) {
|
|
editor.update(() => {
|
|
updateHTML(editor, value, true);
|
|
});
|
|
}
|
|
}, [editor, value]);
|
|
|
|
return null;
|
|
}
|
|
function MyOnChangePlugin({ ignoreHistoryMergeTagChange = true, ignoreSelectionChange = true, onChange }) {
|
|
const [editor] = useLexicalComposerContext();
|
|
useEffect(() => {
|
|
if (typeof onChange === 'function') {
|
|
return editor.registerUpdateListener(({editorState, dirtyElements, dirtyLeaves, prevEditorState, tags}) => {
|
|
|
|
if (
|
|
(ignoreSelectionChange &&
|
|
dirtyElements.size === 0 &&
|
|
dirtyLeaves.size === 0) ||
|
|
(ignoreHistoryMergeTagChange && tags.has('history-merge')) ||
|
|
prevEditorState.isEmpty()
|
|
) {
|
|
return;
|
|
}
|
|
editorState.read(() => {
|
|
const root = $getRoot();
|
|
const textContent = root.getTextContent();
|
|
const html = $generateHtmlFromNodes(editor);
|
|
onChange({ editorStateJSON: editorState.toJSON(), editor, tags, htmlContent: html, textContent });
|
|
});
|
|
});
|
|
}
|
|
}, [editor, ignoreHistoryMergeTagChange, ignoreSelectionChange, onChange]);
|
|
|
|
return null;
|
|
}
|
|
export default function Editor({ isRichText, isDebug, editorRef, onChange, defaultValue, stateJson, ...props }) {
|
|
const {
|
|
settings: {
|
|
isCollab,
|
|
isAutocomplete,
|
|
isMaxLength,
|
|
isCharLimit,
|
|
isCharLimitUtf8,
|
|
// isRichText,
|
|
showTreeView,
|
|
showTableOfContents,
|
|
shouldUseLexicalContextMenu,
|
|
shouldPreserveNewLinesInMarkdown,
|
|
tableCellMerge,
|
|
tableCellBackgroundColor,
|
|
},
|
|
} = useSettings();
|
|
return (
|
|
<LexicalComposer initialConfig={editorConfig}>
|
|
<div className='editor-container'>
|
|
{isRichText && <ToolbarPlugin />}
|
|
<div className='editor-inner'>
|
|
{/* <LexicalPlainText /> */}
|
|
{isRichText ? (
|
|
<RichTextPlugin contentEditable={<ContentEditable className='editor-input' />} placeholder={<Placeholder />} ErrorBoundary={LexicalErrorBoundary} />
|
|
) : (
|
|
<PlainTextPlugin contentEditable={<ContentEditable className='editor-pure-input' />} ErrorBoundary={LexicalErrorBoundary} />
|
|
)}
|
|
<HistoryPlugin />
|
|
{(import.meta.env.DEV && isDebug) && <TreeViewPlugin />}
|
|
<LexicalDefaultValuePlugin value={defaultValue} />
|
|
<DragDropPaste />
|
|
<AutoFocusPlugin />
|
|
<CodeHighlightPlugin />
|
|
<ListPlugin />
|
|
<ListMaxIndentLevelPlugin maxDepth={7} />
|
|
<AutoLinkPlugin />
|
|
<LinkPlugin />
|
|
<MarkdownShortcutPlugin transformers={TRANSFORMERS} />
|
|
<TablePlugin hasCellMerge={tableCellMerge} hasCellBackgroundColor={tableCellBackgroundColor} />
|
|
<TableCellResizer />
|
|
{/* <TableHoverActionsPlugin /> */}
|
|
<TableCellActionMenuPlugin
|
|
// anchorElem={floatingAnchorElem}
|
|
cellMerge={true}
|
|
/>
|
|
<TabFocusPlugin />
|
|
<TabIndentationPlugin />
|
|
<HorizontalRulePlugin />
|
|
<EditorRefPlugin editorRef={editorRef} />
|
|
<ImagesPlugin />
|
|
<InlineImagePlugin />
|
|
<FormatPaintPlugin />
|
|
<MyOnChangePlugin onChange={onChange}/>
|
|
</div>
|
|
</div>
|
|
</LexicalComposer>
|
|
);
|
|
}
|