ckeditor5 1.7.0 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +76 -7
- data/lib/ckeditor5/rails/assets/webcomponent.mjs +280 -20
- data/lib/ckeditor5/rails/context/helpers.rb +13 -0
- data/lib/ckeditor5/rails/context/props.rb +30 -0
- data/lib/ckeditor5/rails/editor/config_helpers.rb +9 -0
- data/lib/ckeditor5/rails/editor/helpers.rb +10 -14
- data/lib/ckeditor5/rails/helpers.rb +2 -0
- data/lib/ckeditor5/rails/presets/preset_builder.rb +2 -0
- data/lib/ckeditor5/rails/version.rb +1 -1
- metadata +5 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bb520612d3f2847e8d70c48a8ffb3d1ad4be25800dd5f141a6948caf30b07978
|
4
|
+
data.tar.gz: 5f9850fb6a39774ab796f67b5dc95fa0ec1e6a5fe6f04fb3481f6ff17c76e026
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7057a3e537f5626b4754b815c1f418ddac64301350aa3b0299d5051dcda474a5f397018389a0e7c773bc1d15687043498a2bcd44518581e4233f8ab4ae85dc52
|
7
|
+
data.tar.gz: 0526b5810f411971ce3adafa1bad252eabaffc566f3d637b6585c2a16a6d6c938078e8b88ed88b34e610477c87c66f5dfc74d092fab2f46f183327f2fca9d287
|
data/README.md
CHANGED
@@ -84,6 +84,7 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
|
|
84
84
|
- [`plugin(name, premium:, import_name:)` method](#pluginname-premium-import_name-method)
|
85
85
|
- [`plugins(*names, **kwargs)` method](#pluginsnames-kwargs-method)
|
86
86
|
- [`inline_plugin(name, code)` method](#inline_pluginname-code-method)
|
87
|
+
- [`ckeditor5_element_ref(selector)` method](#ckeditor5_element_refselector-method)
|
87
88
|
- [Including CKEditor 5 assets 📦](#including-ckeditor-5-assets-)
|
88
89
|
- [Format 📝](#format-)
|
89
90
|
- [Using default preset](#using-default-preset)
|
@@ -100,6 +101,9 @@ Voilà! You have CKEditor 5 integrated with your Rails application. 🎉
|
|
100
101
|
- [Inline editor 📝](#inline-editor-)
|
101
102
|
- [Balloon editor 🎈](#balloon-editor-)
|
102
103
|
- [Decoupled editor 🌐](#decoupled-editor-)
|
104
|
+
- [Using Context 📦](#using-context-)
|
105
|
+
- [Benefits of Using Context in Collaboration 🤝](#benefits-of-using-context-in-collaboration-)
|
106
|
+
- [Using Context in CKEditor 5 🔄](#using-context-in-ckeditor-5-)
|
103
107
|
- [How to access editor instance? 🤔](#how-to-access-editor-instance-)
|
104
108
|
- [Common Tasks and Solutions 💡](#common-tasks-and-solutions-)
|
105
109
|
- [Setting Editor Language 🌐](#setting-editor-language-)
|
@@ -183,9 +187,6 @@ Configuration of the editor can be complex, and it's recommended to use the [CKE
|
|
183
187
|
|
184
188
|
### Available Configuration Methods ⚙️
|
185
189
|
|
186
|
-
<details>
|
187
|
-
<summary>Expand to show available methods 📖</summary>
|
188
|
-
|
189
190
|
#### `cdn(cdn = nil, &block)` method
|
190
191
|
|
191
192
|
Defines the CDN to be used for CKEditor 5 assets. The example below shows how to set the CDN to `:jsdelivr`:
|
@@ -445,7 +446,9 @@ end
|
|
445
446
|
|
446
447
|
#### `configure(name, value)` method
|
447
448
|
|
448
|
-
Allows you to set custom configuration options. You can pass the name of the option and its value as arguments. The
|
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:
|
449
452
|
|
450
453
|
```rb
|
451
454
|
# config/initializers/ckeditor5.rb
|
@@ -453,8 +456,19 @@ Allows you to set custom configuration options. You can pass the name of the opt
|
|
453
456
|
CKEditor5::Rails.configure do
|
454
457
|
# ... other configuration
|
455
458
|
|
456
|
-
configure :
|
457
|
-
|
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
|
+
]
|
458
472
|
}
|
459
473
|
end
|
460
474
|
```
|
@@ -538,7 +552,22 @@ CKEditor5::Rails.configure do
|
|
538
552
|
JS
|
539
553
|
end
|
540
554
|
```
|
541
|
-
|
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
|
+
```
|
542
571
|
|
543
572
|
## Including CKEditor 5 assets 📦
|
544
573
|
|
@@ -934,6 +963,46 @@ If you want to use a decoupled editor, you can pass the `type` keyword argument
|
|
934
963
|
<% end %>
|
935
964
|
```
|
936
965
|
|
966
|
+
## Using Context 📦
|
967
|
+
|
968
|
+
Context CKEditor 5 is a feature that allows multiple editor instances to share a common configuration and state. This is particularly useful in collaborative environments where multiple users are editing different parts of the same document simultaneously. By using a shared context, all editor instances can synchronize their configurations, plugins, and other settings, ensuring a consistent editing experience across all users.
|
969
|
+
|
970
|
+
### Benefits of Using Context in Collaboration 🤝
|
971
|
+
|
972
|
+
1. **Consistency**: Ensures that all editor instances have the same configuration, plugins, and settings, providing a uniform editing experience.
|
973
|
+
2. **Synchronization**: Allows real-time synchronization of content and changes across multiple editor instances, making collaborative editing seamless.
|
974
|
+
3. **Resource Sharing**: Reduces the overhead of loading and initializing multiple editor instances by sharing common resources and configurations.
|
975
|
+
4. **Simplified Management**: Makes it easier to manage and update the configuration and state of multiple editor instances from a single point.
|
976
|
+
|
977
|
+
### Using Context in CKEditor 5 🔄
|
978
|
+
|
979
|
+
Format of the `ckeditor5_context` helper:
|
980
|
+
|
981
|
+
```erb
|
982
|
+
<%= ckeditor5_context config: { ...you context config... }, plugins: [ ...your context plugins... ] do %>
|
983
|
+
<%= ckeditor5_editor %>
|
984
|
+
<%= ckeditor5_editor %>
|
985
|
+
<% end %>
|
986
|
+
```
|
987
|
+
|
988
|
+
Example usage:
|
989
|
+
|
990
|
+
```erb
|
991
|
+
<!-- app/views/demos/index.html.erb -->
|
992
|
+
|
993
|
+
<% content_for :head do %>
|
994
|
+
<%= ckeditor5_assets preset: :ultrabasic %>
|
995
|
+
<% end %>
|
996
|
+
|
997
|
+
<%= ckeditor5_context do %>
|
998
|
+
<%= ckeditor5_editor initial_data: 'Hello World' %>
|
999
|
+
|
1000
|
+
<br>
|
1001
|
+
|
1002
|
+
<%= ckeditor5_editor initial_data: 'Hello World 2' %>
|
1003
|
+
<% end %>
|
1004
|
+
```
|
1005
|
+
|
937
1006
|
## How to access editor instance? 🤔
|
938
1007
|
|
939
1008
|
You can access the editor instance using plain HTML and JavaScript, as CKEditor 5 is a web component with defined `instance`, `instancePromise` and `editables` properties.
|
@@ -57,16 +57,30 @@ class CKEditorComponent extends HTMLElement {
|
|
57
57
|
/** @type {String} Initial HTML passed to component */
|
58
58
|
#initialHTML = '';
|
59
59
|
|
60
|
+
/** @type {CKEditorContextComponent|null} */
|
61
|
+
#context = null;
|
62
|
+
|
63
|
+
/** @type {String} ID of editor within context */
|
64
|
+
#contextEditorId = null;
|
65
|
+
|
60
66
|
/**
|
61
67
|
* Lifecycle callback when element is connected to DOM
|
62
68
|
* Initializes the editor when DOM is ready
|
63
69
|
* @protected
|
64
70
|
*/
|
65
71
|
connectedCallback() {
|
72
|
+
this.#context = this.closest('ckeditor-context-component');
|
66
73
|
this.#initialHTML = this.innerHTML;
|
67
74
|
|
68
75
|
try {
|
69
|
-
execIfDOMReady(() =>
|
76
|
+
execIfDOMReady(async () => {
|
77
|
+
if (this.#context) {
|
78
|
+
await this.#context.instancePromise.promise;
|
79
|
+
this.#context.registerEditor(this);
|
80
|
+
}
|
81
|
+
|
82
|
+
await this.reinitializeEditor();
|
83
|
+
});
|
70
84
|
} catch (error) {
|
71
85
|
console.error('Failed to initialize editor:', error);
|
72
86
|
this.dispatchEvent(new CustomEvent('editor-error', { detail: error }));
|
@@ -84,7 +98,7 @@ class CKEditorComponent extends HTMLElement {
|
|
84
98
|
if (oldValue !== null &&
|
85
99
|
oldValue !== newValue &&
|
86
100
|
CKEditorComponent.observedAttributes.includes(name) && this.isConnected) {
|
87
|
-
await this
|
101
|
+
await this.reinitializeEditor();
|
88
102
|
}
|
89
103
|
}
|
90
104
|
|
@@ -94,9 +108,12 @@ class CKEditorComponent extends HTMLElement {
|
|
94
108
|
* @protected
|
95
109
|
*/
|
96
110
|
async disconnectedCallback() {
|
111
|
+
if (this.#context) {
|
112
|
+
this.#context.unregisterEditor(this);
|
113
|
+
}
|
114
|
+
|
97
115
|
try {
|
98
|
-
await this
|
99
|
-
await this.watchdog?.destroy();
|
116
|
+
await this.#destroy();
|
100
117
|
} catch (error) {
|
101
118
|
console.error('Failed to destroy editor:', error);
|
102
119
|
}
|
@@ -119,6 +136,7 @@ class CKEditorComponent extends HTMLElement {
|
|
119
136
|
|
120
137
|
/**
|
121
138
|
* Determines appropriate editor element tag based on editor type
|
139
|
+
*
|
122
140
|
* @private
|
123
141
|
* @returns {string} HTML tag name to use
|
124
142
|
*/
|
@@ -132,6 +150,40 @@ class CKEditorComponent extends HTMLElement {
|
|
132
150
|
}
|
133
151
|
}
|
134
152
|
|
153
|
+
/**
|
154
|
+
* Gets the CKEditor context instance if available.
|
155
|
+
*
|
156
|
+
* @private
|
157
|
+
* @returns {import('ckeditor5').ContextWatchdog|null}
|
158
|
+
*/
|
159
|
+
get #contextWatchdog() {
|
160
|
+
return this.#context?.instance;
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Destroys the editor instance and watchdog if available
|
165
|
+
*/
|
166
|
+
async #destroy() {
|
167
|
+
if (this.#contextEditorId) {
|
168
|
+
await this.#contextWatchdog.remove(this.#contextEditorId);
|
169
|
+
}
|
170
|
+
|
171
|
+
await this.instance?.destroy();
|
172
|
+
await this.watchdog?.destroy();
|
173
|
+
}
|
174
|
+
|
175
|
+
/**
|
176
|
+
* Gets editor configuration with resolved element references
|
177
|
+
*
|
178
|
+
* @private
|
179
|
+
* @returns {EditorConfig}
|
180
|
+
*/
|
181
|
+
#getConfig() {
|
182
|
+
const config = JSON.parse(this.getAttribute('config') || '{}');
|
183
|
+
|
184
|
+
return resolveElementReferences(config);
|
185
|
+
}
|
186
|
+
|
135
187
|
/**
|
136
188
|
* Creates a new CKEditor instance
|
137
189
|
*
|
@@ -166,13 +218,27 @@ class CKEditorComponent extends HTMLElement {
|
|
166
218
|
plugins,
|
167
219
|
};
|
168
220
|
|
169
|
-
console.warn('Initializing CKEditor with:', { config, watchdog: this.hasWatchdog() });
|
221
|
+
console.warn('Initializing CKEditor with:', { config, watchdog: this.hasWatchdog(), context: this.#context });
|
170
222
|
|
171
223
|
// Initialize watchdog if needed
|
172
224
|
let watchdog = null;
|
173
225
|
let instance = null;
|
174
|
-
|
175
|
-
|
226
|
+
let contextId = null;
|
227
|
+
|
228
|
+
if (this.#context) {
|
229
|
+
contextId = uid();
|
230
|
+
|
231
|
+
await this.#contextWatchdog.add( {
|
232
|
+
creator: (_element, _config) => Editor.create(_element, _config),
|
233
|
+
id: contextId,
|
234
|
+
sourceElementOrData: content,
|
235
|
+
type: 'editor',
|
236
|
+
config,
|
237
|
+
} );
|
238
|
+
|
239
|
+
instance = this.#contextWatchdog.getItem(contextId);
|
240
|
+
} else if (this.hasWatchdog()) {
|
241
|
+
// Let's create use with plain watchdog.
|
176
242
|
const { EditorWatchdog } = await import('ckeditor5');
|
177
243
|
const watchdog = new EditorWatchdog(Editor);
|
178
244
|
|
@@ -180,12 +246,14 @@ class CKEditorComponent extends HTMLElement {
|
|
180
246
|
|
181
247
|
instance = watchdog.editor;
|
182
248
|
} else {
|
249
|
+
// Let's create the editor without watchdog.
|
183
250
|
instance = await Editor.create(content, config);
|
184
251
|
}
|
185
252
|
|
186
253
|
this.dispatchEvent(new CustomEvent('editor-ready', { detail: instance }));
|
187
254
|
|
188
255
|
return {
|
256
|
+
contextId,
|
189
257
|
instance,
|
190
258
|
watchdog,
|
191
259
|
};
|
@@ -197,11 +265,12 @@ class CKEditorComponent extends HTMLElement {
|
|
197
265
|
* @private
|
198
266
|
* @returns {Promise<void>}
|
199
267
|
*/
|
200
|
-
async
|
268
|
+
async reinitializeEditor() {
|
201
269
|
if (this.instance) {
|
202
270
|
this.instancePromise = Promise.withResolvers();
|
203
271
|
|
204
|
-
await this
|
272
|
+
await this.#destroy();
|
273
|
+
|
205
274
|
this.instance = null;
|
206
275
|
}
|
207
276
|
|
@@ -222,10 +291,11 @@ class CKEditorComponent extends HTMLElement {
|
|
222
291
|
}
|
223
292
|
|
224
293
|
try {
|
225
|
-
const { watchdog, instance } = await this.#initializeEditor(this.editables || this.#getConfig().initialData || '');
|
294
|
+
const { watchdog, instance, contextId } = await this.#initializeEditor(this.editables || this.#getConfig().initialData || '');
|
226
295
|
|
227
296
|
this.watchdog = watchdog;
|
228
297
|
this.instance = instance;
|
298
|
+
this.#contextEditorId = contextId;
|
229
299
|
|
230
300
|
this.#setupContentSync();
|
231
301
|
this.#setupEditableHeight();
|
@@ -273,16 +343,6 @@ class CKEditorComponent extends HTMLElement {
|
|
273
343
|
return this.getAttribute('watchdog') === 'true';
|
274
344
|
}
|
275
345
|
|
276
|
-
/**
|
277
|
-
* Parses editor configuration from config attribute
|
278
|
-
*
|
279
|
-
* @private
|
280
|
-
* @returns {EditorConfig}
|
281
|
-
*/
|
282
|
-
#getConfig() {
|
283
|
-
return JSON.parse(this.getAttribute('config') || '{}');
|
284
|
-
}
|
285
|
-
|
286
346
|
/**
|
287
347
|
* Queries and validates editable elements
|
288
348
|
*
|
@@ -455,6 +515,129 @@ class CKEditorComponent extends HTMLElement {
|
|
455
515
|
}
|
456
516
|
}
|
457
517
|
|
518
|
+
/**
|
519
|
+
* Custom element that provides shared CKEditor context for multiple editors.
|
520
|
+
*
|
521
|
+
* @extends HTMLElement
|
522
|
+
* @example
|
523
|
+
*
|
524
|
+
* <ckeditor-context-component plugins='[ ... ]'>
|
525
|
+
* <ckeditor-component type="ClassicEditor" config='{"toolbar": ["bold", "italic"]}'>
|
526
|
+
* <ckeditor-component type="ClassicEditor" config='{"toolbar": ["bold", "italic"]}'>
|
527
|
+
* </ckeditor-component>
|
528
|
+
*/
|
529
|
+
class CKEditorContextComponent extends HTMLElement {
|
530
|
+
static get observedAttributes() {
|
531
|
+
return ['plugins', 'config'];
|
532
|
+
}
|
533
|
+
|
534
|
+
/** @type {import('ckeditor5').Context|null} */
|
535
|
+
instance = null;
|
536
|
+
|
537
|
+
/** @type {Promise<import('ckeditor5').Context>} */
|
538
|
+
instancePromise = Promise.withResolvers();
|
539
|
+
|
540
|
+
/** @type {Set<CKEditorComponent>} */
|
541
|
+
#connectedEditors = new Set();
|
542
|
+
|
543
|
+
async connectedCallback() {
|
544
|
+
try {
|
545
|
+
execIfDOMReady(() => this.#initializeContext());
|
546
|
+
} catch (error) {
|
547
|
+
console.error('Failed to initialize context:', error);
|
548
|
+
this.dispatchEvent(new CustomEvent('context-error', { detail: error }));
|
549
|
+
}
|
550
|
+
}
|
551
|
+
|
552
|
+
async attributeChangedCallback(name, oldValue, newValue) {
|
553
|
+
if (oldValue !== null && oldValue !== newValue) {
|
554
|
+
await this.#initializeContext();
|
555
|
+
}
|
556
|
+
}
|
557
|
+
|
558
|
+
async disconnectedCallback() {
|
559
|
+
if (this.instance) {
|
560
|
+
await this.instance.destroy();
|
561
|
+
this.instance = null;
|
562
|
+
}
|
563
|
+
}
|
564
|
+
|
565
|
+
/**
|
566
|
+
* Register editor component with this context
|
567
|
+
*
|
568
|
+
* @param {CKEditorComponent} editor
|
569
|
+
*/
|
570
|
+
registerEditor(editor) {
|
571
|
+
this.#connectedEditors.add(editor);
|
572
|
+
}
|
573
|
+
|
574
|
+
/**
|
575
|
+
* Unregister editor component from this context
|
576
|
+
*
|
577
|
+
* @param {CKEditorComponent} editor
|
578
|
+
*/
|
579
|
+
unregisterEditor(editor) {
|
580
|
+
this.#connectedEditors.delete(editor);
|
581
|
+
}
|
582
|
+
|
583
|
+
/**
|
584
|
+
* Initialize CKEditor context with shared configuration
|
585
|
+
*
|
586
|
+
* @private
|
587
|
+
*/
|
588
|
+
async #initializeContext() {
|
589
|
+
if (this.instance) {
|
590
|
+
this.instancePromise = Promise.withResolvers();
|
591
|
+
|
592
|
+
await this.instance.destroy();
|
593
|
+
|
594
|
+
this.instance = null;
|
595
|
+
}
|
596
|
+
|
597
|
+
const { Context, ContextWatchdog } = await import('ckeditor5');
|
598
|
+
const plugins = await this.#getPlugins();
|
599
|
+
const config = this.#getConfig();
|
600
|
+
|
601
|
+
this.instance = new ContextWatchdog(Context, {
|
602
|
+
crashNumberLimit: 10
|
603
|
+
});
|
604
|
+
|
605
|
+
await this.instance.create({
|
606
|
+
...config,
|
607
|
+
plugins
|
608
|
+
});
|
609
|
+
|
610
|
+
this.instance.on('itemError', (...args) => {
|
611
|
+
console.error('Context item error:', ...args);
|
612
|
+
});
|
613
|
+
|
614
|
+
this.instancePromise.resolve(this.instance);
|
615
|
+
this.dispatchEvent(new CustomEvent('context-ready', { detail: this.instance }));
|
616
|
+
|
617
|
+
// Reinitialize connected editors.
|
618
|
+
await Promise.all(
|
619
|
+
[...this.#connectedEditors].map(editor => editor.reinitializeEditor())
|
620
|
+
);
|
621
|
+
}
|
622
|
+
|
623
|
+
async #getPlugins() {
|
624
|
+
const raw = this.getAttribute('plugins');
|
625
|
+
|
626
|
+
return loadAsyncImports(raw ? JSON.parse(raw) : []);
|
627
|
+
}
|
628
|
+
|
629
|
+
/**
|
630
|
+
* Gets context configuration with resolved element references.
|
631
|
+
*
|
632
|
+
* @private
|
633
|
+
*/
|
634
|
+
#getConfig() {
|
635
|
+
const config = JSON.parse(this.getAttribute('config') || '{}');
|
636
|
+
|
637
|
+
return resolveElementReferences(config);
|
638
|
+
}
|
639
|
+
}
|
640
|
+
|
458
641
|
/**
|
459
642
|
* Tracks and manages editable roots for CKEditor MultiRoot editor.
|
460
643
|
* Provides a proxy-based API for dynamically managing editable elements with automatic
|
@@ -824,6 +1007,83 @@ function loadAsyncImports(imports = []) {
|
|
824
1007
|
}));
|
825
1008
|
}
|
826
1009
|
|
1010
|
+
|
1011
|
+
/**
|
1012
|
+
* Checks if a key is safe to use in configuration objects to prevent prototype pollution.
|
1013
|
+
*
|
1014
|
+
* @param {string} key - Key name to check
|
1015
|
+
* @returns {boolean} True if key is safe to use.
|
1016
|
+
*/
|
1017
|
+
function isSafeKey(key) {
|
1018
|
+
return typeof key === 'string' &&
|
1019
|
+
key !== '__proto__' &&
|
1020
|
+
key !== 'constructor' &&
|
1021
|
+
key !== 'prototype';
|
1022
|
+
}
|
1023
|
+
|
1024
|
+
/**
|
1025
|
+
* Resolves element references in configuration object.
|
1026
|
+
* Looks for objects with { $element: "selector" } format and replaces them with actual elements.
|
1027
|
+
*
|
1028
|
+
* @param {Object} obj - Configuration object to process
|
1029
|
+
* @returns {Object} Processed configuration object with resolved element references
|
1030
|
+
*/
|
1031
|
+
function resolveElementReferences(obj) {
|
1032
|
+
if (!obj || typeof obj !== 'object') {
|
1033
|
+
return obj;
|
1034
|
+
}
|
1035
|
+
|
1036
|
+
if (Array.isArray(obj)) {
|
1037
|
+
return obj.map(item => resolveElementReferences(item));
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
const result = Object.create(null);
|
1041
|
+
|
1042
|
+
for (const key of Object.getOwnPropertyNames(obj)) {
|
1043
|
+
if (!isSafeKey(key)) {
|
1044
|
+
console.warn(`Suspicious key "${key}" detected in config, skipping`);
|
1045
|
+
continue;
|
1046
|
+
}
|
1047
|
+
|
1048
|
+
const value = obj[key];
|
1049
|
+
|
1050
|
+
if (value && typeof value === 'object') {
|
1051
|
+
if (value.$element) {
|
1052
|
+
const selector = value.$element;
|
1053
|
+
|
1054
|
+
if (typeof selector !== 'string') {
|
1055
|
+
console.warn(`Invalid selector type for "${key}", expected string`);
|
1056
|
+
continue;
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
const element = document.querySelector(selector);
|
1060
|
+
|
1061
|
+
if (!element) {
|
1062
|
+
console.warn(`Element not found for selector: ${selector}`);
|
1063
|
+
}
|
1064
|
+
|
1065
|
+
result[key] = element || null;
|
1066
|
+
} else {
|
1067
|
+
result[key] = resolveElementReferences(value);
|
1068
|
+
}
|
1069
|
+
} else {
|
1070
|
+
result[key] = value;
|
1071
|
+
}
|
1072
|
+
}
|
1073
|
+
|
1074
|
+
return result;
|
1075
|
+
}
|
1076
|
+
|
1077
|
+
/**
|
1078
|
+
* Custom element that provides shared CKEditor context for multiple editors.
|
1079
|
+
*
|
1080
|
+
* @returns {String} unique id
|
1081
|
+
*/
|
1082
|
+
function uid() {
|
1083
|
+
return Math.random().toString(36).substring(2);
|
1084
|
+
}
|
1085
|
+
|
827
1086
|
customElements.define('ckeditor-component', CKEditorComponent);
|
828
1087
|
customElements.define('ckeditor-editable-component', CKEditorEditableComponent);
|
829
1088
|
customElements.define('ckeditor-ui-part-component', CKEditorUIPartComponent);
|
1089
|
+
customElements.define('ckeditor-context-component', CKEditorContextComponent);
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'props'
|
4
|
+
|
5
|
+
module CKEditor5::Rails::Context
|
6
|
+
module Helpers
|
7
|
+
def ckeditor5_context(**config, &block)
|
8
|
+
context_props = Props.new(config)
|
9
|
+
|
10
|
+
tag.send(:'ckeditor-context-component', **context_props.to_attributes, &block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module CKEditor5::Rails
|
4
|
+
module Context
|
5
|
+
class Props
|
6
|
+
def initialize(config)
|
7
|
+
@config = config
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_attributes
|
11
|
+
{
|
12
|
+
plugins: serialize_plugins,
|
13
|
+
config: serialize_config
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
attr_reader :config
|
20
|
+
|
21
|
+
def serialize_plugins
|
22
|
+
(config[:plugins] || []).map { |plugin| Editor::PropsPlugin.normalize(plugin).to_h }.to_json
|
23
|
+
end
|
24
|
+
|
25
|
+
def serialize_config
|
26
|
+
config.except(:plugins).to_json
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -3,9 +3,12 @@
|
|
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
|
11
14
|
class InvalidEditableHeightError < ArgumentError; end
|
@@ -26,16 +29,13 @@ module CKEditor5::Rails
|
|
26
29
|
type ||= preset.type
|
27
30
|
|
28
31
|
validated_height = validate_editable_height(type, editable_height) || preset.editable_height
|
29
|
-
editor_props = Editor::Props.new(
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
html_attributes.merge(validated_height ? { 'editable-height' => validated_height } : {}),
|
37
|
-
&block
|
38
|
-
)
|
32
|
+
editor_props = Editor::Props.new(controller_context, type, config, watchdog: watchdog)
|
33
|
+
|
34
|
+
tag_attributes = html_attributes
|
35
|
+
.merge(editor_props.to_attributes)
|
36
|
+
.merge(validated_height ? { 'editable-height' => validated_height } : {})
|
37
|
+
|
38
|
+
tag.send(:'ckeditor-component', **tag_attributes, &block)
|
39
39
|
end
|
40
40
|
|
41
41
|
def ckeditor5_editable(name = nil, **kwargs, &block)
|
@@ -88,10 +88,6 @@ module CKEditor5::Rails
|
|
88
88
|
raise PresetNotFoundError, "Preset #{preset} is not defined."
|
89
89
|
end
|
90
90
|
|
91
|
-
def render_editor_component(props, html_attributes, &block)
|
92
|
-
tag.send(:'ckeditor-component', **props.to_attributes, **html_attributes, &block)
|
93
|
-
end
|
94
|
-
|
95
91
|
def validate_editable_height(type, height)
|
96
92
|
return nil if height.nil?
|
97
93
|
|
@@ -3,11 +3,13 @@
|
|
3
3
|
require_relative 'cdn/helpers'
|
4
4
|
require_relative 'cloud/helpers'
|
5
5
|
require_relative 'editor/helpers'
|
6
|
+
require_relative 'context/helpers'
|
6
7
|
|
7
8
|
module CKEditor5::Rails
|
8
9
|
module Helpers
|
9
10
|
include Cdn::Helpers
|
10
11
|
include Cloud::Helpers
|
11
12
|
include Editor::Helpers
|
13
|
+
include Context::Helpers
|
12
14
|
end
|
13
15
|
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.
|
4
|
+
version: 1.9.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-
|
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,9 @@ 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/context/helpers.rb
|
55
|
+
- lib/ckeditor5/rails/context/props.rb
|
56
|
+
- lib/ckeditor5/rails/editor/config_helpers.rb
|
54
57
|
- lib/ckeditor5/rails/editor/helpers.rb
|
55
58
|
- lib/ckeditor5/rails/editor/props.rb
|
56
59
|
- lib/ckeditor5/rails/editor/props_inline_plugin.rb
|