From 86d7a41f6591cf7559b11a40162ce08c2a875c8e Mon Sep 17 00:00:00 2001 From: Harish Date: Mon, 26 Jun 2023 16:22:07 +0800 Subject: [PATCH] IS-271: fix bugs on simple mde editor (#1323) * feat: allow selection of images * draft: enable hyperlink and image insertion * fix: remove console logs and dangling props * fix: code cleanup * fix: remove unused var --- src/components/pages/EditorModals.jsx | 64 ++++++++++++++++++++----- src/components/pages/MarkdownEditor.jsx | 23 +++++++-- src/layouts/EditPage.jsx | 5 +- 3 files changed, 72 insertions(+), 20 deletions(-) diff --git a/src/components/pages/EditorModals.jsx b/src/components/pages/EditorModals.jsx index 20b426a44..64dc4b4d0 100644 --- a/src/components/pages/EditorModals.jsx +++ b/src/components/pages/EditorModals.jsx @@ -1,29 +1,60 @@ import HyperlinkModal from "components/HyperlinkModal" import MediaModal from "components/media/MediaModal" import PropTypes from "prop-types" +import { useEffect, useState } from "react" + +const EditorModals = ({ + onSave, + modalType, + onClose, + mediaType, + simpleMde, + lineAndCursor, +}) => { + const [linePos, setLinePos] = useState({ line: 0, ch: 0, sticky: null }) + + // this fixes the issue where line and cursor are being reset to 0 upon opening modals + useEffect(() => { + if (!lineAndCursor && simpleMde) { + setLinePos(lineAndCursor) + simpleMde.codemirror.setSelection(lineAndCursor) + } + if ( + lineAndCursor && + !(lineAndCursor.line === 0 && lineAndCursor.ch === 0) + ) { + setLinePos(lineAndCursor) + } + }, [lineAndCursor]) -const EditorModals = ({ mdeRef, onSave, modalType, onClose, mediaType }) => { const onHyperlinkSave = (text, link) => { - const cm = mdeRef.current.simpleMde.codemirror + const cm = simpleMde.codemirror + cm.setCursor(linePos) + cm.setSelection(linePos) cm.replaceSelection(`[${text}](${link})`) // set state so that rerender is triggered and path is shown - onSave(mdeRef.current.simpleMde.codemirror.getValue()) + onSave(simpleMde.codemirror.getValue()) onClose() } const onMediaSave = (data) => { const { selectedMediaPath, altText } = data - const cm = mdeRef.current.simpleMde.codemirror + const cm = simpleMde.codemirror + + cm.setCursor(linePos) + cm.setSelection(linePos) + if (mediaType === "files") cm.replaceSelection( `[${altText}](${selectedMediaPath.replaceAll(" ", "%20")})` ) - if (mediaType === "images") + if (mediaType === "images") { cm.replaceSelection( `![${altText}](${selectedMediaPath.replaceAll(" ", "%20")})` ) + } // set state so that rerender is triggered and image is shown - onSave(mdeRef.current.simpleMde.codemirror.getValue()) + onSave(simpleMde.codemirror.getValue()) onClose() } @@ -39,7 +70,7 @@ const EditorModals = ({ mdeRef, onSave, modalType, onClose, mediaType }) => { )} {modalType === "hyperlink" && ( @@ -49,14 +80,23 @@ const EditorModals = ({ mdeRef, onSave, modalType, onClose, mediaType }) => { } EditorModals.propTypes = { - mdeRef: PropTypes.oneOfType([ - // https://stackoverflow.com/questions/48007326/what-is-the-correct-proptype-for-a-ref-in-react - PropTypes.func, - PropTypes.shape({ current: PropTypes.instanceOf(Element) }), - ]), onSave: PropTypes.func.isRequired, modalType: PropTypes.oneOf(["hyperlink", "media"]).isRequired, onClose: PropTypes.func.isRequired, + simpleMde: PropTypes.shape({ + codemirror: PropTypes.shape({ + getSelection: PropTypes.func, + getValue: PropTypes.func, + replaceSelection: PropTypes.func, + setCursor: PropTypes.func, + }), + }), + lineAndCursor: PropTypes.shape({ + line: PropTypes.number, + ch: PropTypes.number, + sticky: PropTypes.string, + xRel: PropTypes.number, + }), } export default EditorModals diff --git a/src/components/pages/MarkdownEditor.jsx b/src/components/pages/MarkdownEditor.jsx index d3d3ae39f..8394e57ca 100644 --- a/src/components/pages/MarkdownEditor.jsx +++ b/src/components/pages/MarkdownEditor.jsx @@ -1,5 +1,5 @@ import EditorModals from "components/pages/EditorModals" -import { useMemo, useState } from "react" +import { useMemo, useState, useCallback } from "react" import SimpleMDE from "react-simplemde-editor" import { useMarkdown } from "hooks/useMarkdown" @@ -21,7 +21,6 @@ import { const MarkdownEditor = ({ siteName, - mdeRef, onChange, value, isDisabled, @@ -32,6 +31,8 @@ const MarkdownEditor = ({ const { toMarkdown } = useMarkdown() const options = useMemo( () => ({ + autoRefresh: true, + styleSelectedText: true, toolbar: [ headingButton, boldButton, @@ -94,6 +95,18 @@ const MarkdownEditor = ({ } }) + const [simpleMdeInstance, setMdeInstance] = useState(null) + + const getMdeInstanceCallback = useCallback((simpleMde) => { + setMdeInstance(simpleMde) + }, []) + + const [lineAndCursor, setLineAndCursor] = useState(null) + + const getLineAndCursorCallback = useCallback((position) => { + setLineAndCursor(position) + }, []) + const StatusIcon = () => { if (isDisabled) { return ( @@ -120,7 +133,6 @@ const MarkdownEditor = ({ <> { @@ -128,6 +140,8 @@ const MarkdownEditor = ({ setInsertingMediaType("") }} mediaType={insertingMediaType} + simpleMde={simpleMdeInstance} + lineAndCursor={lineAndCursor} />
diff --git a/src/layouts/EditPage.jsx b/src/layouts/EditPage.jsx index 43dac9b7b..a1bd74916 100644 --- a/src/layouts/EditPage.jsx +++ b/src/layouts/EditPage.jsx @@ -16,7 +16,7 @@ import { WarningModal } from "components/WarningModal" import DOMPurify from "dompurify" import { marked } from "marked" import PropTypes from "prop-types" -import { useEffect, useRef, useState } from "react" +import { useEffect, useState } from "react" import { useCollectionHook } from "hooks/collectionHooks" import { useGetPageHook, useUpdatePageHook } from "hooks/pageHooks" @@ -98,8 +98,6 @@ const EditPage = ({ match }) => { const { setRedirectToNotFound } = useRedirectHook() - const mdeRef = useRef() - const { data: pageData, isLoading: isLoadingPage } = useGetPageHook(params, { onError: () => setRedirectToNotFound(siteName), }) @@ -275,7 +273,6 @@ const EditPage = ({ match }) => { {/* Editor */} setEditorValue(value)} value={editorValue} isLoading={isLoadingPage}