This article covers the use of the editor and some of the development techniques that accompany it.

The editor supports the following functions: basic MD editing, MD syntax shortcut keys, record saving, dark theme, picture uploading/copying picture uploading/cropping picture uploading, formatting content, browser full screen/full screen, preview mode only and other functions, waiting to be used.

For detailed editor API reference: documentation.

  • Image cropping preview

  • Editor preview

1. Basic use

Here are three ways to write in two environments:

1.1 NPM Installation usage

This method supports both. Vue template writing and JSX syntax.

The installation

yarn add md-editor-v3
Copy the code

Vue template basic use

<template>
  <md-editor v-model="text" />
 </template>
 
 <script lang="ts">
 import { defineComponent } from 'vue';
 import MdEditor from 'md-editor-v3';
 import 'md-editor-v3/lib/style.css';
 
 export default defineComponent({
   components: { MdEditor },
   data() {
     return { text: ' '}; }});</script>
Copy the code

JSX syntax basics to use

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

export default defineComponent({
  name: 'MdEditor'.setup() {
    const text = ref(' ');
    return () = > (
      <MdEditor modelValue={text.value} onChange={(v: string) = > (text.value = v)} />); }});Copy the code

1.2 Introduction of script tags

The link is to search mD-editor-v3 at cdn.jsdelivr.net.

<! -- Add style -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/lib/style.css" rel="stylesheet" />
<! - introduce vue3 -- -- >
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.global.prod.min.js"></script>
<! -- Importing components -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lib/md-editor-v3.umd.js"></script>
Copy the code

Certified components

const App = {
 data() {
   return {
      text: 'Hello Editor!! '}; }}; Vue.createApp(App).use(MdEditorV3).mount('#md-editor-v3');
Copy the code

Using the component

<div id="md-editor-v3">
  <md-editor-v3 v-model="text" />
</div>
Copy the code

2. Render content

The editor uses marked to parse MD to HTML without extended syntax.

In general, edit content is stored in MD format and rendered in HTML by marked.

2.1 Default Rendering

Since version 1.3.0, the editor supports previewOnly, which allows you to preview articles directly using the editor without bars, edits, etc.

<template>
  <md-editor
    v-model="text"
    previewOnly
  />
</template>

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

export default defineComponent({
  components: { MdEditor },
  data() { return { text: '## I will only show preview content '}; }});</script>
Copy the code

2.2 Active parsing demo

This is used to save the MD and then parse the MD content yourself.

import marked from 'marked';

// Code highlights
import hljs from 'highlight.js';
// Code highlighting style of your choice
import 'highlight.js/scss/atom-one-dark.scss';

// Use to record the number of headings, according to the business
let count = 0;
// Record the title content
const headstemp = [];

/ / marked Settings
const rendererMD = new marked.Renderer();

// Adjust the title content
rendererMD.heading = (text, level) = > {
  headstemp.push({ text, level });
  count++;
  return `<h${level} id="heading-${count}"><span class="h-text">${text}</span></h${level}> `;
};

// Set the image content to display a unified cache image for lazy loading ~
rendererMD.image = (href, _, text) = >
  `<img data-src="${href}" src="/cos/2020/1211175603.png" alt="${text}" >`;

marked.setOptions({
  highlight(code) {
    return hljs.highlightAuto(code).value
  },
  renderer: rendererMD
});

// The HTML is the element text inserted into the page
const html = marked('## md content ');
Copy the code

2.3 Realization of title navigation

The above example, HeadStemp, records all the titles during parsing to build a title navigation with the UI library component Anchor.

Here is a demo version based on Ant-Design-Vue. If you are using a UI library with similar anchor components, the code will need only minor changes. Code using JSX syntax, VUE template syntax please separate code ~

Link components in Recursive. TSX navigation

import { Anchor } from 'ant-design-vue';
import { defineComponent, PropType } from 'vue';

const { Link } = Anchor;

export interface Head {
  text: string;
  level: number;
}

export interface TocItem extends Head {
  anchor: string; children? :Array<TocItem>;
}

const Recursive = defineComponent({
  props: {
    tocItem: {
      type: Object as PropType<TocItem>,
      default: () = >[]}},setup({ tocItem }) {
    return (
      <Link href={` # ${tocItem.anchor} `}title={tocItem.text}>
        {tocItem.children &&
          tocItem.children.map((item) => <Recursive key={item.anchor} tocItem={item} />)}
      </Link>); }});export default Recursive;
Copy the code

Topicfy.tsx is used to generate the entire navigation content

import { Anchor } from 'ant-design-vue';
import { computed, defineComponent, PropType, ref, watch } from 'vue';

import Recursive, { Head, TocItem } from './Recursive';

