ckeditor5 1.0.4 → 1.0.6

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: 23e478e9988398f0612c2831798b5cd7a2abb9e6f21a8777e22964fe97974041
4
- data.tar.gz: 817dfd3d0a94ef227c8e5845636468cf2aec54dbd19127f09feccd2c65d818cd
3
+ metadata.gz: ad357ef6b45ba35fdea777e70dae559476bd57040b23f49cfefa3581b4b3aafc
4
+ data.tar.gz: 89da75ef1325766ad4a2f0d81152822f8774b55efdf81bd76b8d9479332cb749
5
5
  SHA512:
6
- metadata.gz: 00d576237488fcf65f5ceb1df860f1a2c6a31beccfc4361d57253ae014998072f083f282f66d4d0114e5eb8f50f3c838c56e172ded14bd559dbfe2b6d7c0abf4
7
- data.tar.gz: a76c84be02f3c76acd8c4a39505264677ab34e3b4536887e1309da41417846346cee2f86718fef8e64f897acd4f5488f373def7c2299a49ca5ac8397ae296cf9
6
+ metadata.gz: f4f599b202ef0c109e14ff12c25f09890808d53e2116b2d3b6e51dfbfc5f389ea1d62651aa1a493fb897838e56ca2f3345addee8eebd55f5c2b7126f55169ce3
7
+ data.tar.gz: fc254a7f703defae55c901a444329772e177dfbfc8d0ee4ea3864c6c516b1c36f64e21166ec2e787d79ce368e09981ad524f4187842f31b53b8084ab690ed13b
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,20 +16,68 @@ Add this line to your application's Gemfile:
16
16
  gem 'ckeditor5'
17
17
  ```
18
18
 
19
- ## Presets
19
+ Usage in your Rails application:
20
20
 
21
- 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.
21
+ ```erb
22
+ <!-- app/views/demos/index.html.erb -->
23
+
24
+ <% content_for :head do %>
25
+ <%= ckeditor5_assets version: '43.2.0', translations: [:pl, :es] %>
26
+ <% end %>
27
+
28
+ <%= ckeditor5_editor style: 'width: 600px' %>
29
+ ```
22
30
 
23
- 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.
31
+ Result:
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
+ - [`version(version)` method](#versionversion-method)
43
+ - [`gpl` method](#gpl-method)
44
+ - [`premium` method](#premium-method)
45
+ - [`translations(*languages)` method](#translationslanguages-method)
46
+ - [`license_key(key)` method](#license_keykey-method)
47
+ - [`ckbox` method](#ckbox-method)
48
+ - [`type(type)` method](#typetype-method)
49
+ - [`plugins(*names, **kwargs)` method](#pluginsnames-kwargs-method)
50
+ - [`toolbar(*items, should_group_when_full: true)` method](#toolbaritems-should_group_when_full-true-method)
51
+ - [`menubar(visible: true)` method](#menubarvisible-true-method)
52
+ - [`language(ui, content:)` method](#languageui-content-method)
53
+ - [`configure(name, value)` method](#configurename-value-method)
54
+ - [`plugin(name, premium:, import_name:)` method](#pluginname-premium-import_name-method)
55
+ - [Including CKEditor 5 assets 📦](#including-ckeditor-5-assets-)
56
+ - [Lazy loading 🚀](#lazy-loading-)
57
+ - [GPL usage 🆓](#gpl-usage-)
58
+ - [Commercial usage 💰](#commercial-usage-)
59
+ - [Editor placement 🏗️](#editor-placement-️)
60
+ - [Classic editor 📝](#classic-editor-)
61
+ - [Multiroot editor 🌳](#multiroot-editor-)
62
+ - [Inline editor 📝](#inline-editor-)
63
+ - [Balloon editor 🎈](#balloon-editor-)
64
+ - [Decoupled editor 🌐](#decoupled-editor-)
65
+ - [License 📜](#license-)
66
+
67
+ ## Presets 🎨
24
68
 
25
- The example below shows how to define a custom preset with a classic editor and a custom toolbar:
69
+ 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.
70
+
71
+ You can create your own by defining it in the `config/initializers/ckeditor5.rb` file using the `config.presets.define` method. The example below illustrates the setup of a custom preset with a classic editor and a custom toolbar:
26
72
 
27
73
  ```rb
