diff --git a/demo/src/index.js b/demo/src/index.js index d443aa9..04ac239 100644 --- a/demo/src/index.js +++ b/demo/src/index.js @@ -3,14 +3,14 @@ import { render } from "react-dom"; import Editor from "../../src"; import styles from "../../src/styles.module.css"; -import { ConfigProvider, Divider, Drawer, FloatButton, Select, Space, Switch } from "antd"; +import { Button, ConfigProvider, Divider, Drawer, FloatButton, Select, Space, Switch } from "antd"; import Icons from "../../src/icons"; import i18n from "../../src/i18n"; const Demo = () => { const [open, setOpen] = useState(false); - const [value, setValue] = useState(null); + const [value, setValue] = useState("# this is heading\n\nThis is a paragraph"); const [configuration, setConfiguration] = useState({ richText: true, language: "en", @@ -23,7 +23,9 @@ const Demo = () => { showUndoRedo: true, showExtraFormat: true, showInsertLink: true, + showSave: true, }, + onSave: (contents) => console.log(contents), format: "raw", }); const locale = i18n[configuration.language]; @@ -154,6 +156,10 @@ const Demo = () => { setConfiguration((e) => ({ ...e, toolbar: {... e.toolbar, showInsertLink : checked } })) } /> + + diff --git a/src/commands/saveCommand.js b/src/commands/saveCommand.js new file mode 100644 index 0000000..b86afaa --- /dev/null +++ b/src/commands/saveCommand.js @@ -0,0 +1,26 @@ +import {useLexicalComposerContext} from '@lexical/react/LexicalComposerContext'; +import { + COMMAND_PRIORITY_LOW, + createCommand, +} from 'lexical'; +import {useEffect} from 'react'; + +export const SAVE_COMMAND = createCommand('SAVE_COMMAND'); + +export function SavePlugin({ onSave = () => {} }) { + const [editor] = useLexicalComposerContext(); + + useEffect(() => { + editor.registerCommand( + SAVE_COMMAND, + () => { + console.log('save command fired'); + onSave(editor.getEditorState()); + }, + COMMAND_PRIORITY_LOW, + ); + + }, [editor]); + + return null; +} diff --git a/src/i18n/index.js b/src/i18n/index.js index e9c3035..690107e 100644 --- a/src/i18n/index.js +++ b/src/i18n/index.js @@ -34,6 +34,7 @@ export default { insert: "Insert", horizontalRule: "Horizontal Rule", image: "Insert Image", + save: "Save", }, }, 'ur': { @@ -71,6 +72,7 @@ export default { insert: "متفرقات", horizontalRule: "افقی قاعدہ", image: "تصویر شامل کریں", + save: "محفوظ کریں", } } } diff --git a/src/icons/index.js b/src/icons/index.js index 8918627..da0101c 100644 --- a/src/icons/index.js +++ b/src/icons/index.js @@ -33,7 +33,8 @@ const Icons = { Plus: () => (), HorizontalRule: () => (), Image: () => (), - Down: () => (), - Setting: () => () + Down: () => (), + Setting: () => (), + Save: () => () }; export default Icons diff --git a/src/index.js b/src/index.js index 72ad292..24a6132 100644 --- a/src/index.js +++ b/src/index.js @@ -24,11 +24,13 @@ import ToolbarPlugin from "./plugins/toolbar"; import AutoLinkPlugin from './plugins/autoLink.Plugin'; import { HorizontalRulePlugin } from './plugins/horizontalRulePlugin'; import LinkPlugin from './plugins/link.Plugin'; +import { ControlledValuePlugin } from './plugins/controlledValuePlugin'; import EditorNodes from "./nodes"; import EditorTheme from './themes/editorTheme' // ------------------------------------------------------ import i18n from './i18n'; import styles from './styles.module.css'; // Import css modules stylesheet as styles +import { SavePlugin } from './commands/saveCommand'; // ------------------------------------------------------ const EMPTY_CONTENT = @@ -58,13 +60,14 @@ function Placeholder({ children }) { // ------------------------------------------------------ export default ({ value = EMPTY_CONTENT, - setValue = () => {}, + onChange = () => {}, configuration = { richText : false, toolbar : { fonts : null, - fontSizes: null, + fontSizes: null }, + onSave: () => {}, language : "en", languageTools: false, placeholder : null, @@ -76,24 +79,24 @@ export default ({ value = EMPTY_CONTENT, const [editorState, setEditorState] = useState(value !== null & value === EMPTY_CONTENT && configuration.format == 'markdown'? ' ' : EMPTY_CONTENT); const initialConfig = { namespace: "MyEditor", - editorState: configuration.format == "markdown" ? () => value == null ? '' : $convertFromMarkdownString(value, TRANSFORMERS) : value, + editorState: editorState, nodes: [...EditorNodes], theme: EditorTheme, onError, }; - function onChange(editorState) { - if (configuration.format == "markdown") { - editorState.read(() => { - const markdown = $convertToMarkdownString(TRANSFORMERS); - setValue(markdown); - }); - } else { - const editorStateJSON = editorState.toJSON(); - setEditorState(JSON.stringify(editorStateJSON)); - setValue(JSON.stringify(editorStateJSON)); - } - } + // function onChange(editorState) { + // if (configuration.format == "markdown") { + // editorState.read(() => { + // const markdown = $convertToMarkdownString(TRANSFORMERS); + // setValue(markdown); + // }); + // } else { + // const editorStateJSON = editorState.toJSON(); + // setEditorState(JSON.stringify(editorStateJSON)); + // setValue(JSON.stringify(editorStateJSON)); + // } + // } return (
@@ -119,7 +122,12 @@ export default ({ value = EMPTY_CONTENT, /> } - + { configuration?.toolbar?.showSave && } + {configuration.format == "markdown" && }
diff --git a/src/plugins/controlledValuePlugin.js b/src/plugins/controlledValuePlugin.js new file mode 100644 index 0000000..42c723e --- /dev/null +++ b/src/plugins/controlledValuePlugin.js @@ -0,0 +1,62 @@ +import React, { useEffect } from "react"; + +import { + $getSelection, + $getRoot, + $createParagraphNode, + $createTextNode, + $setSelection} from "lexical"; +import { useLexicalComposerContext } from "@lexical/react/LexicalComposerContext"; + +import { OnChangePlugin } from "@lexical/react/LexicalOnChangePlugin"; + +export const ControlledValuePlugin = ({ value, onChange, isRichtext, format }) => { + useAdoptPlaintextValue(value, isRichtext); + + const handleChange = (editorState) => { + editorState.read(() => { + onChange(editorState); + }); + }; + + return ; +}; + +export const useAdoptPlaintextValue = (value, isRichtext) => { + const [editor] = useLexicalComposerContext(); + + useEffect(() => { + editor.update(() => { + console.log(`Value updated %o`, value); + if (isRichtext) { + const paragraphNode = $createParagraphNode(); + const textNode = $createTextNode(value); + paragraphNode.append(textNode); + $getRoot().clear(); + $getRoot().append(paragraphNode); + } else { + const root = $getRoot(); + root.append(paragraphNode); + const initialSelection = $getSelection()?.clone() ?? null; + $getRoot().clear(); + $getRoot().select(); // for some reason this is not even necessary + $getSelection()?.insertText(value); + $setSelection(initialSelection); + } + }); + }, [value, editor]); +}; + +// export const useAdoptPlaintextValue = (value: string) => { +// const [editor] = useLexicalComposerContext(); + +// useEffect(() => { +// editor.update(() => { +// const paragraph = $createParagraphNode(); +// const text = $createTextNode(value); +// paragraph.append(text); +// $getRoot().clear(); +// $getRoot().append(paragraph); +// }); +// }, [value, editor]); +// }; diff --git a/src/plugins/toolbar.js b/src/plugins/toolbar.js index feb17d2..40d179d 100644 --- a/src/plugins/toolbar.js +++ b/src/plugins/toolbar.js @@ -52,6 +52,7 @@ import Icons from '../icons' import CheckButton from "../components/checkButton"; import AlignFormatDropDown from "./alignFormatDropDown"; import styles from "../styles.module.css"; +import { SAVE_COMMAND } from '../commands/saveCommand'; // ----------------------------------------------------------- @@ -66,6 +67,8 @@ const ToolbarPlugin = ({ configuration = { showUndoRedo: true, showExtraFormat: true, showInsertLink: true, + showSave: false, + onSave: () => {} }, locale }) => { const [editor] = useLexicalComposerContext(); const [activeEditor, setActiveEditor] = useState(editor); @@ -273,6 +276,10 @@ const ToolbarPlugin = ({ configuration = { return (
+ { configuration.showSave && +