const Topicfy = defineComponent({
  props: {
    // A list of parsed titles
    heads: {
      type: Array as PropType<Array<Head>>
    }
  },
  setup(props) {
    const topics = computed(() = > {
      const tocItems: TocItem[] = [];

      // Title counter
      let count = 0;

      const add = (text: string, level: number) = > {
        count++;

        const item = { anchor: `heading-${count}`, level, text };

        if (tocItems.length === 0) {
          // The first item is pushed directly
          tocItems.push(item);
        } else {
          let lastItem = tocItems[tocItems.length - 1]; // 最后一个 item

          if (item.level > lastItem.level) {
            // Item is lastItem's children
            for (let i = lastItem.level + 1; i <= 6; i++) {
              const { children } = lastItem;
              if(! children) {// If children do not exist
                lastItem.children = [item];
                break;
              }
              // reset lastItem to the lastItem of children
              lastItem = children[children.length - 1];

              if (item.level <= lastItem.level) {
                // If item level is less than or equal to lastItem level, it is considered to be at the same level as children
                children.push(item);
                break; }}}else {
            // put it at the toptocItems.push(item); }}}; props.heads? .forEach((item) = > {
        add(item.text, item.level);
      });
      return tocItems;
    });

    return () = > (
      <Anchor affix={false} showInkInFixed={true}>
        {topics.value.map((item) => (
          <Recursive key={item.anchor} tocItem={item} />
        ))}
      </Anchor>); }});export default Topicfy;
Copy the code

The react version is based on Topicfy

2.4 Obtaining THE HTML code

The editor is concerned that the backend may not store mD-formatted text but HTML content, so it provides the onHtmlChanged method, which is used to edit the content changes, marked compiles the callback to the content, and the input is the HTML content.

<template>
  <md-editor
    v-model="text"
    @onHtmlChanged="saveHtml"
  />
</template>

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

export default defineComponent({
  components: { MdEditor },
  data() { return { text: ' ' }; },
  methods: { saveHtml(h: string) { console.log(h) }}
});
</script>
Copy the code

JSX has the same syntax.

3. Function demonstration of the editor

3.1 Extension library links

Editor extension content mostly uses CDN, considering no extranet situation, supports Intranet link extension, demonstration (assuming that external libraries are in the root directory) :

<template>
  <md-editor
    v-model="text"
    highlightJs="/highlight.min.js"
    highlightCss="/atom-one-dark.min.css"
    prettierCDN="/standalone.js"
    prettierMDCDN="/parser-markdown.js"
    cropperJs="/cropper.min.js"
    cropperCss="/cropper.min.css"
  />
</template>

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

export default defineComponent({
  components: { MdEditor },
  data() { return { text: ' '}; }});</script>
Copy the code

V1.2.0 currently supports the above links, and the icon links will be added in the following patches.

3.2 Toolbar Customization

By default, all toolbars are available, and each feature is bound with shortcut keys. If you need to selectively display toolbars, there are two apis: Toolbars, which display all of the array, and toolbarsExclude, which has more weight, which blocks all of the array. Here’s a reference:

The example does not show the Github button

<template>
  <md-editor
    v-model="text"
    :toolbars="toobars"
  />
  
  <md-editor
    v-model="text"
    :toolbarsExclude="toolbarsExclude"
  />
</template>

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

export default defineComponent({
  components: { MdEditor },
  data() {
    return {
      text: ' '.toobars: ['bold'.'underline'.'italic'.'strikeThrough'.'sub'.'sup'.'quote'.'unorderedList'.'orderedList'.'codeRow'.'code'.'link'.'image'.'table'.'revoke'.'next'.'save'.'pageFullscreen'.'fullscreen'.'preview'.'htmlPreview'].toolbarsExclude: ['github']}; }});</script>
Copy the code

3.3 Extended Languages

Both Chinese and English are built into the editor by default, and both can be overridden by the extended API, which is mainly used to set content hints, such as titles in popovers, etc.

To extend a language, we call it zh-NB

<template>
  <md-editor
    v-model="text"
    :language="language"
    :languageUserDefined="languageUserDefined"
  />
</template>

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

const languageUserDefined: { 'zh-NB': StaticTextDefaultValue } = {
  'zh-NB': {
    toolbarTips: {
      bold: 'bold'.underline: 'underline'.italic: 'italics'.strikeThrough: 'Delete line'.title: 'title'.sub: 'the subscript'.sup: 'superscript'.quote: 'reference'.unorderedList: 'Unordered list'.orderedList: 'Ordered list'.codeRow: 'In-line code'.code: 'Block level code'.link: 'link'.image: 'images'.table: 'form'.revoke: 'back'.next: 'forward'.save: 'save'.prettier: 'beautification'.pageFullscreen: 'Browser full screen'.fullscreen: 'Full screen'.preview: 'preview'.htmlPreview: 'HTML Code Preview'.github: 'Source address'
    },
    titleItem: {
      h1: 'First level heading'.h2: 'Secondary heading'.h3: 'Level 3 heading'.h4: 'Level 4 Title'.h5: 'Level 5 heading'.h6: 'Six level headings'
    },
    linkModalTips: {
      title: 'add'.descLable: 'Link Description:'.descLablePlaceHolder: 'Please enter a description... '.urlLable: 'Link address:'.UrlLablePlaceHolder: 'Please enter the link... '.buttonOK: 'sure'.buttonUpload: 'upload'
    },
    / / v1.2.0 added
    clipModalTips: {
      title: 'Crop picture upload'.buttonUpload: 'upload'
    },
    / / v1.1.4 added
    copyCode: {
      text: 'Copy code'.tips: 'Copied'}}};export default defineComponent({
  components: { MdEditor },
  data() {
    return {
      text: ' '.language: "zh-NB", languageUserDefined }; }});</script>
Copy the code

If key = ‘zh-cn’, Chinese overwrite can be implemented, and so on.

3.4 Theme Switch

This section is relatively simple, built-in dark theme and default theme, using themeAPI switch, the demo is as follows:

<template>
  <md-editor
    v-model="text"
    :theme="theme"
  />
</template>

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

export default defineComponent({
  components: { MdEditor },
  data() {
    return {
      text: ' '.theme: 'dark'}; }});</script>
Copy the code

4. At the end

For more updates: MD-editor-v3