44日目 割烹芸人の開発4
正直コード見ても面白くないと思うので後書きだけ眺めてもらえばいいかも。
今回はカスタム区切りを作っていきます。
editor.jsのプラグインにはセパレーターもあるのですが、設定の種類がないのでもう少しカスタマイズします。
デフォルトだとこの種類しかなくてあんまり拡張できないのでカスタマイズします。
具体的には
・線の太さ
・線の種類
・線の色
を指定できるようにします。ということでカスタムブロックを作っていきます。まずはSepatatorクラスを作ります。
class Separator{
}
constructor({data, config, api}){
this.api = api;
this.data = {
type: data.type !== undefined ? data.type : 'solid',
width:data.width !== undefined ? data.width :'medium',
color:data.color !== undefined ? data.color :'#000',
};
this.settings = [
{
name:'solid',
icon:'実線'
},
{
name:'dashed',
icon:'破線'
},
{
name:'dotted',
icon:'点線'
},
{
name:'thin',
icon:'細線'
},
{
name:'medium',
icon:'普通'
},
{
name:'thick',
icon:'太線'
},
{
name:'color',
icon:'線色'
}
]
this._element = this.drawView();
}
render(){
return this._element;
};
drawView() {
let div = document.createElement('DIV');
div.classList.add('separetor');
return div;
}
save(toolsContent){
return{
type:this.data.type,
width:this.data.width,
color:this.data.color
}
}
static get toolbox(){
return{
icon:'区切',
title:'区切り線の挿入'
};
}
次にレンダー、コンストラクタ、セーブ、ツールボックスです。
drawViewでdivを指定して、これにボーダーを付けることで区切り線を実現します。小説家になろうではhrにスタイルを含めるのが無理なのでdivを使ってます。
また本家でiconはSVGを使ってますが、日本語には漢字という便利なものがあるのでそれでいきます。正直日本だけで運用するなら画像より漢字の方が情報量と正確性があっていいと思います。
世界向けに作るならまぁ画像の方が便利そうではありますね。今回は小説家になろう限定なのでそれでいいと思う。
dataのパラメータとして、type、width、colorを用意しました。
線の種類、太さ、色をここに入れるようにしてます。
種類と、太さは3つのボタンで選べるようにして、colorはinputで選択できるようにします。
ということでrendersettingsを設定します。
renderSettings(){
const wrapper = document.createElement('div');
this.settings.forEach(tune =>{
let button = document.createElement('div');
button.classList.add(this.api.styles.settingsButton);
button.innerHTML = tune.icon;
wrapper.appendChild(button);
if(tune.name === 'solid' || tune.name === 'dashed' || tune.name === 'dotted'){
button.classList.toggle(this.api.styles.settingsButtonActive, tune.name === this.data.type);
}else if(tune.name === 'thin' || tune.name === 'medium' || tune.name === 'thick'){
button.classList.toggle(this.api.styles.settingsButtonActive, tune.name === this.data.width);
}else{
if(this._element.innerHTML !==''){
button.classList.toggle(this.api.styles.settingsButtonActive);
}
}
button.addEventListener('click', ()=>{
if(tune.name === 'solid' || tune.name === 'dashed' || tune.name === 'dotted'){
button.classList.add(this.api.styles.settingsButtonActive);
this._changeBorderType(tune.name);
wrapper.querySelectorAll(`.${this.api.styles.settingsButton}`).forEach(el => {
if(tune.name==='solid' && (el.innerHTML === '破線' || el.innerHTML === '点線') ){
el.classList.remove(this.api.styles.settingsButtonActive);
}else if(tune.name==='dashed' && (el.innerHTML === '実線' || el.innerHTML === '点線') ){
el.classList.remove(this.api.styles.settingsButtonActive);
}else if(tune.name==='dotted' && (el.innerHTML === '破線' || el.innerHTML === '実線') ){
el.classList.remove(this.api.styles.settingsButtonActive);
}
});
}else if(tune.name === 'thin' || tune.name === 'medium' || tune.name === 'thick'){
button.classList.add(this.api.styles.settingsButtonActive);
this._changeBorderWidth(tune.name);
wrapper.querySelectorAll(`.${this.api.styles.settingsButton}`).forEach(el => {
if(tune.name==='thin' && (el.innerHTML === '普通' || el.innerHTML === '太線') ){
el.classList.remove(this.api.styles.settingsButtonActive);
}else if(tune.name==='medium' && (el.innerHTML === '太線' || el.innerHTML === '細線') ){
el.classList.remove(this.api.styles.settingsButtonActive);
}else if(tune.name==='thick' && (el.innerHTML === '普通' || el.innerHTML === '細線') ){
el.classList.remove(this.api.styles.settingsButtonActive);
}
});
}else if(tune.name === 'color'){
button.classList.toggle(this.api.styles.settingsButtonActive);
if(button.classList.contains(this.api.styles.settingsButtonActive)){
this._showColorPicker();
}else{
this._hideColorPicker();
}
}else{
}
});
});
return wrapper;
}
_borderStyleSet(){
this._element.setAttribute("style", `border-top:${this.data.width} ${this.data.type} ${this.data.color};`);
}
_changeBorderWidth(tune){
this.data.width=tune;
this._borderStyleSet();
}
_changeBorderType(tune){
this.data.type=tune;
this._borderStyleSet();
}
_showColorPicker(){
const input = document.createElement('input');
input.type = 'color';
input.value = this.data.color;
this._element.appendChild(input);
input.onchange = () => {
this.data.color = input.value;
this._borderStyleSet();
};
}
_hideColorPicker(){
this._element.innerHTML='';
}
種類と太さはそれぞれ1つしか有効にならないように、どれかがアクティブになったらそれ以外を非アクティブ化してます。
正直、一回全部非アクティブにして、該当だけをアクティブにするのがキレイなコードかなと思います。今はちょっと汚い。そのうち直す(たぶん)
showColorPickerで線色を押すとカラーボックスが出てくるようにして、選んだ色を線に反映させるようにしています。
ボタンを非アクティブ化すれば_hideColorPickerで消します。
サニタイズとヴァリエーションは指定してません。というのもユーザーが値を入力できないからです。
ということで完成版です。
はい、そんなわけで区切り線の完成です。