ckeditor5 1.0.3 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 11255f34bfbd4ad2c1657efc1e5df34390d2adefb67a0e39a02ffc5be814ae84
4
- data.tar.gz: 7fb31aa484581713750c55c6c0d0dce21d71a13063b4b77687f9663403b1c35d
3
+ metadata.gz: e8e40a31c033d3d5cfc96667757993fd4a92614a5e5bedacb93ec20d82419bd2
4
+ data.tar.gz: fb172fb186502165dd06475a980fd9ccbd21751076cf5a420edc64ace4129734
5
5
  SHA512:
6
- metadata.gz: a52319bf5229236fbb4bb9f0d7515aa0cd68717c2b483cb2f2dd132f580600c7009d1fbe26e5fad1b356708b743839cd2a8d3998a8476ae24d1b7f3ae7d3f640
7
- data.tar.gz: 960c18eecd8ae02b5caa418b47cbdaff3fd618382f53e630fe83bb310322622f19832e728f9d86ee11ff866da0d2abe8efd37ced1c7a1f8f05103c0f0dba43c5
6
+ metadata.gz: 64889f53d365c640f498efc484fbab12e05cfe93b80a9ab448c5bc5e3cb45bd24f313bce90ebd04cfd656c7cbd7c9cc60f7c982d7cf0aee8128cbc50633304ac
7
+ data.tar.gz: 1f9985d8cf29068eb70ecb93bf2c8e85a177750d0241e28075b1ba916a6e3a43cef71bd5598f4f25e25c6d9751cc36639065aa28065d592ff888012dbfd5bde3
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # CKEditor 5 Rails Integration
1
+ # CKEditor 5 Rails Integration
2
2
 
