Coder Social home page Coder Social logo

imzbf / md-editor-v3 Goto Github PK

View Code? Open in Web Editor NEW
1.2K 10.0 107.0 19.91 MB

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...

Home Page: https://imzbf.github.io/md-editor-v3

License: MIT License

HTML 0.28% TypeScript 91.90% Less 6.87% Vue 0.56% JavaScript 0.38%
vue vue3 markdown-editor jsx tsx theme editor markdown

md-editor-v3's Introduction

🎄 md-editor-v3

English | 中文

Markdown editor for vue3, developed in jsx and typescript.

  • Documentation and example: Go

  • Use it online: Go

  • The same series editor for react: md-editor-rt

⭐️ Features

  • Toolbar, screenfull or screenfull in web pages and so on.
  • Themes, Built-in default and dark themes.
  • Shortcut key for editor.
  • Beautify your content by prettier(only for markdown content, not the code and other text).
  • Multi-language, build-in Chinese and English(default: Chinese).
  • Upload picture, paste or clip the picture and upload it.
  • Render article directly(no editor, no event listener, only preview of content).
  • Theme of preview, default, vuepress, github, cyanosis, mk-cute, smart-blue styles(not identical). It can be customized also(Refer to example page).
  • mermaid(>=1.8.0), katex mathematical formula(>=1.9.0).
  • Customize the toolbar as you like.
  • On-demand Import(>=4.0.0).

📦 Install

yarn add md-editor-v3

Use existing extension of language and theme, such as Japanese

yarn add @vavt/cm-extension

Use existing components of toolbar, such as exporting content as PDF

yarn add @vavt/v3-extension

For more ways to use or contribute, please refer to: md-editor-extension

💡 Usage

✍🏻 Display Editor

<template>
  <MdEditor v-model="text" />
</template>

<script setup>
import { ref } from 'vue';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';

const text = ref('# Hello Editor');
</script>

^4.0.0, internal components can be imported on-demand.

If there are multiple editors on the page, please set different editorId for each editor!

📖 Preview Only

<template>
  <MdPreview :editorId="id" :modelValue="text" />
  <MdCatalog :editorId="id" :scrollElement="scrollElement" />
</template>

<script setup>
import { ref } from 'vue';
import { MdPreview, MdCatalog } from 'md-editor-v3';
import 'md-editor-v3/lib/preview.css';

const id = 'preview-only';
const text = ref('# Hello Editor');
const scrollElement = document.documentElement;
</script>

🗺 Preview

Default theme Dark theme Preview only

Inputing prompt and mark, emoji extensions

🎁 Apis

🔖 MdPreivew Props

name type default description
modelValue string '' Markdown content, use v-model in vue template
theme 'light' | 'dark' 'light' Editor theme
class string ''
language string 'zh-CN' Build-in language('zh-CN','en-US')
editorId string 'md-editor-v3' Editor's id, it is used when there are more than two editors in the same page
showCodeRowNumber boolean false Show row number for code block or not
previewTheme 'default' | 'github' | 'vuepress' | 'mk-cute' | 'smart-blue' | 'cyanosis' 'default' Theme of preview, can be customized
style string | CSSProperties {} Inline style
noMermaid boolean false Use mermaid or not
noKatex boolean false Use katex or not
codeTheme 'atom' | 'a11y' | 'github' | 'gradient' | 'kimbie' | 'paraiso' | 'qtcreator' | 'stackoverflow' 'atom' Highlight code style, can be customized also
mdHeadingId (text: string, level: number, index: number) => string (text) => text H1-H6 ID generator
sanitize (html: string) => string (html) => html Sanitize the html, prevent XSS. After 3.x, dangerous code has been processed by default. Please do not use this attribute unless there are special requirements
noIconfont boolean false Not append iconfont script, download different versions SVGFont Class and import it by yourself
formatCopiedText (text: string) => string (text: string) => text Format copied code
codeStyleReverse boolean true Code style will be reversed to dark while code block of the theme has a dark background
codeStyleReverseList Array<string> ['default', 'mk-cute'] Themes to be reversed
noHighlight boolean false Highlight code or not
noImgZoomIn boolean false Enable the function of enlarging images
customIcon CustomIcon {} Customized icons
sanitizeMermaid (h: string) => Promise<string> (h: string) => Promise.resolve(h) Convert the generated mermaid code

