diff --git a/packages/mini-markdown-editor/src/App.tsx b/packages/mini-markdown-editor/src/App.tsx index 27f96b684d722a8ffc24b54e3b79df834a8f82e4..0560cca30b8726371a50f0f6855d930e5561ce66 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/EditorWrapper.tsx b/packages/mini-markdown-editor/src/EditorWrapper.tsx index 497ef07c1dd90c69e196ac106df8ca8a8ad51282..a3b764926e7fead3427c19a61b8a0b65dc4d7885 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 4094e24da31c99db4d7d501b70f7f7ee16d02a45..a48738258565db1ab5cd10961a7c9e57b8399f4d 100644 --- a/packages/mini-markdown-editor/src/components/Editor/index.tsx +++ b/packages/mini-markdown-editor/src/components/Editor/index.tsx @@ -6,10 +6,9 @@ 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 { EDITOR_CONTENT_KEY, 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%; @@ -38,7 +37,7 @@ const ScrollWrapper = styled.div` } `; -const Editor: FC = () => { +const Editor: FC<{ isSyncScroll: boolean }> = ({ isSyncScroll }) => { const { content, setContent, @@ -48,7 +47,6 @@ const Editor: FC = () => { previewView, editorView, } = useEditorContentStore(); - const localStorage = safeLocalStorage(); // ref转发 const editorViewRef = useRef(); // 存储实例 @@ -61,6 +59,8 @@ const Editor: FC = () => { ); // 监听快捷键 useEditorShortcuts(); + // 持久化存储内容 + const { saveContent, getContent } = usePersistEditorContent(); // 处理重加载后的光标位置 useEffect(() => { @@ -72,6 +72,11 @@ const Editor: FC = () => { } }, [editorView]); + // 初始化时获取本地存储的内容 + useEffect(() => { + setContent(getContent()); + }, []); + // 编辑器挂载完成后将编辑器示例存储起来 const handleCreate = (view: EditorView) => { setEditorViewInstance(view); @@ -81,7 +86,7 @@ const Editor: FC = () => { // 更新store setContent(val); // 本地同步存储 - localStorage.setItem(EDITOR_CONTENT_KEY, val); + saveContent(val); // 更新编辑器实例 setEditorViewInstance(editView.view); }; @@ -90,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 da2b98e8d0e28c0e01bc7c785dcc7d889a307bb4..d800918f84095fe5439a90eb948f8bf795e7a549 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"; @@ -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,45 +15,55 @@ const ScrollWrapper = styled.div` word-wrap: break-word; `; -const Preview: FC<{ content: string }> = ({ content }) => { - // store +const Preview: FC<{ content: string; isSyncScroll: boolean }> = ({ content, isSyncScroll }) => { const { scrollWrapper, setScrollWrapper, setPreviewView, editorView } = useEditorContentStore(); - const localStorage = safeLocalStorage(); + + 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 && localStorage.getItem(SYNC_SCROLL_STATUS) === "true")) 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); diff --git a/packages/mini-markdown-editor/src/components/Status/index.tsx b/packages/mini-markdown-editor/src/components/Status/index.tsx index dec3dbc2d7661f5aac2936b0ab34c0df17c7b0ea..4f5e38b389bd6a9710907a355348bfc17375191d 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 0000000000000000000000000000000000000000..be6bec43993404bd445ac44c3ac59f11bd093afe --- /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 }; +}; 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 0000000000000000000000000000000000000000..085c12d9827f055e9e2655042bb0229dea45a111 --- /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 ca08928a554a4efa401e4a5b109e9d251cf8a38d..43dae0b19da8defdb87d848062930e7a3077fd94 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 }),