ckeditor5-phoenix 1.27.2 → 1.28.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.map +1 -1
- package/dist/hooks/editor/utils/assign-editor-roots-to-config.d.ts.map +1 -1
- package/dist/hooks/editor/utils/query-all-editor-editables.d.ts +1 -0
- package/dist/hooks/editor/utils/query-all-editor-editables.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +134 -122
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
- package/src/hooks/editable.test.ts +29 -0
- package/src/hooks/editable.ts +3 -1
- package/src/hooks/editor/editor.test.ts +94 -0
- package/src/hooks/editor/plugins/sync-editor-with-phoenix.ts +1 -1
- package/src/hooks/editor/utils/assign-editor-roots-to-config.ts +7 -3
- package/src/hooks/editor/utils/query-all-editor-editables.ts +12 -6
- package/test-utils/editor/create-editable-html-element.ts +5 -0
- package/test-utils/editor/create-editor-html-element.ts +5 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ckeditor5-phoenix",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.28.0",
|
|
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.
|
|
28
|
-
"ckeditor5-premium-features": "^48.
|
|
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', () => {
|
package/src/hooks/editable.ts
CHANGED
|
@@ -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,57 @@ 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 global config (if editable model element not specified)', async () => {
|
|
267
|
+
const hookElement = createEditorHtmlElement({
|
|
268
|
+
preset: createEditorPreset('decoupled'),
|
|
269
|
+
modelElement: '$inlineRoot',
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
document.body.appendChild(hookElement);
|
|
273
|
+
document.body.appendChild(createEditableHtmlElement());
|
|
274
|
+
|
|
275
|
+
EditorHook.mounted.call({ el: hookElement });
|
|
276
|
+
|
|
277
|
+
const editor = await waitForTestEditor();
|
|
278
|
+
|
|
279
|
+
expect(editor.model.document.getRoot()?.name).toEqual('$inlineRoot');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('should be possible to specify root element name using editable config alone', async () => {
|
|
283
|
+
const hookElement = createEditorHtmlElement({
|
|
284
|
+
preset: createEditorPreset('decoupled'),
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
document.body.appendChild(hookElement);
|
|
288
|
+
document.body.appendChild(createEditableHtmlElement({
|
|
289
|
+
modelElement: '$inlineRoot',
|
|
290
|
+
}));
|
|
291
|
+
|
|
292
|
+
EditorHook.mounted.call({ el: hookElement });
|
|
293
|
+
|
|
294
|
+
const editor = await waitForTestEditor();
|
|
295
|
+
|
|
296
|
+
expect(editor.model.document.getRoot()?.name).toEqual('$inlineRoot');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('should use editable root element name config if both specified', async () => {
|
|
300
|
+
const hookElement = createEditorHtmlElement({
|
|
301
|
+
preset: createEditorPreset('decoupled'),
|
|
302
|
+
modelElement: '$miamia',
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
document.body.appendChild(hookElement);
|
|
306
|
+
document.body.appendChild(createEditableHtmlElement({
|
|
307
|
+
modelElement: '$inlineRoot',
|
|
308
|
+
}));
|
|
309
|
+
|
|
310
|
+
EditorHook.mounted.call({ el: hookElement });
|
|
311
|
+
|
|
312
|
+
const editor = await waitForTestEditor();
|
|
313
|
+
|
|
314
|
+
expect(editor.model.document.getRoot()?.name).toEqual('$inlineRoot');
|
|
315
|
+
});
|
|
252
316
|
});
|
|
253
317
|
|
|
254
318
|
describe('inline', () => {
|
|
@@ -313,6 +377,36 @@ describe('editor hook', () => {
|
|
|
313
377
|
'<p>Third root</p>',
|
|
314
378
|
);
|
|
315
379
|
});
|
|
380
|
+
|
|
381
|
+
it('should create a multiroot editor with inline editables', async () => {
|
|
382
|
+
const hookElement = createEditorHtmlElement({
|
|
383
|
+
preset: createEditorPreset('multiroot'),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
document.body.appendChild(hookElement);
|
|
387
|
+
document.body.appendChild(
|
|
388
|
+
createEditableHtmlElement({
|
|
389
|
+
name: 'second',
|
|
390
|
+
initialValue: '<p>Second root</p>',
|
|
391
|
+
modelElement: '$inlineRoot',
|
|
392
|
+
}),
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
document.body.appendChild(
|
|
396
|
+
createEditableHtmlElement({
|
|
397
|
+
name: 'third',
|
|
398
|
+
initialValue: '<p>Third root</p>',
|
|
399
|
+
modelElement: '$inlineRoot',
|
|
400
|
+
}),
|
|
401
|
+
);
|
|
402
|
+
|
|
403
|
+
EditorHook.mounted.call({ el: hookElement });
|
|
404
|
+
|
|
405
|
+
const editor = await waitForTestEditor();
|
|
406
|
+
|
|
407
|
+
expect(editor.model.document.getRoot('second')?.name).toEqual('$inlineRoot');
|
|
408
|
+
expect(editor.model.document.getRoot('third')?.name).toEqual('$inlineRoot');
|
|
409
|
+
});
|
|
316
410
|
});
|
|
317
411
|
});
|
|
318
412
|
|
|
@@ -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
|
|
33
|
+
/* v8 ignore start */
|
|
34
34
|
...rootKey in editables
|
|
35
35
|
? {
|
|
36
36
|
initialData: editables[rootKey]!.initialValue,
|
|
37
|
-
|
|
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
|
-
}),
|
|
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 (
|
|
51
|
+
if (acc['main']) {
|
|
51
52
|
return {
|
|
52
53
|
...acc,
|
|
53
54
|
main: {
|
|
54
|
-
...
|
|
55
|
-
|
|
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
|
};
|