3
3
  [![License: MIT](https://img.shields.io/badge/License-MIT-orange.svg?style=flat-square)](https://opensource.org/licenses/MIT)
4
4
  ![Gem Version](https://img.shields.io/gem/v/ckeditor5?style=flat-square)
@@ -8,7 +8,7 @@
8
8
 
9
9
  Unofficial CKEditor 5 Ruby on Rails integration gem. Provides seamless integration of CKEditor 5 with Rails applications through web components and helper methods.
10
10
 
11
- ## Installation
11
+ ## Installation 🛠️
12
12
 
13
13
  Add this line to your application's Gemfile:
14
14
 
@@ -16,29 +16,61 @@ Add this line to your application's Gemfile:
16
16
  gem 'ckeditor5'
17
17
  ```
18
18
 
19
- ## Basic Usage
19
+ Usage in your Rails application:
20
20
 
21
- ### Presets
21
+ ```erb
22
+ <!-- app/views/demos/index.html.erb -->
22
23
 
23
- Override default preset configuration:
24
+ <% content_for :head do %>
25
+ <%= ckeditor5_assets version: '43.2.0', translations: [:pl, :es] %>
26
+ <% end %>
24
27
 
25
- ```rb
26
- # config/initializers/ckeditor5.rb
27
-
28
- CKEditor5::Rails::Engine.configure do |config|
29
- config.presets.override :default do |preset|
30
- preset.menubar visible: false
31
- end
32
- end
28
+ <%= ckeditor5_editor style: 'width: 600px' %>
33
29
  ```
34
30
 
35
- Create new preset:
31
+ Effect:
32
+
33
+ ![CKEditor 5 Classic Editor in Ruby on Rails application](docs/intro-classic-editor.png)
34
+
35
+ ## Table of Contents 📚
36
+
37
+ - [CKEditor 5 Rails Integration ✨](#ckeditor-5-rails-integration-)
38
+ - [Installation 🛠️](#installation-️)
39
+ - [Table of Contents 📚](#table-of-contents-)
40
+ - [Presets 🎨](#presets-)
41
+ - [Available Configuration Methods ⚙️](#available-configuration-methods-️)
42
+ - [`shape(type)` method](#shapetype-method)
43
+ - [`plugins(*names)` method](#pluginsnames-method)
44
+ - [`toolbar(*items, should_group_when_full: true)` method](#toolbaritems-should_group_when_full-true-method)
45
+ - [`menubar(visible: true)` method](#menubarvisible-true-method)
46
+ - [`language(ui, content:)` method](#languageui-content-method)
47
+ - [`configure(name, value)` method](#configurename-value-method)
48
+ - [`plugin(name, premium:, import_name:)` method](#pluginname-premium-import_name-method)
49
+ - [Including CKEditor 5 assets 📦](#including-ckeditor-5-assets-)
50
+ - [Lazy loading 🚀](#lazy-loading-)
51
+ - [GPL usage 🆓](#gpl-usage-)
52
+ - [Commercial usage 💰](#commercial-usage-)
53
+ - [Editor placement 🏗️](#editor-placement-️)
54
+ - [Classic editor 📝](#classic-editor-)
55
+ - [Multiroot editor 🌳](#multiroot-editor-)
56
+ - [Inline editor 📝](#inline-editor-)
57
+ - [Balloon editor 🎈](#balloon-editor-)
58
+ - [Decoupled editor 🌐](#decoupled-editor-)
59
+ - [License 📜](#license-)
60
+
61
+ ## Presets 🎨
62
+
63
+ Presets are predefined configurations of CKEditor 5, allowing quick setup with specific features. The gem includes a `:default` preset with common features like bold, italic, underline, and link for the classic editor.
64
+
65
+ You can override the default preset or create your own by defining a new preset in the `config/initializers/ckeditor5.rb` file using the `config.presets.define` method.
66
+
67
+ The example below shows how to define a custom preset with a classic editor and a custom toolbar:
36
68
 
37
69
  ```rb
38
70
  # config/initializers/ckeditor5.rb
39
71
 
40
72
  CKEditor5::Rails::Engine.configure do |config|
41
- config.presets.define :custom do |preset|
73
+ config.presets.define :custom
42
74
  shape :classic
43
75
 
44
76
  menubar
@@ -59,64 +91,461 @@ CKEditor5::Rails::Engine.configure do |config|
59
91
  :TextTransformation, :TodoList, :Underline, :Undo, :Base64UploadAdapter
60
92
  end
61
93
  end
94
+ ```
95
+
96
+ In order to override existing presets, you can use the `config.presets.override` method. The method takes the name of the preset you want to override and a block with the new configuration. In example below, we override the `:default` preset to hide the menubar.
62
97
 
63
- # Somewhere in your view
98
+ ```rb
99
+ # config/initializers/ckeditor5.rb
64
100
 
65
- = ckeditor5_editor preset: :custom
101
+ CKEditor5::Rails::Engine.configure do |config|
102
+ config.presets.override :default do
103
+ menubar visible: false
104
+ end
105
+ end
66
106
  ```
67
107
 
68
- ### CDN Configuration
108
+ You can generate your preset using the CKEditor 5 [online builder](https://ckeditor.com/ckeditor-5/online-builder/). After generating the configuration, you can copy it to the `config/initializers/ckeditor5.rb` file.
109
+
110
+ ### Available Configuration Methods ⚙️
111
+
112
+ <details>
113
+ <summary>Expand to show available methods 📖</summary>
114
+
115
+ #### `shape(type)` method
116
+
117
+ Defines the type of editor. Available options:
69
118
 
70
- #### jsDelivr (Default)
119
+ - `:classic` - classic edytor
120
+ - `:inline` - inline editor
121
+ - `:decoupled` - decoupled editor
122
+ - `:balloon` - balloon editor
123
+ - `:multiroot` - editor with multiple editing areas
71
124
 
72
- ```slim
73
- - content_for :head
74
- = ckeditor5_assets version: '43.2.0'
125
+ The example below shows how to define a multiroot editor:
126
+
127
+ ```rb
128
+ # config/initializers/ckeditor5.rb
129
+
130
+ config.presets.define :custom do
131
+ shape :multiroot
132
+ end
75
133
  ```
76
134
 
77
- #### unpkg
135
+ #### `plugins(*names)` method
136
+
137
+ Defines the plugins to be included in the editor. You can specify multiple plugins by passing their names as arguments.
78
138
 
79
- ```slim
80
- - content_for :head
81
- = ckeditor5_unpkg_assets version: '43.2.0'
139
+ ```rb
140
+ # config/initializers/ckeditor5.rb
141
+
142
+ config.presets.define :custom do
143
+ plugins :Bold, :Italic, :Underline, :Link
144
+ end
82
145
  ```
83
146
 
84
- #### CKEditor Cloud
147
+ #### `toolbar(*items, should_group_when_full: true)` method
148
+
149
+ Defines the toolbar items. You can use predefined items like `:undo`, `:redo`, `:|` or specify custom items. There are a few special items:
150
+
151
+ - `:_` - breakpoint
152
+ - `:|` - separator
85
153
 
86
- It's available only for licensed users.
154
+ The `should_group_when_full` keyword argument determines whether the toolbar should group items when there is not enough space. It's set to `true` by default.
87
155
 
88
- ```slim
89
- - content_for :head
90
- = ckeditor5_assets version: '43.2.0', license_key: 'YOUR-LICENSE-KEY'
156
+ ```rb
157
+ # config/initializers/ckeditor5.rb
158
+
159
+ config.presets.define :custom do
160
+ # ... other configuration
161
+
162
+ toolbar :undo, :redo, :|, :heading, :|, :bold, :italic, :underline, :|,
163
+ :link, :insertImage, :ckbox, :mediaEmbed, :insertTable, :blockQuote, :|,
164
+ :bulletedList, :numberedList, :todoList, :outdent, :indent
165
+ end
91
166
  ```
92
167
 
93
- ### Editor type
168
+ Keep in mind that the order of items is important, and you should install the corresponding plugins. You can find the list of available plugins in the [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/framework/architecture/plugins.html).
94
169
 
95
- #### Classic Editor
170
+ #### `menubar(visible: true)` method
96
171
 
97
- ```slim
98
- - content_for :head
99
- = ckeditor5_assets version: '43.2.0' # Optional: translations: [ :pl, :es ]
172
+ Defines the visibility of the menubar. By default, it's set to `true`.
100
173
 
101
- = ckeditor5_editor
174
+ ```rb
175
+ # config/initializers/ckeditor5.rb
176
+
177
+ config.presets.define :custom do
178
+ # ... other configuration
179
+
180
+ toolbar :undo, :redo, :|, :heading, :|, :bold, :italic, :underline, :|,
181
+ :link, :insertImage, :ckbox, :mediaEmbed, :insertTable, :blockQuote, :|,
182
+ :bulletedList, :numberedList, :todoList, :outdent, :indent
183
+ end
102
184
  ```
103
185
 
104
- #### Multiroot Editor
186
+ #### `language(ui, content:)` method
187
+
188
+ Defines the language of the editor. You can pass the language code as an argument. Keep in mind that the UI and content language can be different. The example below shows how to set the Polish language for the UI and content:
189
+
190
+ ```rb
191
+ # config/initializers/ckeditor5.rb
192
+
193
+ config.presets.define :custom do
194
+ # ... other configuration
195
+
196
+ language :pl
197
+ end
198
+ ```
199
+
200
+ In order to set the language for the content, you can pass the `content` keyword argument:
201
+
202
+ ```rb
203
+ # config/initializers/ckeditor5.rb
204
+
205
+ config.presets.define :custom do
206
+ # ... other configuration
207
+
208
+ language :en, content: :pl
209
+ end
210
+ ```
211
+
212
+ #### `configure(name, value)` method
213
+
214
+ Allows you to set custom configuration options. You can pass the name of the option and its value as arguments. The example below show how to set the default protocol for the link plugin to `https://`:
215
+
216
+ ```rb
217
+ # config/initializers/ckeditor5.rb
218
+
219
+ config.presets.define :custom do
220
+ # ... other configuration
221
+
222
+ configure :link, {
223
+ defaultProtocol: 'https://'
224
+ }
225
+ end
226
+ ```
227
+
228
+ #### `plugin(name, premium:, import_name:)` method
229
+
230
+ Defines a plugin to be included in the editor. You can pass the name of the plugin as an argument. The `premium` keyword argument determines whether the plugin is premium. The `import_name` keyword argument specifies the name of the package to import the plugin from.
231
+
232
+ The example below show how to import Bold plugin from the `ckeditor5` npm package:
233
+
234
+ ```rb
235
+ # config/initializers/ckeditor5.rb
236
+
237
+ config.presets.define :custom do
238
+ # ... other configuration
239
+
240
+ plugin :Bold
241
+ end
242
+ ```
243
+
244
+ In order to import a plugin from a custom package, you can pass the `import_name` keyword argument:
245
+
246
+ ```rb
247
+ # config/initializers/ckeditor5.rb
248
+
249
+ config.presets.define :custom do
250
+ # ... other configuration
251
+
252
+ plugin :YourPlugin, import_name: 'your-package'
253
+ end
254
+ ```
255
+
256
+ </details>
257
+
258
+ ## Including CKEditor 5 assets 📦
259
+
260
+ To include CKEditor 5 assets in your application, you can use the `ckeditor5_assets` helper method. This method takes the version of CKEditor 5 as an argument and includes the necessary assets. It allows you to specify custom translations to be included.
261
+
262
+ Keep in mind that you need to include the assets in the `head` section of your layout. In examples below, we use `content_for` to include the assets in the `head` section.
263
+
264
+ ### Lazy loading 🚀
265
+
266
+ <details>
267
+ <summary>Loading JS and CSS Assets</summary>
268
+
269
+ All JS assets defined by the `ckeditor5_assets` helper method are loaded asynchronously. It means that the assets are loaded in the background without blocking the rendering of the page. However, the CSS assets are loaded synchronously to prevent the flash of unstyled content and ensure that the editor is styled correctly.
270
+
271
+ It has been achieved by using web components, together with import maps, which are supported by modern browsers. The web components are used to define the editor and its plugins, while the import maps are used to define the dependencies between the assets.
272
+
273
+ </details>
274
+
275
+ ### GPL usage 🆓
276
+
277
+ If you want to use CKEditor 5 under the GPL license, you can include the assets using the `ckeditor5_assets` helper method with the `version` keyword argument. The example below shows how to include the assets for version `43.3.0`:
278
+
279
+ ```erb
280
+ <!-- app/views/demos/index.html.erb -->
281
+
282
+ <% content_for :head do %>
283
+ <%= ckeditor5_assets version: '43.3.0' %>
284
+ <% end %>
285
+ ```
286
+
287
+ It'll include the necessary assets for the GPL license from one of the most popular CDNs. In our scenario, we use the `jsdelivr` CDN which is the default one.
288
+
289
+ In order to use `unpkg` CDN, you can pass the `cdn` keyword argument with the value `:unpkg`:
290
+
291
+ ```erb
292
+ <!-- app/views/demos/index.html.erb -->
293
+
294
+ <% content_for :head do %>
295
+ <%= ckeditor5_assets version: '43.3.0', cdn: :unpkg %>
296
+ <% end %>
297
+ ```
298
+
299
+ or using helper function:
300
+
301
+ ```erb
302
+ <!-- app/views/demos/index.html.erb -->
303
+
304
+ <% content_for :head do %>
305
+ <%= ckeditor5_jsdelivr_assets version: '43.3.0' %>
306
+ <% end %>
307
+ ```
308
+
309
+ Translating CKEditor 5 is possible by passing the `translations` keyword argument with the languages codes array. The example below shows how to include the Polish translations:
310
+
311
+ ```erb
312
+ <!-- app/views/demos/index.html.erb -->
313
+
314
+ <% content_for :head do %>
315
+ <%= ckeditor5_assets version: '43.3.0', translations: [:pl] %>
316
+ <% end %>
317
+ ```
318
+
319
+ Keep in mind, that you need to include the translations in the `config/initializers/ckeditor5.rb` file:
320
+
321
+ ```rb
322
+ # config/initializers/ckeditor5.rb
323
+
324
+ CKEditor5::Rails::Engine.configure do
325
+ presets.override :default do
326
+ language :pl
327
+ end
328
+ end
329
+ ```
330
+
331
+ ### Commercial usage 💰
332
+
333
+ <details>
334
+ <summary>Expand to show more</summary>
335
+
336
+ If you want to use CKEditor 5 under a commercial license, you can include the assets using the `ckeditor5_assets` helper method with the `license_key` keyword argument. The example below shows how to include the assets for the commercial license:
337
+
338
+ ```erb
339
+ <!-- app/views/demos/index.html.erb -->
340
+
341
+ <% content_for :head do %>
342
+ <%= ckeditor5_assets license_key: 'your-license-key' %>
343
+ <% end %>
344
+ ```
345
+
346
+ In this scenario, the assets are included from the official CKEditor 5 CDN which is more reliable and provides better performance, especially for commercial usage.
347
+
348
+ </details>
349
+
350
+ ## Editor placement 🏗️
351
+
352
+ The `ckeditor5_editor` helper renders CKEditor 5 instances in your views. Before using it, ensure you've included the necessary assets in your page's head section.
353
+
354
+ ### Classic editor 📝
355
+
356
+ The classic editor is the most common type of editor. It provides a toolbar with various formatting options like bold, italic, underline, and link.
357
+
358
+ It looks like this:
359
+
360
+ ![CKEditor 5 Classic Editor in Ruby on Rails application with Menubar](docs/classic-editor-with-toolbar.png)
361
+
362
+ The example below shows how to include the classic editor in your view:
363
+
364
+ ```erb
365
+ <!-- app/views/demos/index.html.erb -->
366
+
367
+ <% content_for :head do %>
368
+ <%= ckeditor5_assets version: '43.3.0' %>
369
+ <% end %>
370
+
371
+ <%= ckeditor5_editor style: 'width: 600px' %>
372
+ ```
373
+
374
+ You can pass the `style` keyword argument to the `ckeditor5_editor` helper to define the editor's style. The example above shows how to set the width of the editor to `600px`. However you can pass any HTML attribute you want, such as `class`, `id`, `data-*`, etc.
375
+
376
+ While example above uses predefined `:default` preset, you can use your custom presets by passing the `preset` keyword argument:
377
+
378
+ ```erb
379
+ <!-- app/views/demos/index.html.erb -->
380
+
381
+ <% content_for :head do %>
382
+ <%= ckeditor5_assets version: '43.3.0' %>
383
+ <% end %>
384
+
385
+ <%= ckeditor5_editor preset: :custom, style: 'width: 600px' %>
386
+ ```
387
+
388
+ If your configuration is even more complex, you can pass the `config` and `type` arguments with the configuration hash:
389
+
390
+ ```erb
391
+ <!-- app/views/demos/index.html.erb -->
392
+
393
+ <% content_for :head do %>
394
+ <%= ckeditor5_assets version: '43.3.0' %>
395
+ <% end %>
396
+
397
+ <%= ckeditor5_editor type: :classic, config: { plugins: [:Bold, :Italic], toolbar: [:Bold, :Italic] }, style: 'width: 600px' %>
398
+ ```
399
+
400
+ If you want to override the configuration of the editor specified in default or custom preset, you can pass the `extra_config` keyword argument with the configuration hash:
401
+
402
+ ```erb
403
+ <!-- app/views/demos/index.html.erb -->
404
+
405
+ <% content_for :head do %>
406
+ <%= ckeditor5_assets version: '43.3.0' %>
407
+ <% end %>
408
+
409
+ <%= ckeditor5_editor extra_config: { toolbar: [:Bold, :Italic] }, style: 'width: 600px' %>
410
+ ```
411
+
412
+ ### Multiroot editor 🌳
413
+
414
+ The multiroot editor allows you to create an editor with multiple editable areas. It's useful when you want to create a CMS with multiple editable areas on a single page.
415
+
416
+ - `ckeditor5_editor`: Defines the editor instance.
417
+ - `ckeditor5_editable`: Defines the editable areas within the editor.
418
+ - `ckeditor5_toolbar`: Defines the toolbar for the editor.
419
+
420
+ ![CKEditor 5 Multiroot Editor in Ruby on Rails application](docs/multiroot-editor.png)
421
+
422
+ If you want to use a multiroot editor, you can pass the `type` keyword argument with the value `:multiroot`:
423
+
424
+ ```erb
425
+ <!-- app/views/demos/index.html.erb -->
426
+
427
+ <% content_for :head do %>
428
+ <%= ckeditor5_assets version: '43.2.0' %>
429
+ <% end %>
430
+
431
+ <%= ckeditor5_editor type: :multiroot, style: 'width: 600px' do %>
432
+ <%= ckeditor5_toolbar %>
433
+ <br>
434
+ <%= ckeditor5_editable 'toolbar', style: 'border: 1px solid var(--ck-color-base-border);' do %>
435
+ This is a toolbar editable
436
+ <% end %>
437
+ <br>
438
+ <%= ckeditor5_editable 'content', style: 'border: 1px solid var(--ck-color-base-border)' %>
439
+ <br>
440
+ <% end %>
441
+ ```
442
+
443
+ Roots can be defined later to the editor by simply adding new elements rendered by `ckeditor5_editable` helper.
444
+
445
+ ### Inline editor 📝
446
+
447
+ Inline editor allows you to create an editor that can be placed inside any element. Keep in mind that inline editor does not work with `textarea` elements so it might be not suitable for all use cases.
448
+
449
+ ![CKEditor 5 Inline Editor in Ruby on Rails application](docs/inline-editor.png)
450
+
451
+ If you want to use an inline editor, you can pass the `type` keyword argument with the value `:inline`:
452
+
453
+ ```erb
454
+ <!-- app/views/demos/index.html.erb -->
455
+
456
+ <% content_for :head do %>
457
+ <%= ckeditor5_assets version: '43.2.0' %>
458
+ <% end %>
459
+
460
+ <%= ckeditor5_editor type: :inline, style: 'width: 600px' %>
461
+ ```
462
+
463
+ ### Balloon editor 🎈
464
+
465
+ Balloon editor is a floating toolbar editor that provides a minimalistic interface. It's useful when you want to create a simple editor with a floating toolbar.
466
+
467
+ ![CKEditor 5 Balloon Editor in Ruby on Rails application](docs/balloon-editor.png)
468
+
469
+ If you want to use a balloon editor, you can pass the `type` keyword argument with the value `:balloon`:
470
+
471
+ ```erb
472
+ <!-- app/views/demos/index.html.erb -->
473
+
474
+ <% content_for :head do %>
475
+ <%= ckeditor5_assets version: '43.2.0' %>
476
+ <% end %>
477
+
478
+ <%= ckeditor5_editor type: :balloon, style: 'width: 600px' %>
479
+ ```
105
480
 
106
- ```slim
107
- = ckeditor5_editor type: :multiroot do
108
- = ckeditor5_toolbar
109
- br
110
- = ckeditor5_editable 'editable-a' do
111
- | This is a toolbar editable
112
- br
113
- = ckeditor5_editable 'editable-b'
481
+ ### Decoupled editor 🌐
482
+
483
+ Decoupled editor is a variant of classic editor that allows you to separate the editor from the content area. It's useful when you want to create a custom interface with the editor.
484
+
485
+ ![CKEditor 5 Decoupled Editor in Ruby on Rails application](docs/decoupled-editor.png)
486
+
487
+ If you want to use a decoupled editor, you can pass the `type` keyword argument with the value `:decoupled`:
488
+
489
+ ```erb
490
+ <% content_for :head do %>
491
+ <%= ckeditor5_assets version: '43.2.0', translations: [:pl, :es] %>
492
+ <% end %>
493
+
494
+ <style>
495
+ .menubar-container,
496
+ .editable-container,
497
+ .toolbar-container {
498
+ position: relative;
499
+ border: 1px solid red;
500
+ }
501
+
502
+ .menubar-container::after,
503
+ .editable-container::after,
504
+ .toolbar-container::after {
505
+ content: attr(class);
506
+ position: absolute;
507
+ background: red;
508
+ color: #fff;
509
+ top: 0;
510
+ right: 0;
511
+ font: 10px/2 monospace;
512
+ padding: .1em .3em;
513
+ }
514
+
515
+ .menubar-container,
516
+ .toolbar-container {
517
+ padding: 1em;
518
+ }
519
+
520
+ .editable-container {
521
+ padding: 3em;
522
+ overflow-y: scroll;
523
+ max-height: 300px;
524
+ }
525
+
526
+ .editable-container .ck-editor__editable {
527
+ min-height: 21cm;
528
+ padding: 2em;
529
+ border: 1px #D3D3D3 solid;
530
+ border-radius: var(--ck-border-radius);
531
+ background: white;
532
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
533
+ }
534
+ </style>
535
+
536
+ <%= ckeditor5_editor type: :decoupled, style: 'width: 600px' do %>
537
+ <div class="menubar-container"><%= ckeditor5_menubar %></div>
538
+ <br>
539
+ <div class="toolbar-container"><%= ckeditor5_toolbar %></div>
540
+ <br>
541
+ <div class="editable-container"><%= ckeditor5_editable %></div>
542
+ <% end %>
114
543
  ```
115
544
 
116
- ## License
545
+ ## License 📜
117
546
 
118
547
  The MIT License (MIT)
119
- Copyright (c) Mateusz Bagiński / Łukasz Modliński
548
+ Mateusz Bagiński / Łukasz Modliński
120
549
 
121
550
  Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
122
551
 
@@ -94,7 +94,7 @@ class CKEditorComponent extends HTMLElement {
94
94
  return Promise.resolve(callback(this.instance));
95
95
  }
96
96
 
97
- return this.instancePromise.then(callback);
97
+ return this.instancePromise.promise.then(callback);
98
98
  }
99
99
 
100
100
  /**
@@ -115,25 +115,33 @@ class CKEditorComponent extends HTMLElement {
115
115
  /**
116
116
  * Initializes a new CKEditor instance
117
117
  * @private
118
- * @param {Record<string, HTMLElement>|CKEditorMultiRootEditablesTracker} editables - Editable elements
118
+ * @param {Record<string, HTMLElement>|CKEditorMultiRootEditablesTracker} editablesOrContent - Editable or content
119
119
  * @returns {Promise<import('ckeditor5').Editor>} Initialized editor instance
120
120
  * @throws {Error} When initialization fails
121
121
  */
122
- async #initializeEditor(editables) {
122
+ async #initializeEditor(editablesOrContent) {
123
123
  const Editor = await this.#getEditorConstructor();
124
124
  const [plugins, translations] = await Promise.all([
125
125
  this.#getPlugins(),
126
126
  this.#getTranslations()
127
127
  ]);
128
128
 
129
+ let content = editablesOrContent;
130
+
131
+ if (editablesOrContent instanceof CKEditorMultiRootEditablesTracker) {
132
+ content = editablesOrContent.getAll();
133
+ } else if (typeof editablesOrContent !== 'string') {
134
+ content = editablesOrContent.main;
135
+ }
136
+
129
137
  const instance = await Editor.create(
130
- editables instanceof CKEditorMultiRootEditablesTracker
131
- ? editables.getAll()
132
- : editables.main,
138
+ content,
133
139
  {
134
140
  ...this.#getConfig(),
141
+ ...translations.length && {
142
+ translations
143
+ },
135
144
  plugins,
136
- translations
137
145
  }
138
146
  );
139
147
 
@@ -158,21 +166,21 @@ class CKEditorComponent extends HTMLElement {
158
166
 
159
167
  this.style.display = 'block';
160
168
 
161
- if (!this.#isMultiroot()) {
169
+ if (!this.isMultiroot() && !this.isDecoupled()) {
162
170
  this.innerHTML = `<${this.#editorElementTag}></${this.#editorElementTag}>`;
163
171
  }
164
172
 
165
173
  // Let's track changes in editables if it's a multiroot editor.
166
- const editables = this.#queryEditables();
167
-
168
- if(this.#isMultiroot()) {
169
- this.editables = new CKEditorMultiRootEditablesTracker(this, editables);
174
+ if(this.isMultiroot()) {
175
+ this.editables = new CKEditorMultiRootEditablesTracker(this, this.#queryEditables());
176
+ } else if (this.isDecoupled()) {
177
+ this.editables = null;
170
178
  } else {
171
- this.editables = editables;
179
+ this.editables = this.#queryEditables();
172
180
  }
173
181
 
174
182
  try {
175
- this.instance = await this.#initializeEditor(this.editables);
183
+ this.instance = await this.#initializeEditor(this.editables || this.#getConfig().initialData || '');
176
184
  this.instancePromise.resolve(this.instance);
177
185
  } catch (err) {
178
186
  this.instancePromise.reject(err);
@@ -186,10 +194,20 @@ class CKEditorComponent extends HTMLElement {
186
194
  * @private
187
195
  * @returns {boolean}
188
196
  */
189
- #isMultiroot() {
197
+ isMultiroot() {
190
198
  return this.getAttribute('type') === 'MultiRootEditor';
191
199
  }
192
200
 
201
+ /**
202
+ * Checks if current editor is decoupled type
203
+ *
204
+ * @private
205
+ * @returns {boolean}
206
+ */
207
+ isDecoupled() {
208
+ return this.getAttribute('type') === 'DecoupledEditor';
209
+ }
210
+
193
211
  /**
194
212
  * Parses editor configuration from config attribute
195
213
  *
@@ -208,7 +226,11 @@ class CKEditorComponent extends HTMLElement {
208
226
  * @throws {Error} When required editables are missing
209
227
  */
210
228
  #queryEditables() {
211
- if (this.#isMultiroot()) {
229
+ if (this.isDecoupled()) {
230
+ return {};
231
+ }
232
+
233
+ if (this.isMultiroot()) {
212
234
  const editables = [...this.querySelectorAll('ckeditor-editable-component')];
213
235
 
214
236
  return editables.reduce((acc, element) => {
@@ -444,7 +466,8 @@ class CKEditorEditableComponent extends HTMLElement {
444
466
  * @returns {string} The name attribute value
445
467
  */
446
468
  get name() {
447
- return this.getAttribute('name');
469
+ // The default value is set mainly for decoupled editors where the name is not required.
470
+ return this.getAttribute('name') || 'editable';
448
471
  }
449
472
 
450
473
  /**
@@ -462,16 +485,28 @@ class CKEditorEditableComponent extends HTMLElement {
462
485
  * @throws {Error} If not used as child of ckeditor-component
463
486
  */
464
487
  connectedCallback() {
465
- const editorComponent = this.#queryEditorElement();
488
+ execIfDOMReady(() => {
489
+ const editorComponent = this.#queryEditorElement();
466
490
 
467
- if (!editorComponent ) {
468
- throw new Error('ckeditor-editable-component must be a child of ckeditor-component');
469
- }
491
+ if (!editorComponent ) {
492
+ throw new Error('ckeditor-editable-component must be a child of ckeditor-component');
493
+ }
470
494
 
471
- this.innerHTML = `<div>${this.innerHTML}</div>`;
472
- this.style.display = 'block';
495
+ this.innerHTML = `<div>${this.innerHTML}</div>`;
496
+ this.style.display = 'block';
497
+
498
+ if (editorComponent.isDecoupled()) {
499
+ editorComponent.runAfterEditorReady(editor => {
500
+ this.appendChild(editor.ui.view[this.name].element);
501
+ });
502
+ } else {
503
+ if (!this.name) {
504
+ throw new Error('Editable component missing required "name" attribute');
505
+ }
473
506
 
474
- editorComponent.editables[this.name] = this;
507
+ editorComponent.editables[this.name] = this;
508
+ }
509
+ });
475
510
  }
476
511
 
477
512
  /**
@@ -522,28 +557,31 @@ class CKEditorEditableComponent extends HTMLElement {
522
557
  * @returns {CKEditorComponent|null} Parent editor component or null if not found
523
558
  */
524
559
  #queryEditorElement() {
525
- return this.closest('ckeditor-component');
560
+ return this.closest('ckeditor-component') || document.body.querySelector('ckeditor-component');
526
561
  }
527
562
  }
528
563
 
529
564
  /**
530
- * Custom HTML element that represents a CKEditor toolbar component.
531
- * Manages the toolbar placement and integration with the main editor component.
565
+ * Custom HTML element that represents a CKEditor UI part component.
566
+ * It helpers with management of toolbar and other elements.
532
567
  *
533
568
  * @extends HTMLElement
534
- * @customElement ckeditor-toolbar
569
+ * @customElement ckeditor-ui-part-component
535
570
  * @example
536
- * <ckeditor-toolbar></ckeditor-toolbar>
571
+ * <ckeditor-ui-part-component></ckeditor-ui-part-component>
537
572
  */
538
- class CKEditorToolbarComponent extends HTMLElement {
573
+ class CKEditorUIPartComponent extends HTMLElement {
539
574
  /**
540
575
  * Lifecycle callback when element is added to DOM
541
576
  * Adds the toolbar to the editor UI
542
577
  */
543
- async connectedCallback() {
544
- const editor = await this.#queryEditorElement().instancePromise.promise;
578
+ connectedCallback() {
579
+ execIfDOMReady(async () => {
580
+ const uiPart = this.getAttribute('name');
581
+ const editor = await this.#queryEditorElement().instancePromise.promise;
545
582
 
546
- this.appendChild(editor.ui.view.toolbar.element);
583
+ this.appendChild(editor.ui.view[uiPart].element);
584
+ });
547
585
  }
548
586
 
549
587
  /**
@@ -553,7 +591,7 @@ class CKEditorToolbarComponent extends HTMLElement {
553
591
  * @returns {CKEditorComponent|null} Parent editor component or null if not found
554
592
  */
555
593
  #queryEditorElement() {
556
- return this.closest('ckeditor-component');
594
+ return this.closest('ckeditor-component') || document.body.querySelector('ckeditor-component');
557
595
  }
558
596
  }
559
597
 
@@ -600,4 +638,4 @@ function loadAsyncImports(imports = []) {
600
638
 
601
639
  customElements.define('ckeditor-component', CKEditorComponent);
602
640
  customElements.define('ckeditor-editable-component', CKEditorEditableComponent);
603
- customElements.define('ckeditor-toolbar-component', CKEditorToolbarComponent);
641
+ customElements.define('ckeditor-ui-part-component', CKEditorUIPartComponent);
@@ -25,15 +25,24 @@ module CKEditor5::Rails
25
25
  context: context
26
26
  )
27
27
 
28
- render_editor_component(editor_props, html_attributes, &(type == :multiroot ? block : nil))
28
+ render_editor_component(editor_props, html_attributes,
29
+ &(%i[multiroot decoupled].include?(type) ? block : nil))
29
30
  end
30
31
 
31
- def ckeditor5_editable(name, **kwargs, &block)
32
+ def ckeditor5_editable(name = nil, **kwargs, &block)
32
33
  tag.send(:'ckeditor-editable-component', name: name, **kwargs, &block)
33
34
  end
34
35
 
36
+ def ckeditor5_ui_part(name, **kwargs, &block)
37
+ tag.send(:'ckeditor-ui-part-component', name: name, **kwargs, &block)
38
+ end
39
+
35
40
  def ckeditor5_toolbar(**kwargs)
36
- tag.send(:'ckeditor-toolbar-component', **kwargs)
41
+ ckeditor5_ui_part('toolbar', **kwargs)
42
+ end
43
+
44
+ def ckeditor5_menubar(**kwargs)
45
+ ckeditor5_ui_part('menuBarView', **kwargs)
37
46
  end
38
47
 
39
48
  private
@@ -4,9 +4,11 @@ module CKEditor5::Rails::Editor
4
4
  class PropsPlugin
5
5
  delegate :to_h, to: :import_meta
6
6
 
7
- def initialize(name, premium: false)
7
+ def initialize(name, premium: false, import_name: nil)
8
8
  @name = name
9
9
  @premium = premium
10
+ @import_name = import_name
11
+ @import_name ||= premium ? 'ckeditor5-premium-features' : 'ckeditor5'
10
12
  end
11
13
 
12
14
  def self.normalize(plugin)
@@ -19,12 +21,12 @@ module CKEditor5::Rails::Editor
19
21
 
20
22
  private
21
23
 
22
- attr_reader :name, :premium
24
+ attr_reader :name, :premium, :import_name
23
25
 
24
26
  def import_meta
25
27
  ::CKEditor5::Rails::Assets::JSImportMeta.new(
26
28
  import_as: name,
27
- import_name: premium ? 'ckeditor5-premium-features' : 'ckeditor5'
29
+ import_name: import_name
28
30
  )
29
31
  end
30
32
  end
@@ -80,20 +80,26 @@ module CKEditor5::Rails
80
80
  }
81
81
  end
82
82
 
83
- def toolbar(*items)
84
- @config[:toolbar] = items
83
+ def toolbar(*items, should_group_when_full: true)
84
+ @config[:toolbar] = {
85
+ items: items,
86
+ shouldNotGroupWhenFull: !should_group_when_full
87
+ }
85
88
  end
86
89
 
87
- def plugin(name, premium: false)
88
- @config[:plugins] << Editor::PropsPlugin.new(name, premium: premium)
90
+ def plugin(name, **kwargs)
91
+ @config[:plugins] << Editor::PropsPlugin.new(name, **kwargs)
89
92
  end
90
93
 
91
- def plugins(*names, premium: false)
92
- names.each { |name| plugin(name, premium: premium) }
94
+ def plugins(*names, **kwargs)
95
+ names.each { |name| plugin(name, **kwargs) }
93
96
  end
94
97
 
95
- def language(lang)
96
- @config[:language] = lang
98
+ def language(ui, content: ui) # rubocop:disable Naming/MethodParameterName
99
+ @config[:language] = {
100
+ ui: ui,
101
+ content: content
102
+ }
97
103
  end
98
104
  end
99
105
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CKEditor5::Rails
4
- VERSION = '1.0.3'
4
+ VERSION = '1.0.5'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ckeditor5
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński