1、概述

上篇那个只是一个简单插件的demo,这篇我们来创建一个可正常编辑,换行的插件,因为按上篇那种方式创建出来,设置默认的文字之类的然后去进行编辑会有一些问题。

比如你想插入一个文字然后渲染成一下结构,文字可正常编辑换行

<div><p>default word</p></div>

但是你执行writer.insertText('default word', parent);之后插入的元素会渲染成下面的 (这是在command文件内执行的)

<div>default word</div>

这个时候如果你编辑或者干嘛,会生成下面的结构

<div>default word<p>你编辑的内容</p></div>

还有创建一个插件想选中或者怎样上篇是不能实现的,我们在这个插件里都会一一解决。

2、创建项目

初始化项目:

npm init -y

安装项目依赖

npm install --save postcss-loader@3 raw-loader@3  style-loader@1 webpack@4 webpack-cli@3

创建webpack.config.js文件

'use strict';
const path = require('path');
const { styles } = require('@ckeditor/ckeditor5-dev-utils');
module.exports = {// https://webpack.js.org/configuration/entry-context/entry: './app.js',// https://webpack.js.org/configuration/output/output: {path: path.resolve(__dirname, 'dist'),filename: 'bundle.js'},module: {rules: [{test: /ckeditor5-[^/\\]+[/\\]theme[/\\]icons[/\\][^/\\]+\.svg$/,use: ['raw-loader']},{test: /ckeditor5-[^/\\]+[/\\]theme[/\\].+\.css$/,use: [{loader: 'style-loader',options: {injectType: 'singletonStyleTag'}},{loader: 'postcss-loader',options: styles.getPostCssConfig({themeImporter: {themePath: require.resolve('@ckeditor/ckeditor5-theme-lark')},minify: true})}]}]},// Useful for debugging.devtool: 'source-map',// By default webpack logs warnings if the bundle is bigger than 200kb.performance: { hints: false }
};

安装编辑器依赖

 npm install --save @ckeditor/ckeditor5-dev-utils @ckeditor/ckeditor5-editor-classic @ckeditor/ckeditor5-essentials @ckeditor/ckeditor5-paragraph @ckeditor/ckeditor5-basic-styles @ckeditor/ckeditor5-theme-lark

创建APP.js

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';ClassicEditor.create(document.querySelector('#editor'), {plugins: [Essentials, Paragraph, Bold, Italic],toolbar: ['bold', 'italic']}).then(editor => {console.log('Editor was initialized', editor);}).catch(error => {console.error(error.stack);});

修改package.json,因为要经常编译,所以我加了--watch

  "scripts": {"test": "echo \"Error: no test specified\" && exit 1","build": "webpack --mode development --watch"}

然后构建

npm run build

创建HTML文件index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><title>CKEditor 5 Framework – Quick start</title>
</head>
<body><div id="editor"><p>Editor content goes here.</p></div><script src="dist/bundle.js"></script>
</body>
</html>

然后浏览器打开HTML,这个时候就展示出了一个基本的编辑器最初的模样~以上都是基于文档:https://ckeditor.com/docs/ckeditor5/latest/framework/guides/quick-start.html 现在我们来正式开始创建插件

3、创建插件

我们来创建一个提示框插件,插入一个提示框,上面有一个提示的标题,中间部分内容可编辑,输入想要的文字

首先我们来创建一个tip文件夹,在里面创建四个文件tip.jstipEdit.js,tipUi.js,tipCommand.js,不清楚为什么要创建这四个的可以看前面的。

3.1 编写tip.js

import ui from './tipUi'
import edit from './tipEdit'
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import Widget from '@ckeditor/ckeditor5-widget/src/widget';export default class writeBox extends Plugin {static get requires() {return [ui, edit, Widget];}
}

3.2 确定HTML结构

在编辑edit.js文件之前,我们来确定一下HTML结构,方便写模型及转换器,如果脑子好的话也可以不先写HTML模板,直接开始操作,我脑子不行,所以先写个HTML结构来明确DOM结构

<div class="edit-item-box box"><div class="tip-content-box"><div class="tip-content" ><p>1. 多喝水</p><p>2. 多喝热水</p></div><div class="close"><span class="closeIcon">x</span></div><div class="tip-title"><p>提示的标题</p></div></div>
</div>

CSS样式,真正写插件的时候写成单独的文件,然后引入到edit.js里面即可,这里我就直接扔index.html文件里了