🔩 MdEditor Props

Except for the same as MdPreview:

name type default description
pageFullscreen boolean false Screenfull in web page
preview boolean true Preview content in editor
htmlPreview boolean false Preview html in editor(If true, preview must be false)
toolbars Array<ToolbarNames | number> [toolbars] Show contents of toolbar, all keyssee toolbars below
toolbarsExclude Array<ToolbarNames | number> [] Don't show contents of toolbar, all keystoolbars
noPrettier boolean false Use prettier to beautify content or not
tabWidth number 2 One tab eq some spaces
tableShape [number, number] [6, 4] Preset the size of the table, [columns, rows]
placeholder string ''
footers Array<'markdownTotal' | '=' | 'scrollSwitch' | number> ['markdownTotal', '=', 'scrollSwitch'] Show contents of footer, they are divided by '='. Set it to [] to hidden footer
scrollAuto boolean true Scroll default setting
noUploadImg boolean false Not show the entrance to upload pictures
autoFocus boolean false same as autofocus in native textarea
disabled boolean false same as disabled in native textarea
readOnly boolean false same as readonly in native textarea
maxLength number same as maxlength in native textarea
autoDetectCode boolean false auto detect the type of pasted code, only support that copied from vscode
completions Array<CompletionSource> [] @codemirror/autocomplete List of function to match keywords
showToolbarName boolean false Show toolbar name or not
inputBoxWitdh string '50%' Default width of input box
transformImgUrl (imgUrl: string) => string | Promise<string> t => t Transform image links
『toolbars』
[
  'bold',
  'underline',
  'italic',
  '-',
  'strikeThrough',
  'title',
  'sub',
  'sup',
  'quote',
  'unorderedList',
  'orderedList',
  'task', // ^2.4.0
  '-',
  'codeRow',
  'code',
  'link',
  'image',
  'table',
  'mermaid',
  'katex',
  '-',
  'revoke',
  'next',
  'save',
  '=',
  'pageFullscreen',
  'fullscreen',
  'preview',
  'previewOnly',
  'htmlPreview',
  'catalog',
  'github'
];

You can sort the toolbar as you like, split tools by '-', the left and right toolbars are divided by '='

You can customize the toolbar. To display them, put index of defToolbars into toolbars(this is not standard), for more usage, please refer to docs.

『StaticTextDefaultValue』

Expand language, you need to replace all the content here:

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; // ^2.4.0
  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;
  catalog?: string;
  preview?: string;
  previewOnly?: string;
  htmlPreview?: string;
  github?: string;
  '-'?: string;
  '='?: string;
}

export interface StaticTextDefaultValue {
  // Toolbar hover tips(html title)
  toolbarTips?: ToolbarTips;
  // H1-H6 dropdown menu item
  titleItem?: {
    h1?: string;
    h2?: string;
    h3?: string;
    h4?: string;
    h5?: string;
    h6?: string;
  };
  imgTitleItem?: {
    link: string;
    upload: string;
    clip2upload: string;
  };
  // The modal tips of add link or upload picture
  linkModalTips?: {
    linkTitle?: string;
    imageTitle?: string;
    descLabel?: string;
    descLabelPlaceHolder?: string;
    urlLabel?: string;
    urlLabelPlaceHolder?: string;
    buttonOK?: string;
  };
  // The modal tips of clip the picture, v1.2.0
  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?: {
    // formula inline
    inline: string;
    // formula block
    block: string;
  };
  footer?: {
    markdownTotal: string;
    scrollAuto: string;
  };
}

🧵 MdPreview Events

name type description
onHtmlChanged html: string Compile markdown successful event, you can use it to get the html code
onGetCatalog list: Array<HeadList> Get catalog of article

🪢 MdEditor Events

Except for the same as MdPreview:

