ckeditor5-livewire 1.12.1 → 1.13.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/hooks/editable.d.ts +4 -0
- package/dist/hooks/editable.d.ts.map +1 -1
- package/dist/hooks/editor/editor.d.ts +4 -0
- package/dist/hooks/editor/editor.d.ts.map +1 -1
- package/dist/hooks/editor/utils/assign-editor-roots-to-config.d.ts +17 -0
- package/dist/hooks/editor/utils/assign-editor-roots-to-config.d.ts.map +1 -0
- package/dist/hooks/editor/utils/index.d.ts +2 -3
- package/dist/hooks/editor/utils/index.d.ts.map +1 -1
- package/dist/hooks/editor/utils/query-all-editor-editables.d.ts +24 -0
- package/dist/hooks/editor/utils/query-all-editor-editables.d.ts.map +1 -0
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +175 -214
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/hooks/editable.test.ts +34 -0
- package/src/hooks/editable.ts +7 -1
- package/src/hooks/editor/editor.test.ts +165 -1
- package/src/hooks/editor/editor.ts +45 -64
- package/src/hooks/editor/utils/assign-editor-roots-to-config.ts +61 -0
- package/src/hooks/editor/utils/get-editor-roots-values.ts +1 -1
- package/src/hooks/editor/utils/index.ts +2 -3
- package/src/hooks/editor/utils/query-all-editor-editables.ts +87 -0
- package/dist/hooks/editor/utils/assign-initial-data-to-editor-config.d.ts +0 -10
- package/dist/hooks/editor/utils/assign-initial-data-to-editor-config.d.ts.map +0 -1
- package/dist/hooks/editor/utils/assign-source-elements-to-editor-config.d.ts +0 -12
- package/dist/hooks/editor/utils/assign-source-elements-to-editor-config.d.ts.map +0 -1
- package/dist/hooks/editor/utils/query-editor-editables.d.ts +0 -32
- package/dist/hooks/editor/utils/query-editor-editables.d.ts.map +0 -1
- package/src/hooks/editor/utils/assign-initial-data-to-editor-config.ts +0 -48
- package/src/hooks/editor/utils/assign-source-elements-to-editor-config.ts +0 -61
- package/src/hooks/editor/utils/query-editor-editables.ts +0 -93
package/package.json
CHANGED
|
@@ -71,6 +71,40 @@ describe('editable component', () => {
|
|
|
71
71
|
expect(root.getAttribute('data-test')).toBe('initial');
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
+
it('should set proper root element name on initial added root', async () => {
|
|
75
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
76
|
+
name: 'ckeditor5-editable',
|
|
77
|
+
el: createEditableHtmlElement(),
|
|
78
|
+
canonical: {
|
|
79
|
+
...createEditableSnapshot('foo', '<p>Initial foo component</p>'),
|
|
80
|
+
modelElement: '$inlineRoot',
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
appendMultirootEditor();
|
|
85
|
+
|
|
86
|
+
const editor = await waitForTestEditor();
|
|
87
|
+
|
|
88
|
+
expect(editor.model.document.getRoot('foo')?.name).to.be.equal('$inlineRoot');
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
it('should set proper root element name on lazy added root', async () => {
|
|
92
|
+
appendMultirootEditor();
|
|
93
|
+
|
|
94
|
+
const editor = await waitForTestEditor();
|
|
95
|
+
|
|
96
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
97
|
+
name: 'ckeditor5-editable',
|
|
98
|
+
el: createEditableHtmlElement(),
|
|
99
|
+
canonical: {
|
|
100
|
+
...createEditableSnapshot('foo', '<p>Initial foo component</p>'),
|
|
101
|
+
modelElement: '$inlineRoot',
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
expect(editor.model.document.getRoot('foo')?.name).to.be.equal('$inlineRoot');
|
|
106
|
+
});
|
|
107
|
+
|
|
74
108
|
it('should update root attributes after commit', async () => {
|
|
75
109
|
appendMultirootEditor();
|
|
76
110
|
|
package/src/hooks/editable.ts
CHANGED
|
@@ -26,7 +26,7 @@ export class EditableComponentHook extends ClassHook<Snapshot> {
|
|
|
26
26
|
* Mounts the editable component.
|
|
27
27
|
*/
|
|
28
28
|
override mounted() {
|
|
29
|
-
const { editorId, rootName, content } = this.canonical;
|
|
29
|
+
const { editorId, rootName, content, modelElement } = this.canonical;
|
|
30
30
|
|
|
31
31
|
const unmountEffect = EditorsRegistry.the.mountEffect(editorId, (editor: MultiRootEditor | DecoupledEditor) => {
|
|
32
32
|
/* v8 ignore next if -- @preserve */
|
|
@@ -56,6 +56,7 @@ export class EditableComponentHook extends ClassHook<Snapshot> {
|
|
|
56
56
|
|
|
57
57
|
editor.addRoot(rootName, {
|
|
58
58
|
isUndoable: false,
|
|
59
|
+
modelElement: modelElement ?? '$root',
|
|
59
60
|
...content !== null && {
|
|
60
61
|
initialData: content,
|
|
61
62
|
},
|
|
@@ -267,6 +268,11 @@ export type Snapshot = {
|
|
|
267
268
|
*/
|
|
268
269
|
rootName: string;
|
|
269
270
|
|
|
271
|
+
/**
|
|
272
|
+
* The name of the model element.
|
|
273
|
+
*/
|
|
274
|
+
modelElement: string | null;
|
|
275
|
+
|
|
270
276
|
/**
|
|
271
277
|
* The initial content value for the editable.
|
|
272
278
|
*/
|
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
import type { Snapshot as EditorSnapshot } from './editor';
|
|
29
29
|
|
|
30
30
|
import { timeout } from '../../shared/timeout';
|
|
31
|
+
import { EditableComponentHook } from '../editable';
|
|
31
32
|
import { registerLivewireComponentHook } from '../hook';
|
|
32
33
|
import { CustomEditorPluginsRegistry } from './custom-editor-plugins';
|
|
33
34
|
import { EditorComponentHook } from './editor';
|
|
@@ -42,6 +43,7 @@ describe('editor component', () => {
|
|
|
42
43
|
livewireStub = window.Livewire = new LivewireStub();
|
|
43
44
|
|
|
44
45
|
registerLivewireComponentHook('ckeditor5', EditorComponentHook);
|
|
46
|
+
registerLivewireComponentHook('ckeditor5-editable', EditableComponentHook);
|
|
45
47
|
});
|
|
46
48
|
|
|
47
49
|
afterEach(async () => {
|
|
@@ -135,6 +137,20 @@ describe('editor component', () => {
|
|
|
135
137
|
|
|
136
138
|
expect(editor.getData()).toBe('');
|
|
137
139
|
});
|
|
140
|
+
|
|
141
|
+
it('should be possible to specify root element name', async () => {
|
|
142
|
+
livewireStub.$internal.appendComponentToDOM<EditorSnapshot>({
|
|
143
|
+
name: 'ckeditor5',
|
|
144
|
+
el: createEditorHtmlElement(),
|
|
145
|
+
canonical: createEditorSnapshot({
|
|
146
|
+
modelElement: '$inlineRoot',
|
|
147
|
+
}),
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
const editor = await waitForTestEditor();
|
|
151
|
+
|
|
152
|
+
expect(editor.model.document.getRoot()?.name).toBe('$inlineRoot');
|
|
153
|
+
});
|
|
138
154
|
});
|
|
139
155
|
|
|
140
156
|
describe('inline', () => {
|
|
@@ -234,10 +250,86 @@ describe('editor component', () => {
|
|
|
234
250
|
},
|
|
235
251
|
});
|
|
236
252
|
|
|
237
|
-
await expect(waitForTestEditor()).rejects.
|
|
253
|
+
await expect(waitForTestEditor()).rejects.toThrow(
|
|
238
254
|
/It looks like not all required root elements are present yet/,
|
|
239
255
|
);
|
|
240
256
|
});
|
|
257
|
+
|
|
258
|
+
it('should be possible to specify root element name using editable config alone', async () => {
|
|
259
|
+
livewireStub.$internal.appendComponentToDOM<EditorSnapshot>({
|
|
260
|
+
name: 'ckeditor5',
|
|
261
|
+
el: createEditorHtmlElement({ editorType: 'decoupled' }),
|
|
262
|
+
canonical: {
|
|
263
|
+
...createEditorSnapshot(),
|
|
264
|
+
preset: createEditorPreset('decoupled'),
|
|
265
|
+
},
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
269
|
+
name: 'ckeditor5-editable',
|
|
270
|
+
el: createEditableHtmlElement(),
|
|
271
|
+
canonical: {
|
|
272
|
+
...createEditableSnapshot('main'),
|
|
273
|
+
modelElement: '$inlineRoot',
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
const editor = await waitForTestEditor<DecoupledEditor>();
|
|
278
|
+
|
|
279
|
+
await vi.waitFor(() => {
|
|
280
|
+
expect(editor.model.document.getRoot()?.name).toEqual('$inlineRoot');
|
|
281
|
+
});
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('should use editable root element name config if both specified', async () => {
|
|
285
|
+
livewireStub.$internal.appendComponentToDOM<EditorSnapshot>({
|
|
286
|
+
name: 'ckeditor5',
|
|
287
|
+
el: createEditorHtmlElement({ editorType: 'decoupled' }),
|
|
288
|
+
canonical: createEditorSnapshot({
|
|
289
|
+
preset: createEditorPreset('decoupled'),
|
|
290
|
+
modelElement: '$miaMia',
|
|
291
|
+
}),
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
295
|
+
name: 'ckeditor5-editable',
|
|
296
|
+
el: createEditableHtmlElement(),
|
|
297
|
+
canonical: {
|
|
298
|
+
...createEditableSnapshot('main'),
|
|
299
|
+
modelElement: '$inlineRoot',
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
const editor = await waitForTestEditor<DecoupledEditor>();
|
|
304
|
+
|
|
305
|
+
await vi.waitFor(() => {
|
|
306
|
+
expect(editor.model.document.getRoot()?.name).toEqual('$inlineRoot');
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should use root editor element if editable root name is not specified', async () => {
|
|
311
|
+
livewireStub.$internal.appendComponentToDOM<EditorSnapshot>({
|
|
312
|
+
name: 'ckeditor5',
|
|
313
|
+
el: createEditorHtmlElement({ editorType: 'decoupled' }),
|
|
314
|
+
canonical: createEditorSnapshot({
|
|
315
|
+
preset: createEditorPreset('decoupled'),
|
|
316
|
+
modelElement: '$inlineRoot',
|
|
317
|
+
content: {},
|
|
318
|
+
}),
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
322
|
+
name: 'ckeditor5-editable',
|
|
323
|
+
el: createEditableHtmlElement(),
|
|
324
|
+
canonical: createEditableSnapshot('main'),
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
const editor = await waitForTestEditor<DecoupledEditor>();
|
|
328
|
+
|
|
329
|
+
await vi.waitFor(() => {
|
|
330
|
+
expect(editor.model.document.getRoot()?.name).toEqual('$inlineRoot');
|
|
331
|
+
});
|
|
332
|
+
});
|
|
241
333
|
});
|
|
242
334
|
|
|
243
335
|
describe('balloon', () => {
|
|
@@ -275,6 +367,78 @@ describe('editor component', () => {
|
|
|
275
367
|
expect(editor).toBeInstanceOf(MultiRootEditor);
|
|
276
368
|
});
|
|
277
369
|
|
|
370
|
+
it('should create a multiroot editor with lazy added inline editables', async () => {
|
|
371
|
+
livewireStub.$internal.appendComponentToDOM<EditorSnapshot>({
|
|
372
|
+
name: 'ckeditor5',
|
|
373
|
+
el: createEditorHtmlElement({ editorType: 'multiroot' }),
|
|
374
|
+
canonical: {
|
|
375
|
+
...createEditorSnapshot(),
|
|
376
|
+
preset: createEditorPreset('multiroot'),
|
|
377
|
+
content: {},
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const editor = await waitForTestEditor();
|
|
382
|
+
|
|
383
|
+
await timeout(500); // Simulate some delay before adding the root.
|
|
384
|
+
|
|
385
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
386
|
+
name: 'ckeditor5-editable',
|
|
387
|
+
el: createEditableHtmlElement(),
|
|
388
|
+
canonical: {
|
|
389
|
+
...createEditableSnapshot('header', 'Head'),
|
|
390
|
+
modelElement: '$inlineRoot',
|
|
391
|
+
},
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
395
|
+
name: 'ckeditor5-editable',
|
|
396
|
+
el: createEditableHtmlElement(),
|
|
397
|
+
canonical: {
|
|
398
|
+
...createEditableSnapshot('footer', 'Footer'),
|
|
399
|
+
modelElement: '$inlineRoot',
|
|
400
|
+
},
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
expect(editor.model.document.getRoot('header')?.name).toEqual('$inlineRoot');
|
|
404
|
+
expect(editor.model.document.getRoot('footer')?.name).toEqual('$inlineRoot');
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('should create a multiroot editor with initially added inline editables', async () => {
|
|
408
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
409
|
+
name: 'ckeditor5-editable',
|
|
410
|
+
el: createEditableHtmlElement(),
|
|
411
|
+
canonical: {
|
|
412
|
+
...createEditableSnapshot('header', 'Head'),
|
|
413
|
+
modelElement: '$inlineRoot',
|
|
414
|
+
},
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
livewireStub.$internal.appendComponentToDOM({
|
|
418
|
+
name: 'ckeditor5-editable',
|
|
419
|
+
el: createEditableHtmlElement(),
|
|
420
|
+
canonical: {
|
|
421
|
+
...createEditableSnapshot('footer', 'Footer'),
|
|
422
|
+
modelElement: '$inlineRoot',
|
|
423
|
+
},
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
livewireStub.$internal.appendComponentToDOM<EditorSnapshot>({
|
|
427
|
+
name: 'ckeditor5',
|
|
428
|
+
el: createEditorHtmlElement({ editorType: 'multiroot' }),
|
|
429
|
+
canonical: {
|
|
430
|
+
...createEditorSnapshot(),
|
|
431
|
+
preset: createEditorPreset('multiroot'),
|
|
432
|
+
content: {},
|
|
433
|
+
},
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
const editor = await waitForTestEditor();
|
|
437
|
+
|
|
438
|
+
expect(editor.model.document.getRoot('header')?.name).toEqual('$inlineRoot');
|
|
439
|
+
expect(editor.model.document.getRoot('footer')?.name).toEqual('$inlineRoot');
|
|
440
|
+
});
|
|
441
|
+
|
|
278
442
|
it('should wait and for root elements to be present in DOM if they are not (with content=null value)', async () => {
|
|
279
443
|
livewireStub.$internal.appendComponentToDOM<EditorSnapshot>({
|
|
280
444
|
name: 'ckeditor5',
|
|
@@ -2,6 +2,7 @@ import type { Editor } from 'ckeditor5';
|
|
|
2
2
|
|
|
3
3
|
import type { RootAttributesUpdater } from '../utils';
|
|
4
4
|
import type { EditorId, EditorLanguage, EditorPreset } from './typings';
|
|
5
|
+
import type { EditableItem } from './utils';
|
|
5
6
|
|
|
6
7
|
import { ContextsRegistry } from '../../hooks/context';
|
|
7
8
|
import { isEmptyObject, waitFor } from '../../shared';
|
|
@@ -13,8 +14,7 @@ import {
|
|
|
13
14
|
createSyncEditorWithInputPlugin,
|
|
14
15
|
} from './plugins';
|
|
15
16
|
import {
|
|
16
|
-
|
|
17
|
-
assignSourceElementsToEditorConfig,
|
|
17
|
+
assignEditorRootsToConfig,
|
|
18
18
|
cleanupOrphanEditorElements,
|
|
19
19
|
createEditorInContext,
|
|
20
20
|
isSingleRootEditor,
|
|
@@ -22,8 +22,7 @@ import {
|
|
|
22
22
|
loadEditorConstructor,
|
|
23
23
|
loadEditorPlugins,
|
|
24
24
|
normalizeCustomTranslations,
|
|
25
|
-
|
|
26
|
-
queryEditablesSnapshotContent,
|
|
25
|
+
queryAllEditorEditables,
|
|
27
26
|
resolveEditorConfigElementReferences,
|
|
28
27
|
resolveEditorConfigTranslations,
|
|
29
28
|
setEditorEditableHeight,
|
|
@@ -145,7 +144,6 @@ export class EditorComponentHook extends ClassHook<Snapshot> {
|
|
|
145
144
|
saveDebounceMs,
|
|
146
145
|
language,
|
|
147
146
|
watchdog: useWatchdog,
|
|
148
|
-
content,
|
|
149
147
|
} = this.canonical;
|
|
150
148
|
|
|
151
149
|
const {
|
|
@@ -193,59 +191,37 @@ export class EditorComponentHook extends ClassHook<Snapshot> {
|
|
|
193
191
|
]
|
|
194
192
|
.filter(translations => !isEmptyObject(translations));
|
|
195
193
|
|
|
196
|
-
//
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
194
|
+
// Query all editable elements along with their content in one pass.
|
|
195
|
+
// Roots present in the editor container's data-cke-content but not yet in the DOM
|
|
196
|
+
// are included with element: null so we know which roots to wait for.
|
|
197
|
+
let editables = queryAllEditorEditables(editorId);
|
|
198
|
+
const requiredRoots = Object.keys(editables);
|
|
201
199
|
|
|
202
200
|
if (isSingleRootEditor(editorType)) {
|
|
203
|
-
|
|
201
|
+
requiredRoots.push('main');
|
|
204
202
|
}
|
|
205
203
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
// Handle special case when user specified `initialData` of several root elements, but editable components
|
|
211
|
-
// are not yet present in the DOM. In other words - editor is initialized before attaching root elements.
|
|
212
|
-
if (!(sourceElements instanceof HTMLElement) && !('main' in sourceElements)) {
|
|
213
|
-
const requiredRoots = (
|
|
214
|
-
editorType === 'decoupled'
|
|
215
|
-
? ['main']
|
|
216
|
-
: Object.keys(initialData as Record<string, string>)
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
if (!checkIfAllRootsArePresent(sourceElements, requiredRoots)) {
|
|
220
|
-
sourceElements = await waitForAllRootsToBePresent(editorId, requiredRoots);
|
|
221
|
-
initialData = {
|
|
222
|
-
...content,
|
|
223
|
-
...queryEditablesSnapshotContent(editorId),
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// If single root editor, unwrap the element from the object.
|
|
229
|
-
if (isSingleRootEditor(editorType) && 'main' in sourceElements) {
|
|
230
|
-
sourceElements = sourceElements['main'];
|
|
231
|
-
}
|
|
204
|
+
if (!checkIfAllRootsArePresent(editables, requiredRoots)) {
|
|
205
|
+
editables = await waitForAllRootsToBePresent(editorId, requiredRoots);
|
|
206
|
+
}
|
|
232
207
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
208
|
+
// Do some postprocessing on received configuration.
|
|
209
|
+
let resolvedConfig = {
|
|
210
|
+
...config,
|
|
211
|
+
licenseKey,
|
|
212
|
+
plugins: loadedPlugins,
|
|
213
|
+
language,
|
|
214
|
+
...mixedTranslations.length && {
|
|
215
|
+
translations: mixedTranslations,
|
|
216
|
+
},
|
|
217
|
+
};
|
|
243
218
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
resolvedConfig = assignInitialDataToEditorConfig(initialData, resolvedConfig);
|
|
219
|
+
resolvedConfig = resolveEditorConfigElementReferences(resolvedConfig);
|
|
220
|
+
resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
|
|
221
|
+
resolvedConfig = assignEditorRootsToConfig(Constructor, editables, resolvedConfig);
|
|
248
222
|
|
|
223
|
+
// Depending of the editor type, and parent lookup for nearest context or initialize it without it.
|
|
224
|
+
const editor = await (async () => {
|
|
249
225
|
if (!context) {
|
|
250
226
|
return Constructor.create(resolvedConfig);
|
|
251
227
|
}
|
|
@@ -300,42 +276,42 @@ export class EditorComponentHook extends ClassHook<Snapshot> {
|
|
|
300
276
|
}
|
|
301
277
|
|
|
302
278
|
/**
|
|
303
|
-
* Checks if all required root elements are present in the
|
|
279
|
+
* Checks if all required root elements are present (i.e. have a non-null element) in the editables map.
|
|
304
280
|
*
|
|
305
|
-
* @param
|
|
306
|
-
* @param requiredRoots The list of required root
|
|
307
|
-
* @returns True if all required roots
|
|
281
|
+
* @param editables The editables map keyed by root name.
|
|
282
|
+
* @param requiredRoots The list of required root names.
|
|
283
|
+
* @returns True if all required roots have a DOM element attached, false otherwise.
|
|
308
284
|
*/
|
|
309
|
-
function checkIfAllRootsArePresent(
|
|
310
|
-
return requiredRoots.every(rootId =>
|
|
285
|
+
function checkIfAllRootsArePresent(editables: Record<string, EditableItem>, requiredRoots: string[]): boolean {
|
|
286
|
+
return requiredRoots.every(rootId => editables[rootId]?.element);
|
|
311
287
|
}
|
|
312
288
|
|
|
313
289
|
/**
|
|
314
290
|
* Waits for all required root elements to be present in the DOM.
|
|
315
291
|
*
|
|
316
292
|
* @param editorId The editor's ID.
|
|
317
|
-
* @param requiredRoots The list of required root
|
|
318
|
-
* @returns A promise that resolves to the
|
|
293
|
+
* @param requiredRoots The list of required root names.
|
|
294
|
+
* @returns A promise that resolves to the updated editables map once all elements are attached.
|
|
319
295
|
*/
|
|
320
296
|
async function waitForAllRootsToBePresent(
|
|
321
297
|
editorId: EditorId,
|
|
322
298
|
requiredRoots: string[],
|
|
323
|
-
): Promise<Record<string,
|
|
299
|
+
): Promise<Record<string, EditableItem>> {
|
|
324
300
|
return waitFor(
|
|
325
301
|
() => {
|
|
326
|
-
const
|
|
302
|
+
const editables = queryAllEditorEditables(editorId);
|
|
327
303
|
|
|
328
|
-
if (!checkIfAllRootsArePresent(
|
|
304
|
+
if (!checkIfAllRootsArePresent(editables, requiredRoots)) {
|
|
329
305
|
throw new Error(
|
|
330
306
|
'It looks like not all required root elements are present yet.\n'
|
|
331
307
|
+ '* If you want to wait for them, ensure they are registered before editor initialization.\n'
|
|
332
308
|
+ '* If you want lazy initialize roots, consider removing root values from the `initialData` config '
|
|
333
309
|
+ 'and assign initial data in editable components.\n'
|
|
334
|
-
+ `Missing roots: ${requiredRoots.filter(rootId => !
|
|
310
|
+
+ `Missing roots: ${requiredRoots.filter(rootId => !editables[rootId]?.element).join(', ')}.`,
|
|
335
311
|
);
|
|
336
312
|
}
|
|
337
313
|
|
|
338
|
-
return
|
|
314
|
+
return editables;
|
|
339
315
|
},
|
|
340
316
|
{ timeOutAfter: 2000, retryAfter: 100 },
|
|
341
317
|
);
|
|
@@ -380,6 +356,11 @@ export type Snapshot = {
|
|
|
380
356
|
*/
|
|
381
357
|
editableHeight: number | null;
|
|
382
358
|
|
|
359
|
+
/**
|
|
360
|
+
* Root element name.
|
|
361
|
+
*/
|
|
362
|
+
modelElement: string | null;
|
|
363
|
+
|
|
383
364
|
/**
|
|
384
365
|
* The language of the editor UI and content.
|
|
385
366
|
*/
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { EditorConfig } from 'ckeditor5';
|
|
2
|
+
|
|
3
|
+
import type { EditorRelaxedConstructor } from '../types/editor-relaxed-constructor.type';
|
|
4
|
+
import type { EditableItem } from './query-all-editor-editables';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Assigns DOM elements and initial data to the editor configuration in a way that is compatible
|
|
8
|
+
* with the specific editor type.
|
|
9
|
+
*
|
|
10
|
+
* Roots with `element: null` (pending roots not yet in the DOM) contribute only `initialData`
|
|
11
|
+
* and are skipped for element assignment.
|
|
12
|
+
*
|
|
13
|
+
* @param Editor Constructor of the editor used to determine the location of element config entry.
|
|
14
|
+
* @param editables Map of editable items (element + content) keyed by root name.
|
|
15
|
+
* @param config Config of the editor.
|
|
16
|
+
* @returns The updated configuration object.
|
|
17
|
+
*/
|
|
18
|
+
export function assignEditorRootsToConfig<C extends EditorConfig>(
|
|
19
|
+
Editor: EditorRelaxedConstructor,
|
|
20
|
+
editables: Record<string, EditableItem>,
|
|
21
|
+
config: C,
|
|
22
|
+
): C {
|
|
23
|
+
const isClassicEditor = !Editor.editorName || Editor.editorName === 'ClassicEditor';
|
|
24
|
+
const allRootsKeys = new Set([
|
|
25
|
+
...Object.keys(editables),
|
|
26
|
+
...Object.keys(config.roots ?? {}),
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
/* v8 ignore start -- @preserve */
|
|
30
|
+
const rootsConfig = Array.from(allRootsKeys).reduce((acc, rootKey) => ({
|
|
31
|
+
...acc,
|
|
32
|
+
[rootKey]: {
|
|
33
|
+
...config.roots?.[rootKey],
|
|
34
|
+
...rootKey === 'main' ? config.root : {},
|
|
35
|
+
...rootKey in editables
|
|
36
|
+
? {
|
|
37
|
+
modelElement: editables[rootKey]!.modelElement || '$root',
|
|
38
|
+
...editables[rootKey]!.content !== null && {
|
|
39
|
+
initialData: editables[rootKey]!.content,
|
|
40
|
+
},
|
|
41
|
+
...!isClassicEditor && editables[rootKey]!.element !== null && {
|
|
42
|
+
element: editables[rootKey]!.element,
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
: {},
|
|
46
|
+
},
|
|
47
|
+
}), Object.create(config.roots || {}));
|
|
48
|
+
/* v8 ignore stop */
|
|
49
|
+
|
|
50
|
+
const mappedConfig: C = {
|
|
51
|
+
...config,
|
|
52
|
+
roots: rootsConfig,
|
|
53
|
+
...isClassicEditor && editables['main']?.element && {
|
|
54
|
+
attachTo: editables['main'].element,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
delete mappedConfig.root;
|
|
59
|
+
|
|
60
|
+
return mappedConfig;
|
|
61
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
export * from './assign-
|
|
2
|
-
export * from './assign-source-elements-to-editor-config';
|
|
1
|
+
export * from './assign-editor-roots-to-config';
|
|
3
2
|
export * from './cleanup-orphan-editor-elements';
|
|
4
3
|
export * from './create-editor-in-context';
|
|
5
4
|
export * from './get-editor-roots-values';
|
|
@@ -9,7 +8,7 @@ export * from './load-editor-constructor';
|
|
|
9
8
|
export * from './load-editor-plugins';
|
|
10
9
|
export * from './load-editor-translations';
|
|
11
10
|
export * from './normalize-custom-translations';
|
|
12
|
-
export * from './query-editor-editables';
|
|
11
|
+
export * from './query-all-editor-editables';
|
|
13
12
|
export * from './resolve-editor-config-elements-references';
|
|
14
13
|
export * from './resolve-editor-config-translations';
|
|
15
14
|
export * from './set-editor-editable-height';
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { EditorId } from '../typings';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Queries all editable elements within a specific editor instance. It picks
|
|
5
|
+
* initial values from Livewire component canonicals or from the root editor
|
|
6
|
+
* component's content, whichever is available.
|
|
7
|
+
*
|
|
8
|
+
* Roots present in the editor component's content but without a matching
|
|
9
|
+
* `ckeditor5-editable` component yet are included with `element: null` — these
|
|
10
|
+
* are "pending" roots that haven't been attached to the DOM yet.
|
|
11
|
+
*
|
|
12
|
+
* @param editorId The ID of the editor to query.
|
|
13
|
+
* @returns An object mapping root names to their corresponding elements and content.
|
|
14
|
+
*/
|
|
15
|
+
export function queryAllEditorEditables(editorId: EditorId): Record<string, EditableItem> {
|
|
16
|
+
const acc = window.Livewire
|
|
17
|
+
.all()
|
|
18
|
+
.filter(({ name, canonical }) => name === 'ckeditor5-editable' && canonical['editorId'] === editorId)
|
|
19
|
+
.reduce<Record<string, EditableItem>>((acc, { canonical, el }) => {
|
|
20
|
+
const rootName = canonical['rootName'] as string;
|
|
21
|
+
const modelElement = (canonical['modelElement'] || null) as string | null;
|
|
22
|
+
|
|
23
|
+
acc[rootName] = {
|
|
24
|
+
element: el.querySelector<HTMLElement>('[data-cke-editable-content]'),
|
|
25
|
+
content: canonical['content'],
|
|
26
|
+
modelElement,
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
return acc;
|
|
30
|
+
}, Object.create(null));
|
|
31
|
+
|
|
32
|
+
const rootHook = window.Livewire
|
|
33
|
+
.all()
|
|
34
|
+
.find(({ name, canonical }) => name === 'ckeditor5' && canonical['editorId'] === editorId);
|
|
35
|
+
|
|
36
|
+
/* v8 ignore next -- @preserve */
|
|
37
|
+
if (!rootHook) {
|
|
38
|
+
return acc;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// v8 ignore next -- @preserve
|
|
42
|
+
const editorContent: Record<string, string> = (rootHook.canonical.content as Record<string, string>) ?? {};
|
|
43
|
+
const rootEditorModelElement = (rootHook.canonical.modelElement || null) as string || null;
|
|
44
|
+
const classicMainElement = document.querySelector<HTMLElement>(`#${editorId}_editor`);
|
|
45
|
+
|
|
46
|
+
if ('main' in acc) {
|
|
47
|
+
acc['main'].modelElement ??= rootEditorModelElement;
|
|
48
|
+
}
|
|
49
|
+
else if (classicMainElement) {
|
|
50
|
+
acc['main'] = {
|
|
51
|
+
element: classicMainElement,
|
|
52
|
+
content: editorContent['main'] ?? '',
|
|
53
|
+
modelElement: rootEditorModelElement,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const [rootName, rootContent] of Object.entries(editorContent)) {
|
|
58
|
+
if (acc[rootName]) {
|
|
59
|
+
// Editable component is already present — fill content from editor level if the editable didn't provide its own.
|
|
60
|
+
acc[rootName] = {
|
|
61
|
+
...acc[rootName],
|
|
62
|
+
content: acc[rootName].content ?? rootContent,
|
|
63
|
+
modelElement: acc[rootName].modelElement ?? rootEditorModelElement,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Root has server-provided content but its editable component hasn't been attached to the DOM yet.
|
|
68
|
+
acc[rootName] = {
|
|
69
|
+
element: null,
|
|
70
|
+
content: rootContent,
|
|
71
|
+
modelElement: rootEditorModelElement,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return acc;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Type representing an editable item within an editor.
|
|
81
|
+
* `element` is null when the root's DOM element hasn't appeared yet (pending root).
|
|
82
|
+
*/
|
|
83
|
+
export type EditableItem = {
|
|
84
|
+
element: HTMLElement | null;
|
|
85
|
+
content: string | null;
|
|
86
|
+
modelElement: string | null;
|
|
87
|
+
};
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import { EditorConfig } from 'ckeditor5';
|
|
2
|
-
/**
|
|
3
|
-
* Assigns initial data to specified editor config.
|
|
4
|
-
*
|
|
5
|
-
* @param dataOrMap Initial data to be assigned to config.
|
|
6
|
-
* @param config Config of the editor.
|
|
7
|
-
* @returns The updated configuration object.
|
|
8
|
-
*/
|
|
9
|
-
export declare function assignInitialDataToEditorConfig<C extends EditorConfig>(dataOrMap: string | Record<string, string>, config: C): C;
|
|
10
|
-
//# sourceMappingURL=assign-initial-data-to-editor-config.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"assign-initial-data-to-editor-config.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/editor/utils/assign-initial-data-to-editor-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,CAAC,SAAS,YAAY,EACpE,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1C,MAAM,EAAE,CAAC,GACR,CAAC,CA+BH"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { EditorConfig } from 'ckeditor5';
|
|
2
|
-
import { EditorRelaxedConstructor } from '../types/editor-relaxed-constructor.type';
|
|
3
|
-
/**
|
|
4
|
-
* Assigns a DOM element to the editor configuration in a way that is compatible with the specific editor type.
|
|
5
|
-
*
|
|
6
|
-
* @param Editor Constructor of the editor used to determine the location of element config entry.
|
|
7
|
-
* @param elementOrMap Element to be assigned to config.
|
|
8
|
-
* @param config Config of the editor.
|
|
9
|
-
* @returns The updated configuration object.
|
|
10
|
-
*/
|
|
11
|
-
export declare function assignSourceElementsToEditorConfig<C extends EditorConfig>(Editor: EditorRelaxedConstructor, elementOrMap: HTMLElement | Record<string, HTMLElement>, config: C): C;
|
|
12
|
-
//# sourceMappingURL=assign-source-elements-to-editor-config.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"assign-source-elements-to-editor-config.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/editor/utils/assign-source-elements-to-editor-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,0CAA0C,CAAC;AAEzF;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAAC,CAAC,SAAS,YAAY,EACvE,MAAM,EAAE,wBAAwB,EAChC,YAAY,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,EACvD,MAAM,EAAE,CAAC,GACR,CAAC,CAwCH"}
|