<style>.tip-content-box{width: 100%;position: relative;border: 1px solid #ccc;padding: 6px 10px;box-sizing: border-box;border-radius: 4px;cursor: pointer;}.tip-content-box:hover .close{display: block;}.tip-title{background: white;text-align: center;font-size: 14px;position: absolute;top: -10px;cursor: text;height: 20px;left: 50%;transform: translateX(-50%);}.edit-item-box{margin: 20px 0;}.tip-content{font-size: 14px;cursor: text;}.close{position: absolute;top: -7px;right: -4px;width: 14px;height: 14px;color:white;background:#ccc;text-align: center;line-height: 12px;border-radius: 50%;display: none;z-index: 99;cursor: pointer;}.tip-title p{padding: 0 10px;color: #365f93;font-weight: bold;transform: translateY(-15px);}.closeIcon{transform: scaleX(1.2) translateX(0px) translateY(-1.5px);display: inline-block;font-size: 12px;}
</style>

3.3 编写edit.js

先来安装一个插件npm install @ckeditor/ckeditor5-widget --save,安装完之后记得重新build

import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import command from './tipCommand';
import { toWidget } from '@ckeditor/ckeditor5-widget/src/utils';export default class tipEdit extends Plugin {init() {this._defineSchema();this._defineConverters();this.editor.commands.add('tip', new command(this.editor));}_defineSchema() {const schema = this.editor.model.schema;// 注册模型schema.register('editItemBox', {// 是否为一个完整的对象,不可被回车拆分,意思是回车等行为都是在它自身容器内进行isObject: true,allowWhere: '$block',})schema.register('tipContentBox', {allowWhere: '$block',isLimit: true,allowIn: 'editItemBox',})schema.register('tipTitle', {isLimit: true,allowIn: 'tipContentBox',isObject: true,allowContentOf: '$root'})schema.register('tipContent', {isLimit: true,allowIn: 'tipContentBox',isObject: true,allowContentOf: '$root'})schema.register('tipClose', {isLimit: true,allowIn: 'tipContentBox',allowContentOf: '$block'});schema.register('tipSpan', {isLimit: true,allowIn: 'tipClose',allowContentOf: '$block'});}_defineConverters() {const conversion = this.editor.conversion;// 使用editingDowncast数据管道// 创建一个元件// model的名称整个项目需要唯一conversion.for('editingDowncast').elementToElement({model: 'editItemBox',view: (modelItem, viewWriter) => {const viewWrapper = viewWriter.createContainerElement('div');viewWriter.addClass('edit-item-box', viewWrapper);return toHorizontalLineWidget(viewWrapper, viewWriter);}});// 创建一个普通的转换器conversion.elementToElement({model: 'tipContentBox',view: {name: 'div',classes: 'tip-content-box'}});conversion.elementToElement({model: 'tipClose',view: {name: 'div',classes: 'close'}});conversion.elementToElement({model: 'tipSpan',view: {name: 'span',classes: 'closeIcon'}});// 创建一个普通的可编辑可选择的转换器conversion.for('editingDowncast').elementToElement({model: 'tipTitle',view: (modelItem, writer) => {const nested = writer.createEditableElement('div', { class: 'tip-title' });writer.setAttribute('contenteditable', nested.isReadOnly ? 'false' : 'true', nested);nested.on('change:isReadOnly', (evt, property, is) => {writer.setAttribute('contenteditable', is ? 'false' : 'true', nested);});return nested;}});conversion.for('editingDowncast').elementToElement({model: 'tipContent',view: (modelItem, writer) => {const nested = writer.createEditableElement('div', { class: 'tip-content' });writer.setAttribute('contenteditable', nested.isReadOnly ? 'false' : 'true', nested);nested.on('change:isReadOnly', (evt, property, is) => {writer.setAttribute('contenteditable', is ? 'false' : 'true', nested);});return nested;}});}
}// 这个地方转换原件之后就可以进行选择之类的操作,但是这个方法是不能编辑的,因为源码里面给设置false了,具体操作可以看源码
// 源码:node_modules/@ckeditor/ckeditor5-widget/src/utils.js  => export function toWidget( element, writer, options = {} ) {}function toHorizontalLineWidget(viewElement, writer, label) {// 在当前元素上设置自定义属性,  key  value  targetElementwriter.setCustomProperty('tip', true, viewElement);// toWidget 转化为元件return toWidget(viewElement, writer, { label });
}

3.4 编写tipUi.js

import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
import ClickObserver from '@ckeditor/ckeditor5-engine/src/view/observer/clickobserver';// import icon from './icons/insert.svg';export default class tipUi extends Plugin {init() {const editor = this.editor;const t = editor.t;editor.ui.componentFactory.add('tip', locale => {const view = editor.editing.view;const command = editor.commands.get('tip');view.addObserver(ClickObserver);const buttonView = new ButtonView(locale);buttonView.set({label: t('插入tip'),// 如果要用icon可以把这里打开,等会儿再说icon的问题// icon: icon,tooltip: true,// true表示使用文字,false表示用icon,可以一起用,但是会并列withText:true});buttonView.bind('isOn', 'tip').to(command, 'value', 'isEnabled');this.listenTo(buttonView, 'execute', () => editor.execute('tip'));return buttonView;});}
}

