From 72ba4feba8b50e4eed27106ad6d2d9bf02bca3d3 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Fri, 7 Feb 2025 18:24:40 +0800 Subject: [PATCH 1/5] =?UTF-8?q?chore(mini-markdown-editor):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=8B=96=E6=8B=BD/=E7=B2=98=E8=B4=B4=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=9B=BE=E7=89=87=E7=9A=84=E5=A4=96=E9=83=A8=E9=85=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Editor/index.tsx | 20 ++++- .../src/extensions/codemirror/event.d.ts | 13 +++ .../src/extensions/codemirror/event.ts | 84 ++++++++++++------- .../src/extensions/codemirror/index.ts | 26 +++--- .../src/types/global-config.ts | 5 ++ 5 files changed, 105 insertions(+), 43 deletions(-) create mode 100644 packages/mini-markdown-editor/src/extensions/codemirror/event.d.ts diff --git a/packages/mini-markdown-editor/src/components/Editor/index.tsx b/packages/mini-markdown-editor/src/components/Editor/index.tsx index 3e4a41e..be4363d 100644 --- a/packages/mini-markdown-editor/src/components/Editor/index.tsx +++ b/packages/mini-markdown-editor/src/components/Editor/index.tsx @@ -7,6 +7,7 @@ import { handleEditorScroll } from "@/utils/handle-scroll"; import { usePersistEditorContent } from "@/hooks/use-persist-editor-content"; import { ConfigContext } from "../providers/config-provider"; import { createEditorExtensions } from "@/extensions/codemirror"; +import { Callback } from "@/types/global-config"; const ScrollWrapper = styled.div<{ $lineNumbers?: boolean; @@ -82,7 +83,7 @@ const Editor: FC<{ isSyncScroll: boolean }> = ({ isSyncScroll }) => { setEditorViewInstance(view); }; - const { onChange } = useContext(ConfigContext); + const { onChange, onDragUpload, onPatseUpload } = useContext(ConfigContext); const handleChange = (val: string, editView: ViewUpdate) => { // 更新store @@ -110,9 +111,24 @@ const Editor: FC<{ isSyncScroll: boolean }> = ({ isSyncScroll }) => { setScrollWrapper("editor"); }; + // 拖拽上传 + const handleDragUpload = (file: File, Callback: Callback) => { + onDragUpload?.(file, Callback); + }; + // 粘贴上传 + const handlePatseUpload = (file: File, Callback: Callback) => { + onPatseUpload?.(file, Callback); + }; + // 创建编辑器扩展 const extensions = useMemo( - () => createEditorExtensions({ scrollWrapper, eventExt }), + () => + createEditorExtensions({ + scrollWrapper, + eventExt, + onDragUpload: handleDragUpload, + onPasteUpload: handlePatseUpload, + }), [scrollWrapper], ); diff --git a/packages/mini-markdown-editor/src/extensions/codemirror/event.d.ts b/packages/mini-markdown-editor/src/extensions/codemirror/event.d.ts new file mode 100644 index 0000000..f30d80b --- /dev/null +++ b/packages/mini-markdown-editor/src/extensions/codemirror/event.d.ts @@ -0,0 +1,13 @@ +import { Callback } from "@/types/global-config"; +import { EditorView, ViewPlugin } from "@uiw/react-codemirror"; + +export interface EventOptions { + scrollWrapper: string; + eventExt?: ViewPlugin<{ + dom?: HTMLElement; + view: EditorView; + destroy(): void; + }>; + onDragUpload?: (file: File, callback: Callback) => void; + onPasteUpload?: (file: File, callback: Callback) => void; +} diff --git a/packages/mini-markdown-editor/src/extensions/codemirror/event.ts b/packages/mini-markdown-editor/src/extensions/codemirror/event.ts index 4d13bea..5f39460 100644 --- a/packages/mini-markdown-editor/src/extensions/codemirror/event.ts +++ b/packages/mini-markdown-editor/src/extensions/codemirror/event.ts @@ -1,38 +1,31 @@ import { EditorView, ViewPlugin } from "@uiw/react-codemirror"; import { nanoid } from "nanoid"; - -interface EventOptions { - scrollWrapper: string; - eventExt?: ViewPlugin<{ - dom?: HTMLElement; - view: EditorView; - destroy(): void; - }>; -} +import type { EventOptions } from "./event.d"; +import { Callback } from "@/types/global-config"; // 实例属性 // 提供销毁方法,同时有效避免全局 Url 的变量污染 class ImageHandler { private currentObjectURL: string | null = null; - handleImageFile(file: File, view: EditorView) { - // TODO: 限制图片大小(可以外露) - // if (file.size > 5 * 1024 * 1024) { - // console.warn("图片大小不能超过5MB!"); - // return; - // } - + handleImageFile( + file: File, + view: EditorView, + uploadCallback?: (file: File, callback: Callback) => void, + ) { if (this.currentObjectURL) { URL.revokeObjectURL(this.currentObjectURL); } - const imageUrl = URL.createObjectURL(file); - this.currentObjectURL = imageUrl; - //* 生成随机八位 alt + // 创建临时URL + const temporaryUrl = URL.createObjectURL(file); + this.currentObjectURL = temporaryUrl; const imageAlt = nanoid(8); + // 插入临时图片用作预览 const selection = view.state.selection.main; - const content = `![${imageAlt}](${imageUrl})`; + const content = `![${imageAlt}](${temporaryUrl})`; + const insertPos = selection.from; view.dispatch({ changes: { @@ -41,6 +34,40 @@ class ImageHandler { insert: content, }, }); + + // 处理上传后的信息 + //! 此回调函数内部提供返回的url,作为新图片的地址 + //! 可选传递alt参数,作为图片的描述 + const handleCallback: Callback = (param: { url: string; alt?: string }) => { + try { + const newContent = `![${param.alt || imageAlt}](${param.url})`; + + // 计算需要替换的范围 + const doc = view.state.doc; + const searchStr = content; + const searchPos = doc.slice(insertPos, insertPos + searchStr.length).toString(); + if (searchPos === searchStr) { + view.dispatch({ + changes: { + from: insertPos, + to: insertPos + searchStr.length, + insert: newContent, + }, + }); + } + + // 清理临时URL + URL.revokeObjectURL(temporaryUrl); + this.currentObjectURL = null; + } catch (err) { + console.error("Failed to replace image URL:", err); + } + }; + + // 执行上传回调 + if (uploadCallback) { + uploadCallback(file, handleCallback); + } } destroy() { @@ -51,7 +78,7 @@ class ImageHandler { } // 创建拖拽插件 -const createDropPhotoExtension = () => { +const createDropPhotoExtension = (onDragUpload?: EventOptions["onDragUpload"]) => { return ViewPlugin.fromClass( class { private handler: ImageHandler; @@ -69,7 +96,7 @@ const createDropPhotoExtension = () => { const files = e.dataTransfer?.files; if (!files?.[0]?.type.startsWith("image/")) return; - this.handler.handleImageFile(files[0], view); + this.handler.handleImageFile(files[0], view, onDragUpload); }; this.view.dom.addEventListener("dragover", this.onDragOver); @@ -87,7 +114,7 @@ const createDropPhotoExtension = () => { }; // 创建粘贴插件 -const createPastePhotoExtension = () => { +const createPastePhotoExtension = (onPasteUpload?: EventOptions["onPasteUpload"]) => { return ViewPlugin.fromClass( class { private handler: ImageHandler; @@ -107,8 +134,7 @@ const createPastePhotoExtension = () => { e.preventDefault(); const file = item.getAsFile(); if (file) { - this.handler.handleImageFile(file, view); - //! 只处理第一个图片 + this.handler.handleImageFile(file, view, onPasteUpload); break; } } @@ -132,7 +158,9 @@ export const createEventExtension = (eventOptions: EventOptions): any => { return []; } - return [eventOptions.eventExt, createDropPhotoExtension(), createPastePhotoExtension()].filter( - Boolean, - ); + return [ + eventOptions.eventExt, + createDropPhotoExtension(eventOptions.onDragUpload), + createPastePhotoExtension(eventOptions.onPasteUpload), + ].filter(Boolean); }; diff --git a/packages/mini-markdown-editor/src/extensions/codemirror/index.ts b/packages/mini-markdown-editor/src/extensions/codemirror/index.ts index 1247396..fb6dacc 100644 --- a/packages/mini-markdown-editor/src/extensions/codemirror/index.ts +++ b/packages/mini-markdown-editor/src/extensions/codemirror/index.ts @@ -1,28 +1,28 @@ import { Extension } from "@codemirror/state"; -import { EditorView, ViewPlugin } from "@uiw/react-codemirror"; +import { EditorView } from "@uiw/react-codemirror"; import { history } from "@codemirror/commands"; import { createMarkdownExtension } from "./markdown"; import { createHotkeysExtension } from "./hotkeys"; import { createEventExtension } from "./event"; -interface ExtensionOptions { - scrollWrapper?: string; - eventExt?: ViewPlugin<{ - dom?: HTMLElement; - view: EditorView; - destroy(): void; - }>; +import type { EventOptions } from "./event.d"; + +interface ExtensionOptions extends EventOptions { + // 是否开启快捷键支持 + name?: string; } -export const createEditorExtensions = (options: ExtensionOptions = {}): Extension[] => { - const { scrollWrapper = "editor", eventExt } = options; +export const createEditorExtensions = (options: ExtensionOptions): Extension[] => { + const { scrollWrapper = "editor", eventExt, onDragUpload, onPasteUpload } = options; - return [ + // 创建基础扩展数组 + const extensions: Extension[] = [ createHotkeysExtension(), createMarkdownExtension(), - createEventExtension({ scrollWrapper, eventExt }), + createEventExtension({ scrollWrapper, eventExt, onDragUpload, onPasteUpload }), history(), - //? 自动换行,可以考虑添加配置项 EditorView.lineWrapping, ]; + + return extensions; }; diff --git a/packages/mini-markdown-editor/src/types/global-config.ts b/packages/mini-markdown-editor/src/types/global-config.ts index d6ede95..4e4b4a9 100644 --- a/packages/mini-markdown-editor/src/types/global-config.ts +++ b/packages/mini-markdown-editor/src/types/global-config.ts @@ -43,6 +43,11 @@ export interface GlobalConfig { * @type {(file: File, callback: Callback) => void} */ onDragUpload?: (file: File, callback: Callback) => void; + /** + * 粘贴上传图片时触发 + * @type {(file: File, callback: Callback) => void} + */ + onPatseUpload?: (file: File, callback: Callback) => void; /** * 保存触发 * @type {(value: string, editorView: ViewUpdate) => void} -- Gitee From 68bf883bf7415ce26374bac92d7e9004392e3675 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Fri, 7 Feb 2025 18:30:50 +0800 Subject: [PATCH 2/5] =?UTF-8?q?chore(mini-markdown-editor):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E6=98=AF=E5=90=A6=E5=BC=80=E5=90=AF=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E9=94=AE=E6=94=AF=E6=8C=81=E7=9A=84=E5=A4=96=E9=83=A8=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/Editor/index.tsx | 3 ++- .../mini-markdown-editor/src/config/global.ts | 1 + .../src/extensions/codemirror/index.ts | 18 +++++++++++++----- .../src/types/global-config.ts | 5 +++++ 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/packages/mini-markdown-editor/src/components/Editor/index.tsx b/packages/mini-markdown-editor/src/components/Editor/index.tsx index be4363d..6f3c71d 100644 --- a/packages/mini-markdown-editor/src/components/Editor/index.tsx +++ b/packages/mini-markdown-editor/src/components/Editor/index.tsx @@ -83,7 +83,7 @@ const Editor: FC<{ isSyncScroll: boolean }> = ({ isSyncScroll }) => { setEditorViewInstance(view); }; - const { onChange, onDragUpload, onPatseUpload } = useContext(ConfigContext); + const { enableShortcuts, onChange, onDragUpload, onPatseUpload } = useContext(ConfigContext); const handleChange = (val: string, editView: ViewUpdate) => { // 更新store @@ -124,6 +124,7 @@ const Editor: FC<{ isSyncScroll: boolean }> = ({ isSyncScroll }) => { const extensions = useMemo( () => createEditorExtensions({ + enableShortcuts, scrollWrapper, eventExt, onDragUpload: handleDragUpload, diff --git a/packages/mini-markdown-editor/src/config/global.ts b/packages/mini-markdown-editor/src/config/global.ts index 40a8264..9b90b35 100644 --- a/packages/mini-markdown-editor/src/config/global.ts +++ b/packages/mini-markdown-editor/src/config/global.ts @@ -6,4 +6,5 @@ export const defaultGlobalConfig: GlobalConfig = { theme: "light", // 主题 local: true, // 是否开启本地存储 lineNumbers: false, // 是否显示行号 + enableShortcuts: true, // 是否开启快捷键 }; diff --git a/packages/mini-markdown-editor/src/extensions/codemirror/index.ts b/packages/mini-markdown-editor/src/extensions/codemirror/index.ts index fb6dacc..f092db4 100644 --- a/packages/mini-markdown-editor/src/extensions/codemirror/index.ts +++ b/packages/mini-markdown-editor/src/extensions/codemirror/index.ts @@ -4,25 +4,33 @@ import { history } from "@codemirror/commands"; import { createMarkdownExtension } from "./markdown"; import { createHotkeysExtension } from "./hotkeys"; import { createEventExtension } from "./event"; - import type { EventOptions } from "./event.d"; interface ExtensionOptions extends EventOptions { - // 是否开启快捷键支持 - name?: string; + enableShortcuts?: boolean; } export const createEditorExtensions = (options: ExtensionOptions): Extension[] => { - const { scrollWrapper = "editor", eventExt, onDragUpload, onPasteUpload } = options; + const { + scrollWrapper = "editor", + eventExt, + enableShortcuts, + onDragUpload, + onPasteUpload, + } = options; // 创建基础扩展数组 const extensions: Extension[] = [ - createHotkeysExtension(), createMarkdownExtension(), createEventExtension({ scrollWrapper, eventExt, onDragUpload, onPasteUpload }), history(), EditorView.lineWrapping, ]; + // 是否开启快捷键支持 + if (enableShortcuts) { + extensions.push(createHotkeysExtension()); + } + return extensions; }; diff --git a/packages/mini-markdown-editor/src/types/global-config.ts b/packages/mini-markdown-editor/src/types/global-config.ts index 4e4b4a9..6c6bad4 100644 --- a/packages/mini-markdown-editor/src/types/global-config.ts +++ b/packages/mini-markdown-editor/src/types/global-config.ts @@ -28,6 +28,11 @@ export interface GlobalConfig { * @type {boolean} */ lineNumbers?: boolean; + /** + * 是否开启快捷键支持 + * @type {boolean} + */ + enableShortcuts?: boolean; /** * 改变编辑器内容时触发 * @type {(value: string, editorView: ViewUpdate) => void} -- Gitee From 67d3af9873dffd4387ab65e7326b2cf7c9def6a1 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Fri, 7 Feb 2025 18:51:45 +0800 Subject: [PATCH 3/5] =?UTF-8?q?chore(mini-markdown-editor):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=B2=98=E8=B4=B4/=E5=A4=8D=E5=88=B6=E5=9B=BE?= =?UTF-8?q?=E7=89=87=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + .../src/extensions/codemirror/event.ts | 8 ++++++-- packages/mini-markdown-play/src/pages/ui-test.tsx | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d47ee0d..e1d189d 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "build:ast:play": "pnpm -F @mini-markdown/ast-parser build:play", "dev:editor": "pnpm -F @mini-markdown/editor dev", "build:editor": "pnpm -F @mini-markdown/editor build", + "dev:play": "pnpm -F @mini-markdown/play dev", "prepare": "husky", "lint": "node ./scripts/zx-lint.js", "prettier": "prettier --write ." diff --git a/packages/mini-markdown-editor/src/extensions/codemirror/event.ts b/packages/mini-markdown-editor/src/extensions/codemirror/event.ts index 5f39460..3d06db0 100644 --- a/packages/mini-markdown-editor/src/extensions/codemirror/event.ts +++ b/packages/mini-markdown-editor/src/extensions/codemirror/event.ts @@ -24,7 +24,7 @@ class ImageHandler { // 插入临时图片用作预览 const selection = view.state.selection.main; - const content = `![${imageAlt}](${temporaryUrl})`; + const content = `![${imageAlt}](${temporaryUrl})\n`; const insertPos = selection.from; view.dispatch({ @@ -33,6 +33,10 @@ class ImageHandler { to: selection.to, insert: content, }, + selection: { + anchor: selection.from + content.length, + head: selection.from + content.length, + }, }); // 处理上传后的信息 @@ -40,7 +44,7 @@ class ImageHandler { //! 可选传递alt参数,作为图片的描述 const handleCallback: Callback = (param: { url: string; alt?: string }) => { try { - const newContent = `![${param.alt || imageAlt}](${param.url})`; + const newContent = `![${param.alt || imageAlt}](${param.url})\n`; // 计算需要替换的范围 const doc = view.state.doc; diff --git a/packages/mini-markdown-play/src/pages/ui-test.tsx b/packages/mini-markdown-play/src/pages/ui-test.tsx index 7a307fa..01060af 100644 --- a/packages/mini-markdown-play/src/pages/ui-test.tsx +++ b/packages/mini-markdown-play/src/pages/ui-test.tsx @@ -39,6 +39,20 @@ const App: FC = () => { console.log(val, view); }; + const handlePatseUpload = async (file: File, callback: Callback) => { + await new Promise((resolve) => { + setTimeout(() => { + console.log("settimeout 上传成功", file); + resolve({}); + }, 1500); + }); + callback({ + url: "123", + alt: "123", + }); + message.success("上传成功"); + }; + return ( @@ -48,6 +62,7 @@ const App: FC = () => { local={true} theme={theme} onChange={handleChange} + onPatseUpload={handlePatseUpload} /> ); -- Gitee From 7339b08e5c03b8a09aed2bfcde9945634ebdd404 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Fri, 7 Feb 2025 19:05:17 +0800 Subject: [PATCH 4/5] =?UTF-8?q?chore(mini-markdown-play):=20=E6=B5=8B?= =?UTF-8?q?=E8=AF=95=E7=B2=98=E8=B4=B4/=E6=8B=96=E6=8B=BD=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=9B=BE=E7=89=87=E4=BB=A5=E5=8F=8A=E5=BC=80=E5=90=AF?= =?UTF-8?q?=E5=BF=AB=E6=8D=B7=E9=94=AE=E6=94=AF=E6=8C=81=E7=9A=84=E5=86=85?= =?UTF-8?q?=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mini-markdown-play/src/pages/ui-test.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/mini-markdown-play/src/pages/ui-test.tsx b/packages/mini-markdown-play/src/pages/ui-test.tsx index 01060af..661c76f 100644 --- a/packages/mini-markdown-play/src/pages/ui-test.tsx +++ b/packages/mini-markdown-play/src/pages/ui-test.tsx @@ -47,7 +47,7 @@ const App: FC = () => { }, 1500); }); callback({ - url: "123", + url: "https://www.baidu.com/img/flexible/logo/pc/result@2.png", alt: "123", }); message.success("上传成功"); @@ -62,6 +62,8 @@ const App: FC = () => { local={true} theme={theme} onChange={handleChange} + enableShortcuts={true} + onDragUpload={handlePatseUpload} onPatseUpload={handlePatseUpload} /> -- Gitee From 25e18c626818b1e8e8c65fab87dea1871a2aa843 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Fri, 7 Feb 2025 20:24:39 +0800 Subject: [PATCH 5/5] =?UTF-8?q?feat(mini-markdown-editor):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=B7=A5=E5=85=B7=E6=A0=8F=E4=BF=9D=E5=AD=98=E6=8C=89?= =?UTF-8?q?=E9=92=AE=EF=BC=8C=E9=85=8D=E7=BD=AE=E5=85=A8=E5=B1=80onSave?= =?UTF-8?q?=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mini-markdown-editor/src/App.tsx | 22 +++++++++++++ .../src/components/Editor/index.tsx | 5 ++- .../src/components/Toolbar/Save.tsx | 31 +++++++++++++++++++ .../src/config/toolbar/base.tsx | 5 +++ .../src/config/toolbar/event.ts | 2 +- .../src/hooks/use-persist-editor-content.ts | 1 - .../src/hooks/use-save-content.ts | 15 +++++++++ .../src/types/global-config.ts | 6 ++-- .../mini-markdown-editor/src/types/toolbar.ts | 3 +- 9 files changed, 81 insertions(+), 9 deletions(-) create mode 100644 packages/mini-markdown-editor/src/components/Toolbar/Save.tsx create mode 100644 packages/mini-markdown-editor/src/hooks/use-save-content.ts diff --git a/packages/mini-markdown-editor/src/App.tsx b/packages/mini-markdown-editor/src/App.tsx index 6548f26..e9684ad 100644 --- a/packages/mini-markdown-editor/src/App.tsx +++ b/packages/mini-markdown-editor/src/App.tsx @@ -6,6 +6,7 @@ import { Button, message } from "antd"; // 可根据需要引入不同的主题 import "highlight.js/styles/atom-one-dark.css"; import { ViewUpdate } from "./types/code-mirror"; +import { EditorView } from "@uiw/react-codemirror"; const AppWrapper = styled.div` width: 100%; @@ -43,6 +44,24 @@ const App: FC = () => { console.log(val, view); }; + const handleSave = (content: string, view: EditorView) => { + console.log(content, view); + }; + + const handlePatseUpload = async (file: File, callback: Callback) => { + await new Promise((resolve) => { + setTimeout(() => { + console.log("settimeout 上传成功", file); + resolve({}); + }, 1500); + }); + callback({ + url: "https://www.baidu.com/img/flexible/logo/pc/result@2.png", + alt: "123", + }); + message.success("上传成功"); + }; + return ( @@ -53,6 +72,9 @@ const App: FC = () => { lineNumbers={true} theme={theme as "light" | "dark"} onChange={handleChange} + onSave={handleSave} + onDragUpload={handlePatseUpload} + onPatseUpload={handlePatseUpload} /> ); diff --git a/packages/mini-markdown-editor/src/components/Editor/index.tsx b/packages/mini-markdown-editor/src/components/Editor/index.tsx index 6f3c71d..741f4da 100644 --- a/packages/mini-markdown-editor/src/components/Editor/index.tsx +++ b/packages/mini-markdown-editor/src/components/Editor/index.tsx @@ -83,7 +83,8 @@ const Editor: FC<{ isSyncScroll: boolean }> = ({ isSyncScroll }) => { setEditorViewInstance(view); }; - const { enableShortcuts, onChange, onDragUpload, onPatseUpload } = useContext(ConfigContext); + const { theme, lineNumbers, enableShortcuts, onChange, onDragUpload, onPatseUpload } = + useContext(ConfigContext); const handleChange = (val: string, editView: ViewUpdate) => { // 更新store @@ -133,8 +134,6 @@ const Editor: FC<{ isSyncScroll: boolean }> = ({ isSyncScroll }) => { [scrollWrapper], ); - const { theme, lineNumbers } = useContext(ConfigContext); - return ( { + const { content, editorView } = useEditorContentStore(); + const saveContent = useSaveContent(); + const { onSave } = useContext(ConfigContext); + + const handleSave = () => { + if (content) { + saveContent(content); + // onSave回调 + if (onSave) { + onSave(content, editorView!); + } + } + }; + + return ( + +
+ + ); +}; + +export default Save; diff --git a/packages/mini-markdown-editor/src/config/toolbar/base.tsx b/packages/mini-markdown-editor/src/config/toolbar/base.tsx index 5c2e3ec..9be2813 100644 --- a/packages/mini-markdown-editor/src/config/toolbar/base.tsx +++ b/packages/mini-markdown-editor/src/config/toolbar/base.tsx @@ -21,6 +21,7 @@ import Upload from "@/components/Toolbar/Upload"; import FullScreen from "@/components/Toolbar/FullScreen"; import { Contents, Read, Write, Help, Output } from "@/components/Toolbar/ShowLayout"; import Emoji from "@/components/Toolbar/Emoji"; +import Save from "@/components/Toolbar/Save"; // 快捷键描述 import { Hotkey } from "@/common/hotkeys"; @@ -186,6 +187,10 @@ export const toolbar: ToolbarItem[] = [ type: "fullscreen", component: , }, + { + type: "save", + component: , + }, { type: "write", component: , diff --git a/packages/mini-markdown-editor/src/config/toolbar/event.ts b/packages/mini-markdown-editor/src/config/toolbar/event.ts index dafb4ce..c6c9694 100644 --- a/packages/mini-markdown-editor/src/config/toolbar/event.ts +++ b/packages/mini-markdown-editor/src/config/toolbar/event.ts @@ -10,7 +10,7 @@ export const InsertTextEvent = (type: ToolbarType) => { // 上传图片 export const InsertImageEvent = (url: string, alt: string) => { - const content = `![${alt}](${url})`; + const content = `![${alt}](${url})\n`; const selection = { anchor: 2, head: 2 + alt.length, 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 index 280b942..d44a836 100644 --- a/packages/mini-markdown-editor/src/hooks/use-persist-editor-content.ts +++ b/packages/mini-markdown-editor/src/hooks/use-persist-editor-content.ts @@ -20,7 +20,6 @@ export const usePersistEditorContent = () => { // 获取内容 const getContent = (): string => { - if (!local) return ""; return localStorage.getItem(EDITOR_CONTENT_KEY) ?? ""; }; diff --git a/packages/mini-markdown-editor/src/hooks/use-save-content.ts b/packages/mini-markdown-editor/src/hooks/use-save-content.ts new file mode 100644 index 0000000..d47fab8 --- /dev/null +++ b/packages/mini-markdown-editor/src/hooks/use-save-content.ts @@ -0,0 +1,15 @@ +import { EDITOR_CONTENT_KEY } from "@/common"; +import { safeLocalStorage } from "@/utils/storage"; +import { useDebounceFn } from "ahooks"; + +export const useSaveContent = () => { + const localStorage = safeLocalStorage(); + const { run: saveContent } = useDebounceFn( + (content: string) => { + localStorage.setItem(EDITOR_CONTENT_KEY, content); + }, + { wait: 300 }, + ); + + return saveContent; +}; diff --git a/packages/mini-markdown-editor/src/types/global-config.ts b/packages/mini-markdown-editor/src/types/global-config.ts index 6c6bad4..d6e626d 100644 --- a/packages/mini-markdown-editor/src/types/global-config.ts +++ b/packages/mini-markdown-editor/src/types/global-config.ts @@ -1,4 +1,4 @@ -import { ViewUpdate } from "@uiw/react-codemirror"; +import { EditorView, ViewUpdate } from "@uiw/react-codemirror"; import { ToolbarType } from "./toolbar"; export interface GlobalConfig { @@ -55,9 +55,9 @@ export interface GlobalConfig { onPatseUpload?: (file: File, callback: Callback) => void; /** * 保存触发 - * @type {(value: string, editorView: ViewUpdate) => void} + * @type {(value: string, editorView: EditorView) => void} */ - onSave?: (value: string, editorView: ViewUpdate) => void; + onSave?: (value: string, editorView: EditorView) => void; } export type Callback = (param: { url: string; alt?: string }) => void; diff --git a/packages/mini-markdown-editor/src/types/toolbar.ts b/packages/mini-markdown-editor/src/types/toolbar.ts index 7f2ca1e..01823b6 100644 --- a/packages/mini-markdown-editor/src/types/toolbar.ts +++ b/packages/mini-markdown-editor/src/types/toolbar.ts @@ -55,4 +55,5 @@ export type ToolbarType = | "contents" | "help" | "output" - | "emoji"; + | "emoji" + | "save"; -- Gitee