ckeditor5 1.1.7 → 1.3.0

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: a512b289e611ca5d7480b5088b14f83ed459481b6f99f0c15b4467e627c464e7
4
- data.tar.gz: dc3969b71fd184b7e9bc39a95fc7d0702280203c940197925d8e8f8d17d22a50
3
+ metadata.gz: c7b18841155e113a731b8b81c953fd13547a9ed25f3d5be662786297fc6a985a
4
+ data.tar.gz: 8894ffe8876faad795fab39bdae58c03fdfb4dd014337c30c6573e20c881b74b
5
5
  SHA512:
6
- metadata.gz: d3491c052e7e736f1ed484a67eaf7fae034fd73874977c4bcfd671295e5fe5d9673d7c27845df6877fb527a4a29763ad3529c58e30fcee893f8a174fe066ac84
7
- data.tar.gz: e579d1032b12e79d265da252ad74af5b090922aeb0eb4d68ab12c252d9ed173ec9b1f5e4cc904f4fcc62c4dbc0cb20b4b79ae87ee9cfff40afc33b1497caf464
6
+ metadata.gz: f2abcac7b7c28c763b095b83078ce25b6d081b59120a8a8e40f48a1711334d9ac59828bd047bb3d727bc85c2e2ce2325a263c18941485f1c062fa98ddb789b22
7
+ data.tar.gz: e7d0c636c623904202bb3c6a18e5196c90f5c39bffd18f9a24749e764e338032a7ba8f2c2d0367e24e337b8ea03c616fb2a8308b95deae5f2a81f37ca0199ba8
data/README.md CHANGED
@@ -39,11 +39,9 @@ In your view:
39
39
 
40
40
  <% content_for :head do %>
41
41
  <%= ckeditor5_assets %>
42
- <!-- or using inline config: ckeditor5_assets version: '43.3.0', premium: false -->
43
42
  <% end %>
44
43
 
45
- <%= ckeditor5_editor style: 'width: 600px', initial_data: '<YOUR DATA>' %>
46
- <!-- or using inline config: ckeditor5_editor type: :classic, config: { toolbar: [:Bold, :Italic] }, ... -->
44
+ <%= ckeditor5_editor %>
47
45
  ```
48
46
 
49
47
  Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
@@ -55,6 +53,7 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
55
53
  - [Table of Contents 📚](#table-of-contents-)
56
54
  - [Presets 🎨](#presets-)
57
55
  - [Available Configuration Methods ⚙️](#available-configuration-methods-️)
56
+ - [`cdn(cdn = nil, &block)` method](#cdncdn--nil-block-method)
58
57
  - [`version(version)` method](#versionversion-method)
59
58
  - [`gpl` method](#gpl-method)
60
59
  - [`license_key(key)` method](#license_keykey-method)
@@ -75,6 +74,7 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
75
74
  - [Commercial usage 💰](#commercial-usage-)
76
75
  - [Editor placement 🏗️](#editor-placement-️)
77
76
  - [Setting Initial Content 📝](#setting-initial-content-)
77
+ - [Watchdog 🐕](#watchdog-)
78
78
  - [Classic editor 📝](#classic-editor-)
79
79
  - [Multiroot editor 🌳](#multiroot-editor-)
80
80
  - [Inline editor 📝](#inline-editor-)
@@ -141,21 +141,48 @@ CKEditor5::Rails.configure do |config|
141
141
 
142
142
  toolbar do
143
143
  remove :underline, :heading
144
-
145
- # prepend :underline
146
- # append :heading
147
144
  end
148
145
  end
149
146
  end
150
147
  ```
151
148
 
152
- 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.
149
+ 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.
153
150
 
154
151
  ### Available Configuration Methods ⚙️
155
152
 
156
153
  <details>
157
154
  <summary>Expand to show available methods 📖</summary>
158
155
 
