Angular安装bytemd编辑器

858 阅读1分钟

安装依赖

    "bytemd": "^1.9.2",
    "@bytemd/plugin-breaks": "^1.10.6",
    "@bytemd/plugin-footnotes": "^1.10.6",
    "@bytemd/plugin-frontmatter": "^1.10.6",
    "@bytemd/plugin-gemoji": "^1.10.6",
    "@bytemd/plugin-gfm": "^1.10.6",
    "@bytemd/plugin-highlight": "^1.10.6",
    "@bytemd/plugin-math": "^1.10.6",
    "highlightjs": "^9.16.2",
    "@bytemd/plugin-medium-zoom": "^1.10.6",
    "@bytemd/plugin-mermaid": "^1.10.6",

修改TypeScript配置

不修改的话,angular的配置不兼容bytemd,因为bytemd用的sevelte

{
  "compilerOptions": {
    "skipLibCheck": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true
  }
}

自定义代码高亮插件

export default function highlightStyle() {
  let cacheHL = 'a11y-dark';

  return {
    viewerEffect({file}) {

      // 这里file就是刚才挂载了frontmatter的那个对象
      const hl = file?.frontmatter?.highlight; // hl = 'a11y-dark'

      if (cacheHL === hl) {
        return () => {
        };
      }
      cacheHL = hl || cacheHL;

      const $style = document.createElement('link');
      // 从styleList获取到'a11y-dark'样式插入到head中

      $style.rel = 'stylesheet';
      $style.type = 'text/css';
      $style.href = `assets/code-styles/${cacheHL}.css`;
      document.head.appendChild($style);
      return () => {
        $style.remove()
      }
    },
    actions: []
  }
}

封装editor组件

import {AfterViewInit, ChangeDetectionStrategy, Component, ElementRef, OnInit, ViewChild} from '@angular/core';
import {Editor} from 'bytemd';
import breaks from '@bytemd/plugin-breaks';
import footnotes from '@bytemd/plugin-footnotes';
import frontmatter from '@bytemd/plugin-frontmatter';
// import gemoji from '@bytemd/plugin-gemoji';
import gfm from '@bytemd/plugin-gfm';
import highlight from '@bytemd/plugin-highlight';
import math from '@bytemd/plugin-math';
import mediumZoom from '@bytemd/plugin-medium-zoom';
import mermaid from '@bytemd/plugin-mermaid';
import highlightStyle from '../plugin/highlight.plugin';
import {AttachmentService} from '../../../../../src/app/core/service/biz/attachment.service';
import {map} from 'rxjs/operators';
import zh_Hans from 'bytemd/lib/locales/zh_Hans.json';
import gfm_zh_Hans from '@bytemd/plugin-gfm/lib/locales/zh_Hans.json';
import math_zh_Hans from '@bytemd/plugin-math/lib/locales/zh_Hans.json';
import mermaid_zh_Hans from '@bytemd/plugin-mermaid/lib/locales/zh_Hans.json';

@Component({
  selector: 'app-md-editor',
  template: `
  <div #bytemdHost class="bytemd-host"></div>
  `,
  styleUrls: ['./markdown-editor.component.scss'],
  preserveWhitespaces: true,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MarkdownEditorComponent implements OnInit, AfterViewInit {
  @ViewChild('bytemdHost') hostRef: ElementRef;
  editor: Editor;

  markContent: string;

  constructor(private attachmentService: AttachmentService) {
  }

  ngOnInit(): void {
  }


  ngAfterViewInit(): void {
    const plugins = [breaks(),
      gfm({locale: gfm_zh_Hans}),
      footnotes(),
      highlight(),
      math({locale: math_zh_Hans}),
      mediumZoom(),
      mermaid({locale: mermaid_zh_Hans}),
      frontmatter(),
      highlightStyle()];

    this.editor = new Editor({
      target: this.hostRef.nativeElement,
      locale: zh_Hans,
      props: {
        plugins,
        locale: zh_Hans,
        placeholder: '...',
        uploadImages: (files: File[]) => {
          return this.attachmentService.upload(files).pipe(
            map(res => res.map(item => {
                return {
                  url: `/article-server/attachments/download/${item.id}`,
                  alt: item.name,
                  title: item.name,
                }
              })
            )
          ).toPromise();
        }
      },
    });

    this.editor.$on('change', (e) => {
      this.markContent = e.detail.value;
      this.editor.$set({value: e.detail.value});
    });
  }

  getBriefContent(length: number): string {
    return this.hostRef.nativeElement.querySelector('.markdown-body').textContent?.substring(0, length);
  }

  getMarkContent(): string {
    return this.markContent;
  }
}

自定义viewer组件

import {
  AfterViewInit,
  ChangeDetectionStrategy,
  Component,
  ElementRef,
  Input,
  OnChanges,
  OnInit, SimpleChanges,
  ViewChild
} from '@angular/core';
import {Editor, Viewer} from 'bytemd';
import breaks from '@bytemd/plugin-breaks';
import footnotes from '@bytemd/plugin-footnotes';
import frontmatter from '@bytemd/plugin-frontmatter';
// import gemoji from '@bytemd/plugin-gemoji';
import gfm from '@bytemd/plugin-gfm';
import highlight from '@bytemd/plugin-highlight';
import math from '@bytemd/plugin-math';
import mediumZoom from '@bytemd/plugin-medium-zoom';
import mermaid from '@bytemd/plugin-mermaid'
import highlightStyle from '../plugin/highlight.plugin';
import gfm_zh_Hans from '@bytemd/plugin-gfm/lib/locales/zh_Hans.json';
import math_zh_Hans from '@bytemd/plugin-math/lib/locales/zh_Hans.json';
import mermaid_zh_Hans from '@bytemd/plugin-mermaid/lib/locales/zh_Hans.json';

@Component({
  selector: 'app-md-viewer',
  template: `
<div #bytemdHost class="bytemd-host leading-normal text-3xl"></div>
`,
  styleUrls: ['./markdown-viewer.component.scss'],
  preserveWhitespaces: true,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MarkdownViewerComponent implements OnInit, AfterViewInit, OnChanges {
  @ViewChild('bytemdHost', {static: true}) hostRef: ElementRef;

  @Input()
  markContent: string;

  viewer: Viewer;

  constructor() {
  }

  ngOnInit(): void {
  }


  ngAfterViewInit(): void {
  }

  private renderMarkDown(markContent: string) {
    const plugins = [breaks(),
      gfm({locale: gfm_zh_Hans}),
      footnotes(),
      highlight(),
      math({locale: math_zh_Hans}),
      mediumZoom(),
      mermaid({locale: mermaid_zh_Hans}),
      frontmatter(),
      highlightStyle()];
    this.viewer = new Viewer({
      target: this.hostRef.nativeElement,
      props: {
        plugins,
        value: markContent,
      },
    });
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes.markContent && this.markContent) {
      this.renderMarkDown(this.markContent);
    }
  }
}