ckeditor5 1.6.1 → 1.8.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: 4ad96eb676ba96b39112d59c4f4404dbdb0c0fbce2b4f550b35c3e101912c18f
4
- data.tar.gz: f58e26500ca145f1b4a4cb8421ee525f9dd3865e545a012cac132b9e667489ab
3
+ metadata.gz: 18736efccfe3547f18ff9df5418fd5f93aafa426423c3ca8daca99de481c6c60
4
+ data.tar.gz: 6be07d38f24ba131e3a8444175b8416561706a97d74bd581d8568703ef3544f2
5
5
  SHA512:
6
- metadata.gz: deffecb44d57d886e23adacb80e2849517e43f5528154ee0e956dac016cd0ce541bd949e8d707049f1750687ecb4e9c872b89c719f7b76c1ad2550fceec51aed
7
- data.tar.gz: 595a4e6f3ea8a192ec28973452074dad87d3f6da84b2ff926868c4921afea8238bc2642649ca0a0d45c734d8dfdfb88fb0b23f84686d9ba10727c5778845a59e
6
+ metadata.gz: c40e50c2093c7f8c311b8565a54ddc4835aba887ced1e36b4802c6681769a8f2e4f39812443e688f6e31ff4932eef76282664be82d7cdbb4b667c9febe76374f
7
+ data.tar.gz: f9a999fb8a325323e497b225f3c8bed54cc24befc5bcd2cc90a93a25dece1d539e5e33a34fbe2a4f9570e462527492e061819563b7fad19e61d24fae8a39953e
data/README.md CHANGED
@@ -73,6 +73,7 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
73
73
  - [`gpl` method](#gpl-method)
74
74
  - [`license_key(key)` method](#license_keykey-method)
75
75
  - [`premium` method](#premium-method)
76
+ - [`editable_height(height)` method](#editable_heightheight-method)
76
77
  - [`translations(*languages)` method](#translationslanguages-method)
77
78
  - [`ckbox` method](#ckbox-method)
78
79
  - [`type(type)` method](#typetype-method)
@@ -83,6 +84,7 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
83
84
  - [`plugin(name, premium:, import_name:)` method](#pluginname-premium-import_name-method)
84
85
  - [`plugins(*names, **kwargs)` method](#pluginsnames-kwargs-method)
85
86
  - [`inline_plugin(name, code)` method](#inline_pluginname-code-method)
87
+ - [`ckeditor5_element_ref(selector)` method](#ckeditor5_element_refselector-method)
86
88
  - [Including CKEditor 5 assets 📦](#including-ckeditor-5-assets-)
87
89
  - [Format 📝](#format-)
88
90
  - [Using default preset](#using-default-preset)
@@ -271,6 +273,20 @@ CKEditor5::Rails.configure do
271
273
  end
272
274
  ```
273
275
 
276
+ #### `editable_height(height)` method
277
+
278
+ Defines the height of the editor. The example below shows how to set the height to `300px`:
279
+
280
+ ```rb
281
+ # config/initializers/ckeditor5.rb
282
+
283
+ CKEditor5::Rails.configure do
284
+ # ... other configuration
285
+
286
+ editable_height 300
287
+ end
288
+ ```
289
+
274
290
  #### `translations(*languages)` method
275
291
 
276
292
  Defines the translations of CKEditor 5. You can pass the language codes as arguments. The example below shows how tell integration to fetch Polish and Spanish translations:
@@ -430,7 +446,9 @@ end
430
446
 
431
447
  #### `configure(name, value)` method
432
448
 
433
- 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://`:
449
+ Allows you to set custom configuration options. You can pass the name of the option and its value as arguments. The [`ckeditor5_element_ref(selector)` helper](#ckeditor5_element_refselector-method) allows you to reference DOM elements that will be used by the editor's features. It's particularly useful for features that need to check element presence or operate on specific DOM elements.
450
+
451
+ For example, you can use it to configure font family dropdown to show only fonts available in specific elements:
434
452
 
435
453
  ```rb
436
454
  # config/initializers/ckeditor5.rb
@@ -438,8 +456,19 @@ Allows you to set custom configuration options. You can pass the name of the opt
438
456
  CKEditor5::Rails.configure do
439
457
  # ... other configuration
440
458
 
441
- configure :link, {
442
- defaultProtocol: 'https://'
459
+ configure :fontFamily, {
460
+ supportAllValues: true,
461
+ options: [
462
+ 'default',
463
+ 'Arial, Helvetica, sans-serif',
464
+ 'Courier New, Courier, monospace',
465
+ 'Georgia, serif',
466
+ 'Lucida Sans Unicode, Lucida Grande, sans-serif',
467
+ 'Tahoma, Geneva, sans-serif',
468
+ 'Times New Roman, Times, serif',
469
+ 'Trebuchet MS, Helvetica, sans-serif',
470
+ 'Verdana, Geneva, sans-serif'
471
+ ]
443
472
  }
444
473
  end
445
474
  ```
@@ -523,6 +552,23 @@ CKEditor5::Rails.configure do
523
552
  JS
524
553
  end
525
554
  ```
555
+
556
+ #### `ckeditor5_element_ref(selector)` method
557
+
558
+ Defines a reference to a CKEditor 5 element. In other words, it allows you to reference DOM elements that will be used by the editor's features. It's particularly useful for features that need to check element presence or operate on specific DOM elements. The primary example is the `presence list` feature that requires a reference to the element that will be used to display the list.
559
+
560
+ ```rb
561
+ # config/initializers/ckeditor5.rb
562
+
563
+ CKEditor5::Rails.configure do
564
+ # ... other configuration
565
+
566
+ configure :yourPlugin, {
567
+ element: ckeditor5_element_ref("body")
568
+ }
569
+ end
570
+ ```
571
+
526
572
  </details>
527
573
 
528
574
  ## Including CKEditor 5 assets 📦
@@ -808,6 +854,18 @@ If you want to override the configuration of the editor specified in default or
808
854
  <%= ckeditor5_editor extra_config: { toolbar: [:Bold, :Italic] }, style: 'width: 600px' %>
809
855
  ```
810
856
 
857
+ It's possible to define the height of the editor by passing the `editable_height` keyword argument with the value in pixels:
858
+
859
+ ```erb
860
+ <!-- app/views/demos/index.html.erb -->
861
+
862
+ <% content_for :head do %>
863
+ <%= ckeditor5_assets %>
864
+ <% end %>
865
+
866
+ <%= ckeditor5_editor editable_height: 300 %>
867
+ ```
868
+
811
869
  ### Multiroot editor 🌳
812
870
 
813
871
  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.
@@ -132,6 +132,72 @@ class CKEditorComponent extends HTMLElement {
132
132
  }
133
133
  }
134
134
 
135
+ /**
136
+ * Resolves element references in configuration object.
137
+ * Looks for objects with { $element: "selector" } format and replaces them with actual elements.
138
+ *
139
+ * @private
140
+ * @param {Object} obj - Configuration object to process
141
+ * @returns {Object} Processed configuration object with resolved element references
142
+ */
143
+ #resolveElementReferences(obj) {
144
+ if (!obj || typeof obj !== 'object') {
145
+ return obj;
146
+ }
147
+
148
+ if (Array.isArray(obj)) {
149
+ return obj.map(item => this.#resolveElementReferences(item));
150
+ }
151
+
152
+ const result = Object.create(null);
153
+
154
+ for (const key of Object.getOwnPropertyNames(obj)) {
155
+ if (!isSafeKey(key)) {
156
+ console.warn(`Suspicious key "${key}" detected in config, skipping`);
157
+ continue;
158
+ }
159
+
160
+ const value = obj[key];
161
+
162
+ if (value && typeof value === 'object') {
163
+ if (value.$element) {
164
+ const selector = value.$element;
165
+
166
+ if (typeof selector !== 'string') {
167
+ console.warn(`Invalid selector type for "${key}", expected string`);
168
+ continue;
169
+ }
170
+
171
+ const element = document.querySelector(selector);
172
+
173
+ if (!element) {
174
+ console.warn(`Element not found for selector: ${selector}`);
175
+ }
176
+
177
+ result[key] = element || null;
178
+ } else {
179
+ result[key] = this.#resolveElementReferences(value);
180
+ }
181
+ } else {
182
+ result[key] = value;
183
+ }
184
+ }
185
+
186
+ return result;
187
+ }
188
+
189
+ /**
190
+ * Gets editor configuration with resolved element references
191
+ *
192
+ * @private
193
+ * @returns {EditorConfig}
194
+ */
195
+ #getConfig() {
196
+ const config = JSON.parse(this.getAttribute('config') || '{}');
197
+
198
+ return this.#resolveElementReferences(config);
199
+ }
200
+
135
201
  /**
136
202
  * Creates a new CKEditor instance
137
203
  *
@@ -228,6 +294,7 @@ class CKEditorComponent extends HTMLElement {
228
294
  this.instance = instance;
229
295
 
230
296
  this.#setupContentSync();
297
+ this.#setupEditableHeight();
231
298
 
232
299
  this.instancePromise.resolve(this.instance);
233
300
  } catch (err) {
@@ -236,10 +303,18 @@ class CKEditorComponent extends HTMLElement {
236
303
  }
237
304
  }
238
305
 
306
+ /**
307
+ * Checks if current editor is classic type
308
+ *
309
+ * @returns {boolean}
310
+ */
311
+ isClassic() {
312
+ return this.getAttribute('type') === 'ClassicEditor';
313
+ }
314
+
239
315
  /**
240
316
  * Checks if current editor is multiroot type
241
317
  *
242
- * @private
243
318
  * @returns {boolean}
244
319
  */
245
320
  isMultiroot() {
@@ -249,7 +324,6 @@ class CKEditorComponent extends HTMLElement {
249
324
  /**
250
325
  * Checks if current editor is decoupled type
251
326
  *
252
- * @private
253
327
  * @returns {boolean}
254
328
  */
255
329
  isDecoupled() {
@@ -259,23 +333,12 @@ class CKEditorComponent extends HTMLElement {
259
333
  /**
260
334
  * Checks if current editor has watchdog enabled
261
335
  *
262
- * @private
263
336
  * @returns {boolean}
264
337
  */
265
338
  hasWatchdog() {
266
339
  return this.getAttribute('watchdog') === 'true';
267
340
  }
268
341
 
269
- /**
270
- * Parses editor configuration from config attribute
271
- *
272
- * @private
273
- * @returns {EditorConfig}
274
- */
275
- #getConfig() {
276
- return JSON.parse(this.getAttribute('config') || '{}');
277
- }
278
-
279
342
  /**
280
343
  * Queries and validates editable elements
281
344
  *
@@ -378,6 +441,28 @@ class CKEditorComponent extends HTMLElement {
378
441
  });
379
442
  }
380
443
 
444
+ /**
445
+ * Sets up editable height for ClassicEditor
446
+ *
447
+ * @private
448
+ */
449
+ #setupEditableHeight() {
450
+ if (!this.isClassic()) {
451
+ return;
452
+ }
453
+
454
+ const { instance } = this;
455
+ const height = Number.parseInt(this.getAttribute('editable-height'), 10);
456
+
457
+ if (Number.isNaN(height)) {
458
+ return;
459
+ }
460
+
461
+ instance.editing.view.change((writer) => {
462
+ writer.setStyle('height', `${height}px`, instance.editing.view.document.getRoot());
463
+ });
464
+ }
465
+
381
466
  /**
382
467
  * Loads translation modules
383
468
  *
@@ -795,6 +880,20 @@ function loadAsyncImports(imports = []) {
795
880
  }));
796
881
  }
797
882
 
883
+
884
+ /**
885
+ * Checks if a key is safe to use in configuration objects to prevent prototype pollution.
886
+ *
887
+ * @param {string} key - Key name to check
888
+ * @returns {boolean} True if key is safe to use.
889
+ */
890
+ function isSafeKey(key) {
891
+ return typeof key === 'string' &&
892
+ key !== '__proto__' &&
893
+ key !== 'constructor' &&
894
+ key !== 'prototype';
895
+ }
896
+
798
897
  customElements.define('ckeditor-component', CKEditorComponent);
799
898
  customElements.define('ckeditor-editable-component', CKEditorEditableComponent);
800
899
  customElements.define('ckeditor-ui-part-component', CKEditorUIPartComponent);
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CKEditor5::Rails
4
+ module Editor::ConfigHelpers
5
+ def ckeditor5_element_ref(selector)
6
+ { '$element': selector }
7
+ end
8
+ end
9
+ end
@@ -3,31 +3,42 @@
3
3
  require_relative 'props_plugin'
4
4
  require_relative 'props_inline_plugin'
5
5
  require_relative 'props'
6
+ require_relative 'config_helpers'
6
7
 
7
8
  module CKEditor5::Rails
8
9
  module Editor::Helpers
10
+ include Editor::ConfigHelpers
11
+
9
12
  class EditorContextError < StandardError; end
10
13
  class PresetNotFoundError < ArgumentError; end
14
+ class InvalidEditableHeightError < ArgumentError; end
11
15
 
12
16
  def ckeditor5_editor( # rubocop:disable Metrics/ParameterLists
13
17
  config: nil, extra_config: {},
14
18
  type: nil, preset: nil,
15
19
  initial_data: nil, watchdog: true,
20
+ editable_height: nil,
16
21
  **html_attributes, &block
17
22
  )
18
23
  validate_editor_input!(initial_data, block)
24
+
19
25
  controller_context = validate_and_get_editor_context!
20
26
 
21
27
  preset = resolve_editor_preset(preset || controller_context[:preset])
22
28
  config = build_editor_config(preset, config, extra_config, initial_data)
23
29
  type ||= preset.type
24
30
 
31
+ validated_height = validate_editable_height(type, editable_height) || preset.editable_height
25
32
  editor_props = Editor::Props.new(
26
33
  controller_context, type, config,
27
34
  watchdog: watchdog
28
35
  )
29
36
 
30
- render_editor_component(editor_props, html_attributes, &block)
37
+ render_editor_component(
38
+ editor_props,
39
+ html_attributes.merge(validated_height ? { 'editable-height' => validated_height } : {}),
40
+ &block
41
+ )
31
42
  end
32
43
 
33
44
  def ckeditor5_editable(name = nil, **kwargs, &block)
@@ -83,5 +94,23 @@ module CKEditor5::Rails
83
94
  def render_editor_component(props, html_attributes, &block)
84
95
  tag.send(:'ckeditor-component', **props.to_attributes, **html_attributes, &block)
85
96
  end
97
+
98
+ def validate_editable_height(type, height)
99
+ return nil if height.nil?
100
+
101
+ unless type == :classic
102
+ raise InvalidEditableHeightError,
103
+ 'editable_height can be used only with ClassicEditor'
104
+ end
105
+
106
+ case height
107
+ when String, /^\d+px$/ then height
108
+ when Integer, /^\d+$/ then "#{height}px"
109
+ else
110
+ raise InvalidEditableHeightError,
111
+ "editable_height must be an integer representing pixels or string ending with 'px'\n" \
112
+ "(e.g. 500 or '500px'). Got: #{height.inspect}"
113
+ end
114
+ end
86
115
  end
87
116
  end
@@ -3,6 +3,8 @@
3
3
  module CKEditor5::Rails
4
4
  module Presets
5
5
  class PresetBuilder
6
+ include Editor::ConfigHelpers
7
+
6
8
  attr_reader :config
7
9
 
8
10
  def initialize
@@ -13,6 +15,7 @@ module CKEditor5::Rails
13
15
  @license_key = nil
14
16
  @type = :classic
15
17
  @ckbox = nil
18
+ @editable_height = nil
16
19
  @config = {
17
20
  plugins: [],
18
21
  toolbar: []
@@ -32,6 +35,12 @@ module CKEditor5::Rails
32
35
  }
33
36
  end
34
37
 
38
+ def editable_height(height = nil)
39
+ return @editable_height if height.nil?
40
+
41
+ @editable_height = height
42
+ end
43
+
35
44
  def ckbox(version = nil, theme: :lark)
36
45
  return @ckbox if version.nil?
37
46
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module CKEditor5
4
4
  module Rails
5
- VERSION = '1.6.1'
5
+ VERSION = '1.8.0'
6
6
  end
7
7
  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.6.1
4
+ version: 1.8.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-11 00:00:00.000000000 Z
12
+ date: 2024-11-14 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -51,6 +51,7 @@ files:
51
51
  - lib/ckeditor5/rails/cdn/helpers.rb
52
52
  - lib/ckeditor5/rails/cdn/url_generator.rb
53
53
  - lib/ckeditor5/rails/cloud/helpers.rb
54
+ - lib/ckeditor5/rails/editor/config_helpers.rb
54
55
  - lib/ckeditor5/rails/editor/helpers.rb
55
56
  - lib/ckeditor5/rails/editor/props.rb
56
57
  - lib/ckeditor5/rails/editor/props_inline_plugin.rb