156
+ #### `cdn(cdn = nil, &block)` method
157
+
158
+ Defines the CDN to be used for CKEditor 5 assets. The example below shows how to set the CDN to `:jsdelivr`:
159
+
160
+ ```rb
161
+ # config/initializers/ckeditor5.rb
162
+
163
+ config.presets.define :custom do
164
+ # ... other configuration
165
+
166
+ cdn :jsdelivr
167
+ end
168
+ ```
169
+
170
+ It also allows you to define a custom CDN by passing a block with the bundle, version, and path arguments. The example below shows how to define it for the `jsdelivr` CDN:
171
+
172
+ ```rb
173
+ # config/initializers/ckeditor5.rb
174
+
175
+ config.presets.define :custom do
176
+ # ... other configuration
177
+
178
+ cdn do |bundle, version, path|
179
+ base_url = "https://cdn.jsdelivr.net/npm/#{bundle}@#{version}/dist"
180
+
181
+ "#{base_url}/#{path.start_with?('translations/') ? '' : 'browser/'}#{path}"
182
+ end
183
+ end
184
+ ```
185
+
159
186
  #### `version(version)` method
160
187
 
161
188
  Defines the version of CKEditor 5 to be used. The example below shows how to set the version to `43.2.0`:
@@ -490,7 +517,7 @@ If you want to use CKEditor 5 under the GPL license, you can include the assets
490
517
  <!-- app/views/demos/index.html.erb -->
491
518
 
492
519
  <% content_for :head do %>
493
- <%= ckeditor5_assets %>
520
+ <%= ckeditor5_assets version: '43.3.0' %>
494
521
  <% end %>
495
522
  ```
496
523
 
@@ -562,9 +589,6 @@ end
562
589
 
563
590
  ### Commercial usage 💰
564
591
 
565
- <details>
566
- <summary>Expand to show more</summary>
567
-
568
592
  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:
569
593
 
570
594
  ```erb
@@ -577,8 +601,6 @@ If you want to use CKEditor 5 under a commercial license, you can include the as
577
601
 
578
602
  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.
579
603
 
580
- </details>
581
-
582
604
  ## Editor placement 🏗️
583
605
 
584
606
  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.
@@ -605,6 +627,18 @@ The example below shows how to set the initial content of the editor using the `
605
627
  <% end %>
606
628
  ```
607
629
 
630
+ ### Watchdog 🐕
631
+
632
+ CKEditor 5 uses a watchdog utility to protect you from data loss in case the editor crashes. It saves your content just before the crash and creates a new instance of the editor with your content intact. It's enabled by default in the gem.
633
+
634
+ If you want to disable the watchdog, you can pass the `watchdog` keyword argument with the value `false`:
635
+
636
+ ```erb
637
+ <!-- app/views/demos/index.html.erb -->
638
+
639
+ <%= ckeditor5_editor watchdog: false %>
640
+ ```
641
+
608
642
  ### Classic editor 📝
609
643
 
610
644
  The classic editor is the most common type of editor. It provides a toolbar with various formatting options like bold, italic, underline, and link.
@@ -741,6 +775,8 @@ Decoupled editor is a variant of classic editor that allows you to separate the
741
775
  If you want to use a decoupled editor, you can pass the `type` keyword argument with the value `:decoupled`:
742
776
 
