CKeditor5第二篇,创建一个小插件
1、使用原生插件
首先我们先来说一下怎么用原生插件,CKEditor把原生的各种插件也都抽离出来了,减少了包的体积但是当代码用的多的时候可能要加载特别多的包,这个可以去使用他在线打包的那个工具,前面一篇文章也有说,其次就自定义安装加载了,需要什么加载什么的,我这边用NPM安装其他插件。
这里使用horizontal-line
插件示例,这里假定你已经安装并成功运行了一个最简单的CKEditor项目。
首先安装它npm install --save @ckeditor/ckeditor5-horizontal-lin
然后在app.js
里加载import HorizontalLine from '@ckeditor/ckeditor5-horizontal-line/src/horizontalline';
加载完之后注册到plugins
里面
ClassicEditor
.create(document.querySelector('#editor'), {
// .... 其他配置
plugins: [HorizontalLine],
toolbar: ['HorizontalLine']
})
// 创建成功的回调
.then(editor => {
// to do sth..
})
.catch(error => {
console.error(error.stack);
});
2、创建插件
创建一个很简单的插件作为实例,官网也有对应的实例:https://ckeditor.com/docs/ckeditor5/latest/framework/guides/creating-simple-plugin.html
我这里就不用这个了。
插件基本上就是生成下面的结构
<div class="testEdit">
<span class="edit">编辑</span><span class="delete">删除</span>
</div>
2.1 创建插件的基础
CKEditor创建插件要求尽量低耦合,然后文件管理可以一个文件也可以多个模块组合成一个插件,我这里就用多个模块来处理了。
具体代码的意义我会在注释里加上
在根目录创建一个block
文件夹,一般来说需要4个模块,第一个是入口文件。
// block/block.js
import blockedit from './blockedit'
import blockui from './blockui'
// 插件类,创建插件必须基于这个类扩展
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
// 实例化一个类,然后导出
export default class Block extends Plugin{
static get requires() {
return [blocked, blockui];
}
}
第二个是用来定义架构的文件,CKEditor创建元素需要先在代码里注册好架构模型,然后调用命令去生成元素,而不是直接插入一段HTML。
// block/blockedit.js
// 插件类,需要基于这个扩展
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
// 在这个文件里面创建命令
import command from './blockcommad.js';
export default class Blockedit extends Plugin {
init() {
// 定义架构的方法 规定元素的属性,这个属性不是HTML的属性,是CKEditor内使用的
this._defineSchema();
// 定义转换器 转换器是生成HTML元素的地方 将定义的模型生成HTML结构
this._defineConverters();
// 绑定一个事件 block
this.editor.commands.add('block', new command(this.editor));
}
// 具体参数看文档: https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_model_schema-Schema.html
_defineSchema(){
const schema = this.editor.model.schema;
// 创建一个模型,名字是 testEdit
schema.register('testEdit', {
// 是否为一个完整的对象,不可被回车拆分,意思是回车等行为都是在它自身容器内进行
isObject: true,
// 要求字符串或数组字符串,可以从其他地方继承
allowWhere: '$block',
})
schema.register('edit', {
isLimit: true,
// 在哪个地方使用
allowIn: 'testEdit',
// 设置改节点是块还是根,根可以回车
allowContentOf: '$block'
});
schema.register('delete', {
isLimit: true,
allowIn: 'testEdit',
// 设置改节点是块还是根,根可以回车
allowContentOf: '$block'
});
}
// 文档:https://ckeditor.com/docs/ckeditor5/latest/api/module_editor-classic_classiceditor-ClassicEditor.html#member-conversion
_defineConverters(){
const conversion = this.editor.conversion;
// 定义转换器 testEdit模型转换成视图 -> <div class="testEdit"></div>
conversion.elementToElement({
model: 'testEdit',
view: {
name: 'div',
classes: 'testEdit'
}
});
// 这些同理
conversion.elementToElement({
model: 'edit',
view: {
name: 'button',
classes: 'edit'
}
});
conversion.elementToElement({
model: 'delete',
view: {
name: 'button',
classes: 'delete'
}
});
}
}
关于CSS,CSS是需要额外挂载到HTML上的,这里定义类名。也可以在block/blockedit.js
里面import './css.css'
第三个文件UI文件,也是创建按钮的文件
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
// 这个模块是用来监听 click的
import ClickObserver from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver';
export default class BoxUI extends Plugin {
init() {
console.log('UI#init() got called');
const editor = this.editor;
const t = editor.t;
// “block”按钮必须在编辑器的UI组件中注册
// 将显示在工具栏中。
editor.ui.componentFactory.add('block', locale => {
const view = editor.editing.view;
const command = editor.commands.get('block');
// 使用模块
view.addObserver(ClickObserver);
const buttonView = new ButtonView(locale);
// 文档:https://ckeditor.com/docs/ckeditor5/latest/api/module_ui_button_buttonview-ButtonView.html
buttonView.set({
// 返回一段文字,用作label,当没有ICON的时候显示Babel
label: t('insert block'),
withText: true,
tooltip: true
});
// 将按钮的状态绑定到命令。
buttonView.bind('isOn', 'isEnabled').to(command, 'value', 'isEnabled');
// 单击(执行)按钮时执行命令。
this.listenTo(buttonView, 'execute', () => editor.execute('block'));
return buttonView;
});
}
}
第四个文件就是注册命令的文件了
// block/blockcommad.js
import Command from '@ckeditor/ckeditor5-core/src/command';
import Command from '@ckeditor/ckeditor5-core/src/command';
export default class InsertSimpleBoxCommand extends Command {
// 执行命令会调这个函数
execute() {
this.editor.model.change(writer => {
// 模型里面插入
// 文档:https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_model_model-Model.html
this.editor.model.insertContent(createSimpleBox(writer));
});
}
// 关于这个事件和上面事件或更多,查看文档:https://ckeditor.com/docs/ckeditor5/latest/api/module_engine_model_documentselection-DocumentSelection.html
refresh() {
const model = this.editor.model;
const selection = model.document.selection;
const allowedIn = model.schema.findAllowedParent(selection.getFirstPosition(), 'testEdit');
this.isEnabled = allowedIn !== null;
}
}
function createSimpleBox(writer) {
const testEdit = writer.createElement('testEdit');
const edit = writer.createElement('edit');
writer.insertText('编辑', writer.createPositionAt(edit, 0));
const deleteEl = writer.createElement('delete');
writer.insertText('删除', writer.createPositionAt(deleteEl, 0));
writer.append(edit, testEdit);
writer.append(deleteEl, testEdit);
return testEdit;
}
2.2 使用
文件都创建好我们去使用它
// app.js
// ... 假装这里有很多其他的引用
import Block from './block/block.js'
ClassicEditor
.create(document.querySelector('#editor'), {
plugins: [ HorizontalLine, Block],
toolbar: ['HorizontalLine', 'Block']
})
// 创建成功的回调
.then(editor => {
// ...
// 监听模块的点击事件
editor.editing.view.document.on('click', (evt, data) => {
if (data.domTarget.className == 'edit'){
// to do something
}
});
})
.catch(error => {
console.error(error.stack);
});
3、在外部调用命令
如果想在外面加一个按钮执行上面的命令,插入该组件的话可以定义一个全局变量存放CKEditor实例
// app.js
let myeditor = null;
ClassicEditor
.create(document.querySelector('#editor'), {
})
// 创建成功的回调
.then(editor => {
myeditor = editor;
})
.catch(error => {
console.error(error.stack);
});
// 在其他地方使用
document.getElementById('insertBlock').addEventListener('click', (evt, data) => {
myeditor.execute('block');
})