name type description
onChange value: string Content changed(bind to oninput of textarea)
onSave value: string, html: Promise<string> Saving content, ctrl+s and clicking button will trigger it
onUploadImg files: Array<File>, callback: (urls: string[] | { url: string; alt: string; title: string }[]) => void Uploading picture, when picture is uploading the modal will not close, please provide right urls to the callback function
onError err: { name: 'Cropper' | 'fullscreen' | 'prettier' | 'overlength'; message: string } Catch run-time error, Cropper, fullscreen and prettier are used when they are not loaded. And content exceeds the length limit error
onBlur event: FocusEvent Textarea has lost focus
onFocus event: FocusEvent Textarea has received focus
onInput event: Event Element gets input
onDrop event: DragEvent Selection is being dragged
onInputBoxWitdhChange (width: string) => void Width of input box has been changed

🎍 Slots

name type default description
defToolbars Array<DropdownToolbar | NormalToolbar | ModalToolbar> null Custom toolbar in DropdownToolbar, NormalToolbar or ModalToolbar
defFooters Array<string | VNode | JSX.Element> null Custom footer

NormalToolbar example:

<template>
  <MdEditor>
    <template #defToolbars>
      <NormalToolbar title="mark" @onClick="handler">
        <template #trigger>
          <svg class="md-editor-icon" aria-hidden="true">
            <use xlink:href="#md-editor-icon-mark"></use>
          </svg>
        </template>
      </NormalToolbar>
    </template>
  </MdEditor>
</template>

<script setup lang="ts">
import { MdEditor, NormalToolbar } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';

const handler = () => {
  console.log('NormalToolbar clicked!');
};
</script>

🤱🏼 Expose

After 2.5.0, Editor exposes several methods on the instance, used to get or change the internal status of the editor.

<template>
  <MdEditor ref="editorRef" />
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { MdEditor } from 'md-editor-v3';
import type { ExposeParam } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';

const editorRef = ref<ExposeParam>();

onMounted(() => {
  editorRef.value?.on('catalog', console.log);
  editorRef.value?.toggleCatalog(true);
});
</script>

Switched to the opposite status, if toggle without input parameter.

👂🏼 on

Get the internal state of the editor, including pageFullscreen, fullscreen, preview, htmlPreview, catalog, etc.

  • pageFullscreen

    editorRef.value?.on('pageFullscreen', (status) => console.log(status));
  • fullscreen

    editorRef.value?.on('fullscreen', (status) => console.log(status));
  • preview

    editorRef.value?.on('preview', (status) => console.log(status));
  • previewOnly

    editorRef.value?.on('previewOnly', (status) => console.log(status));
  • htmlPreview

    editorRef.value?.on('htmlPreview', (status) => console.log(status));
  • catalog

    editorRef.value?.on('catalog', (status) => console.log(status));

💻 togglePageFullscreen

Toggle status of fullscreen within the page.

editorRef.value?.togglePageFullscreen(true);

🖥 toggleFullscreen

Toggle status of fullscreen widthin browser.

editorRef.value?.toggleFullscreen(true);

📖 togglePreview

Toggle status of preview.

editorRef.value?.togglePreview(true);

📖 togglePreviewOnly

Toggle into Preview Only Mode.

editorRef.value?.togglePreviewOnly(true);

📼 toggleHtmlPreview

Toggle status of htmlPreview.

editorRef.value?.toggleHtmlPreview(true);

🧬 toggleCatalog

Toggle status of catalog.

editorRef.value?.toggleCatalog(true);

💾 triggerSave

editorRef.value?.triggerSave();

💉 insert

Manually insert content into textarea.

/**
 * @params selectedText
 */
editorRef.value?.insert((selectedText) => {
  /**
   * @return targetValue    Content to be inserted
   * @return select         Automatically select content, default: true
   * @return deviationStart Start position of the selected content, default: 0
   * @return deviationEnd   End position of the selected content, default: 0
   */
  return {
    targetValue: `${selectedText}`,
    select: true,
    deviationStart: 0,
    deviationEnd: 0
  };
});

For more examples, refer to source code of extension component

🎯 focus

Focus on textarea.

import type { FocusOption } from 'md-editor-v3';

const option: FocusOption | undefined = 'start';

// Cursor position when focusing on textarea, default: position when it last lost focus
editorRef.value?.focus(option);

✒️ rerender

Re render the content.

editorRef.value?.rerender();

🔍 getSelectedText