3.5 编写tipCommand.js

import Command from '@ckeditor/ckeditor5-core/src/command';export default class tipCommand extends Command {execute() {this.editor.model.change(writer => {this.editor.model.insertContent(createTip(writer));});}
}function createTip(writer) {const editItemBox = writer.createElement('editItemBox');const tipContentBox = writer.createElement('tipContentBox');const tipTitle = writer.createElement('tipTitle');const tipContent = writer.createElement('tipContent');// 创建关闭按钮const tipClose = writer.createElement('tipClose');const span = writer.createElement('tipSpan');writer.insertText('x', writer.createPositionAt(span, 0));writer.append(span, tipClose);// 创建标题const paragraph = writer.createElement('paragraph');writer.appendText('提示的标题', paragraph);// 创建内容const paragraph1 = writer.createElement('paragraph');writer.appendText('1. 多喝水', paragraph1);const paragraph2 = writer.createElement('paragraph');writer.appendText('2. 多喝热水', paragraph2);writer.insert(paragraph, tipTitle, 0);writer.insert(paragraph1, tipContent, 0);writer.insert(paragraph2, tipContent, 1);writer.append(tipContent, tipContentBox);writer.append(tipClose, tipContentBox);writer.append(tipTitle, tipContentBox);writer.append(tipContentBox, editItemBox);return editItemBox;
}

3.5 注册tip插件

