ckeditor5-phoenix 1.27.0 → 1.27.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ckeditor5-phoenix",
3
- "version": "1.27.0",
3
+ "version": "1.27.2",
4
4
  "description": "CKEditor 5 integration for Phoenix Framework",
5
5
  "author": "Mateusz Bagiński <cziken58@gmail.com>",
6
6
  "license": "MIT",
@@ -1,4 +1,5 @@
1
1
  import type { EditorId } from './typings';
2
+ import type { EditableItem } from './utils';
2
3
 
3
4
  import { isEmptyObject, parseIntIfNotNull, waitFor } from '../../shared';
4
5
  import { ClassHook, makeHook } from '../../shared/hook';
@@ -11,8 +12,7 @@ import {
11
12
  createSyncEditorWithPhoenixPlugin,
12
13
  } from './plugins';
13
14
  import {
14
- assignInitialDataToEditorConfig,
15
- assignSourceElementsToEditorConfig,
15
+ assignEditorRootsToConfig,
16
16
  cleanupOrphanEditorElements,
17
17
  createEditorInContext,
18
18
  isSingleRootEditor,
@@ -20,8 +20,7 @@ import {
20
20
  loadEditorConstructor,
21
21
  loadEditorPlugins,
22
22
  normalizeCustomTranslations,
23
- queryEditablesElements,
24
- queryEditablesSnapshotContent,
23
+ queryAllEditorEditables,
25
24
  readPresetOrThrow,
26
25
  resolveEditorConfigElementReferences,
27
26
  resolveEditorConfigTranslations,
@@ -234,47 +233,21 @@ class EditorHookImpl extends ClassHook {
234
233
  ]
235
234
  .filter(translations => !isEmptyObject(translations));
236
235
 
237
- // Let's query all elements, and create basic configuration.
238
- let initialData: string | Record<string, string> = queryEditablesSnapshotContent(editorId);
236
+ // Query all editable elements along with their initial values in one pass.
237
+ let editables = queryAllEditorEditables(editorId);
238
+ const requiredRoots = Object.keys(editables);
239
239
 
240
240
  if (isSingleRootEditor(type)) {
241
- initialData = initialData['main'] || '';
241
+ requiredRoots.push('main');
242
242
  }
243
243
 
244
- // Depending of the editor type, and parent lookup for nearest context or initialize it without it.
245
- let sourceElements: HTMLElement | Record<string, HTMLElement> = queryEditablesElements(editorId);
246
-
247
- // Handle special case when user specified `initialData` of several root elements, but editable components
248
- // are not yet present in the DOM. In other words - editor is initialized before attaching root elements.
249
- if (!(sourceElements instanceof HTMLElement) && !('main' in sourceElements)) {
250
- const requiredRoots = (
251
- type === 'decoupled'
252
- ? ['main']
253
- : Object.keys(initialData as Record<string, string>)
254
- );
255
-
256
- if (!checkIfAllRootsArePresent(sourceElements, requiredRoots)) {
257
- sourceElements = await waitForAllRootsToBePresent(editorId, requiredRoots);
258
- initialData = queryEditablesSnapshotContent(editorId);
259
- }
244
+ if (!checkIfAllRootsArePresent(editables, requiredRoots)) {
245
+ editables = await waitForAllRootsToBePresent(editorId, requiredRoots);
260
246
  }
261
247
 
262
- // If single root editor, unwrap the element from the object.
263
- if (isSingleRootEditor(type) && 'main' in sourceElements) {
264
- sourceElements = sourceElements['main'];
265
- }
266
-
267
- let resolvedConfig = { ...config };
268
-
269
248
  // Do some postprocessing on received configuration.
270
- resolvedConfig = resolveEditorConfigElementReferences(resolvedConfig);
271
- resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
272
- resolvedConfig = assignSourceElementsToEditorConfig(Constructor, sourceElements, resolvedConfig);
273
- resolvedConfig = assignInitialDataToEditorConfig(initialData, resolvedConfig);
274
-
275
- // Post-processed configuration of the editor.
276
- const parsedConfig = {
277
- ...resolvedConfig,
249
+ let resolvedConfig = {
250
+ ...config,
278
251
  licenseKey: license.key,
279
252
  plugins: loadedPlugins,
280
253
  language,
@@ -283,15 +256,19 @@ class EditorHookImpl extends ClassHook {
283
256
  },
284
257
  };
285
258
 
259
+ resolvedConfig = resolveEditorConfigElementReferences(resolvedConfig);
260
+ resolvedConfig = resolveEditorConfigTranslations([...mixedTranslations].reverse(), language.ui, resolvedConfig);
261
+ resolvedConfig = assignEditorRootsToConfig(Constructor, editables, resolvedConfig);
262
+
286
263
  const editor = await (async () => {
287
- if (!context || !(sourceElements instanceof HTMLElement)) {
288
- return Constructor.create(parsedConfig);
264
+ if (!context) {
265
+ return Constructor.create(resolvedConfig);
289
266
  }
290
267
 
291
268
  const result = await createEditorInContext({
292
269
  context,
293
270
  creator: Constructor,
294
- config: parsedConfig,
271
+ config: resolvedConfig,
295
272
  });
296
273
 
297
274
  return result.editor;
@@ -340,42 +317,42 @@ class EditorHookImpl extends ClassHook {
340
317
  }
341
318
 
342
319
  /**
343
- * Checks if all required root elements are present in the elements object.
320
+ * Checks if all required root elements are present in the editables map.
344
321
  *
345
- * @param elements The elements object mapping root IDs to HTMLElements.
346
- * @param requiredRoots The list of required root IDs.
322
+ * @param editables The editables map keyed by root name.
323
+ * @param requiredRoots The list of required root names.
347
324
  * @returns True if all required roots are present, false otherwise.
348
325
  */
349
- function checkIfAllRootsArePresent(elements: Record<string, HTMLElement>, requiredRoots: string[]): boolean {
350
- return requiredRoots.every(rootId => elements[rootId]);
326
+ function checkIfAllRootsArePresent(editables: Record<string, EditableItem>, requiredRoots: string[]): boolean {
327
+ return requiredRoots.every(rootId => editables[rootId]);
351
328
  }
352
329
 
353
330
  /**
354
331
  * Waits for all required root elements to be present in the DOM.
355
332
  *
356
333
  * @param editorId The editor's ID.
357
- * @param requiredRoots The list of required root IDs.
358
- * @returns A promise that resolves to the record of root elements.
334
+ * @param requiredRoots The list of required root names.
335
+ * @returns A promise that resolves to the map of editable items.
359
336
  */
360
337
  async function waitForAllRootsToBePresent(
361
338
  editorId: EditorId,
362
339
  requiredRoots: string[],
363
- ): Promise<Record<string, HTMLElement>> {
340
+ ): Promise<Record<string, EditableItem>> {
364
341
  return waitFor(
365
342
  () => {
366
- const elements = queryEditablesElements(editorId) as unknown as Record<string, HTMLElement>;
343
+ const editables = queryAllEditorEditables(editorId);
367
344
 
368
- if (!checkIfAllRootsArePresent(elements, requiredRoots)) {
345
+ if (!checkIfAllRootsArePresent(editables, requiredRoots)) {
369
346
  throw new Error(
370
347
  'It looks like not all required root elements are present yet.\n'
371
348
  + '* If you want to wait for them, ensure they are registered before editor initialization.\n'
372
349
  + '* If you want lazy initialize roots, consider removing root values from the `initialData` config '
373
350
  + 'and assign initial data in editable components.\n'
374
- + `Missing roots: ${requiredRoots.filter(rootId => !elements[rootId]).join(', ')}.`,
351
+ + `Missing roots: ${requiredRoots.filter(rootId => !editables[rootId]).join(', ')}.`,
375
352
  );
376
353
  }
377
354
 
378
- return elements;
355
+ return editables;
379
356
  },
380
357
  { timeOutAfter: 2000, retryAfter: 100 },
381
358
  );
@@ -1,45 +1,40 @@
1
1
  import type { EditorConfig } from 'ckeditor5';
2
2
 
3
3
  import type { EditorRelaxedConstructor } from '../types/editor-relaxed-constructor.type';
4
+ import type { EditableItem } from './query-all-editor-editables';
4
5
 
5
6
  /**
6
- * Assigns a DOM element to the editor configuration in a way that is compatible with the specific editor type.
7
+ * Assigns DOM elements and initial data to the editor configuration in a way that is compatible
8
+ * with the specific editor type.
7
9
  *
8
10
  * @param Editor Constructor of the editor used to determine the location of element config entry.
9
- * @param elementOrMap Element to be assigned to config.
11
+ * @param editables Map of editable items (element + initial value) keyed by root name.
10
12
  * @param config Config of the editor.
11
13
  * @returns The updated configuration object.
12
14
  */
13
- export function assignSourceElementsToEditorConfig<C extends EditorConfig>(
15
+ export function assignEditorRootsToConfig<C extends EditorConfig>(
14
16
  Editor: EditorRelaxedConstructor,
15
- elementOrMap: HTMLElement | Record<string, HTMLElement>,
17
+ editables: Record<string, EditableItem>,
16
18
  config: C,
17
19
  ): 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
-
20
+ const isClassicEditor = !Editor.editorName || Editor.editorName === 'ClassicEditor';
27
21
  const allRootsKeys = new Set([
28
- ...Object.keys(elementsMap),
22
+ ...Object.keys(editables),
29
23
  ...Object.keys(config.roots ?? {}),
30
24
  ]);
31
25
 
32
26
  const rootsConfig = Array.from(allRootsKeys).reduce((acc, rootKey) => ({
33
27
  ...acc,
34
28
  [rootKey]: {
35
- /* v8 ignore next */
29
+ /* v8 ignore next 1 */
36
30
  ...config.roots?.[rootKey],
37
31
  ...rootKey === 'main' ? config.root : {},
38
32
 
39
- /* v8 ignore next 5 */
40
- ...rootKey in elementsMap
33
+ /* v8 ignore next 6 */
34
+ ...rootKey in editables
41
35
  ? {
42
- element: elementsMap[rootKey],
36
+ initialData: editables[rootKey]!.initialValue,
37
+ ...!isClassicEditor && { element: editables[rootKey]!.content },
43
38
  }
44
39
  : {},
45
40
  },
@@ -48,13 +43,12 @@ export function assignSourceElementsToEditorConfig<C extends EditorConfig>(
48
43
  const mappedConfig: C = {
49
44
  ...config,
50
45
  roots: rootsConfig,
46
+ ...isClassicEditor && {
47
+ attachTo: editables['main']?.content,
48
+ },
51
49
  };
52
50
 
53
51
  delete mappedConfig.root;
54
52
 
55
53
  return mappedConfig;
56
54
  }
57
-
58
- function toElementsMap(element: HTMLElement | Record<string, HTMLElement>): Record<string, HTMLElement> {
59
- return element instanceof HTMLElement ? { main: element } : { ...element };
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 './is-multiroot-editor-instance';
@@ -1,34 +1,5 @@
1
1
  import type { EditorId } from '../typings';
2
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, ({ content }) => content);
15
- }
16
-
17
- /**
18
- * Gets the initial data for the roots of the editor. If the editor is a single editing-like editor,
19
- * it retrieves the initial value from the element's attribute. Otherwise, it returns an object mapping
20
- * editable names to their initial values.
21
- *
22
- * @param editorId The editor's ID.
23
- * @returns The initial values for the editor's roots.
24
- */
25
- export function queryEditablesSnapshotContent(editorId: EditorId) {
26
- const editables = queryAllEditorEditables(editorId);
27
- const values = mapObjectValues(editables, ({ initialValue }) => initialValue);
28
-
29
- return filterObjectValues(values, value => typeof value === 'string') as Record<string, string>;
30
- }
31
-
32
3
  /**
33
4
  * Queries all editable elements within a specific editor instance.
34
5
  *
@@ -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,CA8BH"}
@@ -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,CAuCH"}
@@ -1,47 +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 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
- }