28
74
  # config/initializers/ckeditor5.rb
29
75
 
30
76
  CKEditor5::Rails::Engine.configure do |config|
31
77
  config.presets.define :custom
32
- shape :classic
78
+ gpl # Use GPL license
79
+
80
+ type :classic
33
81
 
34
82
  menubar
35
83
 
@@ -51,7 +99,7 @@ CKEditor5::Rails::Engine.configure do |config|
51
99
  end
52
100
  ```
53
101
 
54
- 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.
102
+ 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 old configuration. The example below shows how to hide the menubar in the default preset:
55
103
 
56
104
  ```rb
57
105
  # config/initializers/ckeditor5.rb
@@ -63,64 +111,145 @@ CKEditor5::Rails::Engine.configure do |config|
63
111
  end
64
112
  ```
65
113
 
66
- 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.
114
+ Configuration of the editor can be complex, and it's recommended to use the CKEditor 5 [online builder](https://ckeditor.com/ckeditor-5/online-builder/) to generate the configuration. It allows you to select the features you want to include and generate the configuration code in JavaScript format. Keep in mind that you need to convert the JavaScript configuration to Ruby format before using it in this gem.
67
115
 
68
- ### Available Configuration Methods
116
+ ### Available Configuration Methods ⚙️
69
117
 
70
- #### `shape(type)` method
118
+ <details>
119
+ <summary>Expand to show available methods 📖</summary>
71
120
 
72
- Defines the type of editor. Available options:
121
+ #### `version(version)` method
73
122
 
74
- - `:classic` - standard editor
75
- - `:inline` - inline editor
76
- - `:balloon` - balloon editor
77
- - `:multiroot` - editor with multiple editing areas
123
+ Defines the version of CKEditor 5 to be used. The example below shows how to set the version to `43.2.0`:
124
+
125
+ ```rb
126
+ # config/initializers/ckeditor5.rb
78
127
 
79
- The example below shows how to define a multiroot editor:
128
+ config.presets.define :custom do
129
+ # ... other configuration
130
+
131
+ version '43.2.0'
132
+ end
133
+ ```
134
+
135
+ #### `gpl` method
136
+
137
+ Defines the license of CKEditor 5. The example below shows how to set the license to GPL:
80
138
 
81
139
  ```rb
140
+ # config/initializers/ckeditor5.rb
141
+
82
142
  config.presets.define :custom do
83
- shape :multiroot
143
+ # ... other configuration
144
+
145
+ gpl
84
146
  end
85
147
  ```
86
148
 
87
- #### `plugins(*names)` method
149
+ #### `premium` method
88
150
 
89
- Defines the plugins to be included in the editor. You can specify multiple plugins by passing their names as arguments.
151
+ Defines if premium package (`ckeditor5-premium-features`) should be used.
90
152
 
91
153
  ```rb
154
+ # config/initializers/ckeditor5.rb
155
+
92
156
  config.presets.define :custom do
93
- plugins :Bold, :Italic, :Underline, :Link
157
+ # ... other configuration
158
+
159
+ premium
94
160
  end
95
161
  ```
96
162
 
97
- #### `toolbar(*items, should_group_when_full: true)` method
163
+ #### `translations(*languages)` method
98
164
 
99
- Defines the toolbar items. You can use predefined items like `:undo`, `:redo`, `:|` or specify custom items. There are a few special items:
165
+ Defines the translations of CKEditor 5. You can pass the language codes as arguments. The example below shows how to set the Polish and Spanish translations:
100
166
 
101
- - `:_` - breakpoint
102
- - `:|` - separator
167
+ ```rb
168
+ # config/initializers/ckeditor5.rb
103
169
 
104
- 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.
170
+ config.presets.define :custom do
171
+ # ... other configuration
172
+
173
+ translations :pl, :es
174
+ end
175
+ ```
176
+
177
+ #### `license_key(key)` method
178
+
179
+ Defines the license key of CKEditor 5. It calls `premium` method internally. The example below shows how to set the license key:
105
180
 
106
181
  ```rb
182
+ # config/initializers/ckeditor5.rb
183
+
107
184
  config.presets.define :custom do
108
185
  # ... other configuration
109
186
 
110
- toolbar :undo, :redo, :|, :heading, :|, :bold, :italic, :underline, :|,
111
- :link, :insertImage, :ckbox, :mediaEmbed, :insertTable, :blockQuote, :|,
112
- :bulletedList, :numberedList, :todoList, :outdent, :indent
187
+ license_key 'your-license-key'
113
188
  end
114
189
  ```
115
190
 
116
- 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).
191
+ #### `ckbox` method
117
192
 
118
- Defines the toolbar items. You can use predefined items like `:undo`, `:redo`, `:|` or specify custom items. There are few special items:
193
+ Defines the CKBox plugin to be included in the editor. The example below shows how to include the CKBox plugin:
194
+
195
+ ```rb
196
+ # config/initializers/ckeditor5.rb
197
+
198
+ config.presets.define :custom do
199
+ # ... other configuration
200
+
201
+ ckbox '2.5.4', theme: :lark
202
+ end
203
+ ```
204
+
205
+ #### `type(type)` method
206
+
207
+ Defines the type of editor. Available options:
208
+
209
+ - `:classic` - classic edytor
210
+ - `:inline` - inline editor
211
+ - `:decoupled` - decoupled editor
212
+ - `:balloon` - balloon editor
213
+ - `:multiroot` - editor with multiple editing areas
214
+
215
+ The example below sets the editor type to `multiroot` in the custom preset:
216
+
217
+ ```rb
218
+ # config/initializers/ckeditor5.rb
219
+
220
+ config.presets.define :custom do
221
+ # ... other configuration
222
+
223
+ type :multiroot
224
+ end
225
+ ```
226
+
227
+ #### `plugins(*names, **kwargs)` method
228
+
229
+ Defines the plugins to be included in the editor. You can specify multiple plugins by passing their names as arguments. The keyword arguments are identical to the configuration of the `plugin` method defined below.
230
+
231
+ ```rb
232
+ # config/initializers/ckeditor5.rb
233
+
234
+ config.presets.define :custom do
235
+ # ... other configuration
236
+
237
+ plugins :Bold, :Italic, :Underline, :Link
238
+ end
239
+ ```
240
+
241
+ #### `toolbar(*items, should_group_when_full: true)` method
242
+
243
+ Defines the toolbar items. You can use predefined items like `:undo`, `:redo`, `:|` or specify custom items. There are a few special items:
119
244
 
120
245
  - `:_` - breakpoint
121
246
  - `:|` - separator
122
247
 
248
+ 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.
249
+
123
250
  ```rb
251
+ # config/initializers/ckeditor5.rb
252
+
124
253
  config.presets.define :custom do
125
254
  # ... other configuration
126
255
 
@@ -137,6 +266,8 @@ Keep in mind that the order of items is important, and you should install the co
137
266
  Defines the visibility of the menubar. By default, it's set to `true`.
138
267
 
139
268
  ```rb
269
+ # config/initializers/ckeditor5.rb
270
+
140
271
  config.presets.define :custom do
141
272
  # ... other configuration
142
273
 
@@ -151,7 +282,11 @@ end
151
282
  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:
152
283
 
153
284
  ```rb
285
+ # config/initializers/ckeditor5.rb
286
+
154
287
  config.presets.define :custom do
288
+ # ... other configuration
289
+
155
290
  language :pl
156
291
  end
157
292
  ```
@@ -159,7 +294,11 @@ end
159
294
  In order to set the language for the content, you can pass the `content` keyword argument:
160
295
 
161
296
  ```rb
297
+ # config/initializers/ckeditor5.rb
298
+
162
299
  config.presets.define :custom do
300
+ # ... other configuration
301
+
163
302
  language :en, content: :pl
164
303
  end
165
304
  ```
@@ -169,7 +308,11 @@ end
169
308
  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://`:
170
309
 
171
310
  ```rb
311
+ # config/initializers/ckeditor5.rb
312
+
172
313
  config.presets.define :custom do
314
+ # ... other configuration
315
+
173
316
  configure :link, {
174
317
  defaultProtocol: 'https://'
175
318
  }
@@ -183,7 +326,11 @@ Defines a plugin to be included in the editor. You can pass the name of the plug
183
326
  The example below show how to import Bold plugin from the `ckeditor5` npm package:
184
327
 
185
328
  ```rb
329
+ # config/initializers/ckeditor5.rb
330
+
186
331
  config.presets.define :custom do
332
+ # ... other configuration
333
+
187
334
  plugin :Bold
188
335
  end
189
336
  ```
@@ -191,15 +338,330 @@ end
191
338
  In order to import a plugin from a custom package, you can pass the `import_name` keyword argument:
192
339
 
193
340
  ```rb
341
+ # config/initializers/ckeditor5.rb
342
+
194
343
  config.presets.define :custom do
344
+ # ... other configuration
345
+
195
346
  plugin :YourPlugin, import_name: 'your-package'
196
347
  end
197
348
  ```
198
349
 
199
- ## License
350
+ </details>
351
+
352
+ ## Including CKEditor 5 assets 📦
353
+
354
+ 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 resources of the editor. Depending on the specified configuration, it includes the JS and CSS assets from the official CKEditor 5 CDN or one of the popular CDNs.
355
+
356
+ Keep in mind that you need to include the helper result in the `head` section of your layout. In examples below, we use `content_for` helper to include the assets in the `head` section of the view.
357
+
358
+ ### Lazy loading 🚀
359
+
360
+ <details>
361
+ <summary>Loading JS and CSS Assets</summary>
362
+
363
+ 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.
364
+
365
+ 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.
366
+
367
+ </details>
368
+
369
+ ### GPL usage 🆓
370
+
371
+ 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`:
372
+
373
+ ```erb
374
+ <!-- app/views/demos/index.html.erb -->
375
+
376
+ <% content_for :head do %>
377
+ <%= ckeditor5_assets version: '43.3.0' %>
378
+ <% end %>
379
+ ```
380
+
381
+ 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.
382
+
383
+ Version is optional as long as you defined it in the `config/initializers/ckeditor5.rb` file. If you want to use the default version, you can omit the `version` keyword argument:
384
+
385
+ ```erb
386
+ <!-- app/views/demos/index.html.erb -->
387
+
388
+ <% content_for :head do %>
389
+ <%= ckeditor5_assets %>
390
+ <% end %>
391
+ ```
392
+
393
+ Set the version in the `config/initializers/ckeditor5.rb` file:
394
+
395
+ ```rb
396
+ # config/initializers/ckeditor5.rb
397
+
398
+ CKEditor5::Rails::Engine.configure do
399
+ presets.override :default do
400
+ version '43.3.0'
401
+ end
402
+ end
403
+ ```
404
+
405
+ In order to use `unpkg` CDN, you can pass the `cdn` keyword argument with the value `:unpkg`:
406
+
407
+ ```erb
408
+ <!-- app/views/demos/index.html.erb -->
409
+
410
+ <% content_for :head do %>
411
+ <%= ckeditor5_assets version: '43.3.0', cdn: :unpkg %>
412
+ <% end %>
413
+ ```
414
+
415
+ or using helper function:
416
+
417
+ ```erb
418
+ <!-- app/views/demos/index.html.erb -->
419
+
420
+ <% content_for :head do %>
421
+ <%= ckeditor5_jsdelivr_assets version: '43.3.0' %>
422
+ <% end %>
423
+ ```
424
+
425
+ 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:
426
+
427
+ ```erb
428
+ <!-- app/views/demos/index.html.erb -->
429
+
430
+ <% content_for :head do %>
431
+ <%= ckeditor5_assets version: '43.3.0', translations: [:pl] %>
432
+ <% end %>
433
+ ```
434
+
435
+ Keep in mind, that you need to include the translations in the `config/initializers/ckeditor5.rb` file:
436
+
437
+ ```rb
438
+ # config/initializers/ckeditor5.rb
439
+
440
+ CKEditor5::Rails::Engine.configure do
441
+ presets.override :default do
442
+ language :pl
443
+ end
444
+ end
445
+ ```
446
+
447
+ ### Commercial usage 💰
448
+
449
+ <details>
450
+ <summary>Expand to show more</summary>
451
+
452
+ 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:
453
+
454
+ ```erb
455
+ <!-- app/views/demos/index.html.erb -->
456
+
457
+ <% content_for :head do %>
458
+ <%= ckeditor5_assets license_key: 'your-license-key' %>
459
+ <% end %>
460
+ ```
461
+
462
+ 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.
463
+
464
+ </details>
465
+
466
+ ## Editor placement 🏗️
467
+
468
+ 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 otherwise the editor won't work as there are no CKEditor 5 JavaScript and CSS files loaded.
469
+
470
+ ### Classic editor 📝
471
+
472
+ The classic editor is the most common type of editor. It provides a toolbar with various formatting options like bold, italic, underline, and link.
473
+
474
+ It looks like this:
475
+
476
+ ![CKEditor 5 Classic Editor in Ruby on Rails application with Menubar](docs/classic-editor-with-toolbar.png)
477
+
478
+ The example below shows how to include the classic editor in your view:
479
+
480
+ ```erb
481
+ <!-- app/views/demos/index.html.erb -->
482
+
483
+ <% content_for :head do %>
484
+ <%= ckeditor5_assets %>
485
+ <% end %>
486
+
487
+ <%= ckeditor5_editor style: 'width: 600px' %>
488
+ ```
489
+
490
+ 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.
491
+
492
+ While example above uses predefined `:default` preset, you can use your custom presets by passing the `preset` keyword argument:
493
+
494
+ ```erb
495
+ <!-- app/views/demos/index.html.erb -->
496
+
497
+ <% content_for :head do %>
498
+ <%= ckeditor5_assets %>
499
+ <% end %>
500
+
501
+ <%= ckeditor5_editor preset: :custom, style: 'width: 600px' %>
502
+ ```
503
+
504
+ If your configuration is even more complex, you can pass the `config` and `type` arguments with the configuration hash:
505
+
506
+ ```erb
507
+ <!-- app/views/demos/index.html.erb -->
508
+
509
+ <% content_for :head do %>
510
+ <%= ckeditor5_assets %>
511
+ <% end %>
512
+
513
+ <%= ckeditor5_editor type: :classic, config: { plugins: [:Bold, :Italic], toolbar: [:Bold, :Italic] }, style: 'width: 600px' %>
514
+ ```
515
+
516
+ 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:
517
+
518
+ ```erb
519
+ <!-- app/views/demos/index.html.erb -->
520
+
521
+ <% content_for :head do %>
522
+ <%= ckeditor5_assets %>
523
+ <% end %>
524
+
525
+ <%= ckeditor5_editor extra_config: { toolbar: [:Bold, :Italic] }, style: 'width: 600px' %>
526
+ ```
527
+
528
+ ### Multiroot editor 🌳
529
+
530
+ 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.
531
+
532
+ - `ckeditor5_editor`: Defines the editor instance.
533
+ - `ckeditor5_editable`: Defines the editable areas within the editor.
534
+ - `ckeditor5_toolbar`: Defines the toolbar for the editor.
535
+
536
+ ![CKEditor 5 Multiroot Editor in Ruby on Rails application](docs/multiroot-editor.png)
537
+
538
+ If you want to use a multiroot editor, you can pass the `type` keyword argument with the value `:multiroot`:
539
+
540
+ ```erb
541
+ <!-- app/views/demos/index.html.erb -->
542
+
543
+ <% content_for :head do %>
544
+ <%= ckeditor5_assets %>
545
+ <% end %>
546
+
547
+ <%= ckeditor5_editor type: :multiroot, style: 'width: 600px' do %>
548
+ <%= ckeditor5_toolbar %>
549
+ <br>
550
+ <%= ckeditor5_editable 'toolbar', style: 'border: 1px solid var(--ck-color-base-border);' do %>
551
+ This is a toolbar editable
552
+ <% end %>
553
+ <br>
554
+ <%= ckeditor5_editable 'content', style: 'border: 1px solid var(--ck-color-base-border)' %>
555
+ <br>
556
+ <% end %>
557
+ ```
558
+
559
+ Roots can be defined later to the editor by simply adding new elements rendered by `ckeditor5_editable` helper.
560
+
561
+ ### Inline editor 📝
562
+
563
+ 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.
564
+
565
+ ![CKEditor 5 Inline Editor in Ruby on Rails application](docs/inline-editor.png)
566
+
567
+ If you want to use an inline editor, you can pass the `type` keyword argument with the value `:inline`:
568
+
569
+ ```erb
570
+ <!-- app/views/demos/index.html.erb -->
571
+
572
+ <% content_for :head do %>
573
+ <%= ckeditor5_assets %>
574
+ <% end %>
575
+
576
+ <%= ckeditor5_editor type: :inline, style: 'width: 600px' %>
577
+ ```
578
+
579
+ ### Balloon editor 🎈
580
+
581
+ 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.
582
+
583
+ ![CKEditor 5 Balloon Editor in Ruby on Rails application](docs/balloon-editor.png)
584
+
585
+ If you want to use a balloon editor, you can pass the `type` keyword argument with the value `:balloon`:
586
+
587
+ ```erb
588
+ <!-- app/views/demos/index.html.erb -->
589
+
590
+ <% content_for :head do %>
591
+ <%= ckeditor5_assets %>
592
+ <% end %>
593
+
594
+ <%= ckeditor5_editor type: :balloon, style: 'width: 600px' %>
595
+ ```
596
+
597
+ ### Decoupled editor 🌐
598
+
599
+ 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.
600
+
601
+ ![CKEditor 5 Decoupled Editor in Ruby on Rails application](docs/decoupled-editor.png)
602
+
603
+ If you want to use a decoupled editor, you can pass the `type` keyword argument with the value `:decoupled`:
604
+
605
+ ```erb
606
+ <% content_for :head do %>
607
+ <%= ckeditor5_assets %>
608
+ <% end %>
609
+
610
+ <style>
611
+ .menubar-container,
612
+ .editable-container,
613
+ .toolbar-container {
614
+ position: relative;
615
+ border: 1px solid red;
616
+ }
617
+
618
+ .menubar-container::after,
619
+ .editable-container::after,
620
+ .toolbar-container::after {
621
+ content: attr(class);
622
+ position: absolute;
623
+ background: red;
624
+ color: #fff;
625
+ top: 0;
626
+ right: 0;
627
+ font: 10px/2 monospace;
628
+ padding: .1em .3em;
629
+ }
630
+
631
+ .menubar-container,
632
+ .toolbar-container {
633
+ padding: 1em;
634
+ }
635
+
636
+ .editable-container {
637
+ padding: 3em;
638
+ overflow-y: scroll;
639
+ max-height: 300px;
640
+ }
641
+
642
+ .editable-container .ck-editor__editable {
643
+ min-height: 21cm;
644
+ padding: 2em;
645
+ border: 1px #D3D3D3 solid;
646
+ border-radius: var(--ck-border-radius);
647
+ background: white;
648
+ box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
649
+ }
650
+ </style>
651
+
652
+ <%= ckeditor5_editor type: :decoupled, style: 'width: 600px' do %>
653
+ <div class="menubar-container"><%= ckeditor5_menubar %></div>
654
+ <br>
655
+ <div class="toolbar-container"><%= ckeditor5_toolbar %></div>
656
+ <br>
657
+ <div class="editable-container"><%= ckeditor5_editable %></div>
658
+ <% end %>
659
+ ```
660
+
661
+ ## License 📜
200
662
 
