ckeditor5-blazor 1.10.2 → 1.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/elements/context/context.d.ts.map +1 -1
- package/dist/elements/editor/editor.d.ts.map +1 -1
- package/dist/elements/editor/types/editor-relaxed-constructor.type.d.ts +6 -0
- package/dist/elements/editor/types/editor-relaxed-constructor.type.d.ts.map +1 -0
- package/dist/elements/editor/types/index.d.ts +2 -0
- package/dist/elements/editor/types/index.d.ts.map +1 -0
- package/dist/elements/editor/utils/assign-initial-data-to-editor-config.d.ts +10 -0
- package/dist/elements/editor/utils/assign-initial-data-to-editor-config.d.ts.map +1 -0
- package/dist/elements/editor/utils/assign-source-elements-to-editor-config.d.ts +12 -0
- package/dist/elements/editor/utils/assign-source-elements-to-editor-config.d.ts.map +1 -0
- package/dist/elements/editor/utils/create-editor-in-context.d.ts +1 -3
- package/dist/elements/editor/utils/create-editor-in-context.d.ts.map +1 -1
- package/dist/elements/editor/utils/index.d.ts +3 -0
- package/dist/elements/editor/utils/index.d.ts.map +1 -1
- package/dist/elements/editor/utils/normalize-editor-language.d.ts +9 -0
- package/dist/elements/editor/utils/normalize-editor-language.d.ts.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +410 -353
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/elements/context/context.test.ts +107 -66
- package/src/elements/context/context.ts +9 -3
- package/src/elements/editable.ts +2 -2
- package/src/elements/editor/editor.ts +33 -29
- package/src/elements/editor/types/editor-relaxed-constructor.type.ts +6 -0
- package/src/elements/editor/types/index.ts +1 -0
- package/src/elements/editor/utils/assign-initial-data-to-editor-config.ts +47 -0
- package/src/elements/editor/utils/assign-source-elements-to-editor-config.ts +59 -0
- package/src/elements/editor/utils/create-editor-in-context.ts +2 -5
- package/src/elements/editor/utils/index.ts +3 -0
- package/src/elements/editor/utils/normalize-editor-language.test.ts +50 -0
- package/src/elements/editor/utils/normalize-editor-language.ts +25 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ckeditor5-blazor",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "CKEditor 5 integration for Blazor",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
],
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"@vitest/coverage-v8": "^4.0.18",
|
|
31
|
-
"ckeditor5": "^
|
|
32
|
-
"ckeditor5-premium-features": "^
|
|
31
|
+
"ckeditor5": "^48.1.0",
|
|
32
|
+
"ckeditor5-premium-features": "^48.1.0",
|
|
33
33
|
"happy-dom": "^20.8.9",
|
|
34
34
|
"typescript": "^5.9.3",
|
|
35
35
|
"vite": "^7.3.2",
|
|
@@ -84,102 +84,143 @@ describe('context component', () => {
|
|
|
84
84
|
expect(context?.plugins.get('CustomPlugin')).toBeInstanceOf(CustomPlugin);
|
|
85
85
|
});
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
describe('language param', () => {
|
|
88
|
+
it('should support passing null language for context translations', async () => {
|
|
89
|
+
class CustomPlugin extends ContextPlugin {
|
|
90
|
+
static get pluginName() {
|
|
91
|
+
return 'CustomPlugin';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
getHelloTitle() {
|
|
95
|
+
return this.context.t('HELLO');
|
|
96
|
+
}
|
|
91
97
|
}
|
|
92
98
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
CustomEditorPluginsRegistry.the.register('CustomPlugin', () => CustomPlugin);
|
|
100
|
+
|
|
101
|
+
renderTestContext(createContextSnapshot(
|
|
102
|
+
DEFAULT_TEST_CONTEXT_ID,
|
|
103
|
+
{
|
|
104
|
+
customTranslations: {
|
|
105
|
+
en: {
|
|
106
|
+
HELLO: 'Hello from CustomPlugin',
|
|
107
|
+
},
|
|
108
|
+
pl: {
|
|
109
|
+
HELLO: 'Witaj z CustomPlugin',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
config: {
|
|
113
|
+
plugins: ['CustomPlugin'],
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
null,
|
|
117
|
+
));
|
|
97
118
|
|
|
98
|
-
|
|
119
|
+
const { context } = await waitForTestContext();
|
|
120
|
+
const plugin = context?.plugins.get('CustomPlugin') as CustomPlugin;
|
|
99
121
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
122
|
+
expect(plugin.getHelloTitle()).toBe('Hello from CustomPlugin');
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('should support custom language for context translations', async () => {
|
|
126
|
+
class CustomPlugin extends ContextPlugin {
|
|
127
|
+
static get pluginName() {
|
|
128
|
+
return 'CustomPlugin';
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getHelloTitle() {
|
|
132
|
+
return this.context.t('HELLO');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
CustomEditorPluginsRegistry.the.register('CustomPlugin', () => CustomPlugin);
|
|
137
|
+
|
|
138
|
+
renderTestContext(createContextSnapshot(
|
|
139
|
+
DEFAULT_TEST_CONTEXT_ID,
|
|
140
|
+
{
|
|
141
|
+
customTranslations: {
|
|
142
|
+
en: {
|
|
143
|
+
HELLO: 'Hello from CustomPlugin',
|
|
144
|
+
},
|
|
145
|
+
pl: {
|
|
146
|
+
HELLO: 'Witaj z CustomPlugin',
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
config: {
|
|
150
|
+
plugins: ['CustomPlugin'],
|
|
151
|
+
},
|
|
104
152
|
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
153
|
+
{
|
|
154
|
+
ui: 'pl',
|
|
155
|
+
content: 'pl',
|
|
156
|
+
},
|
|
157
|
+
));
|
|
110
158
|
|
|
111
|
-
|
|
112
|
-
|
|
159
|
+
const { context } = await waitForTestContext();
|
|
160
|
+
const plugin = context?.plugins.get('CustomPlugin') as CustomPlugin;
|
|
113
161
|
|
|
114
|
-
|
|
162
|
+
expect(plugin.getHelloTitle()).toBe('Witaj z CustomPlugin');
|
|
163
|
+
});
|
|
115
164
|
});
|
|
116
165
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
166
|
+
describe('translations', () => {
|
|
167
|
+
it('registered plugins should support custom translations', async () => {
|
|
168
|
+
class CustomPlugin extends ContextPlugin {
|
|
169
|
+
static get pluginName() {
|
|
170
|
+
return 'CustomPlugin';
|
|
171
|
+
}
|
|
122
172
|
|
|
123
|
-
|
|
124
|
-
|
|
173
|
+
getHelloTitle() {
|
|
174
|
+
return this.context.t('HELLO');
|
|
175
|
+
}
|
|
125
176
|
}
|
|
126
|
-
}
|
|
127
177
|
|
|
128
|
-
|
|
178
|
+
CustomEditorPluginsRegistry.the.register('CustomPlugin', () => CustomPlugin);
|
|
129
179
|
|
|
130
|
-
|
|
131
|
-
DEFAULT_TEST_CONTEXT_ID,
|
|
132
|
-
{
|
|
180
|
+
renderTestContext(createContextSnapshot(DEFAULT_TEST_CONTEXT_ID, {
|
|
133
181
|
customTranslations: {
|
|
134
182
|
en: {
|
|
135
183
|
HELLO: 'Hello from CustomPlugin',
|
|
136
184
|
},
|
|
137
|
-
pl: {
|
|
138
|
-
HELLO: 'Witaj z CustomPlugin',
|
|
139
|
-
},
|
|
140
185
|
},
|
|
141
186
|
config: {
|
|
142
187
|
plugins: ['CustomPlugin'],
|
|
143
188
|
},
|
|
144
|
-
}
|
|
145
|
-
{
|
|
146
|
-
ui: 'pl',
|
|
147
|
-
content: 'pl',
|
|
148
|
-
},
|
|
149
|
-
));
|
|
189
|
+
}));
|
|
150
190
|
|
|
151
|
-
|
|
152
|
-
|
|
191
|
+
const { context } = await waitForTestContext();
|
|
192
|
+
const plugin = context?.plugins.get('CustomPlugin') as CustomPlugin;
|
|
153
193
|
|
|
154
|
-
|
|
155
|
-
|
|
194
|
+
expect(plugin.getHelloTitle()).toBe('Hello from CustomPlugin');
|
|
195
|
+
});
|
|
156
196
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
197
|
+
it('should support translations references in config', async () => {
|
|
198
|
+
renderTestContext(createContextSnapshot(
|
|
199
|
+
DEFAULT_TEST_CONTEXT_ID,
|
|
200
|
+
{
|
|
201
|
+
customTranslations: {
|
|
202
|
+
en: {
|
|
203
|
+
HELLO: 'Hello from CustomPlugin',
|
|
204
|
+
},
|
|
205
|
+
pl: {
|
|
206
|
+
HELLO: 'Witaj z CustomPlugin',
|
|
207
|
+
},
|
|
164
208
|
},
|
|
165
|
-
|
|
166
|
-
|
|
209
|
+
config: {
|
|
210
|
+
customConfig: { $translation: 'HELLO' },
|
|
167
211
|
},
|
|
168
212
|
},
|
|
169
|
-
|
|
170
|
-
|
|
213
|
+
{
|
|
214
|
+
ui: 'pl',
|
|
215
|
+
content: 'pl',
|
|
171
216
|
},
|
|
172
|
-
|
|
173
|
-
{
|
|
174
|
-
ui: 'pl',
|
|
175
|
-
content: 'pl',
|
|
176
|
-
},
|
|
177
|
-
));
|
|
217
|
+
));
|
|
178
218
|
|
|
179
|
-
|
|
180
|
-
|
|
219
|
+
const { context } = await waitForTestContext();
|
|
220
|
+
const translation = context?.config.get('customConfig') as string;
|
|
181
221
|
|
|
182
|
-
|
|
222
|
+
expect(translation).toBe('Witaj z CustomPlugin');
|
|
223
|
+
});
|
|
183
224
|
});
|
|
184
225
|
});
|
|
185
226
|
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
loadAllEditorTranslations,
|
|
9
9
|
loadEditorPlugins,
|
|
10
10
|
normalizeCustomTranslations,
|
|
11
|
+
normalizeEditorLanguage,
|
|
11
12
|
resolveEditorConfigElementReferences,
|
|
12
13
|
resolveEditorConfigTranslations,
|
|
13
14
|
} from '../editor/utils';
|
|
@@ -46,11 +47,16 @@ export class ContextComponentElement extends HTMLElement {
|
|
|
46
47
|
* Initializes the context component.
|
|
47
48
|
*/
|
|
48
49
|
private async initializeContext(): Promise<void> {
|
|
49
|
-
const contextId = this.getAttribute('data-cke-context-id')!;
|
|
50
|
-
const language = JSON.parse(this.getAttribute('data-cke-language')!) as EditorLanguage;
|
|
51
50
|
const contextConfig = JSON.parse(this.getAttribute('data-cke-context')!) as ContextConfig;
|
|
52
|
-
|
|
53
51
|
const { customTranslations, watchdogConfig, config: { plugins, ...config } } = contextConfig;
|
|
52
|
+
|
|
53
|
+
const contextId = this.getAttribute('data-cke-context-id')!;
|
|
54
|
+
const language = (
|
|
55
|
+
this.getAttribute('data-cke-language')
|
|
56
|
+
? JSON.parse(this.getAttribute('data-cke-language')!)
|
|
57
|
+
: normalizeEditorLanguage(config['language'])
|
|
58
|
+
) as EditorLanguage;
|
|
59
|
+
|
|
54
60
|
const { loadedPlugins, hasPremium } = await loadEditorPlugins(plugins ?? []);
|
|
55
61
|
|
|
56
62
|
// Mix custom translations with loaded translations.
|
package/src/elements/editable.ts
CHANGED
|
@@ -88,9 +88,9 @@ export class EditableComponentElement extends HTMLElement {
|
|
|
88
88
|
|
|
89
89
|
editor.addRoot(rootName, {
|
|
90
90
|
isUndoable: false,
|
|
91
|
-
|
|
91
|
+
modelAttributes: { ...rootAttributes },
|
|
92
92
|
...content !== null && {
|
|
93
|
-
|
|
93
|
+
initialData: content,
|
|
94
94
|
},
|
|
95
95
|
});
|
|
96
96
|
|
|
@@ -14,6 +14,8 @@ import {
|
|
|
14
14
|
createSyncEditorWithInputPlugin,
|
|
15
15
|
} from './plugins';
|
|
16
16
|
import {
|
|
17
|
+
assignInitialDataToEditorConfig,
|
|
18
|
+
assignSourceElementsToEditorConfig,
|
|
17
19
|
cleanupOrphanEditorElements,
|
|
18
20
|
createEditorInContext,
|
|
19
21
|
isSingleRootEditor,
|
|
@@ -21,6 +23,7 @@ import {
|
|
|
21
23
|
loadEditorConstructor,
|
|
22
24
|
loadEditorPlugins,
|
|
23
25
|
normalizeCustomTranslations,
|
|
26
|
+
normalizeEditorLanguage,
|
|
24
27
|
queryEditablesElements,
|
|
25
28
|
queryEditablesSnapshotContent,
|
|
26
29
|
resolveEditorConfigElementReferences,
|
|
@@ -140,16 +143,7 @@ export class EditorComponentElement extends HTMLElement {
|
|
|
140
143
|
* Creates the CKEditor instance.
|
|
141
144
|
*/
|
|
142
145
|
private async createEditor() {
|
|
143
|
-
const editorId = this.getAttribute('data-cke-editor-id')!;
|
|
144
146
|
const preset = JSON.parse(this.getAttribute('data-cke-preset')!) as EditorPreset;
|
|
145
|
-
const contextId = this.getAttribute('data-cke-context-id');
|
|
146
|
-
const rootAttributes = JSON.parse(this.getAttribute('data-cke-root-attributes') || '{}');
|
|
147
|
-
const editableHeight = this.getAttribute('data-cke-editable-height') ? Number.parseInt(this.getAttribute('data-cke-editable-height')!, 10) : null;
|
|
148
|
-
const saveDebounceMs = Number.parseInt(this.getAttribute('data-cke-save-debounce-ms')!, 10);
|
|
149
|
-
const language = JSON.parse(this.getAttribute('data-cke-language')!) as EditorLanguage;
|
|
150
|
-
const useWatchdog = this.hasAttribute('data-cke-watchdog');
|
|
151
|
-
const content = JSON.parse(this.getAttribute('data-cke-content')!) as Record<string, string>;
|
|
152
|
-
|
|
153
147
|
const {
|
|
154
148
|
customTranslations,
|
|
155
149
|
editorType,
|
|
@@ -158,6 +152,19 @@ export class EditorComponentElement extends HTMLElement {
|
|
|
158
152
|
config: { plugins, ...config },
|
|
159
153
|
} = preset;
|
|
160
154
|
|
|
155
|
+
const editorId = this.getAttribute('data-cke-editor-id')!;
|
|
156
|
+
const contextId = this.getAttribute('data-cke-context-id');
|
|
157
|
+
const rootAttributes = JSON.parse(this.getAttribute('data-cke-root-attributes') || '{}');
|
|
158
|
+
const editableHeight = this.getAttribute('data-cke-editable-height') ? Number.parseInt(this.getAttribute('data-cke-editable-height')!, 10) : null;
|
|
159
|
+
const saveDebounceMs = Number.parseInt(this.getAttribute('data-cke-save-debounce-ms')!, 10);
|
|
160
|
+
const useWatchdog = this.hasAttribute('data-cke-watchdog');
|
|
161
|
+
const content = JSON.parse(this.getAttribute('data-cke-content')!) as Record<string, string>;
|
|
162
|
+
const language = (
|
|
163
|
+
this.getAttribute('data-cke-language')
|
|
164
|
+
? JSON.parse(this.getAttribute('data-cke-language')!)
|
|
165
|
+
: normalizeEditorLanguage(preset.config['language'])
|
|
166
|
+
) as EditorLanguage;
|
|
167
|
+
|
|
161
168
|
const Constructor = await loadEditorConstructor(editorType);
|
|
162
169
|
const context = await (
|
|
163
170
|
contextId
|
|
@@ -205,19 +212,19 @@ export class EditorComponentElement extends HTMLElement {
|
|
|
205
212
|
|
|
206
213
|
// Depending of the editor type, and parent lookup for nearest context or initialize it without it.
|
|
207
214
|
const editor = await (async () => {
|
|
208
|
-
let
|
|
215
|
+
let sourceElements: HTMLElement | Record<string, HTMLElement> = queryEditablesElements(editorId);
|
|
209
216
|
|
|
210
217
|
// Handle special case when user specified `initialData` of several root elements, but editable components
|
|
211
218
|
// are not yet present in the DOM. In other words - editor is initialized before attaching root elements.
|
|
212
|
-
if (!
|
|
219
|
+
if (!sourceElements['main']) {
|
|
213
220
|
const requiredRoots = (
|
|
214
221
|
isSingleRootEditor(editorType)
|
|
215
222
|
? ['main']
|
|
216
223
|
: Object.keys(initialData as Record<string, string>)
|
|
217
224
|
);
|
|
218
225
|
|
|
219
|
-
if (!checkIfAllRootsArePresent(
|
|
220
|
-
|
|
226
|
+
if (!checkIfAllRootsArePresent(sourceElements, requiredRoots)) {
|
|
227
|
+
sourceElements = await waitForAllRootsToBePresent(editorId, requiredRoots);
|
|
221
228
|
initialData = {
|
|
222
229
|
...content,
|
|
223
230
|
...queryEditablesSnapshotContent(editorId),
|
|
@@ -226,20 +233,13 @@ export class EditorComponentElement extends HTMLElement {
|
|
|
226
233
|
}
|
|
227
234
|
|
|
228
235
|
// If single root editor, unwrap the element from the object.
|
|
229
|
-
if (isSingleRootEditor(editorType) && 'main' in
|
|
230
|
-
|
|
236
|
+
if (isSingleRootEditor(editorType) && 'main' in sourceElements) {
|
|
237
|
+
sourceElements = sourceElements['main'];
|
|
231
238
|
}
|
|
232
239
|
|
|
233
|
-
//
|
|
234
|
-
let resolvedConfig =
|
|
235
|
-
|
|
236
|
-
// Then resolve translation references in the provided configuration, using the mixed translations.
|
|
237
|
-
resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
|
|
238
|
-
|
|
239
|
-
// Construct parsed config.
|
|
240
|
-
const parsedConfig = {
|
|
241
|
-
...resolvedConfig,
|
|
242
|
-
initialData,
|
|
240
|
+
// Do some postprocessing on received configuration.
|
|
241
|
+
let resolvedConfig = {
|
|
242
|
+
...config,
|
|
243
243
|
licenseKey,
|
|
244
244
|
plugins: loadedPlugins,
|
|
245
245
|
language,
|
|
@@ -248,15 +248,19 @@ export class EditorComponentElement extends HTMLElement {
|
|
|
248
248
|
},
|
|
249
249
|
};
|
|
250
250
|
|
|
251
|
-
|
|
252
|
-
|
|
251
|
+
resolvedConfig = resolveEditorConfigElementReferences(resolvedConfig);
|
|
252
|
+
resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
|
|
253
|
+
resolvedConfig = assignSourceElementsToEditorConfig(Constructor, sourceElements, resolvedConfig);
|
|
254
|
+
resolvedConfig = assignInitialDataToEditorConfig(initialData, resolvedConfig);
|
|
255
|
+
|
|
256
|
+
if (!context) {
|
|
257
|
+
return Constructor.create(resolvedConfig);
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
const result = await createEditorInContext({
|
|
256
261
|
context,
|
|
257
|
-
element: sourceElementOrData,
|
|
258
262
|
creator: Constructor,
|
|
259
|
-
config:
|
|
263
|
+
config: resolvedConfig,
|
|
260
264
|
});
|
|
261
265
|
|
|
262
266
|
return result.editor;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './editor-relaxed-constructor.type';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { EditorConfig } from 'ckeditor5';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Assigns initial data to specified editor config.
|
|
5
|
+
*
|
|
6
|
+
* @param dataOrMap Initial data to be assigned to config.
|
|
7
|
+
* @param config Config of the editor.
|
|
8
|
+
* @returns The updated configuration object.
|
|
9
|
+
*/
|
|
10
|
+
export function assignInitialDataToEditorConfig<C extends EditorConfig>(
|
|
11
|
+
dataOrMap: string | Record<string, string>,
|
|
12
|
+
config: C,
|
|
13
|
+
): C {
|
|
14
|
+
const dataMap = toDataMap(dataOrMap);
|
|
15
|
+
const allRootsKeys = new Set([
|
|
16
|
+
...Object.keys(dataMap),
|
|
17
|
+
...Object.keys(config.roots ?? {}),
|
|
18
|
+
]);
|
|
19
|
+
|
|
20
|
+
const rootsConfig = Array.from(allRootsKeys).reduce((acc, rootKey) => ({
|
|
21
|
+
...acc,
|
|
22
|
+
[rootKey]: {
|
|
23
|
+
...config.roots?.[rootKey],
|
|
24
|
+
...rootKey === 'main' ? config.root : {},
|
|
25
|
+
|
|
26
|
+
/* v8 ignore next 5 */
|
|
27
|
+
...rootKey in dataMap
|
|
28
|
+
? {
|
|
29
|
+
initialData: dataMap[rootKey],
|
|
30
|
+
}
|
|
31
|
+
: {},
|
|
32
|
+
},
|
|
33
|
+
}), Object.create(config.roots || {}));
|
|
34
|
+
|
|
35
|
+
const mappedConfig: C = {
|
|
36
|
+
...config,
|
|
37
|
+
roots: rootsConfig,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
delete mappedConfig.root;
|
|
41
|
+
|
|
42
|
+
return mappedConfig;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function toDataMap(element: string | Record<string, string>): Record<string, string> {
|
|
46
|
+
return typeof element === 'string' ? { main: element } : { ...element };
|
|
47
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { EditorRelaxedConstructor } from '../types/editor-relaxed-constructor.type';
|
|
2
|
+
import type { EditorConfig } from 'ckeditor5';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Assigns a DOM element to the editor configuration in a way that is compatible with the specific editor type.
|
|
6
|
+
*
|
|
7
|
+
* @param Editor Constructor of the editor used to determine the location of element config entry.
|
|
8
|
+
* @param elementOrMap Element to be assigned to config.
|
|
9
|
+
* @param config Config of the editor.
|
|
10
|
+
* @returns The updated configuration object.
|
|
11
|
+
*/
|
|
12
|
+
export function assignSourceElementsToEditorConfig<C extends EditorConfig>(
|
|
13
|
+
Editor: EditorRelaxedConstructor,
|
|
14
|
+
elementOrMap: HTMLElement | Record<string, HTMLElement>,
|
|
15
|
+
config: C,
|
|
16
|
+
): C {
|
|
17
|
+
const elementsMap = toElementsMap(elementOrMap);
|
|
18
|
+
|
|
19
|
+
if (!Editor.editorName || Editor.editorName === 'ClassicEditor') {
|
|
20
|
+
return {
|
|
21
|
+
...config,
|
|
22
|
+
attachTo: elementsMap['main'],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const allRootsKeys = new Set([
|
|
27
|
+
...Object.keys(elementsMap),
|
|
28
|
+
...Object.keys(config.roots ?? {}),
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
const rootsConfig = Array.from(allRootsKeys).reduce((acc, rootKey) => ({
|
|
32
|
+
...acc,
|
|
33
|
+
[rootKey]: {
|
|
34
|
+
/* v8 ignore next */
|
|
35
|
+
...config.roots?.[rootKey],
|
|
36
|
+
...rootKey === 'main' ? config.root : {},
|
|
37
|
+
|
|
38
|
+
/* v8 ignore next 5 */
|
|
39
|
+
...rootKey in elementsMap
|
|
40
|
+
? {
|
|
41
|
+
element: elementsMap[rootKey],
|
|
42
|
+
}
|
|
43
|
+
: {},
|
|
44
|
+
},
|
|
45
|
+
}), Object.create(config.roots || {}));
|
|
46
|
+
|
|
47
|
+
const mappedConfig: C = {
|
|
48
|
+
...config,
|
|
49
|
+
roots: rootsConfig,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
delete mappedConfig.root;
|
|
53
|
+
|
|
54
|
+
return mappedConfig;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function toElementsMap(element: HTMLElement | Record<string, HTMLElement>): Record<string, HTMLElement> {
|
|
58
|
+
return element instanceof HTMLElement ? { main: element } : { ...element };
|
|
59
|
+
}
|
|
@@ -12,19 +12,17 @@ const CONTEXT_EDITOR_WATCHDOG_SYMBOL = Symbol.for('context-editor-watchdog');
|
|
|
12
12
|
* Creates a CKEditor 5 editor instance within a given context watchdog.
|
|
13
13
|
*
|
|
14
14
|
* @param params Parameters for editor creation.
|
|
15
|
-
* @param params.element The DOM element or data for the editor.
|
|
16
15
|
* @param params.context The context watchdog instance.
|
|
17
16
|
* @param params.creator The editor creator utility.
|
|
18
17
|
* @param params.config The editor configuration object.
|
|
19
18
|
* @returns The created editor instance.
|
|
20
19
|
*/
|
|
21
|
-
export async function createEditorInContext({
|
|
20
|
+
export async function createEditorInContext({ context, creator, config }: Attrs) {
|
|
22
21
|
const editorContextId = uid();
|
|
23
22
|
|
|
24
23
|
await context.add({
|
|
25
|
-
creator:
|
|
24
|
+
creator: creator.create.bind(creator),
|
|
26
25
|
id: editorContextId,
|
|
27
|
-
sourceElementOrData: element,
|
|
28
26
|
type: 'editor',
|
|
29
27
|
config,
|
|
30
28
|
});
|
|
@@ -74,7 +72,6 @@ export function unwrapEditorContext(editor: Editor): EditorContextDescriptor | n
|
|
|
74
72
|
type Attrs = {
|
|
75
73
|
context: ContextWatchdog<Context>;
|
|
76
74
|
creator: EditorCreator;
|
|
77
|
-
element: HTMLElement;
|
|
78
75
|
config: EditorConfig;
|
|
79
76
|
};
|
|
80
77
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export * from './assign-initial-data-to-editor-config';
|
|
2
|
+
export * from './assign-source-elements-to-editor-config';
|
|
1
3
|
export * from './cleanup-orphan-editor-elements';
|
|
2
4
|
export * from './create-editor-in-context';
|
|
3
5
|
export * from './get-editor-roots-values';
|
|
@@ -6,6 +8,7 @@ export * from './load-editor-constructor';
|
|
|
6
8
|
export * from './load-editor-plugins';
|
|
7
9
|
export * from './load-editor-translations';
|
|
8
10
|
export * from './normalize-custom-translations';
|
|
11
|
+
export * from './normalize-editor-language';
|
|
9
12
|
export * from './query-all-editor-ids';
|
|
10
13
|
export * from './query-editor-editables';
|
|
11
14
|
export * from './resolve-editor-config-elements-references';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import { normalizeEditorLanguage } from './normalize-editor-language';
|
|
4
|
+
|
|
5
|
+
describe('normalizeEditorLanguage', () => {
|
|
6
|
+
describe('when lang is falsy', () => {
|
|
7
|
+
it('returns default object with "en" for empty string', () => {
|
|
8
|
+
expect(normalizeEditorLanguage('')).toEqual({ ui: 'en', content: 'en' });
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('returns default object with "en" for null', () => {
|
|
12
|
+
expect(normalizeEditorLanguage(null as any)).toEqual({ ui: 'en', content: 'en' });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('returns default object with "en" for undefined', () => {
|
|
16
|
+
expect(normalizeEditorLanguage(undefined as any)).toEqual({ ui: 'en', content: 'en' });
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe('when lang is a string', () => {
|
|
21
|
+
it('returns object with ui and content set to the given language', () => {
|
|
22
|
+
expect(normalizeEditorLanguage('pl')).toEqual({ ui: 'pl', content: 'pl' });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('handles "en" language code correctly', () => {
|
|
26
|
+
expect(normalizeEditorLanguage('en')).toEqual({ ui: 'en', content: 'en' });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('handles "de" language code correctly', () => {
|
|
30
|
+
expect(normalizeEditorLanguage('de')).toEqual({ ui: 'de', content: 'de' });
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe('when lang is an object', () => {
|
|
35
|
+
it('returns the object unchanged when ui and content are the same', () => {
|
|
36
|
+
const lang = { ui: 'pl', content: 'pl' };
|
|
37
|
+
expect(normalizeEditorLanguage(lang)).toEqual(lang);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('returns the object unchanged when ui and content differ', () => {
|
|
41
|
+
const lang = { ui: 'pl', content: 'en' };
|
|
42
|
+
expect(normalizeEditorLanguage(lang)).toEqual(lang);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('returns the exact same object reference', () => {
|
|
46
|
+
const lang = { ui: 'fr', content: 'en' };
|
|
47
|
+
expect(normalizeEditorLanguage(lang)).toBe(lang);
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { EditorLanguage } from '../typings';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalize editor language into object.
|
|
5
|
+
*
|
|
6
|
+
* @param lang Editor language.
|
|
7
|
+
* @returns Language object.
|
|
8
|
+
*/
|
|
9
|
+
export function normalizeEditorLanguage(lang: EditorLanguage | string): EditorLanguage {
|
|
10
|
+
if (!lang) {
|
|
11
|
+
return {
|
|
12
|
+
ui: 'en',
|
|
13
|
+
content: 'en',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (typeof lang === 'string') {
|
|
18
|
+
return {
|
|
19
|
+
ui: lang,
|
|
20
|
+
content: lang,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return lang;
|
|
25
|
+
}
|