From 7c15262269e82a9ed73a75daacf045aede7c806f Mon Sep 17 00:00:00 2001 From: tabzzz Date: Fri, 21 Feb 2025 22:50:23 +0800 Subject: [PATCH 01/11] =?UTF-8?q?docs(mini-markdown-docs):=20=E8=A1=A5?= =?UTF-8?q?=E5=85=85=E4=BB=8B=E7=BB=8D=E9=A1=B5=E7=9A=84=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/guide/index.mdx | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/docs/guide/index.mdx b/docs/docs/guide/index.mdx index c44eab0..32d7b3e 100644 --- a/docs/docs/guide/index.mdx +++ b/docs/docs/guide/index.mdx @@ -4,13 +4,19 @@ Mini Markdown Editor 是 2025年寒假字节青训营「前端」的一个开源项目。 +此项目旨在提供一个简单、易用且高性能的 Markdown 编辑体验。这个项目的核心目标是通过精简和优化,让开发者能够更专注于 Markdown 内容的创作,同时保证其高效的解析与渲染能力。 + ::: -该项目采用 `pnpm` + `monorepo` 进行管理,包含两个核心子项目: +该项目采用 `pnpm` + `monorepo` 进行管理,确保了多个子项目的模块化管理以及高效的构建流程。项目包含两个核心子项目: + +- `@mini-markdown-rc/ast-parser`:核心库。该库负责将 Markdown 文本解析为抽象语法树(AST),并根据需要转换为 HTML 或其他格式。 +- `@mini-markdown-rc/editor`:基于 React 实现的 Markdown 编辑器,集成了语法高亮、实时预览等功能,提供了流畅的编辑体验。 -- `@mini-markdown-rc/ast-parser`:核心库,实现 Markdown 语法的 AST 解析器,用于解析 Markdown 文本,生成 AST、HTML。 -- `@mini-markdown-rc/editor`:一款 React 的 Markdown 编辑器。 +## 功能亮点 -**优点:** +- **简洁易用**:用户可以快速上手,编辑界面直观清晰。 +- **高性能**:采用高效的 AST 解析和渲染机制,即使处理十万+的内容依然保持流畅。 +- **灵活定制**:提供丰富的 API,用户可以根据需求自定义工具栏、主题以及编辑器行为。 +- **兼容性强**:支持常见的 Markdown 语法,并能够生成标准的 HTML 代码,保证兼容性。 -简单易用、轻量、性能高,十万➕内容依然流畅。 -- Gitee From bc7942c9d56cac4b23e6fd802c46bd5ea6c2ae6c Mon Sep 17 00:00:00 2001 From: tabzzz Date: Wed, 26 Feb 2025 00:51:38 +0800 Subject: [PATCH 02/11] =?UTF-8?q?docs(mini-markdown-docs):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0api->toolbars=E9=83=A8=E5=88=86=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 --- docs/docs/guide/api.mdx | 213 ++++++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 94 deletions(-) diff --git a/docs/docs/guide/api.mdx b/docs/docs/guide/api.mdx index 622b660..5951adb 100644 --- a/docs/docs/guide/api.mdx +++ b/docs/docs/guide/api.mdx @@ -4,13 +4,16 @@ ### toolbars -- 类型:`{ addTools?: ToolbarItem[]; excludeTools?: string[] }` - - addTools:可以自定义新增 `toolbar` 配置 - - excludeTools:需要排除的 toolbar 数组 +- 类型:`{ addTools?: ToolbarItem[]; excludeTools?: string[]; orderTools?: { type: string; order: number }[]; }` + - addTools:可以自定义新增 `toolbar` 配置项 + - excludeTools:需要过滤工具项的 `toolbar` 数组配置项 + - orderTools: 排序 `toolbar` 中工具项先后顺序的数组配置项 - 默认值:`undefined` 需要渲染的 toolbar,默认全部渲染。 +**工具栏项接口定义:** + ```ts export interface BaseToolbarItem { type: ToolbarType; @@ -47,6 +50,12 @@ export interface ToolbarItem extends BaseToolbarItem { #### 1. 移除工具栏 +:::warning 警告 + +只能移除已存在的工具栏项,传入未被识别的工具栏项,不会生效。 + +::: + ```tsx 有三种方法可以自定义工具栏:配置项、组件、工具栏实例。 +> **有三种方法可以自定义工具栏:配置项、组件、工具栏实例。** - 方法1:配置项 -```tsx -// 因为 icon 属性需要的是 字符串,所以导入路径的时候需要使用 `?raw` -/** - * 尽量将 svg 的 width、height设置为 1em - * - */ -import CustomToolbarIcon from "@/assets/images/custom-toolbar.svg?raw"; - - { - // 执行快捷键后的回调 + ```tsx + // 因为 icon 属性需要的是 字符串,所以导入路径的时候需要使用 `?raw` + /** + * 尽量将 svg 的 width、height 设置为 1em + * + */ + import CustomToolbarIcon from "@/assets/images/custom-toolbar.svg?raw"; + + { + // 执行快捷键后的回调 + console.log("我是快捷键输出123"); + }, + }, + // 点击工具栏按钮的回调 + onClick: () => { console.log("我是快捷键输出123"); }, }, - // 点击工具栏按钮的回调 - onClick: () => { - console.log("我是快捷键输出123"); - }, - }, - ], - }} -/>; -``` + ], + }} + />; + ``` - 方法2:组件 -该方式,工具栏的样式需要完全自定义。 + 通过配置 `component` 属性,可以自定义工具栏的样式。 -![自定义toolbar的描述](./img/custom-toolbar.png) + > 在该方式中,工具栏的样式需要完全自定义。 + + ![自定义toolbar的描述](./img/custom-toolbar.png) + + ```tsx + 组件, + }, + ], + }} + /> + ``` + +- 方法3: 工具栏实例 + + 可以通过 `ToolbarManager` 实例方法,对工具栏进行操作。 + + 此处给出添加工具栏项的示例: + + ```tsx {1,7-24} + import { Editor, ToolbarManager, insertContent } from "mini-markdown-editor"; + import { useEffect } from "react"; + + const App = () => { + useEffect(() => { + try { + toolbarConfig.addToolItem({ + type: "custom-toolbar", + title: "自定义工具栏", + icon: CustomToolbarIcon, + description: "自定义工具栏的描述", + hotkey: { + command: "Mod-l", + description: "快捷键描述", + handle: () => { + console.log("执行快捷键的回调"); + }, + }, + onClick: () => { + // 插入内容 + insertContent.insertTextAtCursor("123"); + // 更精细控制 + insertContent.insertContent("123", { anchor: 1, head: 1 }); + }, + }); + console.log("添加工具栏成功"); + } catch (error) { + console.error("添加工具栏失败:", error); + } + }, []); + return ( + <> + + + ); + }; + + export default App; + ``` + + > 想要深入了解 ToolbarManager 实例方法,可以移步查看: [ToolbarManager 实例方法](#toolbarmanager)。 + +#### 3. 工具栏排序 + +通过 `orderTools` 配置项,可以对工具栏中的工具项进行排序。 + +> 只对 `orderTools` 中配置的工具项进行排序,未配置的工具项不受影响。 ```tsx 组件, - }, + orderTools: [ + { type: "bold", order: 1 }, + { type: "italic", order: 2 }, + { type: "underline", order: 3 }, ], }} /> ``` -- 方法3: 工具栏实例 - -```tsx {1,7-11,17-35} -import { Editor, ToolbarManager, insertContent } from "mini-markdown-editor"; -import { useEffect } from "react"; - -const App = () => { - useEffect(() => { - try { - toolbarConfig.updateToolbarItem("bold", { - onClick: () => { - console.log("我是粗体"); - }, - }); - } catch (error) { - console.error("更新工具栏失败:", error); - } - - try { - toolbarConfig.addToolItem({ - type: "custom-toolbar", - title: "自定义工具栏", - icon: CustomToolbarIcon, - description: "自定义工具栏的描述", - hotkey: { - command: "Mod-l", - description: "快捷键描述", - handle: () => { - console.log("执行快捷键的回调"); - }, - }, - onClick: () => { - // 插入内容 - insertContent.insertTextAtCursor("123"); - // 更精细控制 - insertContent.insertContent("123", { anchor: 1, head: 1 }); - }, - }); - console.log("添加工具栏成功"); - } catch (error) { - console.error("添加工具栏失败:", error); - } - }, []); - return ( - <> - - - ); -}; - -export default App; -``` - ### placeholder - 类型:`string` -- Gitee From d26ca9f4a527a6460b3d335652dc5c569067d2dd Mon Sep 17 00:00:00 2001 From: tabzzz Date: Wed, 26 Feb 2025 01:20:23 +0800 Subject: [PATCH 03/11] =?UTF-8?q?docs(mini-markdown-docs):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E5=A4=9A=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81=E7=9A=84?= =?UTF-8?q?=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/guide/api.mdx | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/docs/docs/guide/api.mdx b/docs/docs/guide/api.mdx index 5951adb..2b9b222 100644 --- a/docs/docs/guide/api.mdx +++ b/docs/docs/guide/api.mdx @@ -210,7 +210,7 @@ export interface ToolbarItem extends BaseToolbarItem { 编辑器内容初始化内容。 -:::warning 注意 +:::warning 警告 当 `local` 为 `false` 时,`value` 才会起作用。不然会默认使用 `localStorage` 中的内容。 @@ -266,6 +266,17 @@ const App = () => { }; ``` +### locale + +- 类型:`"en" | "cn" | "tw"` +- 默认值:`"en"` + +语言环境。 + +```tsx + +``` + ### lineNumbers - 类型:`boolean` @@ -1050,13 +1061,21 @@ interface GlobalConfig extends ReactCodeMirrorProps { value?: string; /** * 配置工具栏 - * 添加工具; 排除工具 + * 添加工具; 排除工具; 排序工具 */ - toolbars?: { addTools?: ToolbarItem[]; excludeTools?: string[] }; + toolbars?: { + addTools?: ToolbarItem[]; + excludeTools?: string[]; + orderTools?: { type: string; order: number }[]; + }; /** * 底部状态栏是否显示,默认显示 */ status?: boolean; + /** + * 语言环境 + */ + locale?: "en" | "cn" | "tw"; /** * 编辑器主题 */ -- Gitee From c43f1c9ecb4ebba857d320662af09edf60bd5dcd Mon Sep 17 00:00:00 2001 From: tabzzz Date: Wed, 26 Feb 2025 23:54:53 +0800 Subject: [PATCH 04/11] =?UTF-8?q?docs(mini-markdown-docs):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E7=BB=A7=E6=89=BFcm=E7=9A=84=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/guide/api.mdx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/docs/guide/api.mdx b/docs/docs/guide/api.mdx index 2b9b222..fc7f43b 100644 --- a/docs/docs/guide/api.mdx +++ b/docs/docs/guide/api.mdx @@ -2,6 +2,12 @@ ## 属性 +:::tip 提示 + +`Editor` 组件基于 `CodeMirror` 实现,支持所有 CodeMirror 的原生配置选项,并进行了功能扩展。本文档中仅列出了扩展属性和部分常用配置,如需了解全部配置选项,请查阅 [CodeMirror 官方文档](https://codemirror.net/doc/manual.html#config)。 + +::: + ### toolbars - 类型:`{ addTools?: ToolbarItem[]; excludeTools?: string[]; orderTools?: { type: string; order: number }[]; }` @@ -309,6 +315,13 @@ const App = () => { ## 事件 +:::tip 提示 + +`Editor` 组件的事件系统基于 `CodeMirror` 进行了扩展,部分基础事件已重命名或优化。本文档列出了常用事件处理方法,如需了解全部事件类型及详细用法,请参考 [CodeMirror 事件文档](https://codemirror.net/doc/manual.html#events)。 + +::: + + ### onChange - 类型:`(value: string, editorView: ViewUpdate) => void` @@ -860,7 +873,7 @@ import { insertContent } from "mini-markdown-editor"; 在编辑区插入内容(相比 `insertTextAtCursor` 方法,该方法控制更加精细)。 -:::info selection参数 +:::info selection 参数 `anchor、head`:用于控制光标位置,当 `anchor` 和 `head` 相等时,表示光标在当前位置,如果 `anchor` 和 `head` 不相等,则表示光标选中某段内容。 @@ -933,11 +946,11 @@ import { insertContent } from "mini-markdown-editor"; ### EDITOR_CONTENT_KEY -编辑区内容存储(localStorage)的 `key`,值为 `markdown-editor-content`。 +编辑区内容存储( localStorage )的 `key`,值为 `markdown-editor-content`。 ### SYNC_SCROLL_STATUS -同步滚动状态(存储在localStorage中),值为 `markdown-editor-sync-scroll`。 +同步滚动状态(存储在 localStorage 中),值为 `markdown-editor-sync-scroll`。 ## 类型 -- Gitee From b389a0b1667213efc368ceed604e6faa952b62f0 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Thu, 27 Feb 2025 00:30:24 +0800 Subject: [PATCH 05/11] =?UTF-8?q?docs(mini-markdown-docs):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0ToolbarManager=E6=96=B9=E6=B3=95=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 --- docs/docs/guide/api.mdx | 198 +++++++++++++++++++++++++++++++--------- 1 file changed, 156 insertions(+), 42 deletions(-) diff --git a/docs/docs/guide/api.mdx b/docs/docs/guide/api.mdx index fc7f43b..acf2a35 100644 --- a/docs/docs/guide/api.mdx +++ b/docs/docs/guide/api.mdx @@ -687,11 +687,13 @@ export default function () { ### ToolbarManager -工具栏管理器方法,用于自定义工具栏。 +工具栏管理器,提供丰富的工具栏自定义功能。 :::tip 注意 -需要渲染 `` 组件后,才能使用 `ToolbarManager`。 +必须在渲染 `` 组件后才能使用 `ToolbarManager`。 + +建议使用 `try...catch` 包裹相关操作以捕获可能的错误。 ::: @@ -699,16 +701,34 @@ export default function () { import { ToolbarManager } from "mini-markdown-editor"; ``` -> 可以使用 `try...catch`包裹 `ToolbarManager`,捕获错误。 +**基本用法示例:** + +```tsx +import { Editor, ToolbarManager } from "mini-markdown-editor"; + +export default function EditorComponent() { + useEffect(() => { + try { + // 工具栏管理器操作... + const toolbars = ToolbarManager.getAllToolbars(); + console.log(toolbars); + } catch (error) { + console.error("工具栏操作失败:", error); + } + }, []); + + return ; +} +``` + +#### getDefaultToolbar -#### 1. getDefaultToolbar +获取默认工具栏配置。 - 参数:`void` - 返回值:`ToolbarItem[]` +- 说明:返回原始默认工具栏配置,不受 `toolbar.excludeTools` 属性影响。 -获取默认工具栏内容。 - -> 获取的 toolbar 内容默认的内容,即使 `toolbar.excludeTools` 排除后,还是会获取。 ```tsx {1,5} import { Editor, ToolbarManager } from "mini-markdown-editor"; @@ -726,40 +746,49 @@ export default function () { } ``` -#### 2. getAllToolbars +#### getAllToolbars + +获取当前所有工具栏项。 - 参数:`void` - 返回值:`ToolbarItem[]` +- 说明:返回当前实际使用的工具栏配置,包括所有自定义修改。 -获取所有工具栏项。 ```tsx useEffect(() => { - const res = ToolbarManager.getAllToolbars(); + const currentToolbars = ToolbarManager.getAllToolbars(); + console.log(currentToolbars); }, []); ``` -#### 3. addToolItem +#### addToolItem -- 参数:`item: ToolbarItem` +添加新的工具栏项。 + +- 参数:`item: ToolbarItem` - 工具栏项配置对象 - 返回值:`void` +- 说明:如果添加的工具栏类型已存在,将抛出错误。 -添加指定工具栏项。 -```tsx {1,5-12} +```tsx {1,5-16} import { Editor, ToolbarManager } from "mini-markdown-editor"; export default function () { useEffect(() => { + try { ToolbarManager.addToolItem({ - type: "heading", - icon: "heading", - title: "标题", + type: "custom-heading", // 工具栏类型,必须唯一 + icon: "heading", // 图标名称 + title: "自定义标题", // 悬停提示文本 onClick: () => { - console.log("标题"); + console.log("标题功能被点击"); }, }); - }, []); + } catch (error) { + console.error("添加工具栏失败:", error); + } +}, []); return ( <> @@ -767,12 +796,18 @@ export default function () { ); ``` -#### 4. updateToolbars +#### updateToolbars -- 参数:`newToolbars: ToolbarItem[]` +完全替换工具栏配置。 + +- 参数:`newToolbars: ToolbarItem[]` - 新的工具栏配置数组 - 返回值:`void` -更新工具栏内容。 +:::warning 警告 + +此操作会覆盖所有现有工具栏项,请谨慎使用! + +::: ```tsx useEffect(() => { @@ -782,45 +817,50 @@ useEffect(() => { icon: "heading", title: "标题", onClick: () => { - console.log("标题"); + console.log("标题功能被触发"); }, }, + // 添加其他需要保留的工具栏项... ]); }, []); ``` -> 注意:上面写法👆,会覆盖工具栏配置,最后只剩一个 `heading` 工具栏。 +#### updateToolbarItem -#### 5. updateToolbarItem +更新特定工具栏项的配置。 -- 参数:`type: ToolbarType, partialToolbarItem: Partial` +- 参数: + - type: `ToolbarType` - 要更新的工具栏类型 + - partialToolbarItem: `Partial` - 部分工具栏配置 - 返回值:`void` +- 说明:只更新指定的属性,其他属性保持不变。 -修改特定工具栏项的功能。 ```tsx useEffect(() => { ToolbarManager.updateToolbarItem("bold", { + // 只更新以下属性,其他属性保持不变 onClick: () => { - console.log("我是粗体"); + console.log("自定义粗体功能"); }, hotkey: { command: "Mod-b", - description: "快捷键描述", + description: "加粗文本 (Ctrl+B)", handle: () => { - console.log("我是快捷键粗体"); + console.log("通过快捷键触发粗体功能"); }, }, }); }, []); ``` -#### 6. removeToolItem +#### removeToolItem -- 参数:`type: ToolbarType` -- 返回值:`void` +移除指定的工具栏项。 -移除指定工具栏项。 +- 参数:`type: ToolbarType` - 要移除的工具栏类型 +- 返回值:`void` +- 说明:如果指定类型不存在,此操作不会产生任何效果。 ```tsx useEffect(() => { @@ -828,30 +868,87 @@ useEffect(() => { }, []); ``` -#### 7. reset +#### reset + +重置工具栏到初始状态。 - 参数:`void` - 返回值:`void` +- 说明:恢复到默认工具栏配置。 -重置工具栏。 ```tsx -ToolbarManager.reset(); +useEffect(() => { + // 在进行多次自定义后,恢复到默认配置 + ToolbarManager.reset(); +}, []); ``` -#### 8. reorderToolbar - -- 参数:`newOrder: ToolbarType[]` -- 返回值:`void` +#### reorderToolbar 重新排序工具栏。 +- 参数:`newOrder: ToolbarType[]` - 新的工具栏顺序数组 +- 返回值:`void` +- 说明:必须包含所有现有工具栏类型,否则将抛出错误。 + ```tsx useEffect(() => { - ToolbarManager.reorderToolbar(["heading", "line", "table", "undo", "redo", "fullscreen"]); + try { + ToolbarManager.reorderToolbar([ + "heading", "line", "table", + "undo", "redo", "fullscreen" + // 需包含所有现有工具栏类型 + ]); + } catch (error) { + console.error("工具栏重排序失败:", error); + } }, []); ``` +#### 事件监听 + +`ToolbarManager` 继承自 `BaseClass`,因此具有完整的事件管理能力,可以监听和响应工具栏变化事件。 + +> 通过事件系统,您可以实现对工具栏变化的实时响应,如状态同步、UI更新等。 + + +**事件监听方法示例:** + +```tsx + +// 监听工具栏变化事件 +ToolbarManager.on("TOOLBAR_ADDED", (toolbarItem) => { + console.log("新增了工具栏项:", toolbarItem); +}); + +// 一次性事件监听 +ToolbarManager.once("TOOLBAR_REMOVED", (type) => { + console.log(`工具栏项 ${type} 已被移除`); +}); + +// 移除事件监听 +const updateHandler = (updatedItem) => { + console.log("工具栏项已更新:", updatedItem); +}; +ToolbarManager.on("TOOLBAR_UPDATED", updateHandler); +// 稍后移除监听 +ToolbarManager.off("TOOLBAR_UPDATED", updateHandler); + + +``` + +:::tip 提示 + +更多自定义时间类型,详见 [类型 ToolbarEvents](#toolbarevents)。 + +::: + + +#### 扩展方法 + + + ### insertContent 对 `Editor` 的编辑区进行操作的方法,可结合 `自定义toolbar` 使用,实现完整功能。 @@ -1044,6 +1141,23 @@ interface ToolbarItem extends BaseToolbarItem { } ``` +### ToolbarEvents + +工具栏基础自定义事件类型。 + +```ts + +enum ToolbarEvents { + TOOLBAR_ADDED = "toolbar:added", + TOOLBAR_REMOVED = "toolbar:removed", + TOOLBAR_UPDATED = "toolbar:updated", + TOOLBAR_REORDERED = "toolbar:reordered", + TOOLBAR_RESET = "toolbar:reset", + TOOLBAR_ERROR = "toolbar:error", +} + +``` + ### ToolbarContextValues 工具栏上下文值接口,可以对工具栏进行“增删改查”。 -- Gitee From 71848cf4c8dae170bba13045bd5ae1bb787b23ce Mon Sep 17 00:00:00 2001 From: tabzzz Date: Thu, 27 Feb 2025 00:37:36 +0800 Subject: [PATCH 06/11] =?UTF-8?q?feat(mini-markdown-editor):=20=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E5=85=81=E8=AE=B8=E7=94=A8=E6=88=B7=E8=87=AA=E5=AE=9A?= =?UTF-8?q?=E4=B9=89=E6=89=A9=E5=B1=95ToolbarManager=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E5=87=BD=E6=95=B0=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/config/toolbar/index.ts | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/packages/mini-markdown-editor/src/config/toolbar/index.ts b/packages/mini-markdown-editor/src/config/toolbar/index.ts index 206e87a..491659e 100644 --- a/packages/mini-markdown-editor/src/config/toolbar/index.ts +++ b/packages/mini-markdown-editor/src/config/toolbar/index.ts @@ -9,6 +9,7 @@ class ToolbarConfig extends BaseClass { private readonly defaultToolbars: ToolbarItem[]; private toolbarOrderMap: Map; private initialized: boolean = false; + private plugins: Map any> = new Map(); constructor(initialToolbars: ToolbarItem[]) { super({ @@ -319,6 +320,85 @@ class ToolbarConfig extends BaseClass { throw error; } } + + // ---------------------扩展函数----------------------- + + // 注册方法 + public registerMethod( + name: string, + method: (...args: any[]) => any, + override: boolean = false, + ): void { + try { + this.checkDestroyed(); + + // 防止覆盖 + const protectedMethods = [ + "addToolItem", + "removeToolItem", + "updateToolbars", + "reset", + "registerMethod", + "unregisterMethod", + "callMethod", + "destroy", + ]; + + if (protectedMethods.includes(name)) { + throw new Error(`Cannot override protected method: ${name}`); + } + + if (this.plugins.has(name) && !override) { + throw new Error(`Method "${name}" already exists. Set override to true to replace it.`); + } + + // 注册 + this.plugins.set(name, method.bind(this)); + this.emit("METHOD_REGISTERED", name); + } catch (error) { + this.error("Error registering method:", error); + throw error; + } + } + + // 注销方法 + public unregisterMethod(name: string): void { + try { + this.checkDestroyed(); + + if (!this.plugins.has(name)) { + return; + } + + this.plugins.delete(name); + this.emit("METHOD_UNREGISTERED", name); + } catch (error) { + this.error("Error unregistering method:", error); + throw error; + } + } + + // 调用自定义方法 + public callMethod(name: string, ...args: any[]): any { + try { + this.checkDestroyed(); + + if (!this.plugins.has(name)) { + throw new Error(`Method "${name}" not found`); + } + + return this.plugins.get(name)!(...args); + } catch (error) { + this.error(`Error calling method "${name}":`, error); + throw error; + } + } + + // 销毁 + public destroy(): void { + this.plugins.clear(); + super.destroy(); + } } export const toolbarConfig = new ToolbarConfig(defaultToolbar); -- Gitee From aaf8d1c52485d2406bf76ea96b8a38d10d0a895b Mon Sep 17 00:00:00 2001 From: tabzzz Date: Thu, 27 Feb 2025 00:48:25 +0800 Subject: [PATCH 07/11] =?UTF-8?q?chore(mini-markdown-editor):=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E5=B7=A5=E5=85=B7=E6=A0=8F=E4=BA=8B=E4=BB=B6=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE=E9=A1=B9=EF=BC=8C=E8=A7=84=E8=8C=83=E7=B1=BB=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E4=B8=AD=E7=9A=84=E4=BA=8B=E4=BB=B6=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mini-markdown-editor/src/config/toolbar/index.ts | 4 ++-- packages/mini-markdown-editor/src/types/toolbar.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/mini-markdown-editor/src/config/toolbar/index.ts b/packages/mini-markdown-editor/src/config/toolbar/index.ts index 491659e..676b3ed 100644 --- a/packages/mini-markdown-editor/src/config/toolbar/index.ts +++ b/packages/mini-markdown-editor/src/config/toolbar/index.ts @@ -354,7 +354,7 @@ class ToolbarConfig extends BaseClass { // 注册 this.plugins.set(name, method.bind(this)); - this.emit("METHOD_REGISTERED", name); + this.emit(ToolbarEvents.METHOD_REGISTERED, name); } catch (error) { this.error("Error registering method:", error); throw error; @@ -371,7 +371,7 @@ class ToolbarConfig extends BaseClass { } this.plugins.delete(name); - this.emit("METHOD_UNREGISTERED", name); + this.emit(ToolbarEvents.METHOD_UNREGISTERED, name); } catch (error) { this.error("Error unregistering method:", error); throw error; diff --git a/packages/mini-markdown-editor/src/types/toolbar.ts b/packages/mini-markdown-editor/src/types/toolbar.ts index 783e904..956f891 100644 --- a/packages/mini-markdown-editor/src/types/toolbar.ts +++ b/packages/mini-markdown-editor/src/types/toolbar.ts @@ -89,10 +89,13 @@ export enum ToolbarEvents { TOOLBAR_ADDED = "toolbar:added", TOOLBAR_REMOVED = "toolbar:removed", TOOLBAR_UPDATED = "toolbar:updated", - // 重排序 TOOLBAR_REORDERED = "toolbar:reordered", TOOLBAR_RESET = "toolbar:reset", TOOLBAR_ERROR = "toolbar:error", + METHOD_REGISTERED = "method:registered", + METHOD_UNREGISTERED = "method:unregistered", + beforeDestroy = "beforeDestroy", + destroy = "destroy", } // 类型保护函数 -- Gitee From 38796c487b05339cde21d9f33d6492ec8f8cba44 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Thu, 27 Feb 2025 01:11:12 +0800 Subject: [PATCH 08/11] =?UTF-8?q?docs(mini-markdown-docs):=20=E5=AE=8C?= =?UTF-8?q?=E6=88=90Toolbarmanager=E7=9A=84=E6=89=80=E6=9C=89=E6=96=87?= =?UTF-8?q?=E6=A1=A3=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/guide/api.mdx | 223 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 214 insertions(+), 9 deletions(-) diff --git a/docs/docs/guide/api.mdx b/docs/docs/guide/api.mdx index acf2a35..f3d27e8 100644 --- a/docs/docs/guide/api.mdx +++ b/docs/docs/guide/api.mdx @@ -746,6 +746,40 @@ export default function () { } ``` +#### getToolbarOrder + +获取指定工具栏项的当前顺序。 + +- 参数:`type`: `ToolbarType` - 工具栏类型 +- 返回值:`number` - 工具栏项的顺序索引,如果不存在则返回 -1 +- 说明:顺序索引从 0 开始,表示工具栏项在工具栏中的位置 + +```tsx + +useEffect(() => { + const boldOrder = ToolbarManager.getToolbarOrder("bold"); + console.log(`粗体按钮的位置是: ${boldOrder}`); +}, []); + +``` + +#### getAllToolbarsOrder + +获取所有工具栏项的顺序信息。 + +- 参数:`void` +- 返回值:`{ type: ToolbarType; order: number }[]` - 工具栏类型和顺序的数组 +- 说明:返回按顺序排列的工具栏类型和索引位置信息 + +```tsx + +useEffect(() => { + const toolbarsOrder = ToolbarManager.getAllToolbarsOrder(); + console.log("工具栏顺序:", toolbarsOrder); +}, []); + +``` + #### getAllToolbars 获取当前所有工具栏项。 @@ -754,7 +788,6 @@ export default function () { - 返回值:`ToolbarItem[]` - 说明:返回当前实际使用的工具栏配置,包括所有自定义修改。 - ```tsx useEffect(() => { const currentToolbars = ToolbarManager.getAllToolbars(); @@ -796,11 +829,58 @@ export default function () { ); ``` +#### setToolbarItemOrder + +设置单个工具栏项的顺序。 + +- 参数: + - `type`: `ToolbarType` - 工具栏类型 + - `newOrder`: `number` - 新的顺序位置 +- 返回值:`void` +- 说明:会自动调整其他工具栏项的顺序以适应变化 + +```tsx + +useEffect(() => { + try { + // 将粗体按钮移动到第一位置 + ToolbarManager.setToolbarItemOrder("bold", 0); + } catch (error) { + console.error("设置工具栏顺序失败:", error); + } +}, []); + +``` + +#### setToolbarOrder + +批量设置多个工具栏项的顺序。 + +- 参数:`orders`: `Record` - 类型到顺序的映射对象 +- 返回值:`void` +- 说明:可以同时调整多个工具栏项的顺序 + +```tsx + +useEffect(() => { + try { + ToolbarManager.setToolbarsOrder({ + "bold": 0, + "italic": 1, + "underline": 2 + }); + } catch (error) { + console.error("批量设置工具栏顺序失败:", error); + } +}, []); + +``` + #### updateToolbars 完全替换工具栏配置。 -- 参数:`newToolbars: ToolbarItem[]` - 新的工具栏配置数组 +- 参数:`newToolbars`: `ToolbarItem[]` - 新的工具栏配置数组 - 返回值:`void` :::warning 警告 @@ -830,8 +910,8 @@ useEffect(() => { 更新特定工具栏项的配置。 - 参数: - - type: `ToolbarType` - 要更新的工具栏类型 - - partialToolbarItem: `Partial` - 部分工具栏配置 + - `type`: `ToolbarType` - 要更新的工具栏类型 + - `partialToolbarItem`: `Partial` - 部分工具栏配置 - 返回值:`void` - 说明:只更新指定的属性,其他属性保持不变。 @@ -858,7 +938,7 @@ useEffect(() => { 移除指定的工具栏项。 -- 参数:`type: ToolbarType` - 要移除的工具栏类型 +- 参数:`type`: `ToolbarType` - 要移除的工具栏类型 - 返回值:`void` - 说明:如果指定类型不存在,此操作不会产生任何效果。 @@ -888,7 +968,7 @@ useEffect(() => { 重新排序工具栏。 -- 参数:`newOrder: ToolbarType[]` - 新的工具栏顺序数组 +- 参数:`newOrder`: `ToolbarType[]` - 新的工具栏顺序数组 - 返回值:`void` - 说明:必须包含所有现有工具栏类型,否则将抛出错误。 @@ -906,6 +986,29 @@ useEffect(() => { }, []); ``` +#### swapToolbarsPosition + +- 参数: + - `firstType`: `ToolbarType` - 第一个工具栏类型 + - `secondType`: `ToolbarType` - 第二个工具栏类型 +- 返回值:`void` +- 说明:如果指定的工具栏类型不存在,将抛出错误。 + +交换两个工具栏项的位置。 + +```tsx + +useEffect(() => { + try { + // 交换粗体和斜体按钮的位置 + ToolbarManager.swapToolbarsPosition("bold", "italic"); + } catch (error) { + console.error("交换工具栏位置失败:", error); + } +}, []); + +``` + #### 事件监听 `ToolbarManager` 继承自 `BaseClass`,因此具有完整的事件管理能力,可以监听和响应工具栏变化事件。 @@ -940,14 +1043,116 @@ ToolbarManager.off("TOOLBAR_UPDATED", updateHandler); :::tip 提示 -更多自定义时间类型,详见 [类型 ToolbarEvents](#toolbarevents)。 +更多自定义事件类型,详见 [类型 ToolbarEvents](#toolbarevents)。 ::: - #### 扩展方法 +`ToolbarManagerx` 提供了插件系统,允许您注册自定义方法来扩展其功能,无需继承或修改原始类。 + +:::warning 注意 + +自定义方法中,`this` 指向 `ToolbarManager` 实例,因此可以访问所有公共方法。但应避免直接访问私有属性或修改内部状态,以免导致意外行为。 + +::: + +##### registerMethod + +注册自定义方法到工具栏管理器。 + +- 参数: + - `name`: `string` - 方法名称 + - `method`: `(...args: any[]) => any` - 方法实现 + - `override`: `boolean = false` - 是否允许覆盖已存在的方法 +- 返回值:`void` +```tsx + +useEffect(() => { + try { + ToolbarManager.registerMethod("groupToolbars", function(groupName) { + // 此处的 this 指向 ToolbarManager 实例 + const allToolbars = this.getAllToolbars(); + + // 根据类型分组工具栏 + return { + formatting: allToolbars.filter(tool => + ["bold", "italic", "underline"].includes(tool.type) + ), + insert: allToolbars.filter(tool => + ["image", "link", "table"].includes(tool.type) + ) + }; + }); + } catch (error) { + console.error("注册自定义方法失败:", error); + } +}, []); + +``` + +##### callMethod + +调用已注册的自定义方法。 + +- 参数: + - `name`: `string` - 方法名称 + - `...args`: `any[]` - 传递给方法的参数 +- 返回值: `any` - 自定义方法的返回值 +- 说明: 如果方法不存在,将抛出错误 + +```tsx + +// 假定已注册了名为 groupToolbars 的自定义方法 +useEffect(() => { + try { + // 调用自定义分组方法 + const groups = ToolbarManager.callMethod("groupToolbars", "default"); + console.log("格式化工具:", groups.formatting); + console.log("插入工具:", groups.insert); + } catch (error) { + console.error("调用自定义方法失败:", error); + } +}, []); + +``` + +##### unregisterMethod + +移除已注册的自定义方法。 + +- 参数: `name`: `string` - 方法名称 +- 返回值: `void` +- 说明: 如果方法不存在,此操作不会产生任何效果 + +```tsx + +useEffect(() => { + // 当不再需要某个自定义方法时 + ToolbarManager.unregisterMethod("groupToolbars"); +}, []); + +``` + +#### 资源清理 + +当不再需要 ToolbarManager 时,应当调用 `destroy()` 方法释放资源: + +```tsx + +useEffect(() => { + // ... + + return () => { + // 组件卸载时清理资源 + ToolbarManager.destroy(); + }; +}, []); + +``` + +这将移除所有事件监听器和自定义方法,并标记实例为已销毁,防止内存泄漏和意外操作。 ### insertContent @@ -1306,4 +1511,4 @@ interface EditorRef { */ getPreviewInstance: () => HTMLElement | null; } -``` +``` \ No newline at end of file -- Gitee From c557d2439b51de06a353d9cfd7f69b2f1f86833c Mon Sep 17 00:00:00 2001 From: tabzzz Date: Thu, 27 Feb 2025 01:12:29 +0800 Subject: [PATCH 09/11] =?UTF-8?q?chore(mini-markdown-editor):=20=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E5=B7=A5=E5=85=B7=E7=B1=BB=E6=96=B9=E6=B3=95=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mini-markdown-editor/src/config/toolbar/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/mini-markdown-editor/src/config/toolbar/index.ts b/packages/mini-markdown-editor/src/config/toolbar/index.ts index 676b3ed..9fe0d8a 100644 --- a/packages/mini-markdown-editor/src/config/toolbar/index.ts +++ b/packages/mini-markdown-editor/src/config/toolbar/index.ts @@ -117,7 +117,7 @@ class ToolbarConfig extends BaseClass { } // 设置单个工具项的顺序 - public setToolbarOrder(type: ToolbarType, newOrder: number): void { + public setToolbarItemOrder(type: ToolbarType, newOrder: number): void { try { this.checkDestroyed(); @@ -146,7 +146,7 @@ class ToolbarConfig extends BaseClass { } // 批量设置工具项顺序 - public setToolbarsOrder(orders: Record): void { + public setToolbarOrder(orders: Record): void { try { this.checkDestroyed(); -- Gitee From a6b252b66a239e4cead8d5dc24e19cbc30e47274 Mon Sep 17 00:00:00 2001 From: tabzzz Date: Thu, 27 Feb 2025 02:37:02 +0800 Subject: [PATCH 10/11] =?UTF-8?q?chore(mini-markdown-editor):=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=BC=96=E8=BE=91=E5=99=A8=E7=9A=84=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E8=81=9A=E7=84=A6=E7=8A=B6=E6=80=81=E4=B8=BAfalse?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/mini-markdown-editor/src/App.tsx | 1 + .../mini-markdown-editor/src/components/Editor/index.tsx | 1 - packages/mini-markdown-editor/src/types/global-config.ts | 6 ++++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/mini-markdown-editor/src/App.tsx b/packages/mini-markdown-editor/src/App.tsx index cb660e0..117dc66 100644 --- a/packages/mini-markdown-editor/src/App.tsx +++ b/packages/mini-markdown-editor/src/App.tsx @@ -228,6 +228,7 @@ const App: FC = () => { orderTools: [{ type: "123", order: 0 }], }} value="## Hello World." + autoFocus={true} /> ); diff --git a/packages/mini-markdown-editor/src/components/Editor/index.tsx b/packages/mini-markdown-editor/src/components/Editor/index.tsx index 1a3bf73..5e1fc4d 100644 --- a/packages/mini-markdown-editor/src/components/Editor/index.tsx +++ b/packages/mini-markdown-editor/src/components/Editor/index.tsx @@ -166,7 +166,6 @@ const Editor: FC = (props) => { autocompletion: false, defaultKeymap: true, }} - autoFocus={true} style={{ height: "100%", ...(style || {}) }} onChange={handleChange} onMouseEnter={handleMouseEnter} diff --git a/packages/mini-markdown-editor/src/types/global-config.ts b/packages/mini-markdown-editor/src/types/global-config.ts index 1967eb2..1612547 100644 --- a/packages/mini-markdown-editor/src/types/global-config.ts +++ b/packages/mini-markdown-editor/src/types/global-config.ts @@ -22,6 +22,11 @@ export interface GlobalConfig extends ReactCodeMirrorProps { * @type {boolean} */ status?: boolean; + /** + * 是否自动获取焦点,默认不获取 + * @type {boolean} + */ + autoFocus?: boolean; /** * 编辑器主题 * @type {"light" | "dark"} @@ -84,6 +89,7 @@ export type GlobalContextConfig = Pick< | "toolbars" | "status" | "local" + | "autoFocus" | "lineNumbers" | "enableShortcuts" | "onUpload" -- Gitee From 7d1eb82dff2d6ce64f274880901cecf9a01f6f7a Mon Sep 17 00:00:00 2001 From: tabzzz Date: Thu, 27 Feb 2025 02:38:24 +0800 Subject: [PATCH 11/11] =?UTF-8?q?docs(mini-markdown-docs):=20=E6=9B=B4?= =?UTF-8?q?=E6=96=B0api=E7=9A=84autoFocus=E9=83=A8=E5=88=86=E6=96=87?= =?UTF-8?q?=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/docs/guide/api.mdx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/docs/guide/api.mdx b/docs/docs/guide/api.mdx index f3d27e8..6f501c0 100644 --- a/docs/docs/guide/api.mdx +++ b/docs/docs/guide/api.mdx @@ -283,6 +283,17 @@ const App = () => { ``` +### autoFocus + +- 类型:`boolean` +- 默认值:`false` + +是否自动聚焦,默认不聚焦。 + +```tsx + +``` + ### lineNumbers - 类型:`boolean` -- Gitee