ckeditor5 1.1.6 → 1.2.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: b95ee184f10909e8acc238dcc5650a4bcbd828cb8cb32719cb98465223c7bc6c
4
- data.tar.gz: 31364b050c64792a5561b4a9fd9de3dfe86a8a338f6a8f8075ca334c1558758d
3
+ metadata.gz: '028a09c0049e3eb489434b0447796369924801d0f5273a400e9783dbd3a8a70c'
4
+ data.tar.gz: e9a3c65f03a85b1361b612c85b00c5cb9d1d8385eeca716c5cea4f0e5817474b
5
5
  SHA512:
6
- metadata.gz: 916eee6c8da16ad1b6d5ed9c7cd4358724e2a07d64b08b1ba77fdb03e2164a9633d3ce5403a6af70d938f0e1ef73e3a725549b83d93689b43c6225b0700456ea
7
- data.tar.gz: c19f382d14e66e8fc8a0f0b49dfa18687cad2697de005700a39532bd6a19928b7f1093ec1aea4fc5d067e36b4edb20ce0476b67946b0700d77045af38d1545e8
6
+ metadata.gz: 5bf454b35f45aca3a44a2463d007cea579714c835d82edf7e6ed351db3c93a94aff59489bab7cea69cd78853acf50473ad9f7cf64819ee5c3c5bc5c0ef2d20a8
7
+ data.tar.gz: e394db6e299f6a48c88f50a306500bcae7c87c0eae6092bdf44c7f62e4a179da542f7299448db18045c086047da16a5818f3915416d9b10bb00747bda9e431ce
data/README.md CHANGED
@@ -75,6 +75,7 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
75
75
  - [Commercial usage 💰](#commercial-usage-)
76
76
  - [Editor placement 🏗️](#editor-placement-️)
77
77
  - [Setting Initial Content 📝](#setting-initial-content-)
78
+ - [Watchdog 🐕](#watchdog-)
78
79
  - [Classic editor 📝](#classic-editor-)
79
80
  - [Multiroot editor 🌳](#multiroot-editor-)
80
81
  - [Inline editor 📝](#inline-editor-)
@@ -490,7 +491,7 @@ If you want to use CKEditor 5 under the GPL license, you can include the assets
490
491
  <!-- app/views/demos/index.html.erb -->
491
492
 
492
493
  <% content_for :head do %>
493
- <%= ckeditor5_assets %>
494
+ <%= ckeditor5_assets version: '43.3.0' %>
494
495
  <% end %>
495
496
  ```
496
497
 
@@ -562,9 +563,6 @@ end
562
563
 
563
564
  ### Commercial usage 💰
564
565
 
565
- <details>
566
- <summary>Expand to show more</summary>
567
-
568
566
  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
567
 
570
568
  ```erb
@@ -577,8 +575,6 @@ If you want to use CKEditor 5 under a commercial license, you can include the as
577
575
 
578
576
  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
577
 
580
- </details>
581
-
582
578
  ## Editor placement 🏗️
583
579
 
584
580
  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 +601,18 @@ The example below shows how to set the initial content of the editor using the `
605
601
  <% end %>
606
602
  ```
607
603
 
604
+ ### Watchdog 🐕
605
+
606
+ 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.
607
+
608
+ If you want to disable the watchdog, you can pass the `watchdog` keyword argument with the value `false`:
609
+
610
+ ```erb
611
+ <!-- app/views/demos/index.html.erb -->
612
+
613
+ <%= ckeditor5_editor watchdog: false %>
614
+ ```
615
+
608
616
  ### Classic editor 📝
609
617
 
610
618
  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 +749,8 @@ Decoupled editor is a variant of classic editor that allows you to separate the
741
749
  If you want to use a decoupled editor, you can pass the `type` keyword argument with the value `:decoupled`:
742
750
 
743
751
  ```erb
752
+ <!-- app/views/demos/index.html.erb -->
753
+
744
754
  <% content_for :head do %>
745
755
  <%= ckeditor5_assets %>
746
756
  <% end %>
@@ -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
  *
@@ -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
@@ -2,16 +2,13 @@
2
2
 
3
3
  require 'rails/engine'
4
4
 
5
- require_relative 'toolbar_builder'
6
- require_relative 'preset_builder'
7
- require_relative 'presets_manager'
8
-
5
+ require_relative 'presets/manager'
9
6
  require_relative 'hooks/form'
10
7
 
11
8
  module CKEditor5::Rails
12
9
  class Engine < ::Rails::Engine
13
10
  config.ckeditor5 = ActiveSupport::OrderedOptions.new
14
- config.ckeditor5.presets = PresetsManager.new
11
+ config.ckeditor5.presets = Presets::Manager.new
15
12
 
16
13
  initializer 'helper' do
17
14
  ActiveSupport.on_load(:action_view) { include Helpers }
@@ -1,7 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module CKEditor5::Rails
4
- class PresetsManager
3
+ require_relative 'preset_builder'
4
+ require_relative 'toolbar_builder'
5
+
6
+ module CKEditor5::Rails::Presets
7
+ class Manager
5
8
  attr_reader :presets
6
9
 
7
10
  def initialize
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails
4
+ module Presets
5
+ class PresetBuilder
6
+ attr_reader :config
7
+
8
+ def initialize
9
+ @version = nil
10
+ @premium = false
11
+ @cdn = :jsdelivr
12
+ @translations = []
13
+ @license_key = nil
14
+ @type = :classic
15
+ @ckbox = nil
16
+ @config = {
17
+ plugins: [],
18
+ toolbar: []
19
+ }
20
+ end
21
+
22
+ def to_h_with_overrides(**overrides)
23
+ {
24
+ version: overrides.fetch(:version, version),
25
+ premium: overrides.fetch(:premium, premium),
26
+ cdn: overrides.fetch(:cdn, cdn),
27
+ translations: overrides.fetch(:translations, translations),
28
+ license_key: overrides.fetch(:license_key, license_key),
29
+ type: overrides.fetch(:type, type),
30
+ ckbox: overrides.fetch(:ckbox, ckbox),
31
+ config: config.merge(overrides.fetch(:config, {}))
32
+ }
33
+ end
34
+
35
+ def ckbox(version = nil, theme: :lark)
36
+ return @ckbox if version.nil?
37
+
38
+ @ckbox = { version: version, theme: theme }
39
+ end
40
+
41
+ def license_key(license_key = nil)
42
+ return @license_key if license_key.nil?
43
+
44
+ @license_key = license_key
45
+ end
46
+
47
+ def gpl
48
+ license_key('GPL')
49
+ premium(false)
50
+ end
51
+
52
+ def premium(premium = nil)
53
+ return @premium if premium.nil?
54
+
55
+ @premium = premium
56
+ end
57
+
58
+ def translations(*translations)
59
+ return @translations if translations.empty?
60
+
61
+ @translations = translations
62
+ end
63
+
64
+ def version(version = nil)
65
+ return @version.to_s if version.nil?
66
+
67
+ @version = Semver.new(version)
68
+ end
69
+
70
+ def cdn(cdn = nil)
71
+ return @cdn if cdn.nil?
72
+
73
+ @cdn = cdn
74
+ end
75
+
76
+ def type(type = nil)
77
+ return @type if type.nil?
78
+ raise ArgumentError, "Invalid editor type: #{type}" unless Editor::Props.valid_editor_type?(type)
79
+
80
+ @type = type
81
+ end
82
+
83
+ def configure(key, value)
84
+ @config[key] = value
85
+ end
86
+
87
+ def menubar(visible: true)
88
+ @config[:menuBar] = {
89
+ isVisible: visible
90
+ }
91
+ end
92
+
93
+ def toolbar(*items, should_group_when_full: true, &block)
94
+ if @config[:toolbar].blank? || !items.empty?
95
+ @config[:toolbar] = {
96
+ items: items,
97
+ shouldNotGroupWhenFull: !should_group_when_full
98
+ }
99
+ end
100
+
101
+ return unless block
102
+
103
+ builder = ToolbarBuilder.new(@config[:toolbar])
104
+ builder.instance_eval(&block)
105
+ end
106
+
107
+ def inline_plugin(name, code)
108
+ @config[:plugins] << Editor::PropsInlinePlugin.new(name, code)
109
+ end
110
+
111
+ def plugin(name, **kwargs)
112
+ @config[:plugins] << Editor::PropsPlugin.new(name, **kwargs)
113
+ end
114
+
115
+ def plugins(*names, **kwargs)
116
+ names.each { |name| plugin(name, **kwargs) }
117
+ end
118
+
119
+ def language(ui, content: ui) # rubocop:disable Naming/MethodParameterName
120
+ @config[:language] = {
121
+ ui: ui,
122
+ content: content
123
+ }
124
+ end
125
+ end
126
+ end
127
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module CKEditor5::Rails
3
+ module CKEditor5::Rails::Presets
4
4
  class ToolbarBuilder
5
5
  def initialize(toolbar_config)
6
6
  @toolbar_config = toolbar_config
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CKEditor5::Rails
4
- VERSION = '1.1.6'
4
+ VERSION = '1.2.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.6
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mateusz Bagiński
@@ -59,10 +59,10 @@ files:
59
59
  - lib/ckeditor5/rails/helpers.rb
60
60
  - lib/ckeditor5/rails/hooks/form.rb
61
61
  - lib/ckeditor5/rails/hooks/simple_form.rb
62
- - lib/ckeditor5/rails/preset_builder.rb
63
- - lib/ckeditor5/rails/presets_manager.rb
62
+ - lib/ckeditor5/rails/presets/manager.rb
63
+ - lib/ckeditor5/rails/presets/preset_builder.rb
64
+ - lib/ckeditor5/rails/presets/toolbar_builder.rb
64
65
  - lib/ckeditor5/rails/semver.rb
65
- - lib/ckeditor5/rails/toolbar_builder.rb
66
66
  - lib/ckeditor5/rails/version.rb
67
67
  homepage: https://github.com/Mati365/ckeditor5-rails
68
68
  licenses:
@@ -1,125 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module CKEditor5::Rails
4
- class PresetBuilder
5
- attr_reader :config
6
-
7
- def initialize
8
- @version = nil
9
- @premium = false
10
- @cdn = :jsdelivr
11
- @translations = []
12
- @license_key = nil
13
- @type = :classic
14
- @ckbox = nil
15
- @config = {
16
- plugins: [],
17
- toolbar: []
18
- }
19
- end
20
-
21
- def to_h_with_overrides(**overrides)
22
- {
23
- version: overrides.fetch(:version, version),
24
- premium: overrides.fetch(:premium, premium),
25
- cdn: overrides.fetch(:cdn, cdn),
26
- translations: overrides.fetch(:translations, translations),
27
- license_key: overrides.fetch(:license_key, license_key),
28
- type: overrides.fetch(:type, type),
29
- ckbox: overrides.fetch(:ckbox, ckbox),
30
- config: config.merge(overrides.fetch(:config, {}))
31
- }
32
- end
33
-
34
- def ckbox(version = nil, theme: :lark)
35
- return @ckbox if version.nil?
36
-
37
- @ckbox = { version: version, theme: theme }
38
- end
39
-
40
- def license_key(license_key = nil)
41
- return @license_key if license_key.nil?
42
-
43
- @license_key = license_key
44
- end
45
-
46
- def gpl
47
- license_key('GPL')
48
- premium(false)
49
- end
50
-
51
- def premium(premium = nil)
52
- return @premium if premium.nil?
53
-
54
- @premium = premium
55
- end
56
-
57
- def translations(*translations)
58
- return @translations if translations.empty?
59
-
60
- @translations = translations
61
- end
62
-
63
- def version(version = nil)
64
- return @version.to_s if version.nil?
65
-
66
- @version = Semver.new(version)
67
- end
68
-
69
- def cdn(cdn = nil)
70
- return @cdn if cdn.nil?
71
-
72
- @cdn = cdn
73
- end
74
-
75
- def type(type = nil)
76
- return @type if type.nil?
77
- raise ArgumentError, "Invalid editor type: #{type}" unless Editor::Props.valid_editor_type?(type)
78
-
79
- @type = type
80
- end
81
-
82
- def configure(key, value)
83
- @config[key] = value
84
- end
85
-
86
- def menubar(visible: true)
87
- @config[:menuBar] = {
88
- isVisible: visible
89
- }
90
- end
91
-
92
- def toolbar(*items, should_group_when_full: true, &block)
93
- if @config[:toolbar].blank? || !items.empty?
94
- @config[:toolbar] = {
95
- items: items,
96
- shouldNotGroupWhenFull: !should_group_when_full
97
- }
98
- end
99
-
100
- return unless block
101
-
102
- builder = ToolbarBuilder.new(@config[:toolbar])
103
- builder.instance_eval(&block)
104
- end
105
-
106
- def inline_plugin(name, code)
107
- @config[:plugins] << Editor::PropsInlinePlugin.new(name, code)
108
- end
109
-
110
- def plugin(name, **kwargs)
111
- @config[:plugins] << Editor::PropsPlugin.new(name, **kwargs)
112
- end
113
-
114
- def plugins(*names, **kwargs)
115
- names.each { |name| plugin(name, **kwargs) }
116
- end
117
-
118
- def language(ui, content: ui) # rubocop:disable Naming/MethodParameterName
119
- @config[:language] = {
120
- ui: ui,
121
- content: content
122
- }
123
- end
124
- end
125
- end