201
663
  The MIT License (MIT)
202
- Copyright (c) Mateusz Bagiński / Łukasz Modliński
664
+ Mateusz Bagiński / Łukasz Modliński
203
665
 
204
666
  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:
205
667
 
@@ -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);
@@ -7,7 +7,7 @@ module CKEditor5::Rails
7
7
 
8
8
  attr_reader :cdn, :version, :theme, :translations
9
9
 
10
- def initialize(version, theme: :lark, cdn: Engine.base.default_cdn, translations: [])
10
+ def initialize(version, theme: :lark, cdn: Engine.default_preset.cdn, translations: [])
11
11
  raise ArgumentError, 'version must be semver' unless version.is_a?(Semver)
12
12
  raise ArgumentError, 'theme must be a string' unless theme.is_a?(String)
13
13
  raise ArgumentError, 'translations must be an array' unless translations.is_a?(Array)
@@ -7,7 +7,7 @@ module CKEditor5::Rails
7
7
 
8
8
  attr_reader :version, :translations, :import_name
9
9
 
10
- def initialize(version, import_name, cdn: Engine.base.default_cdn, translations: [])
10
+ def initialize(version, import_name, cdn: Engine.default_preset.cdn, translations: [])
11
11
  raise ArgumentError, 'version must be semver' unless version.is_a?(Semver)
