ckeditor5-livewire 1.12.0 → 1.12.2

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.
Files changed (25) hide show
  1. package/dist/hooks/editor/editor.d.ts.map +1 -1
  2. package/dist/hooks/editor/utils/assign-editor-roots-to-config.d.ts +17 -0
  3. package/dist/hooks/editor/utils/assign-editor-roots-to-config.d.ts.map +1 -0
  4. package/dist/hooks/editor/utils/index.d.ts +2 -3
  5. package/dist/hooks/editor/utils/index.d.ts.map +1 -1
  6. package/dist/hooks/editor/utils/query-all-editor-editables.d.ts +23 -0
  7. package/dist/hooks/editor/utils/query-all-editor-editables.d.ts.map +1 -0
  8. package/dist/index.cjs +2 -2
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.mjs +159 -206
  11. package/dist/index.mjs.map +1 -1
  12. package/package.json +1 -1
  13. package/src/hooks/editor/editor.ts +43 -70
  14. package/src/hooks/editor/utils/assign-editor-roots-to-config.ts +60 -0
  15. package/src/hooks/editor/utils/index.ts +2 -3
  16. package/src/hooks/editor/utils/query-all-editor-editables.ts +77 -0
  17. package/dist/hooks/editor/utils/assign-initial-data-to-editor-config.d.ts +0 -10
  18. package/dist/hooks/editor/utils/assign-initial-data-to-editor-config.d.ts.map +0 -1
  19. package/dist/hooks/editor/utils/assign-source-elements-to-editor-config.d.ts +0 -12
  20. package/dist/hooks/editor/utils/assign-source-elements-to-editor-config.d.ts.map +0 -1
  21. package/dist/hooks/editor/utils/query-editor-editables.d.ts +0 -32
  22. package/dist/hooks/editor/utils/query-editor-editables.d.ts.map +0 -1
  23. package/src/hooks/editor/utils/assign-initial-data-to-editor-config.ts +0 -48
  24. package/src/hooks/editor/utils/assign-source-elements-to-editor-config.ts +0 -61
  25. package/src/hooks/editor/utils/query-editor-editables.ts +0 -93
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ckeditor5-livewire",
3
- "version": "1.12.0",
3
+ "version": "1.12.2",
4
4
  "description": "CKEditor 5 integration for Laravel Livewire",
5
5
  "author": "Mateusz Bagiński <cziken58@gmail.com>",
6
6
  "license": "MIT",
@@ -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
- assignInitialDataToEditorConfig,
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
- queryEditablesElements,
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,70 +191,45 @@ export class EditorComponentHook extends ClassHook<Snapshot> {
193
191
  ]
194
192
  .filter(translations => !isEmptyObject(translations));
195
193
 
196
- // Let's query all elements, and create basic configuration.
197
- let initialData: string | Record<string, string> = {
198
- ...content,
199
- ...queryEditablesSnapshotContent(editorId),
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
- initialData = initialData['main'] || '';
201
+ requiredRoots.push('main');
204
202
  }
205
203
 
206
- // Depending of the editor type, and parent lookup for nearest context or initialize it without it.
207
- const editor = await (async () => {
208
- let sourceElements: HTMLElement | Record<string, HTMLElement> = queryEditablesElements(editorId);
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
- }
204
+ if (!checkIfAllRootsArePresent(editables, requiredRoots)) {
205
+ editables = await waitForAllRootsToBePresent(editorId, requiredRoots);
206
+ }
227
207
 
228
- // If single root editor, unwrap the element from the object.
229
- if (isSingleRootEditor(editorType) && 'main' in sourceElements) {
230
- sourceElements = sourceElements['main'];
231
- }
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
+ };
232
218
 
233
- let resolvedConfig = { ...config };
234
-
235
- // Do some postprocessing on received configuration.
236
- resolvedConfig = resolveEditorConfigElementReferences(resolvedConfig);
237
- resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
238
- resolvedConfig = assignSourceElementsToEditorConfig(Constructor, sourceElements, resolvedConfig);
239
- resolvedConfig = assignInitialDataToEditorConfig(initialData, resolvedConfig);
240
-
241
- // Construct parsed config.
242
- const parsedConfig = {
243
- ...resolvedConfig,
244
- licenseKey,
245
- plugins: loadedPlugins,
246
- language,
247
- ...mixedTranslations.length && {
248
- translations: mixedTranslations,
249
- },
250
- };
219
+ resolvedConfig = resolveEditorConfigElementReferences(resolvedConfig);
220
+ resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
221
+ resolvedConfig = assignEditorRootsToConfig(Constructor, editables, resolvedConfig);
251
222
 
252
- if (!context || !(sourceElements instanceof HTMLElement)) {
253
- return Constructor.create(parsedConfig);
223
+ // Depending of the editor type, and parent lookup for nearest context or initialize it without it.
224
+ const editor = await (async () => {
225
+ if (!context) {
226
+ return Constructor.create(resolvedConfig);
254
227
  }
255
228
 
256
229
  const result = await createEditorInContext({
257
230
  context,
258
231
  creator: Constructor,
259
- config: parsedConfig,
232
+ config: resolvedConfig,
260
233
  });
261
234
 
262
235
  return result.editor;
@@ -303,42 +276,42 @@ export class EditorComponentHook extends ClassHook<Snapshot> {
303
276
  }
304
277
 
305
278
  /**
306
- * Checks if all required root elements are present in the elements object.
279
+ * Checks if all required root elements are present (i.e. have a non-null element) in the editables map.
307
280
  *
308
- * @param elements The elements object mapping root IDs to HTMLElements.
309
- * @param requiredRoots The list of required root IDs.
310
- * @returns True if all required roots are present, false otherwise.
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.
311
284
  */
312
- function checkIfAllRootsArePresent(elements: Record<string, HTMLElement>, requiredRoots: string[]): boolean {
313
- return requiredRoots.every(rootId => elements[rootId]);
285
+ function checkIfAllRootsArePresent(editables: Record<string, EditableItem>, requiredRoots: string[]): boolean {
286
+ return requiredRoots.every(rootId => editables[rootId]?.element);
314
287
  }
315
288
 
316
289
  /**
317
290
  * Waits for all required root elements to be present in the DOM.
318
291
  *
319
292
  * @param editorId The editor's ID.
320
- * @param requiredRoots The list of required root IDs.
321
- * @returns A promise that resolves to the record of root elements.
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.
322
295
  */
323
296
  async function waitForAllRootsToBePresent(
324
297
  editorId: EditorId,
325
298
  requiredRoots: string[],
326
- ): Promise<Record<string, HTMLElement>> {
299
+ ): Promise<Record<string, EditableItem>> {
327
300
  return waitFor(
328
301
  () => {
329
- const elements = queryEditablesElements(editorId) as unknown as Record<string, HTMLElement>;
302
+ const editables = queryAllEditorEditables(editorId);
330
303
 
331
- if (!checkIfAllRootsArePresent(elements, requiredRoots)) {
304
+ if (!checkIfAllRootsArePresent(editables, requiredRoots)) {
332
305
  throw new Error(
333
306
  'It looks like not all required root elements are present yet.\n'
334
307
  + '* If you want to wait for them, ensure they are registered before editor initialization.\n'
335
308
  + '* If you want lazy initialize roots, consider removing root values from the `initialData` config '
336
309
  + 'and assign initial data in editable components.\n'
337
- + `Missing roots: ${requiredRoots.filter(rootId => !elements[rootId]).join(', ')}.`,
310
+ + `Missing roots: ${requiredRoots.filter(rootId => !editables[rootId]?.element).join(', ')}.`,
338
311
  );
339
312
  }
340
313
 
341
- return elements;
314
+ return editables;
342
315
  },
343
316
  { timeOutAfter: 2000, retryAfter: 100 },
344
317
  );
@@ -0,0 +1,60 @@
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
+ ...editables[rootKey]!.content !== null && {
38
+ initialData: editables[rootKey]!.content,
39
+ },
40
+ ...!isClassicEditor && editables[rootKey]!.element !== null && {
41
+ element: editables[rootKey]!.element,
42
+ },
43
+ }
44
+ : {},
45
+ },
46
+ }), Object.create(config.roots || {}));
47
+ /* v8 ignore stop */
48
+
49
+ const mappedConfig: C = {
50
+ ...config,
51
+ roots: rootsConfig,
52
+ ...isClassicEditor && editables['main']?.element && {
53
+ attachTo: editables['main'].element,
54
+ },
55
+ };
56
+
57
+ delete mappedConfig.root;
58
+
59
+ return mappedConfig;
60
+ }
@@ -1,5 +1,4 @@
1
- export * from './assign-initial-data-to-editor-config';
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,77 @@
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
+
22
+ acc[rootName] = {
23
+ element: el.querySelector<HTMLElement>('[data-cke-editable-content]'),
24
+ content: canonical['content'],
25
+ };
26
+
27
+ return acc;
28
+ }, Object.create({}));
29
+
30
+ const rootHook = window.Livewire
31
+ .all()
32
+ .find(({ name, canonical }) => name === 'ckeditor5' && canonical['editorId'] === editorId);
33
+
34
+ /* v8 ignore next -- @preserve */
35
+ if (!rootHook) {
36
+ return acc;
37
+ }
38
+
39
+ // v8 ignore next -- @preserve
40
+ const editorContent: Record<string, string> = (rootHook.canonical.content as Record<string, string>) ?? {};
41
+ const classicMainElement = document.querySelector<HTMLElement>(`#${editorId}_editor`);
42
+
43
+ if (classicMainElement && !acc['main']) {
44
+ acc['main'] = {
45
+ element: classicMainElement,
46
+ content: editorContent['main'] ?? '',
47
+ };
48
+ }
49
+
50
+ for (const [rootName, rootContent] of Object.entries(editorContent)) {
51
+ if (acc[rootName]) {
52
+ // Editable component is already present — fill content from editor level if the editable didn't provide its own.
53
+ acc[rootName] = {
54
+ ...acc[rootName],
55
+ content: acc[rootName].content ?? rootContent,
56
+ };
57
+ }
58
+ else {
59
+ // Root has server-provided content but its editable component hasn't been attached to the DOM yet.
60
+ acc[rootName] = {
61
+ element: null,
62
+ content: rootContent,
63
+ };
64
+ }
65
+ }
66
+
67
+ return acc;
68
+ }
69
+
70
+ /**
71
+ * Type representing an editable item within an editor.
72
+ * `element` is null when the root's DOM element hasn't appeared yet (pending root).
73
+ */
74
+ export type EditableItem = {
75
+ element: HTMLElement | null;
76
+ content: string | null;
77
+ };
@@ -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"}
@@ -1,32 +0,0 @@
1
- import { EditorId } from '../typings';
2
- /**
3
- * Gets the initial root elements for the editor based on its type.
4
- *
5
- * @param editorId The editor's ID.
6
- * @returns The root element(s) for the editor.
7
- */
8
- export declare function queryEditablesElements(editorId: EditorId): Record<string, HTMLElement>;
9
- /**
10
- * Queries all editable elements within a specific editor instance.
11
- *
12
- * @param editorId The ID of the editor to query.
13
- * @returns An object mapping editable names to their corresponding elements and initial values.
14
- */
15
- export declare function queryAllEditorEditables(editorId: EditorId): Record<string, EditableItem>;
16
- /**
17
- * Gets the initial data for the roots of the editor. If the editor is a single editing-like editor,
18
- * it retrieves the initial value from the element's attribute. Otherwise, it returns an object mapping
19
- * editable names to their initial values.
20
- *
21
- * @param editorId The editor's ID.
22
- * @returns The initial values for the editor's roots.
23
- */
24
- export declare function queryEditablesSnapshotContent(editorId: EditorId): Record<string, string>;
25
- /**
26
- * Type representing an editable item within an editor.
27
- */
28
- export type EditableItem = {
29
- element: HTMLElement;
30
- content: string | null;
31
- };
32
- //# sourceMappingURL=query-editor-editables.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"query-editor-editables.d.ts","sourceRoot":"","sources":["../../../../../src/hooks/editor/utils/query-editor-editables.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,QAAQ,+BAIxD;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CA+CxF;AAED;;;;;;;GAOG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,QAAQ,GAIW,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAChG;AAED;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,WAAW,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAAC"}
@@ -1,48 +0,0 @@
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 start -- @preserve */
27
- ...rootKey in dataMap
28
- ? {
29
- initialData: dataMap[rootKey],
30
- }
31
- : {},
32
- /* v8 ignore stop -- @preserve */
33
- },
34
- }), Object.create(config.roots || {}));
35
-
36
- const mappedConfig: C = {
37
- ...config,
38
- roots: rootsConfig,
39
- };
40
-
41
- delete mappedConfig.root;
42
-
43
- return mappedConfig;
44
- }
45
-
46
- function toDataMap(element: string | Record<string, string>): Record<string, string> {
47
- return typeof element === 'string' ? { main: element } : { ...element };
48
- }
@@ -1,61 +0,0 @@
1
- import type { EditorConfig } from 'ckeditor5';
2
-
3
- import type { EditorRelaxedConstructor } from '../types/editor-relaxed-constructor.type';
4
-
5
- /**
6
- * Assigns a DOM element to the editor configuration in a way that is compatible with the specific editor type.
7
- *
8
- * @param Editor Constructor of the editor used to determine the location of element config entry.
9
- * @param elementOrMap Element to be assigned to config.
10
- * @param config Config of the editor.
11
- * @returns The updated configuration object.
12
- */
13
- export function assignSourceElementsToEditorConfig<C extends EditorConfig>(
14
- Editor: EditorRelaxedConstructor,
15
- elementOrMap: HTMLElement | Record<string, HTMLElement>,
16
- config: C,
17
- ): C {
18
- const elementsMap = toElementsMap(elementOrMap);
19
-
20
- if (!Editor.editorName || Editor.editorName === 'ClassicEditor') {
21
- return {
22
- ...config,
23
- attachTo: elementsMap['main'],
24
- };
25
- }
26
-
27
- const allRootsKeys = new Set([
28
- ...Object.keys(elementsMap),
29
- ...Object.keys(config.roots ?? {}),
30
- ]);
31
-
32
- const rootsConfig = Array.from(allRootsKeys).reduce((acc, rootKey) => ({
33
- ...acc,
34
- [rootKey]: {
35
- /* v8 ignore next */
36
- ...config.roots?.[rootKey],
37
- ...rootKey === 'main' ? config.root : {},
38
-
39
- /* v8 ignore start -- @preserve */
40
- ...rootKey in elementsMap
41
- ? {
42
- element: elementsMap[rootKey],
43
- }
44
- : {},
45
- /* v8 ignore stop -- @preserve */
46
- },
47
- }), Object.create(config.roots || {}));
48
-
49
- const mappedConfig: C = {
50
- ...config,
51
- roots: rootsConfig,
52
- };
53
-
54
- delete mappedConfig.root;
55
-
56
- return mappedConfig;
57
- }
58
-
59
- function toElementsMap(element: HTMLElement | Record<string, HTMLElement>): Record<string, HTMLElement> {
60
- return element instanceof HTMLElement ? { main: element } : { ...element };
61
- }
@@ -1,93 +0,0 @@
1
- import type { EditorId } from '../typings';
2
-
3
- import { filterObjectValues, mapObjectValues } from '../../../shared';
4
-
5
- /**
6
- * Gets the initial root elements for the editor based on its type.
7
- *
8
- * @param editorId The editor's ID.
9
- * @returns The root element(s) for the editor.
10
- */
11
- export function queryEditablesElements(editorId: EditorId) {
12
- const editables = queryAllEditorEditables(editorId);
13
-
14
- return mapObjectValues(editables, ({ element }) => element);
15
- }
16
-
17
- /**
18
- * Queries all editable elements within a specific editor instance.
19
- *
20
- * @param editorId The ID of the editor to query.
21
- * @returns An object mapping editable names to their corresponding elements and initial values.
22
- */
23
- export function queryAllEditorEditables(editorId: EditorId): Record<string, EditableItem> {
24
- const acc = (
25
- window.Livewire
26
- .all()
27
- .filter(({ name, canonical }) => name === 'ckeditor5-editable' && canonical['editorId'] === editorId)
28
- .reduce<Record<string, EditableItem>>((acc, { canonical, el }) => ({
29
- ...acc,
30
- [canonical['rootName'] as string]: {
31
- element: el.querySelector('[data-cke-editable-content]')!,
32
- content: canonical['content'],
33
- },
34
- }), Object.create({}))
35
- );
36
-
37
- const rootHook = (
38
- window.Livewire
39
- .all()
40
- .find(({ name, canonical }) => name === 'ckeditor5' && canonical['editorId'] === editorId)
41
- );
42
-
43
- const initialRootEditableValue = rootHook?.canonical.content as Record<string, string> | null;
44
- const contentElement = document.querySelector<HTMLElement>(`#${editorId}_editor `);
45
- const currentMain = acc['main'];
46
-
47
- // If found `main` editable, but it has no content, try to fill it from the editor container.
48
- if (currentMain && initialRootEditableValue?.['main']) {
49
- return {
50
- ...acc,
51
- main: {
52
- ...currentMain,
53
- content: currentMain.content || initialRootEditableValue['main'],
54
- },
55
- };
56
- }
57
-
58
- // If no `main` editable found, try to create it from the editor container.
59
- if (contentElement) {
60
- return {
61
- ...acc,
62
- main: {
63
- element: contentElement,
64
- content: initialRootEditableValue?.['main'] || null,
65
- },
66
- };
67
- }
68
-
69
- return acc;
70
- }
71
-
72
- /**
73
- * Gets the initial data for the roots of the editor. If the editor is a single editing-like editor,
74
- * it retrieves the initial value from the element's attribute. Otherwise, it returns an object mapping
75
- * editable names to their initial values.
76
- *
77
- * @param editorId The editor's ID.
78
- * @returns The initial values for the editor's roots.
79
- */
80
- export function queryEditablesSnapshotContent(editorId: EditorId) {
81
- const editables = queryAllEditorEditables(editorId);
82
- const values = mapObjectValues(editables, ({ content }) => content);
83
-
84
- return filterObjectValues(values, value => typeof value === 'string') as Record<string, string>;
85
- }
86
-
87
- /**
88
- * Type representing an editable item within an editor.
89
- */
90
- export type EditableItem = {
91
- element: HTMLElement;
92
- content: string | null;
93
- };