TOAST UIのMarkdownエディタのReact版にTypeScriptの型が付いたよ

TOAST UIというWebのUIツールキットにMarkdownエディタがあるのですが、これがかなり便利で、以前記事を書いたQiitaクローンで使っています。

↓こんなエディタが簡単に作れます f:id:devilune:20200225231554p:plain

ただ、このエディタのReact版はTypeScriptの型定義がなかったので自分で型を付けて使っていました...が今日最新版にアップデートしたら元々のリポジトリがdeprecated化&ソースがエディタ本体のリポジトリに移動&型定義が作成されていました。

こちらが元のリポジトリ
github.com

こちらが移動後のリポジトリ
github.com

npmのパッケージ自体は@toast-ui/react-editorから変わっていないので単純にアップデートすればOKです。
以前はあまりメンテナンスされていない感じだったのですが、本体に取り込まれたのもあり、今のところ活発にメンテナンスされているようです。
今後どうなるかは怪しい部分もありますが、WebでMarkdownエディタを組み込むのであればオススメのライブラリです。

ちなみにVue.js版もあります。
github.com

供養

これを書くために記事をしたためたのですが、このエディタは普通に使おうと思うと

<div id="editor"></div>
const editor = new toastui.Editor({
  el: document.querySelector('#editor'),
  previewStyle: 'vertical',
  height: '500px',
  initialValue: content
});

こんな感じのコードになるのですが、QiitaクローンはReact製なのでこんなことはしたくないわけです。
自分でラップしてもいいんですが、前述の通り公式のReactコンポーネントが用意されているのでそれを使っていました&自分で型を付けていました。
せっかく作ったので、役に立つことはないと思いますがコードを置いておきます。

dummy.d.ts

declare module "@toast-ui/react-editor";

Editor.tsx

// Editor.tsx
import {
  Editor as TuiEditor,
  Viewer as TuiViewer
} from "@toast-ui/react-editor";
import "codemirror/lib/codemirror.css";
import "highlight.js/styles/gml.css";
import React from "react";
import "tui-editor/dist/tui-editor-contents.min.css";
import "tui-editor/dist/tui-editor-extChart";
import "tui-editor/dist/tui-editor-extColorSyntax";
import "tui-editor/dist/tui-editor-extScrollSync";
import "tui-editor/dist/tui-editor-extTable";
import "tui-editor/dist/tui-editor-extUML";
import "tui-editor/dist/tui-editor.min.css";

interface Hooks {
  previewBeforeHook: (...args: any[]) => void;
  addImageBlobHook: (
    fileOrBlob: File | Blob,
    callback: (...args: any[]) => void,
    source: string
  ) => void;
}

interface ToMarkOptions {
  gfm?: boolean;
  renderer?: any;
}

interface Converter {
  getMarkdownitHighlightRenderer(): markdownit;
  initHtmlSanitizer(): void;
  toHTML(makrdown: string): string;
  toHTMLWithCodeHightlight(markdown: string): string;
  toMarkdown(html: string, toMarkdownOptions: ToMarkOptions): string;
}

interface EditorProps {
  height?: string;
  minHeight?: string;
  initialValue?: string;
  previewStyle?: "tab" | "vertical";
  initialEditType?: "markdown" | "wysiwyg";
  onLoad?: (...args: any[]) => void;
  onChange?: (...args: any[]) => void;
  onStateChange?: (...args: any[]) => void;
  onFocus?: (...args: any[]) => void;
  onBlur?: (...args: any[]) => void;
  hooks?: Array<Hooks>;
  language?: string;
  useCommandShortcut?: boolean;
  useDefaultHTMLSanitizer?: boolean;
  codeBlockLanguages?: Array<string>;
  usageStatistics?: boolean;
  toolbarItems?: string;
  hideModeSwitch?: boolean;
  exts?: Array<any>;
  customConvertor?: Converter;
  placeholder?: string;
  previewDelayTime?: string;
  linkAttribute?: any;
}

interface ViewerProps {
  initialValue?: string;
  onLoad?: (...args: any[]) => void;
  onChange?: (...args: any[]) => void;
  onStateChange?: (...args: any[]) => void;
  onFocus?: (...args: any[]) => void;
  onBlur?: (...args: any[]) => void;
  hooks?: Array<Hooks>;
  exts?: Array<any>;
}

export const Editor = React.forwardRef<any, EditorProps>((props, ref) => (
  <TuiEditor
    ref={ref}
    usageStatistics={false}
    exts={[
      {
        name: "chart",
        minWidth: 100,
        maxWidth: 600,
        minHeight: 100,
        maxHeight: 300
      },
      "scrollSync",
      "colorSyntax",
      "uml",
      "mark",
      "table"
    ]}
    {...props}
  />
));

export const Viewer = React.forwardRef<any, ViewerProps>((props, ref) => (
  <TuiViewer
    ref={ref}
    exts={[
      {
        name: "chart",
        minWidth: 100,
        maxWidth: 600,
        minHeight: 100,
        maxHeight: 300
      },
      "scrollSync",
      "colorSyntax",
      "uml",
      "mark",
      "table"
    ]}
    {...props}
  />
));

見ての通り、結局ラップしてるだけじゃねーか、という感じではあります...。