Quillで表を挿入する

WYSIWYGエディタの一つであるQuillで表を挿入する方法が、誰かの役に立てばと思い投稿します。
最大７ｘ７マスの表を縦横のマス数を自由に設定して表を挿入することが出来ます。

ちなみに、この記事はnpmもjQueryも使わない欲張りセットです。
正直、両方とも使ってしまえば多分話が早いです。
（今回使用しているQuill用プラグインは公式ではnpmのインストールのみが掲載されている＆jQueryを使う場合は[何方かが作成したものがすでにある](https://jsfiddle.net/Rudy_Hascouet/qzcj07an/)ため）

この記事は、上記をvanillaJSで書き直したものです。

注意点：Quillに表を挿入するプラグインである「quill-better-table」はquill ver.2を必要とするので、quillのバージョンに気を付ける必要があります。

```html
<!DOCTYPE html>
<html lang="ja">
	<head>
		<meta charset="UTF-8">
		<title></title>
		<link rel="stylesheet" href="./editor.css">
		<script src="https://cdnjs.cloudflare.com/ajax/libs/quill/2.0.0-dev.1/quill.min.js"></script>
		<link href="https://cdnjs.cloudflare.com/ajax/libs/quill/2.0.0-dev.1/quill.snow.min.css" rel="stylesheet">
		<script src="https://cdn.jsdelivr.net/npm/quill-better-table@latest/dist/quill-better-table.min.js"></script>
		<link type="text/css" href="https://unpkg.com/quill-better-table@latest/dist/quill-better-table.css" rel="stylesheet" />
	</head>
	
	<body>
		<div id="myeditor">
		</div>

		<script src="./editor.js"></script>
	</body>
</html>
```
前述したように、npmを使わないでCDNから落としてきています。
そして注意点としては、執筆時点（2021年06月14日）では
quillのバージョン部分をlatestにすると1.7.3が選ばれてしまうので、2.0.0-dev.1と指定してあげる必要があります。

ここでは、myeditorをWYSIWYGエディタにしたいと思います。

```CSS:editor.css
.ql-snow .ql-Table-Input .ql-picker-options
{
	padding: 3px 5px;
	width: 152px;
}

.ql-snow .ql-Table-Input .ql-picker-item
{
	border: 1px solid transparent;
	float: left;
	height: 16px;
	margin: 2px;
	padding: 0px;
	width: 16px;
	background: lightsteelblue;
}

.ql-snow .ql-Table-Input .ql-picker-item:hover, .ql-snow .ql-Table-Input .ql-picker-item.ql-picker-item-highlight
{
	border-color: #000;
	background: steelblue;
}

.ql-snow .ql-Table-Input.ql-picker
{
	width: 28px;
}

.ql-snow .ql-Table-Input.ql-picker .ql-picker-label {
	padding: 2px 4px;
}
```
CSSに関しては、先ほど紹介した[jQueryを使う方法](https://jsfiddle.net/Rudy_Hascouet/qzcj07an/)をそのままコピペしました。

```javascript:editor.js
var _MODE = (typeof window.ontouchstart == "undefined")? 'click' : 'touchstart';
var _MODE_HOVER = (typeof window.ontouchstart == "undefined")? 'mouseover' : 'touchstart';

// WYSIWYGクラス
class C_Editor
{
	constructor(id)
	{
		this.editor = this.Init(id);
		this.IconSet(id)
	}
	
	// Quill起動
	Init(id)
	{
		// 表の大きさ指定用（最大７ｘ７マス）
		var tableopt = []
		for (let r = 1; r <= 7; r++)
		{
			for (let c = 1; c <= 7; c++)
			{
				tableopt.push('newtable_' + r + '_' + c);
			}
		}
		
		// ツールバーに表示する機能
		var tb_opt = [
			// 見出し
			{'header': [1, 2, 3, false]},
			// 太字、斜体、下線、打消線
			'bold', 'italic', 'underline', 'strike',
			// 上／下付き文字
			{'script': 'super'}, {'script': 'sub'},
			// 文字色、文字背景色
			{'color': []}, {'background': []},
			// リスト
			{'list': 'ordered'}, {'list': 'bullet'},
			// 文字寄せ
			{'align': []},
			// インデント
			{'indent': '-1'}, {'indent': '+1'},
			// コードブロック(インラインコードのアイコンと区別がつかないのでコードブロックのみ)
			'code-block',
			// 数式
			'formula',
			// 表
			{'Table-Input': tableopt},
			//画像・動画挿入、URLリンク
			'image', 'video', 'link',
			// 装飾の削除
			'clean',
		];
		
		var quill = new Quill(
								document.getElementById(id),
								{
									theme: 'snow',
									modules:
									{
										toolbar:
										{
											container: tb_opt,
											handlers: {'Table-Input': () => {}}
										},
										// 表
										table: false,
										'better-table': {operationMenu: {}}
									}
								}
					);
		return quill;
	}
	
	// 表範囲の選択CSS
	TableSizeCSS()
	{
		var row = Number(this.dom.dataset.value.substring(9).split('_')[0]);
		var col = Number(this.dom.dataset.value.substring(9).split('_')[1]);
		for(var elm of this.dom.parentNode.children)
		{
			var rowindex1 = Number(elm.dataset.value.substring(9).split('_')[0]);
			var colindex1 = Number(elm.dataset.value.substring(9).split('_')[1]);
			if (rowindex1 <= row && colindex1 <= col)
			{
				elm.classList.add("ql-picker-item-highlight");
			}
			else
			{
				elm.classList.remove("ql-picker-item-highlight");
			}
		};
	}
	
	// 表の挿入
	TableDraw()
	{
		var row = Number(this.dataset.value.substring(9).split('_')[0]);
		var col = Number(this.dataset.value.substring(9).split('_')[1]);
		
		// 表の挿入
		var qbt = this.ed.getModule('better-table');
			qbt.insertTable(row, col);
	}
	
	// 表のサイズ設定
	TableInit(dom)
	{
		var ed = this.editor;
		
		var tabledoms = dom.childNodes[1].children;
			for (var tabledom of tabledoms)
			{
				// クリック時の挙動
				tabledom.addEventListener(_MODE, {ed:this.editor, dataset:tabledom.dataset, handleEvent:this.TableDraw});
				
				//　マウスオーバー時の挙動
				tabledom.addEventListener(_MODE_HOVER, {dom:tabledom, handleEvent:this.TableSizeCSS});
			}
	}
	
	// quillBetterTableアイコン表示＆動作設定
	IconSet(id)
	{
		var dom = document.getElementById(id).parentNode.getElementsByClassName('ql-Table-Input')[0];
		dom.children[0].innerHTML = "<svg style=\"right: 4px;\" viewbox=\"0 0 18 18\"> <rect class=ql-stroke height=12 width=12 x=3 y=3></rect> <rect class=ql-fill height=2 width=3 x=5 y=5></rect> <rect class=ql-fill height=2 width=4 x=9 y=5></rect> <g class=\"ql-fill ql-transparent\"> <rect height=2 width=3 x=5 y=8></rect> <rect height=2 width=4 x=9 y=8></rect> <rect height=2 width=3 x=5 y=11></rect> <rect height=2 width=4 x=9 y=11></rect> </g> </svg>";
		
		this.TableInit(dom);
	}
}

window.addEventListener('load', function()
{
	// Quillにquill-better-tableを登録
	Quill.register({
		'modules/better-table': window.quillBetterTable
	}, true);
	var ed = new C_Editor("myeditor");
});

```
大まかにコメントを入れているので、大体わかるのではないでしょうか。
補足事項としては

1. window.onload時に「Quillにquill-better-tableを登録」していますが、これは恐らく一回すればよいと思われる（未確認です）ので、複数の箇所でWYSIWYGエディタを使えるようにクラス作成をしていることもあり、クラス内で登録をしないようにしています。
同じページ内で一つしかWYSIWYGエディタを使わないのであれば、インスタンス作成時にidを引数に取る必要もありませんし、`new Quill`の直前にquill-better-tableを登録するようにすれば、もっとすっきりします。

1. quillBetterTableアイコン表示＆動作設定部分でのsvgは[jQueryを使う方法](https://jsfiddle.net/Rudy_Hascouet/qzcj07an/)をそのままコピペしました。

1. `new Quill`のところで`handlers: {'Table-Input': () => {}}`と一見無駄なことをしていますが、これをしないと`logger.js:6 quill:toolbar ignoring attaching to nonexistent format Table-Input`という黄色い警告が管理者ツールで出てきます。本来はここにアイコンクリック時の挙動をいれるのですが、別のところで処理しているのでここでは空のアロー関数としています

```js
// 要するに
toolbar:
{
  container: tb_opt,
  handlers: {'Table-Input': () => {}}
},
// でなく
toolbar:tb_opt,
// は警告が出る
```

独学日曜プログラマのため、
してはいけないことをしているかもしれませんし、もっといい書き方あるかもしれませんが、その時はコメントで優しく教えていただければ幸いです。