12
12
  raise ArgumentError, 'import_name must be a string' unless import_name.is_a?(String)
13
13
  raise ArgumentError, 'translations must be an array' unless translations.is_a?(Array)
@@ -6,7 +6,17 @@ require_relative 'ckbox_bundle'
6
6
 
7
7
  module CKEditor5::Rails
8
8
  module Cdn::Helpers
9
- def ckeditor5_cdn_assets(version:, cdn:, license_key: 'GPL', premium: false, translations: [], ckbox: nil)
9
+ def ckeditor5_cdn_assets(preset: :default, **kwargs)
10
+ merge_with_editor_preset(preset, **kwargs) => {
11
+ cdn:,
12
+ version:,
13
+ translations:,
14
+ ckbox:,
15
+ license_key:,
16
+ premium:,
17
+ **kwargs
18
+ }
19
+
10
20
  bundle = build_base_cdn_bundle(cdn, version, translations)
11
21
  bundle << build_premium_cdn_bundle(cdn, version, translations) if premium
12
22
  bundle << build_ckbox_cdn_bundle(ckbox) if ckbox
@@ -29,12 +39,34 @@ module CKEditor5::Rails
29
39
  if kwargs[:license_key] && kwargs[:license_key] != 'GPL'
30
40
  ckeditor5_cloud_assets(**kwargs)