import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
import tip from './tip/tip'ClassicEditor.create(document.querySelector('#editor'), {plugins: [Essentials, Paragraph, Bold, tip,  Italic, tip],toolbar: ['bold', 'italic', 'tip']}).then(editor => {// 注册删除事件editor.editing.view.document.on('click', (evt, data) => {if (data.domTarget.className == 'closeIcon' || data.domTarget.className == 'close' ) {const selection = editor.model.document.selection;editor.model.document.model.deleteContent(selection)};});}).catch(error => {console.error(error.stack);});

4、重置样式

在style标签里面加入下面代码就重置了

/* 选中及鼠标移入样式重置 */
.ck .ck-widget_selected{background: rgba(72,145,255,.16);
}
.ck .ck-widget{outline-style: none;outline-color:transparent !important;transition: none !important;
}
.ck .ck-widget:hover{outline-style:dashed;outline-color:#ccc !important;outline-width:1.2px;
}
.ck .ck-widget_selected.ck-widget{outline-style:dashed !important;outline-color:#ccc !important;outline-width:1.2px !important;
}
.tip-title, .tip-content, .tip-content-box{outline:none !important;
}
/* 功能按钮区域移入变色 */
.ck.ck-button:not(.ck-disabled):hover, a.ck.ck-button:not(.ck-disabled):hover{color: green;
}
/* 重置宽高 */
.ck-editor__editable {min-height: 300px;width: 800px !important;
}
.ck-toolbar, .ck-sticky-panel__content{width: 821px !important;
}

CKEditor5系列三:创建一个功能相对完善的插件相关推荐

  1. 学习ASP.NET Core Razor 编程系列三——创建数据表及创建项目基本页面

    原文:学习ASP.NET Core Razor 编程系列三--创建数据表及创建项目基本页面 学习ASP.NET Core Razor 编程系列目录 学习ASP.NET Core Razor 编程系列一 ...

  2. WF4.0入门系列1——创建一个简单的工作流

    WF4.0入门系列1--创建一个简单的工作流 打开VS2010,选择文件-新建-项目,选择Workflow项 工作流台应用程序,在名称处输入chapter01,选择合适的位置,这里默认,单击确定. V ...

  3. Foxit MobilePDF SDK_如何快速创建一个功能丰富的PDF 阅读器

    一直以来,我都比较关注福昕的PDF SDK套件,这几天稍微有时间看了下最新发布的4.0版本,然后呢,然后呢,我就被深深的震惊到了,为甚么? 因为这个版本可以很轻松很容易的创建一个功能超级丰富的PDF阅 ...

  4. 安卓入门系列-02创建一个项目

    利用Android Studio创建一个项目 说明 Android Studio 是项目式开发,和eclipse有所不同,需要习惯. 创建项目 点击第一行,创建一个新项目. 在1处填入项目名称,2处填 ...

  5. Minecraft 1.12.2模组开发(三) 创建一个物品(item)+物品栏

    本次我们来介绍一下如何创建一个基础物品: 演示包名:com.Joy187.newmod (之后都简称为包名) 1. 新建 -> 创建一个 包名.init 包 2.在刚刚创建的init包中新建一个 ...

  6. SpringCloud系列(三) 创建Euraka客户端

    公众号 关注公众号和我一起学习哦! 创建客户端的目的只是 确保可以正确无误的连接上 注册中心 1. 创建一个新的模块 选择SpringBoot项目 名称为: spring-cloud-netflix- ...

  7. 一、如何创建一个状态栏扩展(火狐插件扩展开发教程)

    因为项目的需要,一些功能需要在火狐上面实现,一点也不了解火狐插件的开发,网上的中文资料也少得可怜, 没办法,只好自己研究一下英文文档,慢慢开发了,在这里备份一下. 学编程,当然是从Hellow,wor ...

  8. java 广告插件_徒手创建一个chrome扩展-屏蔽广告插件

    创建一个文件夹,创建以下文件 maniftest.json  background.js和 icon图片 maniftest.json文件设置如下 { "name": " ...

  9. Office 365 系列三 ------ 创建Office 365普通账号

    当我们购买或者试用Office 365的时候,微软或者世纪互联会发一封邮件给我们,里面就只有管理员的账号,那么作为我们IT 管理员应该给员工创建账号, 创建的过程如下: 一.登陆: http://po ...

  10. CKEditor5系列一:安装及使用

    1.使用 官网:https://ckeditor.com/ckeditor-5/ 如果要直接使用官方版本,不自己做扩展只是简单使用的话可以使用在线构建,选择你喜欢的功能然后构建,直接下载使用就好了. ...

最新文章

  1. 深度盘点:详细介绍机器学习中的7种交叉验证方法!
  2. ansible的条件判断、迭代执行、tags
  3. Tcpdump源码分析系列7:main函数
  4. 华为交换机 路由器 常用命令
  5. php 表单录入,PHP 表单和用户输入
  6. win10雷电3接口驱动_雷电3 ,高速又多用,尽在一个接口
  7. hdu 4738 Caocao's Bridges 求无向图的桥【Tarjan】
  8. python 日期 格式转换 英文_量化数据预处理-中文日期(含)转英文日期
  9. 大蚂蚁在64位系统下,右键没有快发的解决方案
  10. 射线检测(Summary)
  11. Python之Pandas库
  12. 【Java】在eclipse中使用maven进行项目构建 入门篇
  13. jquery操作checkbox最佳方法
  14. MATLAB中好用的快捷键
  15. 【C语言数据结构】数组
  16. 计算机软件工程师考试试题,计算机考试软件工程师试题
  17. ch3 系统总线(一)
  18. 如何在供应链金融中防范风险?
  19. 【kubernetes/k8s源码分析】 kubelet cgroup 资源预留源码分析
  20. HDU 5446 Unknown Treasure(Lucas定理+CRT)

热门文章

  1. 计算机编程语言用英语怎么说,编程用英语怎么说
  2. 三次握手,为什么不是两次,也不是四次
  3. 浏览器报ScriptResource.axd异常
  4. 2016 server win 假死_Windows10出现假死的几种表现形式及对应解决方案
  5. 计算机消极影响英语作文,大学英语作文:电脑游戏的危害
  6. Windows文件映射
  7. 业界 | 数据科学家要先学逻辑回归?图样图森破!
  8. nginx反向代理实现直接域名访问
  9. 如何打造一个顶尖的精确营销系统?
  10. go语法 — 多路选择操作符 select的用法