ckeditor5-phoenix 1.27.2 → 1.28.1

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.2",
3
+ "version": "1.28.1",
4
4
  "description": "CKEditor 5 integration for Phoenix Framework",
5
5
  "author": "Mateusz Bagiński <cziken58@gmail.com>",
6
6
  "license": "MIT",
@@ -24,8 +24,8 @@
24
24
  "test-utils"
25
25
  ],
26
26
  "dependencies": {
27
- "ckeditor5": "^48.1.0",
28
- "ckeditor5-premium-features": "^48.1.0",
27
+ "ckeditor5": "^48.2.0",
28
+ "ckeditor5-premium-features": "^48.2.0",
29
29
  "phoenix_live_view": "^1.0.17"
30
30
  },
31
31
  "devDependencies": {
@@ -105,6 +105,35 @@ describe('editable hook', () => {
105
105
 
106
106
  expect(editor.getData({ rootName: 'foo' })).toBe('<p>Foo</p>');
107
107
  });
108
+
109
+ it('should set proper root element name on initial added root', async () => {
110
+ const editable = createEditableHtmlElement({
111
+ name: 'foo',
112
+ initialValue: '<p>Foo</p>',
113
+ modelElement: '$inlineRoot',
114
+ });
115
+
116
+ document.body.appendChild(editable);
117
+
118
+ const editor = await appendMultirootEditor();
119
+ EditableHook.mounted.call({ el: editable });
120
+
121
+ expect(editor.model.document.getRoot('foo')?.name).to.be.equal('$inlineRoot');
122
+ });
123
+
124
+ it('should set proper root element name on lazy added root', async () => {
125
+ const editor = await appendMultirootEditor();
126
+ const editable = createEditableHtmlElement({
127
+ name: 'foo',
128
+ initialValue: '<p>Foo</p>',
129
+ modelElement: '$inlineRoot',
130
+ });
131
+
132
+ document.body.appendChild(editable);
133
+ EditableHook.mounted.call({ el: editable });
134
+
135
+ expect(editor.model.document.getRoot('foo')?.name).to.be.equal('$inlineRoot');
136
+ });
108
137
  });
109
138
 
