UNPKG

md-editor-v3

Version:

Markdown editor for vue3, developed in jsx and typescript, dark theme、beautify content by prettier、render articles directly、paste or clip the picture and upload it...

535 lines (534 loc) 14.2 kB
import { Compartment, Extension } from '@codemirror/state'; import { KeyBinding, EditorView } from '@codemirror/view'; import { LooseRequired } from '@vue/shared'; import markdownit, { Token } from 'markdown-it'; import { Component, SetupContext, ExtractPropTypes, VNode } from 'vue'; import { IconName } from './components/Icon/Icon'; import { editorProps, mdPreviewProps } from './props'; import { ToolDirective } from './utils/content-help'; declare global { interface Window { hljs: any; prettier: any; prettierPlugins: any; Cropper: any; screenfull: any; mermaid: any; katex: any; echarts: any; } } export interface ToolbarTips { bold?: string; underline?: string; italic?: string; strikeThrough?: string; title?: string; sub?: string; sup?: string; quote?: string; unorderedList?: string; orderedList?: string; task?: string; codeRow?: string; code?: string; link?: string; image?: string; table?: string; mermaid?: string; katex?: string; revoke?: string; next?: string; save?: string; prettier?: string; pageFullscreen?: string; fullscreen?: string; preview?: string; previewOnly?: string; htmlPreview?: string; catalog?: string; github?: string; '-'?: string; '='?: string; } export interface StaticTextDefaultValue { toolbarTips?: ToolbarTips; titleItem?: { h1?: string; h2?: string; h3?: string; h4?: string; h5?: string; h6?: string; }; imgTitleItem?: { link: string; upload: string; clip2upload: string; }; linkModalTips?: { linkTitle?: string; imageTitle?: string; descLabel?: string; descLabelPlaceHolder?: string; urlLabel?: string; urlLabelPlaceHolder?: string; buttonOK?: string; }; clipModalTips?: { title?: string; buttonUpload?: string; }; copyCode?: { text?: string; successTips?: string; failTips?: string; }; mermaid?: { flow?: string; sequence?: string; gantt?: string; class?: string; state?: string; pie?: string; relationship?: string; journey?: string; }; katex?: { inline: string; block: string; }; footer?: { markdownTotal: string; scrollAuto: string; }; } export interface StaticTextDefault { 'zh-CN': StaticTextDefaultValue; 'en-US': StaticTextDefaultValue; } export type StaticTextDefaultKey = keyof StaticTextDefault; export type ToolbarNames = keyof ToolbarTips | number; export type Footers = '=' | 'markdownTotal' | 'scrollSwitch' | number; export interface SettingType { pageFullscreen: boolean; fullscreen: boolean; preview: boolean; htmlPreview: boolean; previewOnly: boolean; } export type Themes = 'light' | 'dark'; /** * 预览主题 * * @list ['default', 'github', 'vuepress', 'mk-cute', 'smart-blue', 'cyanosis'] */ export type PreviewThemes = string; export interface HeadList { text: string; level: 1 | 2 | 3 | 4 | 5 | 6; active?: boolean; line: number; currentToken?: Token; nextToken?: Token; } export type MdHeadingId = (options: { text: string; level: number; index: number; currentToken?: Token; nextToken?: Token; }) => string; export interface MermaidTemplate { /** * 流程图 */ flow?: string; /** * 时序图 */ sequence?: string; /** * 甘特图 */ gantt?: string; /** * 类图 */ class?: string; /** * 状态图 */ state?: string; /** * 饼图 */ pie?: string; /** * 关系图 */ relationship?: string; /** * 旅程图 */ journey?: string; } export interface MarkdownItConfigPlugin { type: string; plugin: markdownit.PluginWithParams; options: any; } /** * CodeMirror扩展类型 * * ^6.0.0 */ export interface CodeMirrorExtension { /** * 仅用来提供开发者分别不同扩展的依据 */ type: string; /** * CodeMirror的扩展 */ extension: Extension | ((options: any) => Extension); /** * 包裹扩展的Compartment,只有部分扩展有,提供扩展更新的能力 */ compartment?: Compartment; options?: any; } export interface GlobalConfig { /** * 编辑器内部依赖库 */ editorExtensions: { highlight?: { instance?: any; js?: string; css?: CodeCss; }; prettier?: { prettierInstance?: any; parserMarkdownInstance?: any; standaloneJs?: string; parserMarkdownJs?: string; }; cropper?: { instance?: any; js?: string; css?: string; }; screenfull?: { instance?: any; js?: string; }; mermaid?: { instance?: any; js?: string; /** * 是否启用缩放功能 * * @default true */ enableZoom?: boolean; }; katex?: { instance?: any; js?: string; css?: string; }; echarts?: { instance?: any; js?: string; }; }; /** * 对应editorExtensions中的cdn链接标签属性 * * 不要尝试在editorExtensionsAttrs定义script的src\onload\id,link的rel\href\id * 它们会被默认值覆盖 */ editorExtensionsAttrs: { highlight?: { js?: Partial<HTMLElementTagNameMap['script']>; css?: CodeCssAttrs; }; prettier?: { standaloneJs?: Partial<HTMLElementTagNameMap['script']>; parserMarkdownJs?: Partial<HTMLElementTagNameMap['script']>; }; cropper?: { js?: Partial<HTMLElementTagNameMap['script']>; css?: Partial<HTMLElementTagNameMap['link']>; }; screenfull?: { js?: Partial<HTMLElementTagNameMap['script']>; }; mermaid?: { js?: Partial<HTMLElementTagNameMap['script']>; }; katex?: { js?: Partial<HTMLElementTagNameMap['script']>; css?: Partial<HTMLElementTagNameMap['link']>; }; echarts?: { js?: Partial<HTMLElementTagNameMap['script']>; }; }; editorConfig: { /** * 自定义提示语言 */ languageUserDefined?: { [key: string]: StaticTextDefaultValue; }; /** * 自定义内部mermaid模块 */ mermaidTemplate?: MermaidTemplate; /** * 输入渲染延迟(ms) */ renderDelay?: number; /** * 内部的弹窗、下拉框等内联zIndex * @default 20000 */ zIndex?: number; }; /** * 根据主题和内部默认的codeMirror扩展自定义新的扩展 * * @params theme 当前主题 * @params innerExtensions 当前主题下的扩展列表 * [keymap, minimalSetup, markdown, EditorView.lineWrapping, EditorView.updateListener, EditorView.domEventHandlers, oneDark??oneLight] * [快捷键, 最低配置, markdown识别, 横向自动换行, 更新事件, dom监听事件, oneDark主题(暗夜模式下), oneLight(默认模式下)] * * @params keyBindings md-editor-v3内置的快捷键 */ codeMirrorExtensions: (extensions: Array<CodeMirrorExtension>, options: { editorId: string; theme: Themes; keyBindings: Array<KeyBinding>; }) => Array<CodeMirrorExtension>; /** * 自定义markdown-it核心库扩展、属性等 */ markdownItConfig: (md: markdownit, options: { editorId: string; }) => void; /** * 挑选编辑器已预设的markdownIt的扩展 * * @param plugins markdownIt的扩展,带编辑器已设定的属性 * @returns plugins */ markdownItPlugins: (plugins: Array<MarkdownItConfigPlugin>, options: { editorId: string; }) => Array<MarkdownItConfigPlugin>; /** * mermaid配置项 * * @param base * @returns */ mermaidConfig: (base: any) => any; /** * katex配置 * * @param baseConfig * @returns */ katexConfig: (baseConfig: any) => any; /** * echarts配置 * * @returns */ echartsConfig: (base: any) => any; } /** * 扩展编辑器内部功能,包括marked和一些内部依赖实例,如highlight、cropper等 */ export type Config = (options: Partial<GlobalConfig>) => void; /** * 编辑器操作潜在的错误 */ export interface InnerError { name: 'Cropper' | 'fullscreen' | 'prettier' | 'overlength' | 'mermaid'; message: string; data?: any; error?: Error; } export interface CodeCss { [key: string]: { light: string; dark: string; }; } export interface CodeCssAttrs { [key: string]: { light: Partial<HTMLElementTagNameMap['link']>; dark: Partial<HTMLElementTagNameMap['link']>; }; } export type UpdateSetting = (k: keyof SettingType, v?: boolean) => void; export type ChangeEvent = (v: string) => void; export type SaveEvent = (v: string, h: Promise<string>) => void; export type UploadImgCallBackParam = string[] | Array<{ url: string; alt: string; title: string; }>; export type UploadImgCallBack = (urls: UploadImgCallBackParam) => void; export type UploadImgEvent = (files: Array<File>, callBack: UploadImgCallBack) => void; export type HtmlChangedEvent = (h: string) => void; export type GetCatalogEvent = (list: HeadList[]) => void; export type ErrorEvent = (err: InnerError) => void; export interface ExposeEvent { pageFullscreen(status: boolean): void; fullscreen(status: boolean): void; preview(status: boolean): void; previewOnly(status: boolean): void; htmlPreview(status: boolean): void; catalog(status: boolean): void; } export type DOMEventHandlers = { [e in keyof HTMLElementEventMap]?: (event: HTMLElementEventMap[e], view: EditorView) => boolean | void; }; export interface InsertParam { targetValue: string; select?: boolean; deviationStart?: number; deviationEnd?: number; } /** * 插入的内容的构造函数 */ export type InsertContentGenerator = (selectedText: string) => InsertParam; /** * 插入内容的通用函数类型 */ export type Insert = (generate: InsertContentGenerator) => void; export type FocusOption = 'start' | 'end' | { rangeAnchor?: number; rangeHead?: number; cursorPos: number; }; export interface ExposeParam { /** * 添加事件监听 * * @param eventName 事件名称 * @param callBack 事件回调函数 */ on<E extends keyof ExposeEvent, C extends ExposeEvent[E]>(eventName: E, callBack: C): void; /** * 切换页面内全屏 * * @param status 是否页面全屏 */ togglePageFullscreen(status?: boolean): void; /** * 切换屏幕全屏 * * @param status 是否屏幕全屏 */ toggleFullscreen(status?: boolean): void; /** * 切换是否显示预览 * * @param status 是否显示预览 */ togglePreview(status?: boolean): void; togglePreviewOnly(status?: boolean): void; /** * 切换是否显示html预览 * * @param status html预览状态 */ toggleHtmlPreview(status?: boolean): void; /** * 切换是否显示目录 * * @param status 是否显示目录,不设置默认相反 */ toggleCatalog(status?: boolean): void; /** * 触发保存 */ triggerSave(): void; /** * 手动向文本框插入内容 * * @param {Function} generate 构造插入内容方法 * 构造方法提供「当前选中」的内容为入参 * 返回「待插入内容」和插入的属性 * 入参 selectedText 当前选中的内容 * * targetValue 待插入内容 * select 插入后是否自动选中内容 * deviationStart 插入后选中位置的开始偏移量 * deviationEnd 插入后选中位置的结束偏移量 * */ insert: Insert; /** * 手动聚焦 * * @param options 聚焦时光标的位置,不提供默认上次失焦时的位置 */ focus(options?: FocusOption): void; /** * 手动重新渲染 */ rerender(): void; /** * 获取当前选中的文本 */ getSelectedText(): string | undefined; /** * 重置已经存在的历史记录 */ resetHistory(): void; /** * codemirror事件 * * @param handlers */ domEventHandlers(handlers: DOMEventHandlers): void; /** * 执行内部插入命令 * * @param direct */ execCommand(direct: ToolDirective): void; /** * 获取编辑器实例 */ getEditorView(): EditorView | undefined; } export type ExposePreviewParam = Pick<ExposeParam, 'rerender'>; export type EditorProps = Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof editorProps>>>>; export type MdPreviewProps = Readonly<LooseRequired<Readonly<ExtractPropTypes<typeof mdPreviewProps>>>>; export type EditorEmits = Array<'onChange' | 'onSave' | 'onUploadImg' | 'onHtmlChanged' | 'onGetCatalog' | 'onError' | 'update:modelValue' | 'onBlur' | 'onFocus' | 'onInput' | 'onDrop' | 'oninputBoxWidthChange' | 'onRemount'>; export type EditorContext = SetupContext<EditorEmits>; export type CustomStrIcon = { copy?: string; 'collapse-tips'?: string; pin?: string; 'pin-off'?: string; check?: string; }; /** * 自定义图标的数据类型 */ export type CustomIcon = { [key in IconName]?: { component: Component | VNode | string; props?: { [key: string | number | symbol]: any; }; }; } & CustomStrIcon;