もう平成も終わろうとしているのに未だ神Excel依存な弊社で、せめて所属部署、所属課だけでも良いのでMarkdownを使ってくれ...と思い、色々考えてました。
まず「Markdownは難しくない」と感じてもらう為に、エディタ/ビューアを用意しなければなりません。
しかし、いきなりVSCodeだのStackblitzだの言っても確実にコケると直感しました。
この手のは、とっつきで「難しそう」と感じられてしまったら確実に失敗するんですよね。
イントラにアクセスするだけで誰でも使える、Webページ埋込タイプのエディタが無いか探してたら、丁度良い物があるではないですか。
SimpleMDE
JavaScript/CSSの2ファイルをロードするだけで使える、お手軽Markdownエディタです。
つかいかた
<!DOCTYPE html>
<html lang="ja">
<head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/simplemde@latest/dist/simplemde.min.css">
<script src="https://cdn.jsdelivr.net/npm/simplemde@latest/dist/simplemde.min.js"></script>
</head>
<body>
<textarea id="mde"></textarea>
</body>
<script>
const mde = new SimpleMDE({
element: document.getElementById("mde")
});
</script>
</html>
たったこれだけで立派なMarkdownエディタが実装出来ます。
ちょうど公式サイトの一番上のデモと同じ感じになります。
機能の追加/削除
素の状態でも十分使えるスグレモノですが .md
ファイルの読込/保存やDataURLで軽量バイナリ埋込など、痒い所に手を届かせる自作の機能も追加可能です。
インスタンス生成時にオプション指定可能です。
new SimpleMDE({
element: document.getElementById("mde"),
toolbar: [
...,
{
name: "save", // 機能名
action: saveHandler, // アクションハンドラ
className: "fa fa-download", // アイコン
title: "Save Markdown" // 説明
}]
});
function saveHandler(editor){ // コンテキスト
saveAs(new Blob([editor.value()]), "markdown.md");
editor.codemirror.focus();
}
"ツールバー" が機能の実体なので、MixInを使って toolbar
オプションに新しい機能を宣言することで追加可能です。
なお、MixInを使用しないと上書きになるので、BoldやItalicなどの基本的な機能も再宣言しなければなりません。
アイコンは FontAwesome の物が使えます。
ハンドラにはエディタのコンテキストが引数として渡されます。
エディタとプレビューのフォントを変えたい場合は、既存のCSSを上書きする事で可能です。
/* エディタ領域 */
.CodeMirror {
font-family: "MigMix 1M";
}
/* プレビュー領域 */
.editor-preview,
.editor-preview-side {
font-family: "M PLUS 1p";
}
/* プレビュー領域内コード */
.editor-preview code,
.editor-preview-side code {
font-family: "Kosugi";
}
NotoSansやHiraginoなど、お好きなフォントを使ってください。
あ、僕はMigMixが好きです。
Tips: D&D(ドラッグアンドドロップ)対応
放り込みたくなりますよね。
// ウィンドウ全域でD&D禁止
addEventListener("dragover", (event)=>{
event.preventDefault();
event.dataTransfer.dropEffect = "none";
});
addEventListener("drop", (event)=>{
event.preventDefault();
});
const dnd = document.getElementsByClassName("CodeMirror-scroll")[0];
// エディタ領域のみD&D許可
dnd.addEventListener("dragover", (event)=>{
event.preventDefault();
event.dataTransfer.dropEffect = "move";
});
dnd.addEventListener("drop", async(event)=>{
event.preventDefault();
mde.value(await fileReader(event.dataTransfer.files[0]));
mde.codemirror.focus();
});
// ここら辺はお好みで
function fileReader(blob){
return new Promise((res, rej)=>{
const fr = new FileReader();
fr.addEventListener("load", () => res(fr.result));
fr.addEventListener("error", () => rej(fr.error));
fr.readAsText(blob);
});
}
これでエディタ領域のみD&Dが可能となります。
Tips: ファイルピッカー呼出
ツールバーにファイル入力を設けたいとき。
もはやHTML初心者講座になってる気がしないでもないですが、とりあえず書いときます。
// Toolbar アクションハンドラ
async function loadHandler(editor){
editor.value(await filePicker().then(b => fileReader(b)));
editor.codemirror.focus();
}
function filePicker(){
return new Promise((res)=>{
const input = document.createElement("input");
input.type = "file";
input.id = "file";
input.style.display = "none";
document.body.appendChild(input);
const file = document.getElementById("file");
file.addEventListener("change", function fn(event){
input.removeEventListener("change", fn);
document.body.removeChild(file);
res(event.target.files[0]);
});
file.click();
});
}
本当は once: true
オプションを使いたいのですが、神Excelが横行してるような職場のブラウザシェアを考えるとね...