110
139
  describe('value synchronization', () => {
@@ -22,6 +22,7 @@ class EditableHookImpl extends ClassHook {
22
22
  editableId: this.el.getAttribute('id')!,
23
23
  editorId: this.el.getAttribute('data-cke-editor-id') || null,
24
24
  rootName: this.el.getAttribute('data-cke-editable-root-name')!,
25
+ modelElement: this.el.getAttribute('data-cke-editable-root-model-element-name') || null,
25
26
  initialValue: this.el.getAttribute('data-cke-editable-initial-value') || '',
26
27
  };
27
28
 
@@ -39,7 +40,7 @@ class EditableHookImpl extends ClassHook {
39
40
  * Mounts the editable component.
40
41
  */
41
42
  override mounted() {
42
- const { editableId, editorId, rootName, initialValue } = this.attrs;
43
+ const { editableId, editorId, rootName, initialValue, modelElement } = this.attrs;
43
44
 
44
45
  const unmountEffect = EditorsRegistry.the.mountEffect(editorId, (editor: MultiRootEditor | DecoupledEditor) => {
45
46
  const contentElement = this.el.querySelector('[data-cke-editable-content]') as HTMLElement;
@@ -57,6 +58,7 @@ class EditableHookImpl extends ClassHook {
57
58
  editor.addRoot(rootName, {
58
59
  isUndoable: false,
59
60
  initialData: initialValue,
61
+ modelElement: modelElement || '$root',
60
62
  });
61
63
 
62
64
  const editable = ui.view.createEditable(rootName, contentElement);
@@ -111,6 +111,19 @@ describe('editor hook', () => {
111
111
 
112
112
  expect(isEditorShown()).toBe(true);
113
113
  });
114
+
115
+ it('should be possible to specify root element name', async () => {
116
+ const hookElement = createEditorHtmlElement({
117
+ modelElement: '$inlineRoot',
118
+ });
119
+
120
+ document.body.appendChild(hookElement);
121
+ EditorHook.mounted.call({ el: hookElement });
122
+
123
+ const editor = await waitForTestEditor();
124
+
125
+ expect(editor.model.document.getRoot()?.name).to.be.equal('$inlineRoot');
126
+ });
114
127
  });
115
128
 
116
129
  describe('decoupled', () => {
@@ -249,6 +262,41 @@ describe('editor hook', () => {
249
262
  expect(editor).toBeInstanceOf(DecoupledEditor);
250
263
  expect(editor.getData()).toBe('<p>XD</p>');
251
264
  });
265
+
266
+ it('should be possible to specify root element name using editable config alone', async () => {
267
+ const hookElement = createEditorHtmlElement({
268
+ preset: createEditorPreset('decoupled'),
269
+ });
270
+
271
+ document.body.appendChild(hookElement);
272
+ document.body.appendChild(createEditableHtmlElement({
273
+ modelElement: '$inlineRoot',
274
+ }));
275
+
276
+ EditorHook.mounted.call({ el: hookElement });
277
+
278
+ const editor = await waitForTestEditor();
279
+
280
+ expect(editor.model.document.getRoot()?.name).toEqual('$inlineRoot');
281
+ });
282
+
283
+ it('should use editable root element name config if both specified', async () => {
284
+ const hookElement = createEditorHtmlElement({
285
+ preset: createEditorPreset('decoupled'),
286
+ modelElement: '$miamia',
287
+ });
288
+
289
+ document.body.appendChild(hookElement);
290
+ document.body.appendChild(createEditableHtmlElement({
291
+ modelElement: '$inlineRoot',
292
+ }));
293
+
294
+ EditorHook.mounted.call({ el: hookElement });
295
+
296
+ const editor = await waitForTestEditor();
297
+
298
+ expect(editor.model.document.getRoot()?.name).toEqual('$inlineRoot');
299
+ });
252
300
  });
253
301
 
254
302
  describe('inline', () => {
@@ -313,6 +361,36 @@ describe('editor hook', () => {
313
361
  '<p>Third root</p>',
314
362
  );
315
363
  });
364
+
365
+ it('should create a multiroot editor with inline editables', async () => {
366
+ const hookElement = createEditorHtmlElement({
367
+ preset: createEditorPreset('multiroot'),
368
+ });
369
+
370
+ document.body.appendChild(hookElement);
371
+ document.body.appendChild(
372
+ createEditableHtmlElement({
373
+ name: 'second',
374
+ initialValue: '<p>Second root</p>',
375
+ modelElement: '$inlineRoot',
376
+ }),
377
+ );
378
+
379
+ document.body.appendChild(
380
+ createEditableHtmlElement({
381
+ name: 'third',
382
+ initialValue: '<p>Third root</p>',
383
+ modelElement: '$inlineRoot',
384
+ }),
385
+ );
386
+
387
+ EditorHook.mounted.call({ el: hookElement });
388
+
389
+ const editor = await waitForTestEditor();
390
+
391
+ expect(editor.model.document.getRoot('second')?.name).toEqual('$inlineRoot');
392
+ expect(editor.model.document.getRoot('third')?.name).toEqual('$inlineRoot');
393
+ });
316
394
  });
317
395
  });
318
396
 
@@ -168,7 +168,7 @@ function getEditorRootsValues(editor: Editor) {
168
168
  return roots.reduce<Record<string, string>>((acc, rootName) => {
169
169
  acc[rootName] = editor.getData({ rootName });
170
170
  return acc;
171
- }, Object.create({}));
171
+ }, Object.create(null));
172
172
  }
173
173
 
174
174
  /**
@@ -30,15 +30,19 @@ export function assignEditorRootsToConfig<C extends EditorConfig>(
30
30
  ...config.roots?.[rootKey],
31
31
  ...rootKey === 'main' ? config.root : {},
32
32
 
33
- /* v8 ignore next 6 */
33
+ /* v8 ignore start */
34
34
  ...rootKey in editables
35
35
  ? {
36
36
  initialData: editables[rootKey]!.initialValue,
37
- ...!isClassicEditor && { element: editables[rootKey]!.content },
37
+ modelElement: editables[rootKey]!.modelElement || '$root',
38
+ ...!isClassicEditor && {
39
+ element: editables[rootKey]!.content,
40
+ },
38
41
  }
39
42
  : {},
43
+ /* v8 ignore end */
40
44
  },
41
- }), Object.create(config.roots || {}));
45
+ }), { ...config.roots || {} });
42
46
 
