├── .babelrc ├── .eslintrc ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── dist ├── vue-ckeditor5.js └── vue-ckeditor5.js.map ├── examples ├── custom-editors │ ├── ckeditor.js │ ├── ckeditor.js.map │ └── translations │ │ └── ru.js ├── example1 │ └── index.html ├── example2 │ └── index.html ├── example4 │ ├── index.html │ └── style.css ├── example5 │ └── index.html └── example6 │ └── index.html ├── package.json ├── src ├── index.js └── vue-ckeditor.js └── webpack.config.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["@babel/preset-env"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "extends": "standard", 4 | "globals": { 5 | "VERSION": true 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | *.html text eol=lf 4 | *.js text eol=lf 5 | *.json text eol=lf 6 | *.md text eol=lf 7 | .babelrc text eol=lf 8 | .eslintrc text eol=lf 9 | .gitattributes text eol=lf 10 | .gitignore text eol=lf 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea 3 | .git 4 | 5 | package-lock.json 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 igorxut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

VueCkeditor5

2 | 3 | [![License](https://img.shields.io/github/license/mashape/apistatus.svg)](https://github.com/igorxut/vue-ckeditor5/blob/master/LICENSE) 4 | [![NPM version](https://img.shields.io/npm/v/vue-ckeditor5.svg)](https://www.npmjs.com/package/vue-ckeditor5) 5 | [![File size](https://img.shields.io/github/size/igorxut/vue-ckeditor5/dist/vue-ckeditor5.js.svg)](https://github.com/igorxut/vue-ckeditor5/blob/master/dist/vue-ckeditor5.js) 6 | 7 |

Description

8 | 9 |
10 | Vue 2 11 | CKEditor 5 12 |
13 | 14 |

Component CKEditor 5 for Vue 2.

15 | 16 |

Project is outdated. See official release.

17 | 18 |

Installation

19 | 20 |

NMP

21 | 22 | ```shell 23 | npm install vue-ckeditor5 24 | ``` 25 | 26 |

Manual

27 | 28 |

Download file from repository, paste it in your project and insert path to it in your page by code: 29 | 30 | ```html 31 | 32 | ``` 33 |

34 | 35 |

36 | 37 |

Usage

38 | 39 |

How to

40 | 41 |

You must paste CKEditor's 5 implementations to vue component. You can use even custom build of CKEditor 5.

42 | 43 |

See examples.

44 | 45 |

First way - Global

46 | 47 |

You can register component globaly by plugin (recommended):

48 | 49 | ```javascript 50 | import Vue from 'vue' 51 | import ClassicEditor from '@ckeditor/ckeditor5-build-classic' 52 | import VueCkeditor from 'vue-ckeditor5' 53 | 54 | const options = { 55 | editors: { 56 | classic: ClassicEditor, 57 | }, 58 | name: 'ckeditor' 59 | } 60 | 61 | Vue.use(VueCkeditor.plugin, options); 62 | ``` 63 | 64 |

Then you can use the component in your template:

65 | 66 | ```html 67 | 68 | ``` 69 | 70 |
Plugin options
71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 |
propertytyperequireddefaultdescription
editorsObjecttrue 89 | Map of editors: 90 |
    91 |
  • value - CKEditor 5 implementation
  • 92 |
  • key - alias for it (for prop 'type')
  • 93 |
94 |
nameStringfalse'vue-ckeditor'Name of component.
105 | 106 |

Second way - Local

107 | 108 |

You can register component localy:

109 | 110 | ```javascript 111 | import Vue from 'vue' 112 | import ClassicEditor from '@ckeditor/ckeditor5-build-classic' 113 | import VueCkeditor from 'vue-ckeditor5' 114 | 115 | new Vue({ 116 | el: '#app', 117 | components: { 118 | 'vue-ckeditor': VueCkeditor.component 119 | }, 120 | data: { 121 | value1: 'hello', 122 | value2: 'world', 123 | editors: { 124 | classic: ClassicEditor 125 | } 126 | }, 127 | template: 128 | `` 129 | }) 130 | ``` 131 | 132 |

Component's props

133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 |
proptyperequireddefaultdescription
configObjectfalse{language:'en'}Object with config properties for CKEditor 5 instance.
editorsObjectfalse{} 158 | Map of editors: 159 |
    160 |
  • value - CKEditor 5 implementation
  • 161 |
  • key - alias for it (for prop 'type')
  • 162 |
163 |
emptyValuefalseSet if you want to change the 'v-model' value of emptiness editor.
readonlyBooleanfalsefalseRead-only mode for CKEditor 5 instance.
tagStringfalsedivHTMLElement for rendering.
toolbarContainerStringfalsenullCSS-selector of DOM-element for CKEditor toolbar. The element is searched by Document.querySelector().
typeStringtrueKey for CKEditor 5 implementation of 'editors' prop.
valueStringtrue''Value for data bindings. Use 'v-model' for it.
209 | 210 |

Normal HTML attributes

211 | 212 |

You can bind normal HTML attributes to component like this (data-api for example):

213 | 214 | ```html 215 | 216 | ``` 217 | 218 |

Component's events

219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 237 | 238 | 239 | 240 | 241 | 247 | 248 | 249 | 250 | 251 | 263 | 264 | 265 | 266 |
nameparametersdescription
ready(instance) 232 |
233 |
instance
234 |
Instance of CKEditor.
235 |
236 |
Instance of CKEditor is ready.
destroy(instance) 242 |
243 |
instance
244 |
Instance of CKEditor.
245 |
246 |
Instance of CKEditor is destroyed.
input(newValue, instance, eventInfo, batch) 252 |
253 |
newValue
254 |
New value of CKEditor's data.
255 |
instance
256 |
Instance of CKEditor.
257 |
eventInfo
258 |
An object containing information about the fired event.
259 |
batch
260 |
The batch that was used in the executed changes block.
261 |
262 |
Data is changed.
267 | 268 |

Events from engine.view.document (check example5).

269 | 270 |

License

271 | 272 |

MIT

273 | -------------------------------------------------------------------------------- /dist/vue-ckeditor5.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.VueCkeditor=t():e.VueCkeditor=t()}(window,(function(){return function(e){var t={};function n(r){if(t[r])return t[r].exports;var o=t[r]={i:r,l:!1,exports:{}};return e[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=e,n.c=t,n.d=function(e,t,r){n.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:r})},n.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.t=function(e,t){if(1&t&&(e=n(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)n.d(r,o,function(t){return e[t]}.bind(null,o));return r},n.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return n.d(t,"a",t),t},n.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},n.p="",n(n.s=0)}([function(e,t,n){"use strict";n.r(t);var r={name:"vue-ckeditor",render:function(e){return e(this.tag,{attrs:this.$attrs})},props:{config:{default:function(){return{language:"en"}},required:!1,type:Object},editors:{default:function(){return{}},required:!1,type:Object},readonly:{default:function(){return!1},required:!1,type:Boolean},emptyValue:{required:!1,type:String},tag:{default:function(){return"div"},required:!1,type:String},toolbarContainer:{default:function(){return null},required:!1,type:String},type:{required:!0,type:String},value:{default:function(){return""},required:!1,type:String}},data:function(){return{instance:null}},watch:{value:function(e){var t=this.instance;null==t||e===t.getData()||this.emptyValueProvided&&e===this.emptyValue||t.setData(e)}},computed:{emptyValueProvided:function(){return Object.prototype.hasOwnProperty.call(this.$options.propsData,"emptyValue")},isEmpty:function(){var e=this.instance.model.document;return!e.model.hasContent(e.getRoot())}},methods:{create:function(){var e=this;if(null==this.instance){var t=this.type,n=this.$VueCkeditorEditors||this.editors;if(!Object.keys(n).length)throw new Error("There are no CKEditor 5 implementations.");var r=n[t];if(null==r)throw new Error("Wrong key '".concat(t,"'. Allowed keys: ").concat(Object.keys(n)));r.create(this.$el,this.config).then((function(t){e.instance=t;var n=e.instance;e.createToolbarContainer(),e.setEventListeners(),n.isReadOnly=e.readonly,n.setData(e.value),e.$emit("ready",n)})).catch((function(e){console.log(e)}))}},createToolbarContainer:function(){var e=this.instance,t=this.toolbarContainer;if(null!=t&&null!=e){var n=document.querySelector(t);null!=n&&n.appendChild(e.ui.view.toolbar.element)}},destroy:function(){var e=this.instance;null!=e&&(e.destroy(),this.$emit("destroy",e))},setEventListeners:function(){var e=this,t=this.instance;if(null!=t){t.model.document.on("change:data",(function(){var n=t.getData();if(e.value!==n){e.emptyValueProvided&&e.isEmpty&&(n=e.emptyValue);for(var r=arguments.length,o=new Array(r),i=0;i {\n return {\n language: 'en'\n }\n },\n required: false,\n type: Object\n },\n\n editors: {\n default: () => {\n return {}\n },\n required: false,\n type: Object\n },\n\n readonly: {\n default: () => false,\n required: false,\n type: Boolean\n },\n\n emptyValue: {\n required: false,\n type: String\n },\n\n tag: {\n default: () => 'div',\n required: false,\n type: String\n },\n\n toolbarContainer: {\n default: () => null,\n required: false,\n type: String\n },\n\n type: {\n required: true,\n type: String\n },\n\n value: {\n default: () => '',\n required: false,\n type: String\n }\n },\n\n data () {\n return {\n instance: null\n }\n },\n\n watch: {\n value (newValue) {\n const instance = this.instance\n\n if (\n instance != null &&\n newValue !== instance.getData() &&\n !(\n this.emptyValueProvided &&\n newValue === this.emptyValue\n )\n ) {\n instance.setData(newValue)\n }\n }\n },\n\n computed: {\n emptyValueProvided () {\n return Object.prototype.hasOwnProperty.call(this.$options.propsData, 'emptyValue')\n },\n isEmpty () {\n const document = this.instance.model.document\n return !document.model.hasContent(document.getRoot())\n }\n },\n\n methods: {\n create () {\n if (this.instance == null) {\n const type = this.type\n const editors = this.$VueCkeditorEditors || this.editors\n\n if (!Object.keys(editors).length) {\n throw new Error('There are no CKEditor 5 implementations.')\n }\n\n const editor = editors[type]\n\n if (editor == null) {\n throw new Error(`Wrong key '${type}'. Allowed keys: ${Object.keys(editors)}`)\n }\n\n editor\n .create(this.$el, this.config)\n .then(editor => {\n this.instance = editor\n const instance = this.instance\n\n this.createToolbarContainer()\n\n this.setEventListeners()\n\n instance.isReadOnly = this.readonly\n instance.setData(this.value)\n\n this.$emit('ready', instance)\n })\n .catch(error => {\n console.log(error)\n })\n }\n },\n createToolbarContainer () {\n const instance = this.instance\n const toolbarContainer = this.toolbarContainer\n\n if (\n toolbarContainer != null &&\n instance != null\n ) {\n const toolbarContainerElement = document.querySelector(toolbarContainer)\n\n if (toolbarContainerElement != null) {\n toolbarContainerElement.appendChild(instance.ui.view.toolbar.element)\n }\n }\n },\n destroy () {\n const instance = this.instance\n\n if (instance != null) {\n instance.destroy()\n this.$emit('destroy', instance)\n }\n },\n setEventListeners () {\n const instance = this.instance\n\n if (instance != null) {\n instance.model.document.on('change:data', (...args) => {\n let newValue = instance.getData()\n\n if (this.value !== newValue) {\n if (\n this.emptyValueProvided &&\n this.isEmpty\n ) {\n newValue = this.emptyValue\n }\n\n this.$emit('input', newValue, instance, ...args)\n }\n })\n\n const editingViewDocument = instance.editing.view.document\n const events = editingViewDocument._events\n for (const key of Object.keys(events)) {\n editingViewDocument.on(key, (...args) => {\n this.$emit(key, instance, ...args)\n })\n }\n }\n }\n },\n\n mounted () {\n this.create()\n },\n\n beforeDestroy () {\n this.destroy()\n }\n}\n","import VueCkeditorComponent from './vue-ckeditor'\n\nconst VueCkeditor = {\n version: VERSION,\n component: VueCkeditorComponent,\n plugin: {\n install (Vue, { name, editors }) {\n Vue.prototype.$VueCkeditorEditors = editors || {}\n\n Vue.component(name || VueCkeditorComponent.name, VueCkeditorComponent)\n }\n }\n}\n\nexport default VueCkeditor\n"],"sourceRoot":""} -------------------------------------------------------------------------------- /examples/custom-editors/translations/ru.js: -------------------------------------------------------------------------------- 1 | (function(d){d['ru']=Object.assign(d['ru']||{},{a:"Невозможно загрузить файл",b:"Image toolbar",c:"Table toolbar",d:"Выравнивание по левому краю",e:"Выравнивание по правому краю",f:"Выравнивание по центру",g:"Выравнивание по ширине",h:"Выравнивание текста",i:"Выравнивание",j:"Жирный",k:"Вставьте изображение или файл",l:"Исходный код",m:"Зачеркнутый",n:"медиа-виджет",o:"Идёт загрузка",p:"Вставить таблицу",q:"Столбец заголовков",r:"Вставить столбец слева",s:"Вставить столбец справа",t:"Удалить столбец",u:"Столбец",v:"Строка заголовков",w:"Вставить строку ниже",x:"Вставить строку выше",y:"Удалить строку",z:"Строка",aa:"Объединить с ячейкой сверху",ab:"Объединить с ячейкой справа",ac:"Объединить с ячейкой снизу",ad:"Объединить с ячейкой слева",ae:"Разделить ячейку вертикально",af:"Разделить ячейку горизонтально",ag:"Объединить ячейки",ah:"Нумерованный список",ai:"Маркированный список",aj:"Вставить медиа",ak:"URL не должен быть пустым.",al:"Этот URL медиа не поддерживается.",am:"Увеличить отступ",an:"Уменьшить отступ",ao:"Загрузка не выполнена",ap:"Вставить изображение",aq:"Ссылка",ar:"Подпись к изображению",as:"Оригинальный размер изображения",at:"Боковое изображение",au:"Выравнивание по левому краю",av:"Выравнивание по центру",aw:"Выравнивание по правому краю",ax:"Виджет изображений",ay:"Выделение жёлтым маркером",az:"Выделение зелёным маркером",ba:"Выделение розовым маркером",bb:"Выделение синим маркером",bc:"Красный цвет текста",bd:"Зеленый цвет текста",be:"Убрать выделение",bf:"Выделить",bg:"Text highlight toolbar",bh:"Не удалось получить URL с измененным размером изображения.",bi:"Выбор изображения с измененным размером не удался",bj:"Нельзя вставить изображение на текущую позицию.",bk:"Вставка изображения не удалась",bl:"Widget toolbar",bm:"Редактор",bn:"Editor toolbar",bo:"Show more items",bp:"Dropdown toolbar",bq:"Редактор, %0",br:"Open in a new tab",bs:"Downloadable",bt:"Редактировать альтернативный текст",bu:"Убрать ссылку",bv:"Редактировать ссылку",bw:"Открыть ссылку в новой вкладке",bx:"Для этой ссылки не установлен адрес URL",by:"Сохранить",bz:"Отмена",ca:"Ссылка URL",cb:"%0 of %1",cc:"Previous",cd:"Next",ce:"Альтернативный текст",cf:"Вставьте URL медиа в поле ввода.",cg:"Подсказка: Вставьте URL в контент для быстрого включения.",ch:"URL медиа",ci:"Отменить",cj:"Повторить",ck:"Выбрать заголовок",cl:"Заголовок",cm:"Размер шрифта",cn:"По умолчанию",co:"Очень мелкий",cp:"Мелкий",cq:"Крупный",cr:"Очень крупный",cs:"Цитата",ct:"Семейство шрифтов",cu:"Подчеркнутый",cv:"Курсив",cw:"Параграф",cx:"Заголовок 1",cy:"Заголовок 2",cz:"Заголовок 3",da:"Заголовок 4",db:"Заголовок 5",dc:"Заголовок 6",dd:"Чёрный",de:"Тёмно-серый",df:"Серый",dg:"Светло-серый",dh:"Белый",di:"Красный",dj:"Оранжевый",dk:"Жёлтый",dl:"Салатовый",dm:"Зелёный",dn:"Аквамариновый",do:"Бирюзовый",dp:"Голубой",dq:"Синий",dr:"Фиолетовый"})})(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={})); -------------------------------------------------------------------------------- /examples/example1/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Custom CKEditor example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /examples/example2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | List Rendering 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /examples/example4/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DecoupledEditor example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /examples/example4/style.css: -------------------------------------------------------------------------------- 1 | .document-editor { 2 | border: 1px solid var(--ck-color-base-border); 3 | border-radius: var(--ck-border-radius); 4 | 5 | /* Set vertical boundaries for the document editor. */ 6 | max-height: 700px; 7 | 8 | /* This element is a flex container for easier rendering. */ 9 | display: flex; 10 | flex-flow: column nowrap; 11 | } 12 | 13 | .document-editor__toolbar { 14 | /* Make sure the toolbar container is always above the editable. */ 15 | z-index: 1; 16 | 17 | /* Create the illusion of the toolbar floating over the editable. */ 18 | box-shadow: 0 0 5px hsla( 0,0%,0%,.2 ); 19 | 20 | /* Use the CKEditor CSS variables to keep the UI consistent. */ 21 | border-bottom: 1px solid var(--ck-color-toolbar-border); 22 | } 23 | 24 | /* Adjust the look of the toolbar inside the container. */ 25 | .document-editor__toolbar .ck-toolbar { 26 | border: 0; 27 | border-radius: 0; 28 | } 29 | 30 | /* Make the editable container look like the inside of a native word processor application. */ 31 | .document-editor__editable-container { 32 | padding: calc( 2 * var(--ck-spacing-large) ); 33 | background: var(--ck-color-base-foreground); 34 | 35 | /* Make it possible to scroll the "page" of the edited content. */ 36 | overflow-y: scroll; 37 | } 38 | 39 | .document-editor__editable-container .ck-editor__editable { 40 | /* Set the dimensions of the "page". */ 41 | width: 15.8cm; 42 | min-height: 21cm; 43 | 44 | /* Keep the "page" off the boundaries of the container. */ 45 | padding: 1cm 2cm 2cm; 46 | 47 | border: 1px hsl( 0,0%,82.7% ) solid; 48 | border-radius: var(--ck-border-radius); 49 | background: white; 50 | 51 | /* The "page" should cast a slight shadow (3D illusion). */ 52 | box-shadow: 0 0 5px hsla( 0,0%,0%,.1 ); 53 | 54 | /* Center the "page". */ 55 | margin: 0 auto; 56 | } 57 | 58 | /* Set the default font for the "page" of the content. */ 59 | .document-editor .ck-content, 60 | .document-editor .ck-heading-dropdown .ck-list .ck-button__label { 61 | font: 16px/1.6 "Helvetica Neue", Helvetica, Arial, sans-serif; 62 | } 63 | 64 | /* Adjust the headings dropdown to host some larger heading styles. */ 65 | .document-editor .ck-heading-dropdown .ck-list .ck-button__label { 66 | line-height: calc( 1.7 * var(--ck-line-height-base) * var(--ck-font-size-base) ); 67 | min-width: 6em; 68 | } 69 | 70 | /* Scale down all heading previews because they are way too big to be presented in the UI. 71 | Preserve the relative scale, though. */ 72 | .document-editor .ck-heading-dropdown .ck-list .ck-button:not(.ck-heading_paragraph) .ck-button__label { 73 | transform: scale(0.8); 74 | transform-origin: left; 75 | } 76 | 77 | /* Set the styles for "Heading 1". */ 78 | .document-editor .ck-content h2, 79 | .document-editor .ck-heading-dropdown .ck-heading_heading1 .ck-button__label { 80 | font-size: 2.18em; 81 | font-weight: normal; 82 | } 83 | 84 | .document-editor .ck-content h2 { 85 | line-height: 1.37em; 86 | padding-top: .342em; 87 | margin-bottom: .142em; 88 | } 89 | 90 | /* Set the styles for "Heading 2". */ 91 | .document-editor .ck-content h3, 92 | .document-editor .ck-heading-dropdown .ck-heading_heading2 .ck-button__label { 93 | font-size: 1.75em; 94 | font-weight: normal; 95 | color: hsl( 203, 100%, 50% ); 96 | } 97 | 98 | .document-editor .ck-heading-dropdown .ck-heading_heading2.ck-on .ck-button__label { 99 | color: var(--ck-color-list-button-on-text); 100 | } 101 | 102 | /* Set the styles for "Heading 2". */ 103 | .document-editor .ck-content h3 { 104 | line-height: 1.86em; 105 | padding-top: .171em; 106 | margin-bottom: .357em; 107 | } 108 | 109 | /* Set the styles for "Heading 3". */ 110 | .document-editor .ck-content h4, 111 | .document-editor .ck-heading-dropdown .ck-heading_heading3 .ck-button__label { 112 | font-size: 1.31em; 113 | font-weight: bold; 114 | } 115 | 116 | .document-editor .ck-content h4 { 117 | line-height: 1.24em; 118 | padding-top: .286em; 119 | margin-bottom: .952em; 120 | } 121 | 122 | /* Set the styles for "Paragraph". */ 123 | .document-editor .ck-content p { 124 | font-size: 1em; 125 | line-height: 1.63em; 126 | padding-top: .5em; 127 | margin-bottom: 1.13em; 128 | } 129 | 130 | /* Make the block quoted text serif with some additional spacing. */ 131 | .document-editor .ck-content blockquote { 132 | font-family: Georgia, serif; 133 | margin-left: calc( 2 * var(--ck-spacing-large) ); 134 | margin-right: calc( 2 * var(--ck-spacing-large) ); 135 | } 136 | -------------------------------------------------------------------------------- /examples/example5/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Check events 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 22 | 23 | 24 | 25 |
26 | 27 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /examples/example6/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Empty value 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 33 | 34 | 35 | 36 |
37 | 38 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-ckeditor5", 3 | "version": "0.5.0", 4 | "description": "CKEditor 5 component for Vue 2.", 5 | "author": "Igor Iakovlev ", 6 | "license": "MIT", 7 | "private": false, 8 | "main": "dist/vue-ckeditor5.js", 9 | "files": [ 10 | "dist", 11 | "src" 12 | ], 13 | "keywords": [ 14 | "ckeditor", 15 | "component", 16 | "plugin", 17 | "vue", 18 | "vuejs" 19 | ], 20 | "bugs": { 21 | "url": "https://github.com/igorxut/vue-ckeditor5/issues" 22 | }, 23 | "homepage": "https://github.com/igorxut/vue-ckeditor5#readme", 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/igorxut/vue-ckeditor5.git" 27 | }, 28 | "scripts": { 29 | "lint": "eslint src", 30 | "build": "webpack" 31 | }, 32 | "peerDependencies": { 33 | "vue": "^2.6.11" 34 | }, 35 | "devDependencies": { 36 | "@babel/core": "^7.8.3", 37 | "@babel/preset-env": "^7.8.3", 38 | "babel-loader": "^8.0.6", 39 | "eslint": "^6.8.0", 40 | "eslint-config-standard": "^14.1.0", 41 | "eslint-plugin-import": "^2.20.0", 42 | "eslint-plugin-node": "^11.0.0", 43 | "eslint-plugin-promise": "^4.2.1", 44 | "eslint-plugin-standard": "^4.0.1", 45 | "webpack": "^4.41.5", 46 | "webpack-cli": "^3.3.10" 47 | }, 48 | "engines": { 49 | "node": ">= 12.14.1", 50 | "npm": ">= 6.13.6" 51 | }, 52 | "browserslist": [ 53 | "> 1%", 54 | "last 2 versions", 55 | "not ie <= 8" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import VueCkeditorComponent from './vue-ckeditor' 2 | 3 | const VueCkeditor = { 4 | version: VERSION, 5 | component: VueCkeditorComponent, 6 | plugin: { 7 | install (Vue, { name, editors }) { 8 | Vue.prototype.$VueCkeditorEditors = editors || {} 9 | 10 | Vue.component(name || VueCkeditorComponent.name, VueCkeditorComponent) 11 | } 12 | } 13 | } 14 | 15 | export default VueCkeditor 16 | -------------------------------------------------------------------------------- /src/vue-ckeditor.js: -------------------------------------------------------------------------------- 1 | export default { 2 | name: 'vue-ckeditor', 3 | 4 | render (createElement) { 5 | return createElement(this.tag, { 6 | attrs: this.$attrs 7 | }) 8 | }, 9 | 10 | props: { 11 | config: { 12 | default: () => { 13 | return { 14 | language: 'en' 15 | } 16 | }, 17 | required: false, 18 | type: Object 19 | }, 20 | 21 | editors: { 22 | default: () => { 23 | return {} 24 | }, 25 | required: false, 26 | type: Object 27 | }, 28 | 29 | readonly: { 30 | default: () => false, 31 | required: false, 32 | type: Boolean 33 | }, 34 | 35 | emptyValue: { 36 | required: false, 37 | type: String 38 | }, 39 | 40 | tag: { 41 | default: () => 'div', 42 | required: false, 43 | type: String 44 | }, 45 | 46 | toolbarContainer: { 47 | default: () => null, 48 | required: false, 49 | type: String 50 | }, 51 | 52 | type: { 53 | required: true, 54 | type: String 55 | }, 56 | 57 | value: { 58 | default: () => '', 59 | required: false, 60 | type: String 61 | } 62 | }, 63 | 64 | data () { 65 | return { 66 | instance: null 67 | } 68 | }, 69 | 70 | watch: { 71 | value (newValue) { 72 | const instance = this.instance 73 | 74 | if ( 75 | instance != null && 76 | newValue !== instance.getData() && 77 | !( 78 | this.emptyValueProvided && 79 | newValue === this.emptyValue 80 | ) 81 | ) { 82 | instance.setData(newValue) 83 | } 84 | } 85 | }, 86 | 87 | computed: { 88 | emptyValueProvided () { 89 | return Object.prototype.hasOwnProperty.call(this.$options.propsData, 'emptyValue') 90 | }, 91 | isEmpty () { 92 | const document = this.instance.model.document 93 | return !document.model.hasContent(document.getRoot()) 94 | } 95 | }, 96 | 97 | methods: { 98 | create () { 99 | if (this.instance == null) { 100 | const type = this.type 101 | const editors = this.$VueCkeditorEditors || this.editors 102 | 103 | if (!Object.keys(editors).length) { 104 | throw new Error('There are no CKEditor 5 implementations.') 105 | } 106 | 107 | const editor = editors[type] 108 | 109 | if (editor == null) { 110 | throw new Error(`Wrong key '${type}'. Allowed keys: ${Object.keys(editors)}`) 111 | } 112 | 113 | editor 114 | .create(this.$el, this.config) 115 | .then(editor => { 116 | this.instance = editor 117 | const instance = this.instance 118 | 119 | this.createToolbarContainer() 120 | 121 | this.setEventListeners() 122 | 123 | instance.isReadOnly = this.readonly 124 | instance.setData(this.value) 125 | 126 | this.$emit('ready', instance) 127 | }) 128 | .catch(error => { 129 | console.log(error) 130 | }) 131 | } 132 | }, 133 | createToolbarContainer () { 134 | const instance = this.instance 135 | const toolbarContainer = this.toolbarContainer 136 | 137 | if ( 138 | toolbarContainer != null && 139 | instance != null 140 | ) { 141 | const toolbarContainerElement = document.querySelector(toolbarContainer) 142 | 143 | if (toolbarContainerElement != null) { 144 | toolbarContainerElement.appendChild(instance.ui.view.toolbar.element) 145 | } 146 | } 147 | }, 148 | destroy () { 149 | const instance = this.instance 150 | 151 | if (instance != null) { 152 | instance.destroy() 153 | this.$emit('destroy', instance) 154 | } 155 | }, 156 | setEventListeners () { 157 | const instance = this.instance 158 | 159 | if (instance != null) { 160 | instance.model.document.on('change:data', (...args) => { 161 | let newValue = instance.getData() 162 | 163 | if (this.value !== newValue) { 164 | if ( 165 | this.emptyValueProvided && 166 | this.isEmpty 167 | ) { 168 | newValue = this.emptyValue 169 | } 170 | 171 | this.$emit('input', newValue, instance, ...args) 172 | } 173 | }) 174 | 175 | const editingViewDocument = instance.editing.view.document 176 | const events = editingViewDocument._events 177 | for (const key of Object.keys(events)) { 178 | editingViewDocument.on(key, (...args) => { 179 | this.$emit(key, instance, ...args) 180 | }) 181 | } 182 | } 183 | } 184 | }, 185 | 186 | mounted () { 187 | this.create() 188 | }, 189 | 190 | beforeDestroy () { 191 | this.destroy() 192 | } 193 | } 194 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const DefinePlugin = require('webpack').DefinePlugin 3 | 4 | module.exports = { 5 | mode: 'production', 6 | devtool: 'source-map', 7 | entry: './src/index.js', 8 | resolve: { 9 | extensions: [ 10 | '.js' 11 | ], 12 | alias: { 13 | 'vue-ckeditor5': path.join(__dirname, 'dist', 'vue-ckeditor5') 14 | } 15 | }, 16 | output: { 17 | path: path.join(__dirname, 'dist'), 18 | filename: 'vue-ckeditor5.js', 19 | library: 'VueCkeditor', 20 | libraryExport: 'default', 21 | libraryTarget: 'umd' 22 | }, 23 | module: { 24 | rules: [ 25 | { 26 | test: /\.js$/, 27 | include: [ 28 | path.join(__dirname, 'src') 29 | ], 30 | use: [ 31 | { 32 | loader: 'babel-loader' 33 | } 34 | ] 35 | } 36 | ] 37 | }, 38 | plugins: [ 39 | new DefinePlugin({ 40 | VERSION: JSON.stringify(require('./package.json').version) 41 | }) 42 | ] 43 | } 44 | --------------------------------------------------------------------------------