Get the currently selected text.

console.log(editorRef.value?.getSelectedText());

🗑 resetHistory

Clear current history.

🎛 domEventHandlers

Supports listening to all DOM events.

editorRef.value?.domEventHandlers({
  compositionstart: () => {
    console.log('compositionstart');
  }
});

🎛 execCommand

Insert content into the editor via trigger.

editorRef.value?.execCommand('bold');

💴 Config Editor

Use config(option: ConfigOption) to reconfigure markdown-it and so on.

codeMirrorExtensions

Customize new extensions based on theme and default extensions f codeMirror.

Example: Editor does not render the line number of textarea by default, this extension needs to be manually added

import { config } from 'md-editor-v3';
import { lineNumbers } from '@codemirror/view';

config({
  codeMirrorExtensions(_theme, extensions) {
    return [...extensions, lineNumbers()];
  }
});

markdownItConfig

Customize extensions, attributes of markdown-it, etc.

type MarkdownItConfig = (
  md: markdownit,
  options: {
    editorId: string;
  }
) => void;

Example: Use markdown-it-anchor to render a hyperlink symbol to the right of the title

import { config } from 'md-editor-v3';
import ancher from 'markdown-it-anchor';

config({
  markdownItConfig(mdit) {
    mdit.use(ancher, {
      permalink: true
    });
  }
});

markdownItPlugins

Select and add built-in plugins to markdown-it.

type MarkdownItPlugins = (
  plugins: Array<MarkdownItConfigPlugin>,
  options: {
    editorId: string;
  }
) => Array<MarkdownItConfigPlugin>;

Example: Modify the class name of the image.

import { config } from 'md-editor-v3';

config({
  markdownItPlugins(plugins) {
    return plugins.map((p) => {
      if (p.type === 'image') {
        return {
          ...p,
          options: {
            ...p.options,
            classes: 'my-class'
          }
        };
      }

      return p;
    });
  }
});

editorConfig

Add more languages, reset mermaid template or delay rendering time:

import { config } from 'md-editor-v3';

config({
  editorConfig: {
    languageUserDefined: { lang: StaticTextDefaultValue },
    mermaidTemplate: {
      flow: `flow tempalte`,
      ...more
    },
    // Default 500ms. It is set to 0ms when preview only and not set.
    renderDelay: 500
  }
});

editorExtensions

Config some dependency libraries, like highlight..

import { config } from 'md-editor-v3';

config({
  editorExtensions: { iconfont: 'https://xxx.cc' }
});
『EditorExtensions』
export interface EditorExtensions {
  highlight?: {
    instance?: any;
    js?: string;
    css?: {
      [key: string]: {
        light: string;
        dark: string;
      };
    };
  };
  prettier?: {
    standaloneJs?: string;
    parserMarkdownJs?: string;
  };
  cropper?: {
    instance?: any;
    js?: string;
    css?: string;
  };
  iconfont?: string;
  screenfull?: {
    instance?: any;
    js?: string;
  };
  mermaid?: {
    instance?: any;
    js?: string;
  };
  katex?: {
    instance?: any;
    js?: string;
    css?: string;
  };
}

🫨 iconfontType

Set the way to display icons:

  • svg: with symbol
  • class: with font-class

If the icon is customized through the attribute customIcon, the customized icon will be used first.

🎨 mermaidConfig

Configure mermaid, Details

import { config } from 'md-editor-v3';

config({
  mermaidConfig(base: any) {
    return {
      ...base,
      logLevel: 'error'
    };
  }
});

🪡 Shortcut Key

Pay attention: shortcut keys are only available when the textarea has received focus!

