From 432fc1f0f3f525c0c3c2d6fbc69096a4da1b2ea5 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Thu, 30 Jan 2025 23:30:57 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(mini-markdown-editor):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E9=80=9A=E8=BF=87=E5=85=A8=E5=B1=80config=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E6=8C=81=E4=B9=85=E5=8C=96=E7=BC=96=E8=BE=91=E5=86=85?= =?UTF-8?q?=E5=AE=B9=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mini-markdown-editor/src/App.tsx | 4 ++-- .../src/components/Editor/index.tsx | 12 ++++++++-- .../src/hooks/use-persist-editor-content.ts | 24 +++++++++++++++++++ .../mini-markdown-editor/src/store/editor.ts | 6 +---- 4 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 packages/mini-markdown-editor/src/hooks/use-persist-editor-content.ts diff --git a/packages/mini-markdown-editor/src/App.tsx b/packages/mini-markdown-editor/src/App.tsx index 27f96b6..0560cca 100644 --- a/packages/mini-markdown-editor/src/App.tsx +++ b/packages/mini-markdown-editor/src/App.tsx @@ -18,7 +18,7 @@ const App: FC = () => { const handleUpload = async (file: File, callback: Callback) => { await new Promise((resolve) => { setTimeout(() => { - console.log("settimout 上传成功", file); + console.log("settimeout 上传成功", file); resolve({}); }, 1500); }); @@ -30,7 +30,7 @@ const App: FC = () => { }; return ( - + ); }; diff --git a/packages/mini-markdown-editor/src/components/Editor/index.tsx b/packages/mini-markdown-editor/src/components/Editor/index.tsx index 4094e24..7e10864 100644 --- a/packages/mini-markdown-editor/src/components/Editor/index.tsx +++ b/packages/mini-markdown-editor/src/components/Editor/index.tsx @@ -7,9 +7,10 @@ import * as events from "@uiw/codemirror-extensions-events"; import { useEditorContentStore } from "@/store/editor"; import { handleEditorScroll } from "@/utils/handle-scroll"; import { safeLocalStorage } from "@/utils/storage"; -import { EDITOR_CONTENT_KEY, SYNC_SCROLL_STATUS } from "@/common"; +import { SYNC_SCROLL_STATUS } from "@/common"; import { useEditorShortcuts } from "@/hooks/use-editor-shortcuts"; import { HotkeysContext } from "../providers/hotkeys-provider"; +import { usePersistEditorContent } from "@/hooks/use-persist-editor-content"; const ScrollWrapper = styled.div` width: 100%; @@ -61,6 +62,8 @@ const Editor: FC = () => { ); // 监听快捷键 useEditorShortcuts(); + // 持久化存储内容 + const { saveContent, getContent } = usePersistEditorContent(); // 处理重加载后的光标位置 useEffect(() => { @@ -72,6 +75,11 @@ const Editor: FC = () => { } }, [editorView]); + // 初始化时获取本地存储的内容 + useEffect(() => { + setContent(getContent()); + }, []); + // 编辑器挂载完成后将编辑器示例存储起来 const handleCreate = (view: EditorView) => { setEditorViewInstance(view); @@ -81,7 +89,7 @@ const Editor: FC = () => { // 更新store setContent(val); // 本地同步存储 - localStorage.setItem(EDITOR_CONTENT_KEY, val); + saveContent(val); // 更新编辑器实例 setEditorViewInstance(editView.view); }; diff --git a/packages/mini-markdown-editor/src/hooks/use-persist-editor-content.ts b/packages/mini-markdown-editor/src/hooks/use-persist-editor-content.ts new file mode 100644 index 0000000..085c12d --- /dev/null +++ b/packages/mini-markdown-editor/src/hooks/use-persist-editor-content.ts @@ -0,0 +1,24 @@ +import { EDITOR_CONTENT_KEY } from "@/common"; +import { ConfigContext } from "@/components/providers/config-provider"; +import { safeLocalStorage } from "@/utils/storage"; +import { useContext } from "react"; + +export const usePersistEditorContent = () => { + const localStorage = safeLocalStorage(); + const { local } = useContext(ConfigContext); + + // 保存内容 + const saveContent = (content: string) => { + if (local) { + localStorage.setItem(EDITOR_CONTENT_KEY, content); + } + }; + + // 获取内容 + const getContent = (): string => { + if (!local) return ""; + return localStorage.getItem(EDITOR_CONTENT_KEY) ?? ""; + }; + + return { saveContent, getContent }; +}; diff --git a/packages/mini-markdown-editor/src/store/editor.ts b/packages/mini-markdown-editor/src/store/editor.ts index ca08928..43dae0b 100644 --- a/packages/mini-markdown-editor/src/store/editor.ts +++ b/packages/mini-markdown-editor/src/store/editor.ts @@ -1,7 +1,5 @@ import { create } from "zustand"; import type { EditorView } from "@codemirror/view"; -import { safeLocalStorage } from "@/utils/storage"; -import { EDITOR_CONTENT_KEY } from "@/common"; interface EditorContentStoreType { content: string; @@ -16,11 +14,9 @@ interface EditorContentStoreType { setPreviewView: (view: HTMLElement | null) => void; } -const localStorage = safeLocalStorage(); - // 编辑器内容状态 const useEditorContentStore = create((set) => ({ - content: localStorage.getItem(EDITOR_CONTENT_KEY) || "", + content: "", setContent: (content: string) => set({ content }), scrollWrapper: "", setScrollWrapper: (scrollWrapper: string) => set({ scrollWrapper }), -- Gitee From 224a4fb59be79c30e4c790457878d5db5d66d8a0 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Fri, 31 Jan 2025 00:34:37 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(mini-markdown-editor):=20=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D=E5=90=8C=E6=AD=A5=E6=BB=9A=E5=8A=A8=E6=8C=89=E9=92=AE?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/EditorWrapper.tsx | 11 +++++-- .../src/components/Editor/index.tsx | 9 +++--- .../src/components/Preview/index.tsx | 7 ++-- .../src/components/Status/index.tsx | 24 ++++---------- .../src/hooks/use-init-sync-scroll-status.ts | 32 +++++++++++++++++++ 5 files changed, 54 insertions(+), 29 deletions(-) create mode 100644 packages/mini-markdown-editor/src/hooks/use-init-sync-scroll-status.ts diff --git a/packages/mini-markdown-editor/src/EditorWrapper.tsx b/packages/mini-markdown-editor/src/EditorWrapper.tsx index 497ef07..a3b7649 100644 --- a/packages/mini-markdown-editor/src/EditorWrapper.tsx +++ b/packages/mini-markdown-editor/src/EditorWrapper.tsx @@ -11,6 +11,7 @@ import { ConfigProvider } from "@/components/providers/config-provider"; import { HotkeysProvider } from "@/components/providers/hotkeys-provider"; import { GlobalConfig } from "./types/global-config"; import { useToolbarStore } from "./store/toolbar"; +import { useInitSyncScrollStatus } from "./hooks/use-init-sync-scroll-status"; const Container = styled.div` width: 100%; @@ -143,6 +144,7 @@ const EditorWrapper: FC = (config) => { const content = useEditorContentStore((state) => state.content); const deferredContent = useDeferredValue(content); const isFullScreen = useToolbarStore((state) => state.isFullScreen); + const { isSyncScroll, updateSyncScrollStatus } = useInitSyncScrollStatus(); return ( @@ -155,12 +157,17 @@ const EditorWrapper: FC = (config) => { {/* 内容区域 */} - } preview={} /> + } + preview={} + /> {/* 底部状态栏 */} - {config.status ? : null} + {config.status ? ( + + ) : null} ); diff --git a/packages/mini-markdown-editor/src/components/Editor/index.tsx b/packages/mini-markdown-editor/src/components/Editor/index.tsx index 7e10864..a487382 100644 --- a/packages/mini-markdown-editor/src/components/Editor/index.tsx +++ b/packages/mini-markdown-editor/src/components/Editor/index.tsx @@ -6,8 +6,6 @@ import { languages } from "@codemirror/language-data"; import * as events from "@uiw/codemirror-extensions-events"; import { useEditorContentStore } from "@/store/editor"; import { handleEditorScroll } from "@/utils/handle-scroll"; -import { safeLocalStorage } from "@/utils/storage"; -import { SYNC_SCROLL_STATUS } from "@/common"; import { useEditorShortcuts } from "@/hooks/use-editor-shortcuts"; import { HotkeysContext } from "../providers/hotkeys-provider"; import { usePersistEditorContent } from "@/hooks/use-persist-editor-content"; @@ -39,7 +37,7 @@ const ScrollWrapper = styled.div` } `; -const Editor: FC = () => { +const Editor: FC<{ isSyncScroll: boolean }> = ({ isSyncScroll }) => { const { content, setContent, @@ -49,7 +47,6 @@ const Editor: FC = () => { previewView, editorView, } = useEditorContentStore(); - const localStorage = safeLocalStorage(); // ref转发 const editorViewRef = useRef(); // 存储实例 @@ -98,7 +95,9 @@ const Editor: FC = () => { scroll: () => { if (scrollWrapper !== "editor") return; const view = editorViewRef.current; - if (!(view && previewView && localStorage.getItem(SYNC_SCROLL_STATUS) === "true")) return; + console.log(view); + if (!(view && previewView && isSyncScroll)) return; + console.log(isSyncScroll); handleEditorScroll({ editorView: view, previewView }); }, }); diff --git a/packages/mini-markdown-editor/src/components/Preview/index.tsx b/packages/mini-markdown-editor/src/components/Preview/index.tsx index da2b98e..c766191 100644 --- a/packages/mini-markdown-editor/src/components/Preview/index.tsx +++ b/packages/mini-markdown-editor/src/components/Preview/index.tsx @@ -6,8 +6,6 @@ import styled from "styled-components"; import { useEditorContentStore } from "@/store/editor"; import { handlePreviewScroll } from "@/utils/handle-scroll"; import React from "react"; -import { safeLocalStorage } from "@/utils/storage"; -import { SYNC_SCROLL_STATUS } from "@/common"; const ScrollWrapper = styled.div` width: 100%; @@ -17,10 +15,9 @@ const ScrollWrapper = styled.div` word-wrap: break-word; `; -const Preview: FC<{ content: string }> = ({ content }) => { +const Preview: FC<{ content: string; isSyncScroll: boolean }> = ({ content, isSyncScroll }) => { // store const { scrollWrapper, setScrollWrapper, setPreviewView, editorView } = useEditorContentStore(); - const localStorage = safeLocalStorage(); // 渲染 html 节点 const node = React.useMemo(() => { @@ -39,7 +36,7 @@ const Preview: FC<{ content: string }> = ({ content }) => { const handleScroll = (e: React.UIEvent) => { if (scrollWrapper !== "preview") return; const previewView = e.currentTarget; - if (!(editorView && previewView && localStorage.getItem(SYNC_SCROLL_STATUS) === "true")) return; + if (!(editorView && previewView && isSyncScroll)) return; handlePreviewScroll({ previewView, editorView }); }; diff --git a/packages/mini-markdown-editor/src/components/Status/index.tsx b/packages/mini-markdown-editor/src/components/Status/index.tsx index dec3dbc..4f5e38b 100644 --- a/packages/mini-markdown-editor/src/components/Status/index.tsx +++ b/packages/mini-markdown-editor/src/components/Status/index.tsx @@ -1,10 +1,8 @@ -import { FC, useEffect, useMemo, useState } from "react"; +import { FC, useMemo } from "react"; import styled from "styled-components"; import { useEditorContentStore } from "@/store/editor"; import { Checkbox } from "antd"; import type { CheckboxProps } from "antd"; -import { SYNC_SCROLL_STATUS } from "@/common"; -import { safeLocalStorage } from "@/utils/storage"; const StatusWrapper = styled.div` width: 100%; @@ -38,34 +36,26 @@ const StatusWrapper = styled.div` } `; -const Status: FC = () => { +const Status: FC<{ isSyncScroll: boolean; updateSyncScrollStatus: (val: boolean) => void }> = ({ + isSyncScroll, + updateSyncScrollStatus, +}) => { const content = useEditorContentStore((state) => state.content); - const localStorage = safeLocalStorage(); - const [syncScroll, setSyncScroll] = useState(); const contentNum = useMemo(() => { return content.replace(/[\s\n]/g, "").length; }, [content]); - // 初始化时从 localStorage 读取状态 - useEffect(() => { - const savedStatus = localStorage.getItem(SYNC_SCROLL_STATUS); - //! 明确转换为布尔值 - const initialStatus = savedStatus === null ? true : savedStatus === "true"; - setSyncScroll(initialStatus); - }, [localStorage]); - // 状态改变处理函数 const handleSyncScrollChange: CheckboxProps["onChange"] = (e) => { - setSyncScroll(e.target.checked); - localStorage.setItem(SYNC_SCROLL_STATUS, String(e.target.checked)); + updateSyncScrollStatus(e.target.checked); }; return (
字数: {contentNum}
- + 同步滚动
滚动到顶部
diff --git a/packages/mini-markdown-editor/src/hooks/use-init-sync-scroll-status.ts b/packages/mini-markdown-editor/src/hooks/use-init-sync-scroll-status.ts new file mode 100644 index 0000000..be6bec4 --- /dev/null +++ b/packages/mini-markdown-editor/src/hooks/use-init-sync-scroll-status.ts @@ -0,0 +1,32 @@ +import { SYNC_SCROLL_STATUS } from "@/common"; +import { safeLocalStorage } from "@/utils/storage"; +import { useEffect, useState } from "react"; + +export const useInitSyncScrollStatus = () => { + const localStorage = safeLocalStorage(); + const [isSyncScroll, setIsSyncScroll] = useState(() => { + const status = localStorage.getItem(SYNC_SCROLL_STATUS); + return status === null ? true : status !== "false"; + }); + + // 初始化同步滚动状态 + const initSyncScrollStatus = () => { + const status = localStorage.getItem(SYNC_SCROLL_STATUS); + if (status === null) { + localStorage.setItem(SYNC_SCROLL_STATUS, "true"); + } + }; + + const updateSyncScrollStatus = (status: boolean) => { + localStorage.setItem(SYNC_SCROLL_STATUS, String(status)); + //! 更新状态 + setIsSyncScroll(status); + }; + + // 初始化同步滚动状态 + useEffect(() => { + initSyncScrollStatus(); + }, []); + + return { isSyncScroll, updateSyncScrollStatus }; +}; -- Gitee From fa14358116b7f08801471c4029d9a9b445771c70 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Fri, 31 Jan 2025 00:38:54 +0800 Subject: [PATCH 3/3] =?UTF-8?q?perf(mini-markdown-editor):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E9=A2=84=E8=A7=88=E5=8C=BA=E6=9B=B4=E6=96=B0=E6=B8=B2?= =?UTF-8?q?=E6=9F=93=E5=AE=9E=E4=BE=8B=E7=9A=84=E9=80=BB=E8=BE=91=EF=BC=8C?= =?UTF-8?q?=E8=A7=A3=E5=86=B3=E9=87=8D=E6=B8=B2=E6=9F=93=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E6=97=B6=E5=AE=9E=E4=BE=8B=E5=8A=A0=E8=BD=BD=E8=BF=87=E6=85=A2?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Preview/index.tsx | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/packages/mini-markdown-editor/src/components/Preview/index.tsx b/packages/mini-markdown-editor/src/components/Preview/index.tsx index c766191..d800918 100644 --- a/packages/mini-markdown-editor/src/components/Preview/index.tsx +++ b/packages/mini-markdown-editor/src/components/Preview/index.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useRef } from "react"; +import { FC, useEffect, useRef, useCallback } from "react"; import { parseMarkdown, transformHtml } from "@mini-markdown/ast-parser"; import "@/assets/styles/preview.css"; import "highlight.js/styles/atom-one-dark.css"; @@ -16,43 +16,54 @@ const ScrollWrapper = styled.div` `; const Preview: FC<{ content: string; isSyncScroll: boolean }> = ({ content, isSyncScroll }) => { - // store const { scrollWrapper, setScrollWrapper, setPreviewView, editorView } = useEditorContentStore(); + const previewRef = useRef(null); + + // 更新预览视图 + const updatePreviewView = useCallback( + (element: HTMLDivElement | null) => { + if (element) { + setPreviewView(element); + } + }, + [setPreviewView], + ); + // 渲染 html 节点 const node = React.useMemo(() => { const ast = parseMarkdown(content); return transformHtml(ast); }, [content]); - const previewRef = useRef(null); // 更新渲染实例 useEffect(() => { - if (previewRef.current && node) { - setPreviewView(previewRef.current); - } - }, [node]); - - const handleScroll = (e: React.UIEvent) => { - if (scrollWrapper !== "preview") return; - const previewView = e.currentTarget; - if (!(editorView && previewView && isSyncScroll)) return; - handlePreviewScroll({ previewView, editorView }); - }; - - const handleMoseEnter = () => { + updatePreviewView(previewRef.current); + }, [updatePreviewView, node]); + + const handleScroll = useCallback( + (e: React.UIEvent) => { + if (scrollWrapper !== "preview") return; + const previewView = e.currentTarget; + if (!(editorView && previewView && isSyncScroll)) return; + handlePreviewScroll({ previewView, editorView }); + }, + [scrollWrapper, editorView, isSyncScroll], + ); + + const handleMouseEnter = useCallback(() => { setScrollWrapper("preview"); - }; + }, [setScrollWrapper]); return ( - // className='markdown-editor-preview' 重置样式的节点 + /> ); }; -export default Preview; + +export default React.memo(Preview); -- Gitee