43
47
  const mappedConfig: C = {
44
48
  ...config,
@@ -20,6 +20,7 @@ export function queryAllEditorEditables(editorId: EditorId): Record<string, Edit
20
20
  .from(iterator)
21
21
  .reduce<Record<string, EditableItem>>((acc, element) => {
22
22
  const name = element.getAttribute('data-cke-editable-root-name');
23
+ const modelElement = element.getAttribute('data-cke-editable-root-model-element-name') || null;
23
24
  const initialValue = element.getAttribute('data-cke-editable-initial-value') || '';
24
25
  const content = element.querySelector('[data-cke-editable-content]') as HTMLElement;
25
26
 
@@ -32,9 +33,10 @@ export function queryAllEditorEditables(editorId: EditorId): Record<string, Edit
32
33
  [name]: {
33
34
  content,
34
35
  initialValue,
36
+ modelElement,
35
37
  },
36
38
  };
37
- }, Object.create({}))
39
+ }, Object.create(null))
38
40
  );
39
41
 
40
42
  const rootEditorElement = document.querySelector<HTMLElement>(`[phx-hook="CKEditor5"][id="${editorId}"]`);
@@ -43,26 +45,29 @@ export function queryAllEditorEditables(editorId: EditorId): Record<string, Edit
43
45
  return acc;
44
46
  }
45
47
 
48
+ const rootEditorModelElement = rootEditorElement.getAttribute('data-cke-root-model-element-name');
46
49
  const initialRootEditableValue = rootEditorElement.getAttribute('data-cke-initial-value') || '';
47
- const contentElement = rootEditorElement.querySelector<HTMLElement>(`#${editorId}_editor `);
48
- const currentMain = acc['main'];
49
50
 
50
- if (currentMain) {
51
+ if (acc['main']) {
51
52
  return {
52
53
  ...acc,
53
54
  main: {
54
- ...currentMain,
55
- initialValue: currentMain.initialValue || initialRootEditableValue,
55
+ ...acc['main'],
56
+ modelElement: acc['main'].modelElement || rootEditorModelElement,
57
+ initialValue: acc['main'].initialValue || initialRootEditableValue,
56
58
  },
57
59
  };
58
60
  }
59
61
 
62
+ const contentElement = rootEditorElement.querySelector<HTMLElement>(`#${editorId}_editor `);
63
+
60
64
  if (contentElement) {
61
65
  return {
62
66
  ...acc,
63
67
  main: {
64
68
  content: contentElement,
65
69
  initialValue: initialRootEditableValue,
70
+ modelElement: rootEditorElement.getAttribute('data-cke-root-model-element-name') || '$root',
66
71
  },
67
72
  };
68
73
  }
@@ -76,4 +81,5 @@ export function queryAllEditorEditables(editorId: EditorId): Record<string, Edit
76
81
  export type EditableItem = {
77
82
  content: HTMLElement;
78
83
  initialValue: string;
84
+ modelElement: string | null;
79
85
  };
@@ -12,12 +12,14 @@ export function createEditableHtmlElement(
12
12
  editorId,
13
13
  initialValue,
14
14
  withInput = false,
15
+ modelElement,
15
16
  }: {
16
17
  id?: string;
17
18
  editorId?: EditorId;
18
19
  name?: string;
19
20
  initialValue?: string;
20
21
  withInput?: boolean;
22
+ modelElement?: string;
21
23
  } = {},
22
24
  ) {
23
25
  return html.div(
@@ -30,6 +32,9 @@ export function createEditableHtmlElement(
30
32
  ...initialValue && {
31
33
  'data-cke-editable-initial-value': initialValue,
32
34
  },
35
+ ...modelElement && {
36
+ 'data-cke-editable-root-model-element-name': modelElement,
37
+ },
33
38
  },
34
39
  html.div({
35
40
  'class': 'editable-content',
@@ -20,6 +20,7 @@ export function createEditorHtmlElement(
20
20
  readyEvent = false,
21
21
  watchdog = false,
22
22
  saveDebounceMs = undefined,
23
+ modelElement,
23
24
  language,
24
25
  hookAttrs,
25
26
  }: EditorCreatorAttrs = {},
@@ -63,6 +64,9 @@ export function createEditorHtmlElement(
63
64
  ...contextId && {
64
65
  'data-cke-context-id': contextId,
65
66
  },
67
+ ...modelElement && {
68
+ 'data-cke-root-model-element-name': modelElement,
69
+ },
66
70
  ...hookAttrs,
67
71
  },
68
72
  !['multiroot', 'decoupled'].includes(preset.type) && html.div({ id: `${id}_editor` }),
@@ -91,5 +95,6 @@ type EditorCreatorAttrs = {
91
95
  watchdog?: boolean;
92
96
  saveDebounceMs?: number;
93
97
  hookAttrs?: Record<string, string>;
98
+ modelElement?: string;
94
99
  language?: { ui?: string; content?: string; };
95
100
  };