key function description
TAB insert space Insert space, the length eq tabWidth, default: 2, support multiline
SHIFT + TAB delete space, setting is the same as Tab
CTRL + C copy When selected, copy the selected content. When not selected, copy the content of the current line
CTRL + X shear When selected, cut the selected content. When not selected, cut the current line
CTRL + D delete When selected, delete the selected content. When not selected, delete the current line
CTRL + S save Trigger onSave event
CTRL + B bold text **bold**
CTRL + U underline <u>underline</u>
CTRL + I italic *italic*
CTRL + 1-6 h1-h6 # title
CTRL + ↑ superscript <sup>superscript</sup>
CTRL + ↓ subscript <sub>subscript</sub>
CTRL + O ordered list 1. ordered list
CTRL + L link [link](https://github.com/imzbf/md-editor-v3)
CTRL + Z withdraw Withdraw history in editor, not the function of system
CTRL + SHIFT + S line-through ~line-through~
CTRL + SHIFT + U unordered list - unordered list
CTRL + SHIFT + C code block
CTRL + SHIFT + I picture ![picture](https://imzbf.github.io/md-editor-v3/imgs/preview-light.png)
CTRL + SHIFT + Z forward Forward history in editor, not the function of system
CTRL + SHIFT + F Beautify
CTRL + ALT + C code row
CTRL + SHIFT + ALT + T table |table|

🪤 Internal Components

<script lang="ts" setup>
import { NormalToolbar } from 'md-editor-v3';
</script>

On-demand import. For more examples, refer to document.

🐣 NormalToolbar

NormalToolbar

  • props

    • title: string, not necessary, title of toolbar.
  • events

    • onClick: (e: MouseEvent) => void, necessary.
  • slots

    • trigger: VNode | JSX.Element, necessary, it is usually an icon, which is displayed on the toolbar.

🐼 DropdownToolbar

DropdownToolbar

  • props

    • title: string, not necessary, title of toolbar.
    • visible: boolean, necessary.
  • events

    • onChange: (visible: boolean) => void, necessary.
  • slots

    • trigger: VNode | JSX.Element, necessary, it is usually an icon, which is displayed on the toolbar.
    • overlay: VNode | JSX.Element, necessary, content of dropdown box.

🦉 ModalToolbar

ModalToolbar

  • props

    • title: string, not necessary, title of toolbar.
    • modalTitle: string, not necessary, title of the Modal.
    • visible: boolean, necessary, visibility of Modal.
    • width: string, not necessary, width of Modal, default auto.
    • height: string, same as width.
    • showAdjust: boolean, not necessary, visibility of fullscreen button.
    • isFullscreen: boolean, necessary when showAdjust = true, status of fullscreen.
  • events

    • onClick: () => void, necessary.
    • onClose: () => void, necessary, closed event.
    • onAdjust: (val: boolean) => void, fullscreen button was clicked.
  • slots

    • trigger: VNode | JSX.Element, necessary, it is usually an icon, which is displayed on the toolbar.
    • default: VNode | JSX.Element, necessary, content of Modal.

🐻 MdCatalog

MdCatalog

  • props

    • editorId: string, necessary, same as editor's editorId, used to register listening events.
    • class: string, not necessary.
    • mdHeadingId: MdHeadingId, not necessary, same as editor.
    • scrollElement: string | HTMLElement, not necessary, it is an element selector when its type is string. When previewOnly eq true, it is usually set to document.documentElement.
    • theme: 'light' | 'dark', not necessary, provide it when you want to change theme online, it is the same as Editor theme.
    • offsetTop: number, not necessary, highlight current item of catalogs when title is offsetTop pixels from the top, default 20.
    • scrollElementOffsetTop: number, not necessary, offsetTop of the scroll container,default 0.
  • events

    • onClick: (e: MouseEvent, t: TocItem) => void, not necessary, heading was clicked.
    • onActive: (heading: HeadList | undefined) => void, not necessary, heading was highlighted.

🛸 MdModal

MdModal

  • props

    • title: string, not necessary, title of Modal.
    • visible: boolean, necessary, visibility of Modal.
    • width: string, not necessary, width of Modal, default auto.
    • height: string, same as width.
    • showAdjust: boolean, not necessary, visibility of fullscreen button.
    • isFullscreen: boolean, necessary when showAdjust = true, status of fullscreen.
    • className: string, not necessary.
    • style: CSSProperties, not necessary.
  • events

    • onClose: () => void, necessary, closed event.
    • onAdjust: (val: boolean) => void, fullscreen button was clicked.
  • slots

    • default: VNode | JSX.Element, necessary, content of Modal.

🪤 Internal Configuration

import {
  iconfontClassUrl,
  iconfontSvgUrl,
  allToolbar,
  allFooter,
  zh_CN,
  en_US
} from 'md-editor-v3';

console.log(iconfontClassUrl, iconfontSvgUrl, allToolbar, allFooter, zh_CN, en_US);

🗂 Examples

🎸 Jsx Template

import { defineComponent, reactive } from 'vue';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';

export default defineComponent({
  setup() {
    const md = reactive({
      text: '# Hello Editor'
    });
    return () => (
      <MdEditor modelValue={md.text} onChange={(value) => (md.text = value)} />
    );
  }
});

🥹 Upload Picture

Tips: When you paste and upload GIF, it will upload a static picture. So you should upload it by file system!

<template>
  <MdEditor v-model="text" @onUploadImg="onUploadImg" />
</template>

<script setup>
import { ref } from 'vue';
import axios from 'axios';
import { MdEditor } from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';

const text = ref('# Hello Editor');

const onUploadImg = async (files, callback) => {
  const res = await Promise.all(
    files.map((file) => {
      return new Promise((rev, rej) => {
        const form = new FormData();
        form.append('file', file);

        axios
          .post('/api/img/upload', form, {
            headers: {
              'Content-Type': 'multipart/form-data'
            }
          })
          .then((res) => rev(res))
          .catch((error) => rej(error));
      });
    })
  );

  // Approach 1
  callback(res.map((item) => item.data.url));

  // Approach 2
  // callback(
  //   res.map((item: any) => ({
  //     url: item.data.url,
  //     alt: 'alt',
  //     title: 'title'
  //   }))
  // );
};
</script>

🧙‍♂️ Change Styles

.css-vars(@isDark) {
  --md-color: if(@isDark, #999, #222);
  --md-hover-color: if(@isDark, #bbb, #000);
  --md-bk-color: if(@isDark, #000, #fff);
  --md-bk-color-outstand: if(@isDark, #111, #f6f6f6);
  --md-bk-hover-color: if(@isDark, #1b1a1a, #f5f7fa);
  --md-border-color: if(@isDark, #2d2d2d, #e6e6e6);
  --md-border-hover-color: if(@isDark, #636262, #b9b9b9);
  --md-border-active-color: if(@isDark, #777, #999);
  --md-modal-mask: #00000073;
  --md-scrollbar-bg-color: if(@isDark, #0f0f0f, #e2e2e2);
  --md-scrollbar-thumb-color: if(@isDark, #2d2d2d, #0000004d);
  --md-scrollbar-thumb-hover-color: if(@isDark, #3a3a3a, #00000059);
  --md-scrollbar-thumb-active-color: if(@isDark, #3a3a3a, #00000061);
}

.md-editor {
  .css-vars(false);
}

.md-editor-dark {
  .css-vars(true);
}

Change background color in dark mode:

.md-editor-dark {
  --md-bk-color: #333 !important;
}

md-editor-v3's People

Contributors

bohecola avatar cyrusmcc avatar imzbf avatar labbomb avatar nomango avatar tofandel avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

md-editor-v3's Issues

怎么解决多个富文本编辑器在同个.vue中,上传图片重复问题

我在同一个页面的dialog里写了2个MdEditor,分别叫:MdEditor,MdEditor1,同时设置了editorId分别为taskProgress,task,上传图片的方法也修改成不同的名字uploadImg,taskProgressUploadImg
但是在上传图片的时候,我在当前编辑器上传,在另一个编辑器也会出现图片链接,而且还是两条
image

链接中的URL替换

你好 我这边想对Markdown中的链接进行全局替换,这个怎么替换
就是点击下方示例图片中的[目录结构]文字,能够跳转到自定义的url中

# Web文档

1.[目录结构](./目录结构.md)

1646117274(1)

TOC以及工具栏

请问是否可以添加 TOC 生成目录的功能?工具栏是否可以自定义图标与回调事件?

输入链接及描述,点击确定,直接刷新页面是什么鬼

我使用的是vue3.0+el UI+ts,
md-editor的版本是1.4.1

用文档默认导入方法导入的md-editor,出现两个问题

1.点击链接,输入描述及链接,点击确定时确实在输入框显示相应的描述信息,但是过多一会就自动刷新当前页面,model也没有关闭

2.点击上传文件的时候,在选择文件时原本的弹出层,自动关闭

建议增加一些emoji表情

建议增加一些emoji表情

这样在进行markdown写作的时候,有些emoji表情进行互动还是挺不错的

依赖包marked不存在

PS E:\work\web\pc\xxxxxxxx> pnpm build

> [email protected] build E:\work\web\pc\xxxxxxxx
> vue-tsc --noEmit && vite build

node_modules/.pnpm/[email protected]/node_modules/md-editor-v3/lib/MdEditor/Editor.d.ts:2:25 - error TS2307: Cannot find module 'marked' or its corresponding type declarations.

2 import { Slugger } from 'marked';
                          ~~~~~~~~


Found 1 error.

 ELIFECYCLE  Command failed with exit code 2.

报错在 md-editor-v3/lib/MdEditor/Editor.d.ts:2:25
TS2307: Cannot find module 'marked' or its corresponding type declarations.

// md-editor-v3/package.json
{
  "devDependencies": {
    "marked": "^2.1.3",
  }
}

在生产环境用到了marked开发环境依赖,打包时ts检查无法通过

自动保存

没有自动保存功能吗 或者能提供js触发保存的方法吗

md-editor-v3 有个严重的bug

bug描述: 当上传了图片, 且输入框出现滚动条, 且开启markdown预览功能时, 在输入框的底部输入文字, 滚动条会自动往上走, 使输入无法再进行.

bug版本: v1.7.3

重现条件:

  1. 上传图片. (必须, 否则不能重现)
  2. 输入一大段文字, 使输入框出现滚动条.
  3. 开启预览功能.
  4. 在输入框的最底部进行输入.

粘贴板上传图片问题

粘贴板上传图片是什么意思呢?复制图片粘贴?还是 复制一段富文本(含图片),粘贴过来时图片上传成功。
这两种方法,我均尝试了,好像都没有成功,并且没有触发 @uploadImg 事件

使用标题在仅预览模式下会跳转,希望不要跳转

使用标题在仅预览模式下会跳转,希望不要跳转
使用 @markedHeading 发现没有走这个函数
不清楚应该怎么处理

使用属性 previewOnly

<template>
  <Mgr>
    <md-editor class="body" v-model="mdText"
               @markedHeading="markedHeading"
               previewOnly/>
  </Mgr>
</template>

<script>
import Mgr from '@/components/mgr/Mgr'
import MdEditor from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
import {getDocContent} from "@/api/doc/doc";

export default {
  name: "WebDoc",
  components: {Mgr, MdEditor},
  data() {
    return {
      mdText: ''
    }
  },
  created() {
    getDocContent('/web/webDoc').then(res => {
      this.mdText = res
    })
  },
  methods:{
    markedHeading(text, level, raw){
      console.log('aaaaa')
      console.log(text)
      console.log(level)
      console.log(raw)
    }
  }
}
</script>
<style lang="less" scoped>
.body {
  margin-left: 50px;
  margin-right: 50px;
}
</style>

弹窗问题

点击添加链接后,按住鼠标拖动弹窗,鼠标松开后弹窗依然跟随鼠标移动

图片预览问题

在DOC和API里面找了找,能找到onUploadImage的代码,也跑通了,但是预览不出来。

在其他ISSUE看到相关问题,说是直接下载模式不行,那我应该调整成什么模式?

另外,下载需要携带 JWT, 要使用自己封装的axios,应该如何配置?

谢谢

我提几个建议

  1. 目前不支持复制粘贴上传图片
  2. 上传图片的按钮,能不能是一个下拉 里面包含上传图片和图片链接? 这样上传图片就少一步操作逻辑

这种tip功能样式我觉得可以考虑一下

就这种tip样式

还有请问,根据onGetCatalog事件,我成功做了一个目录,然后,element-plus没有类似iviewAnchor 锚点组件,该怎么做目录和文章的相互定位。

我现在做了一个文章目录组件,能够根据onGetCatalog事件的数据,做成锚点列表,定位到文章,但是不知道怎么在文章浏览滚动的时候,自动地使目录滚动到相应位置active,还有在刷新页面的时候,路由的锚点无法定位,滚动条一直在最上面

我太菜了0.0

MdEditorV3存在安全隐患

MdEditorV3安全隐患

当前的编辑器可能存在被注入JS代码攻击的安全隐患。

可以将本Markdown内容完整的复制到MdEditorV3编辑器中测试。

Markdown完整内容:链接

测试信息

  • 测试版本:v1.7.3
  • 测试时间:2021/12/05

注入示例

示例1

SCRIPT👉Hello👈SCRIPT

示例2

这个代码已经被注入,不过没能被直接调用(个人不是干这块的,不是很懂)。

SCRIPT👉<script type='text/javascript'>alert('你好');</script>👈SCRIPT

示例3

<iframe src="https://next.vuex.vuejs.org/" width="500" height="300"></iframe>

参考工具代码

不使用字符串表示字符串(用于支持复杂的字符串)

function StringToIntArray(str){
    let arr = [];
    for(let i=0;i<str.length;i++)
        arr.push(str.charAt(i));
    return str;
}
// IntArrayToString
var str = function (arr){
  var zstr = String();
  return arr.map(x=>String.fromCharCode(x)).join(zstr);
}

markedHeading 无效

在仅预览模式下 这个函数是无效的 markedHeading
仅预览模式属性 previewOnly

<template>
  <Mgr>
    <md-editor class="body" v-model="mdText"
               @markedHeading="markedHeading"
               previewOnly/>
  </Mgr>
</template>

<script>
import Mgr from '@/components/mgr/Mgr'
import MdEditor from 'md-editor-v3';
import 'md-editor-v3/lib/style.css';
// import {getDocContent} from "@/api/doc/doc";

export default {
  name: "WebDoc",
  components: {Mgr, MdEditor},
  data() {
    return {
      mdText: '# AAAA'
    }
  },
  created() {
    // getDocContent('/web/webDoc').then(res => {
    //   this.mdText = res
    // })
  },
  methods:{
    markedHeading(text, level, raw){
      console.log('aaaaa')
      console.log(text)
      console.log(level)
      console.log(raw)
    }
  }
}
</script>
<style lang="less" scoped>
.body {
  margin-left: 50px;
  margin-right: 50px;
}
</style>

我注释掉的这两行代码起什么作用?在离开md页面时会报错

onUnmounted(() => {
if (props2.trigger === "click") {
triggerRef.value.removeEventListener("click", triggerHandler);
document.removeEventListener("click", clickHidden);
} else {
// triggerRef.value.removeEventListener("mouseenter", triggerHandler);
// triggerRef.value.removeEventListener("mouseleave", leaveHidden);
}
});

失灵

项目中失灵了,点击任何按钮无反应
我的配置
{
"name": "vue3-js",
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview"
},
"dependencies": {
"@ckeditor/ckeditor5-build-classic": "^29.0.0",
"@ckeditor/ckeditor5-vue": "^2.0.1",
"axios": "^0.21.1",
"echarts": "^5.1.2",
"element-plus": "^1.0.2-beta.55",
"md-editor-v3": "^1.1.2",
"vue": "^3.1.5",
"vue-router": "^4.0.10"
},
"devDependencies": {
"@vitejs/plugin-vue": "^1.2.2",
"@vue/compiler-sfc": "^3.0.5",
"sass": "^1.32.8",
"vite": "^2.3.3",
"vuex": "^4.0.0"
}
}

麻烦搞一下CDN支持

我不想把依赖打包进来,但是直接用链接导入会报错Uncaught TypeError: Cannot read property 'defineComponent' of undefined

createWebHashHistory与heading生成的链接锚点冲突

作者您好,有幸能够使用上您的MD编辑器组件,他对于我的帮助特别大,我也非常喜欢。

现在有个特殊场景需要使用createWebHashHistory创建的RouterHistory,这样一来本身页面的链接就会以#开头。
同时md-editor本身会根据Markdown中的标题生成对应的锚点,然后生成对应的a标签元素能够导航到标题处,但是这样一来就与Router冲突了。

这个场景是必须使用createWebHashHistory而不能使用其他的RouterHistory创建方式,所以可以考虑为开放一个自定义渲染标题的接口或者在内部做出调整吗?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.