743
777
  ```erb
778
+ <!-- app/views/demos/index.html.erb -->
779
+
744
780
  <% content_for :head do %>
745
781
  <%= ckeditor5_assets %>
746
782
  <% end %>
@@ -868,34 +904,25 @@ The example below shows how to define a custom plugin that allows toggling the h
868
904
  config.presets.define :custom do
869
905
  # ... other configuration
870
906
 
871
- # 1. You can define it inline like below or in a separate file.
872
-
873
- # In case if plugin is located in external file (recommended), you can simply import it:
874
-
875
- # inline_plugin :MyCustomPlugin, <<~JS
876
- # import MyPlugin from 'app/javascript/custom_plugins/highlight.js';
877
- # export default MyPlugin;
878
- # JS
879
-
880
- # 2. You can also use "window_name" option to import plugin from window object:
907
+ # 1. You can also use "window_name" option to import plugin from window object:
881
908
 
882
909
  # plugin :MyPlugin, window_name: 'MyPlugin'
883
910
 
884
- # 3. Create JavaScript file in app/javascript/custom_plugins/highlight.js:
885
- # You can also use "plugin" to import plugin from file using 'import_name' option.
886
- # Your `my-custom-plugin` must be present in import map.
911
+ # 2. Create JavaScript file in app/javascript/custom_plugins/highlight.js,
912
+ # add it to import map and then load it in initializer:
887
913
 
888
914
  # plugin :MyCustomPlugin, import_name: 'my-custom-plugin'
889
915
 
890
- # 4 Create JavaScript file in app/javascript/custom_plugins/highlight.js:
916
+ # 3 Create JavaScript file in app/javascript/custom_plugins/highlight.js
917
+ # and then load it in initializer:
891
918
 
892
919
  # In Ruby initializer you can also load plugin code directly from file:
893
- plugin :MyCustomPlugin, File.read(
920
+ inline_plugin :MyCustomPlugin, File.read(
894
921
  Rails.root.join('app/javascript/custom_plugins/highlight.js')
895
922
  )
896
923
 
897
- # 5. Or even define it inline:
898
- # plugin :MyCustomPlugin, <<~JS
924
+ # 4. Or even define it inline:
925
+ # inline_plugin :MyCustomPlugin, <<~JS
899
926
  # import { Plugin } from 'ckeditor5';
900
927
  #
901
928
  # export default class MyCustomPlugin extends Plugin {
@@ -45,6 +45,9 @@ class CKEditorComponent extends HTMLElement {
45
45
  /** @type {Promise<import('ckeditor5').Editor>|null} Promise to initialize editor instance */
46
46
  instancePromise = Promise.withResolvers();
47
47
 
48
+ /** @type {import('ckeditor5').Watchdog|null} Editor watchdog */
49
+ watchdog = null;
50
+
48
51
  /** @type {import('ckeditor5').Editor|null} Current editor instance */
49
52
  instance = null;
50
53
 
@@ -93,6 +96,7 @@ class CKEditorComponent extends HTMLElement {
93
96
  async disconnectedCallback() {
94
97
  try {
95
98
  await this.instance?.destroy();
99
+ await this.watchdog?.destroy();
96
100
  } catch (error) {
97
101
  console.error('Failed to destroy editor:', error);
98
102
  }
@@ -129,10 +133,11 @@ class CKEditorComponent extends HTMLElement {
129
133
  }
130
134
 
131
135
  /**
132
- * Initializes a new CKEditor instance
136
+ * Creates a new CKEditor instance
137
+ *
133
138
  * @private
134
139
  * @param {Record<string, HTMLElement>|CKEditorMultiRootEditablesTracker} editablesOrContent - Editable or content
135
- * @returns {Promise<import('ckeditor5').Editor>} Initialized editor instance
140
+ * @returns {Promise<{ editor: import('ckeditor5').Editor, watchdog: editor: import('ckeditor5').EditorWatchdog }>} Initialized editor instance
136
141
  * @throws {Error} When initialization fails
137
142
  */
138
143
  async #initializeEditor(editablesOrContent) {
@@ -142,6 +147,9 @@ class CKEditorComponent extends HTMLElement {
142
147
  this.#getTranslations()
143
148
  ]);
144
149
 
150
+ // Depending on the type of the editor the content supplied on the first
151
+ // argument is different. For ClassicEditor it's a element or string, for MultiRootEditor
152
+ // it's an object with editables, for DecoupledEditor it's string.
145
153
  let content = editablesOrContent;
146
154
 
147
155
  if (editablesOrContent instanceof CKEditorMultiRootEditablesTracker) {
@@ -158,13 +166,29 @@ class CKEditorComponent extends HTMLElement {
158
166
  plugins,
159
167
  };
160
168
 
161
- console.warn('Initializing CKEditor with config:', config);
169
+ console.warn('Initializing CKEditor with:', { config, watchdog: this.hasWatchdog() });
170
+
171
+ // Initialize watchdog if needed
172
+ let watchdog = null;
173
+ let instance = null;
162
174
 
163
- const instance = await Editor.create(content, config);
175
+ if (this.hasWatchdog()) {
176
+ const { EditorWatchdog } = await import('ckeditor5');
177
+ const watchdog = new EditorWatchdog(Editor);
178
+
179
+ await watchdog.create(content, config);
180
+
181
+ instance = watchdog.editor;
182
+ } else {
183
+ instance = await Editor.create(content, config);
184
+ }
164
185
 
165
186
  this.dispatchEvent(new CustomEvent('editor-ready', { detail: instance }));
166
187
 
167
- return instance;
188
+ return {
189
+ instance,
190
+ watchdog,
191
+ };
168
192
  }
169
193
 
170
194
  /**
@@ -198,7 +222,11 @@ class CKEditorComponent extends HTMLElement {
198
222
  }
199
223
 
200
224
  try {
201
- this.instance = await this.#initializeEditor(this.editables || this.#getConfig().initialData || '');
225
+ const { watchdog, instance } = await this.#initializeEditor(this.editables || this.#getConfig().initialData || '');
226
+
227
+ this.watchdog = watchdog;
228
+ this.instance = instance;
229
+
202
230
  this.#setupContentSync();
203
231
 
204
232
  this.instancePromise.resolve(this.instance);
@@ -228,6 +256,16 @@ class CKEditorComponent extends HTMLElement {
228
256
  return this.getAttribute('type') === 'DecoupledEditor';
229
257
  }
230
258
 
259
+ /**
260
+ * Checks if current editor has watchdog enabled
261
+ *
262
+ * @private
263
+ * @returns {boolean}
264
+ */
265
+ hasWatchdog() {
266
+ return this.getAttribute('watchdog') === 'true';
267
+ }
268
+
231
269
  /**
232
270
  * Parses editor configuration from config attribute
233
271
  *
@@ -19,8 +19,11 @@ module CKEditor5::Rails::Cdn
19
19
  }.freeze
20
20
 
21
21
  CDN_COMMERCIAL_GENERATORS = {
22
- cloud: ->(bundle, version, path) { "https://cdn.ckeditor.com/#{bundle}/#{version}/#{path}" },
23
- ckbox: ->(bundle, version, path) { "https://cdn.ckbox.io/#{bundle}/#{version}/#{path}" }
22
+ cloud: lambda { |bundle, version, path|
23
+ domain = bundle == 'ckbox' ? 'ckbox.io' : 'ckeditor.com'
24
+
25
+ "https://cdn.#{domain}/#{bundle}/#{version}/#{path}"
26
+ }
24
27
  }.freeze
25
28
 
26
29
  included do
@@ -28,7 +31,7 @@ module CKEditor5::Rails::Cdn
28
31
  end
29
32
 
30
33
  def create_cdn_url(bundle, version, path)
31
- generator = CDN_THIRD_PARTY_GENERATORS[cdn] || CDN_COMMERCIAL_GENERATORS[cdn]
34
+ generator = CDN_THIRD_PARTY_GENERATORS[cdn] || CDN_COMMERCIAL_GENERATORS[cdn] || cdn
32
35
 
33
36
  raise ArgumentError, "Unknown provider: #{cdn}" unless generator
34
37
 
@@ -9,13 +9,13 @@ module CKEditor5::Rails
9
9
  class EditorContextError < StandardError; end
10
10
  class PresetNotFoundError < ArgumentError; end
11
11
 
12
- def ckeditor5_editor(
12
+ def ckeditor5_editor( # rubocop:disable Metrics/ParameterLists
13
13
  config: nil, extra_config: {},
14
14
  type: nil, preset: :default,
15
- initial_data: nil,
15
+ initial_data: nil, watchdog: true,
16
16
  **html_attributes, &block
17
17
  )
18
- context = validate_and_get_editor_context!
18
+ controller_context = validate_and_get_editor_context!
19
19
  preset = fetch_editor_preset(preset)
20
20
 
21
21
  config ||= preset.config
@@ -26,10 +26,9 @@ module CKEditor5::Rails
26
26
 
27
27
  raise ArgumentError, 'Cannot pass initial data and block at the same time.' if initial_data && block
28
28
 
29
- editor_props = build_editor_props(
30
- config: config,
31
- type: type,
32
- context: context
29
+ editor_props = Editor::Props.new(
30
+ controller_context, type, config,
31
+ watchdog: watchdog
33
32
  )
34
33
 
35
34
  render_editor_component(editor_props, html_attributes, &block)
@@ -68,10 +67,6 @@ module CKEditor5::Rails
68
67
  raise PresetNotFoundError, "Preset #{preset} is not defined."
69
68
  end
70
69
 
71
- def build_editor_props(config:, type:, context:)
72
- Editor::Props.new(context, type, config)
73
- end
74
-
75
70
  def render_editor_component(props, html_attributes, &block)
76
71
  tag.send(:'ckeditor-component', **props.to_attributes, **html_attributes, &block)
77
72
  end
@@ -12,10 +12,11 @@ module CKEditor5::Rails::Editor
12
12
  multiroot: 'MultiRootEditor'
13
13
  }.freeze
14
14
 
15
- def initialize(context, type, config)
15
+ def initialize(controller_context, type, config, watchdog: true)
16
16
  raise ArgumentError, "Invalid editor type: #{type}" unless Props.valid_editor_type?(type)
17
17
 
18
- @context = context
18
+ @controller_context = controller_context
19
+ @watchdog = watchdog
19
20
  @type = type
20
21
  @config = config
21
22
  end
@@ -33,18 +34,19 @@ module CKEditor5::Rails::Editor
33
34
 
34
35
  private
35
36
 
36
- attr_reader :context, :type, :config
37
+ attr_reader :controller_context, :watchdog, :type, :config
37
38
 
38
39
  def serialized_attributes
39
40
  {
40
41
  translations: serialize_translations,
41
42
  plugins: serialize_plugins,
42
- config: serialize_config
43
+ config: serialize_config,
44
+ watchdog: watchdog
43
45
  }
44
46
  end
45
47
 
46
48
  def serialize_translations
47
- context[:bundle].translations_scripts.map(&:to_h).to_json
49
+ controller_context[:bundle].translations_scripts.map(&:to_h).to_json
48
50
  end
49
51
 
50
52
  def serialize_plugins
@@ -54,7 +56,7 @@ module CKEditor5::Rails::Editor
54
56
  def serialize_config
55
57
  config
56
58
  .except(:plugins)
57
- .tap { |cfg| cfg[:licenseKey] = context[:license_key] if context[:license_key] }
59
+ .tap { |cfg| cfg[:licenseKey] = controller_context[:license_key] if controller_context[:license_key] }
58
60
  .to_json
59
61
  end
60
62
  end
@@ -67,10 +67,19 @@ module CKEditor5::Rails
67
67
  @version = Semver.new(version)
68
68
  end
69
69
 
70
- def cdn(cdn = nil)
71
- return @cdn if cdn.nil?
72
-
73
- @cdn = cdn
70
+ def cdn(cdn = nil, &block)
71
+ return @cdn if cdn.nil? && block.nil?
72
+
73
+ if block_given?
74
+ unless block.arity == 3
75
+ raise ArgumentError,
76
+ 'Block must accept exactly 3 arguments: bundle, version, path'
77
+ end
78
+
79
+ @cdn = block
80
+ else
81
+ @cdn = cdn
82
+ end
74
83
  end
75
84
 
76
85
  def type(type = nil)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CKEditor5::Rails
4
- VERSION = '1.1.7'
4
+ VERSION = '1.3.0'
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.1.7
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2024-11-01 00:00:00.000000000 Z
12
+ date: 2024-11-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails