Appearance
cherry-markdown
cherry-markdown 是腾讯推出的在线 markdown 编辑器
安装
pnpm add cherry-markdown
文档
示例
- full model
- basic
- mobile
- multiple instances
- editor without toolbar
- pure preview
- XSS(Not allowed by default)
- img wysiwyg
- table wysiwyg
- headers with auto num
- 流式输入模式(AI chart场景)
- VIM 编辑模式
react 组件
下面定义 react 组件,方便于在项目中复用
定义类型
因为 cherry 包没有 ts类型,所以我们要先定义模块的 typescript,修改 vite-env.d.ts 文件添加如下内容。如果你使用其他方法,请自行修改该文件。
declare module 'cherry-markdown' {
export default class Cherry {
constructor(options: any)
destroy: () => void
setMarkdown: (markdown: string) => void
getMarkdown: () => string
}
}
定义组件
下面定义组件 components/Markdown.tsx
props 说明
prop | 说明 |
---|---|
height | 编辑器调试 |
value | 初始内容 |
onChange | 内容更改时的回调函数 |
onBlur | 失去焦点时的触发函数 |
下面 react 组件的代码
import { useUplaodImageMutation } from '@/services/image'
import Cherry from 'cherry-markdown'
import 'cherry-markdown/dist/cherry-markdown.css'
import React, { memo, useEffect, useImperativeHandle, useRef } from 'react'
interface Props {
height: string
value: string
onChange: (value: string) => void
onBlur?: ({ e, cherry }: { e: MouseEvent; cherry: Cherry }) => void
}
export interface MarkdownRef {
editor: React.MutableRefObject<Cherry | null>
}
export const MarkdownEditor = memo(
React.forwardRef<MarkdownRef, Props>((props, ref) => {
const uploadImageMutation = useUplaodImageMutation()
const markdownEditorRef = useRef<Cherry | null>(null)
useImperativeHandle(ref, () => {
return {
editor: markdownEditorRef,
}
})
useEffect(() => {
markdownEditorRef.current = new Cherry({
id: 'markdown-container',
value: props.value,
fileUpload: (file: File, callback: (url: string) => void) => {
uploadImageMutation.mutate(file, {
onSuccess: (data: { url: string }) => {
callback(data.url)
},
})
},
editor: {
//编辑器样式
// edit&preview: 双栏编辑预览模式
// editOnly: 纯编辑模式(没有预览,可通过toolbar切换成双栏或预览模式)
// previewOnly: 预览模式(没有编辑框,toolbar只显示“返回编辑”按钮,可通过toolbar切换成编辑模式)
defaultModel: 'edit&preview',
codemirror: {
// 是否自动focus 默认为true
autofocus: false,
},
},
event: {
//内容更改后的事件
afterChange: (text: string) => props.onChange(text),
//失焦时的事件
blur: ({ e, cherry }: { e: MouseEvent; cherry: Cherry }) => {
props.onBlur?.({ e, cherry })
},
},
toolbars: {
// 定义顶部工具栏
toolbar: [
'bold',
'italic',
'strikethrough',
'|',
'color',
'header',
'ruby',
'|',
'list',
'panel',
'settings',
'|',
'code',
'image',
],
// 定义侧边栏,默认为空
// sidebar: ['mobilePreview', 'copy', 'theme'],
// sidebar: false,
// 定义顶部右侧工具栏,默认为空
toolbarRight: ['fullScreen'],
//编辑时,右侧显示目录
toc: false,
// 定义选中文字时弹出的“悬浮工具栏”,默认为 ['bold', 'italic', 'underline', 'strikethrough', 'sub', 'sup', 'quote', '|', 'size', 'color']
bubble: false,
// 定义光标出现在行首位置时出现的“提示工具栏”,默认为 ['h1', 'h2', 'h3', '|', 'checklist', 'quote', 'table', 'code']
float: false,
},
previewer: {
// 自定义markdown预览区域class
// className: 'markdown'
floatWhenClosePreviewer: false,
enablePreviewerBubble: false,
},
themeSettings: {
// 主题列表,用于切换主题
themeList: [
{ className: 'default', label: '默认' },
{ className: 'dark', label: '黑' },
{ className: 'light', label: '白' },
{ className: 'green', label: '绿' },
{ className: 'red', label: '粉' },
{ className: 'violet', label: '紫' },
{ className: 'blue', label: '蓝' },
],
// 目前应用的主题
mainTheme: 'light',
// 目前应用的代码块主题
codeBlockTheme: 'one dark',
},
})
//组件卸载时删除编辑器
return () => markdownEditorRef.current?.destroy()
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return <div id='markdown-container' style={{ height: props.height, width: '100%' }}></div>
}),
)
使用组件
现在可以使用编辑器组件了
<MarkdownEditor
ref={markdownRef}
{...props}
height='300px'
value={field.state.value}
onChange={() => removeError(name)}
onBlur={({ cherry }) => field.handleChange(cherry.getMarkdown())}
/>
markdownRef 可以获取编辑器对象
markdownRef.current?.editor.current?.setMarkdown('')