31
41
  else
32
- ckeditor5_cdn_assets(**kwargs.merge(cdn: Engine.base.default_cdn))
42
+ ckeditor5_cdn_assets(**kwargs.merge(cdn: Engine.default_preset.cdn))
33
43
  end
34
44
  end
35
45
 
36
46
  private
37
47
 
48
+ def merge_with_editor_preset(preset, **kwargs)
49
+ found_preset = Engine.base.presets[preset]
50
+
51
+ if found_preset.blank?
52
+ raise ArgumentError,
53
+ "Poor thing. You forgot to define your #{preset} preset. " \
54
+ 'Please define it in initializer. Thank you!'
55
+ end
56
+
57
+ hash = found_preset.to_h_with_overrides(**kwargs)
58
+
59
+ %i[version type].each do |key|
60
+ next if hash[key].present?
61
+
62
+ raise ArgumentError,
63
+ "Poor thing. You forgot to define #{key}. Make sure you passed `#{key}:` parameter to " \
64
+ "`ckeditor5_cdn_assets` or defined default one in your `#{preset}` preset!"
65
+ end
66
+
67
+ hash
68
+ end
69
+
38
70
  def build_base_cdn_bundle(cdn, version, translations)
39
71
  Cdn::CKEditorBundle.new(
40
72
  Semver.new(version),
@@ -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
@@ -54,7 +54,7 @@ module CKEditor5::Rails::Editor
54
54
  def serialize_config
55
55
  config
56
56
  .except(:plugins)
57
- .merge(licenseKey: context[:license_key] || 'GPL')
57
+ .tap { |cfg| cfg[:licenseKey] = context[:license_key] if context[:license_key] }
58
58
  .to_json
59
59
  end
60
60
  end
@@ -7,12 +7,6 @@ module CKEditor5::Rails
7
7
  class Engine < ::Rails::Engine
8
8
  config.ckeditor5 = ActiveSupport::OrderedOptions.new
9
9
 
10
- # Specifies which CDN should be used to load CKEditor 5 using the ckeditor5_assets helper.
11
- # It is possible to use the following CDNs:
12
- # - :unpkg
13
- # - :jsdelivr (default)
14
- config.ckeditor5.default_cdn = :jsdelivr
15
-
16
10
  # Specifies configuration of editors generated by gem.
17
11
  config.ckeditor5.presets = PresetsManager.new
18
12
 
@@ -26,6 +20,10 @@ module CKEditor5::Rails
26
20
  config.ckeditor5
27
21
  end
28
22
 
23
+ def self.default_preset
24
+ config.ckeditor5.presets.default
25
+ end
26
+
29
27
  def self.configure
30
28
  yield config.ckeditor5
31
29
  end
@@ -31,7 +31,9 @@ module CKEditor5::Rails
31
31
 
32
32
  def define_default_preset
33
33
  define :default do
34
- shape :classic
34
+ gpl
35
+
36
+ type :classic
35
37
 
36
38
  menubar
37
39
 
@@ -54,17 +56,79 @@ module CKEditor5::Rails
54
56
  end
55
57
 
56
58
  class PresetBuilder
57
- attr_reader :type, :config
59
+ attr_reader :config
58
60
 
59
61
  def initialize
62
+ @version = nil
63
+ @premium = false
64
+ @cdn = :jsdelivr
65
+ @translations = []
66
+ @license_key = nil
60
67
  @type = :classic
68
+ @ckbox = nil
61
69
  @config = {
62
70
  plugins: [],
63
71
  toolbar: []
64
72
  }
65
73
  end
66
74
 
67
- def shape(type)
75
+ def to_h_with_overrides(**overrides)
76
+ {
77
+ version: overrides.fetch(:version, version),
78
+ premium: overrides.fetch(:premium, premium),
79
+ cdn: overrides.fetch(:cdn, cdn),
80
+ translations: overrides.fetch(:translations, translations),
81
+ license_key: overrides.fetch(:license_key, license_key),
82
+ type: overrides.fetch(:type, type),
83
+ ckbox: overrides.fetch(:ckbox, ckbox),
84
+ config: config.merge(overrides.fetch(:config, {}))
85
+ }
86
+ end
87
+
88
+ def ckbox(version = nil, theme: :lark)
89
+ return @ckbox if version.nil?
90
+
91
+ @ckbox = { version: version, theme: theme }
92
+ end
93
+
94
+ def license_key(license_key = nil)
95
+ return @license_key if license_key.nil?
96
+
97
+ @license_key = license_key
98
+ end
99
+
100
+ def gpl
101
+ license_key('GPL')
102
+ premium(false)
103
+ end
104
+
105
+ def premium(premium = nil)
106
+ return @premium if premium.nil?
107
+
108
+ @premium = premium
109
+ end
110
+
111
+ def translations(*translations)
112
+ return @translations if translations.empty?
113
+
114
+ @translations = translations
115
+ end
116
+
117
+ def version(version = nil)
118
+ return @version.to_s if version.nil?
119
+
120
+ @version = Semver.new(version)
121
+ end
122
+
123
+ def cdn(cdn = nil)
124
+ return @cdn if cdn.nil?
125
+
126
+ @cdn = cdn
127
+ end
128
+
129
+ def type(type = nil)
130
+ return @type if type.nil?
131
+
68
132
  raise ArgumentError, "Invalid editor type: #{type}" unless Editor::Props.valid_editor_type?(type)
69
133
 
70
134
  @type = type
@@ -6,7 +6,7 @@ class CKEditor5::Rails::Semver
6
6
  alias to_s :version
7
7
 
8
8
  def initialize(version)
9
- @version = version
9
+ @version = version.to_s
10
10
  validate!
11
11
  end
12
12
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CKEditor5::Rails
4
- VERSION = '1.0.4'
4
+ VERSION = '1.0.6'
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.4
4
+ version: 1.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński