ckeditor5-blazor 1.3.0 → 1.5.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/elements/editor/typings.d.ts +14 -2
- package/dist/elements/editor/typings.d.ts.map +1 -1
- package/dist/elements/editor/utils/load-editor-plugins.d.ts +2 -1
- package/dist/elements/editor/utils/load-editor-plugins.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +156 -144
- package/dist/index.mjs.map +1 -1
- package/dist/interop/create-editable-blazor-interop.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/elements/editor/typings.ts +15 -2
- package/src/elements/editor/utils/load-editor-plugins.test.ts +48 -1
- package/src/elements/editor/utils/load-editor-plugins.ts +25 -4
- package/src/interop/create-editable-blazor-interop.test.ts +21 -0
- package/src/interop/create-editable-blazor-interop.ts +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-editable-blazor-interop.d.ts","sourceRoot":"","sources":["../../../src/interop/create-editable-blazor-interop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"create-editable-blazor-interop.d.ts","sourceRoot":"","sources":["../../../src/interop/create-editable-blazor-interop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAQ9C;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,aAAa;IAgDpF;;OAEG;;IAiBH;;;OAGG;sBACqB,MAAM;EAWjC"}
|
package/package.json
CHANGED
|
@@ -10,9 +10,22 @@ export type EditorId = string;
|
|
|
10
10
|
export type EditorType = 'inline' | 'classic' | 'balloon' | 'decoupled' | 'multiroot';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Represents a
|
|
13
|
+
* Represents a custom plugin loaded from a JavaScript module path.
|
|
14
|
+
* Serialized from C# as <c>{ "$import": { "name": "...", "path": "..." } }</c>.
|
|
14
15
|
*/
|
|
15
|
-
export type
|
|
16
|
+
export type EditorPluginImport = {
|
|
17
|
+
$import: {
|
|
18
|
+
name: string;
|
|
19
|
+
path: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Represents a CKEditor5 plugin — either a built-in string identifier or a
|
|
25
|
+
* custom {@link EditorPluginImport} descriptor that loads a named export from
|
|
26
|
+
* the given JavaScript module path.
|
|
27
|
+
*/
|
|
28
|
+
export type EditorPlugin = string | EditorPluginImport;
|
|
16
29
|
|
|
17
30
|
/**
|
|
18
31
|
* Configuration object for CKEditor5 editor instance.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { EditorPlugin } from '../typings';
|
|
2
2
|
|
|
3
|
-
import { afterEach, describe, expect, it } from 'vitest';
|
|
3
|
+
import { afterEach, describe, expect, it, vi } from 'vitest';
|
|
4
4
|
|
|
5
5
|
import { CustomEditorPluginsRegistry } from '../custom-editor-plugins';
|
|
6
6
|
import { loadEditorPlugins } from './load-editor-plugins';
|
|
@@ -11,6 +11,22 @@ class CustomPlugin {
|
|
|
11
11
|
}
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
class ImportedPlugin {
|
|
15
|
+
static get pluginName() {
|
|
16
|
+
return 'ImportedPlugin';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
vi.mock('custom-import-module', () => ({
|
|
21
|
+
ImportedPlugin,
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
vi.mock('default-export-module', () => ({
|
|
25
|
+
default: ImportedPlugin,
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
vi.mock('empty-module', () => ({}));
|
|
29
|
+
|
|
14
30
|
describe('loadEditorPlugins', () => {
|
|
15
31
|
it('should load plugins from base package', async () => {
|
|
16
32
|
const plugins: EditorPlugin[] = ['Bold', 'Italic'];
|
|
@@ -97,4 +113,35 @@ describe('loadEditorPlugins', () => {
|
|
|
97
113
|
expect(loadedPlugins[0]).toEqual(CustomPlugin);
|
|
98
114
|
});
|
|
99
115
|
});
|
|
116
|
+
|
|
117
|
+
describe('import descriptor plugins', () => {
|
|
118
|
+
it('should load plugin from named export via $import descriptor', async () => {
|
|
119
|
+
const plugins: EditorPlugin[] = [{ $import: { name: 'ImportedPlugin', path: 'custom-import-module' } }];
|
|
120
|
+
const { loadedPlugins } = await loadEditorPlugins(plugins);
|
|
121
|
+
|
|
122
|
+
expect(loadedPlugins).toHaveLength(1);
|
|
123
|
+
expect(loadedPlugins[0]).toEqual(ImportedPlugin);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('should fall back to default export when named export is absent', async () => {
|
|
127
|
+
const plugins: EditorPlugin[] = [{ $import: { name: 'NonExistent', path: 'default-export-module' } }];
|
|
128
|
+
const { loadedPlugins } = await loadEditorPlugins(plugins);
|
|
129
|
+
|
|
130
|
+
expect(loadedPlugins).toHaveLength(1);
|
|
131
|
+
expect(loadedPlugins[0]).toEqual(ImportedPlugin);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('should throw when neither named nor default export is found', async () => {
|
|
135
|
+
const plugins: EditorPlugin[] = [{ $import: { name: 'MissingExport', path: 'empty-module' } }];
|
|
136
|
+
await expect(loadEditorPlugins(plugins)).rejects.toThrowError(/not found in module/);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should mix string plugins and import descriptors', async () => {
|
|
140
|
+
const plugins: EditorPlugin[] = ['Bold', { $import: { name: 'ImportedPlugin', path: 'custom-import-module' } }];
|
|
141
|
+
const { loadedPlugins } = await loadEditorPlugins(plugins);
|
|
142
|
+
|
|
143
|
+
expect(loadedPlugins).toHaveLength(2);
|
|
144
|
+
expect(loadedPlugins[1]).toEqual(ImportedPlugin);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
100
147
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EditorPlugin } from '../typings';
|
|
1
|
+
import type { EditorPlugin, EditorPluginImport } from '../typings';
|
|
2
2
|
import type { PluginConstructor } from 'ckeditor5';
|
|
3
3
|
|
|
4
4
|
import { CKEditor5BlazorError } from '../../../ckeditor5-blazor-error';
|
|
@@ -7,8 +7,9 @@ import { CustomEditorPluginsRegistry } from '../custom-editor-plugins';
|
|
|
7
7
|
/**
|
|
8
8
|
* Loads CKEditor plugins from base and premium packages.
|
|
9
9
|
* First tries to load from the base 'ckeditor5' package, then falls back to 'ckeditor5-premium-features'.
|
|
10
|
+
* Supports custom import descriptors ({ $import: { name, path } }) for plugins loaded from custom modules.
|
|
10
11
|
*
|
|
11
|
-
* @param plugins - Array of plugin names to load
|
|
12
|
+
* @param plugins - Array of plugin names or import descriptors to load
|
|
12
13
|
* @returns Promise that resolves to an array of loaded Plugin instances
|
|
13
14
|
* @throws Error if a plugin is not found in either package
|
|
14
15
|
*/
|
|
@@ -17,8 +18,21 @@ export async function loadEditorPlugins(plugins: EditorPlugin[]): Promise<Loaded
|
|
|
17
18
|
let premiumPackage: Record<string, any> | null = null;
|
|
18
19
|
|
|
19
20
|
const loaders = plugins.map(async (plugin) => {
|
|
20
|
-
//
|
|
21
|
-
|
|
21
|
+
// Handle custom import descriptor: { $import: { name, path } }
|
|
22
|
+
if (isPluginImport(plugin)) {
|
|
23
|
+
const { name, path } = plugin.$import;
|
|
24
|
+
|
|
25
|
+
const mod = await import(/* @vite-ignore */ path);
|
|
26
|
+
const typedMod = mod as Record<string, unknown>;
|
|
27
|
+
const ctor = (Object.prototype.hasOwnProperty.call(typedMod, name) ? typedMod[name] : undefined)
|
|
28
|
+
?? (Object.prototype.hasOwnProperty.call(typedMod, 'default') ? typedMod['default'] : undefined);
|
|
29
|
+
|
|
30
|
+
if (!ctor) {
|
|
31
|
+
throw new CKEditor5BlazorError(`Plugin "${name}" not found in module "${path}".`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return ctor as PluginConstructor;
|
|
35
|
+
}
|
|
22
36
|
|
|
23
37
|
// If the plugin is not found in the base package, try custom plugins.
|
|
24
38
|
const customPlugin = await CustomEditorPluginsRegistry.the.get(plugin);
|
|
@@ -63,6 +77,13 @@ export async function loadEditorPlugins(plugins: EditorPlugin[]): Promise<Loaded
|
|
|
63
77
|
};
|
|
64
78
|
}
|
|
65
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Returns `true` when the plugin entry is an import descriptor (`{ $import: ... }`).
|
|
82
|
+
*/
|
|
83
|
+
function isPluginImport(plugin: EditorPlugin): plugin is EditorPluginImport {
|
|
84
|
+
return typeof plugin === 'object' && plugin !== null && '$import' in plugin;
|
|
85
|
+
}
|
|
86
|
+
|
|
66
87
|
/**
|
|
67
88
|
* Type representing the loaded plugins and whether premium features are available.
|
|
68
89
|
*/
|
|
@@ -78,6 +78,27 @@ describe('createEditableBlazorInterop', () => {
|
|
|
78
78
|
);
|
|
79
79
|
});
|
|
80
80
|
|
|
81
|
+
it('falls back to the first available editor id when data-cke-editor-id attribute is missing', async () => {
|
|
82
|
+
element.removeAttribute('data-cke-editor-id');
|
|
83
|
+
|
|
84
|
+
createEditableBlazorInterop(element, dotnetInterop);
|
|
85
|
+
|
|
86
|
+
const editor = await waitForTestEditor();
|
|
87
|
+
const changeEvent = new CKEditor5ChangeDataEvent({
|
|
88
|
+
editorId: DEFAULT_TEST_EDITOR_ID,
|
|
89
|
+
editor,
|
|
90
|
+
roots: { main: 'fallback changed' },
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
document.body.dispatchEvent(changeEvent);
|
|
94
|
+
|
|
95
|
+
expect(dotnetInterop.invokeMethodAsync).toHaveBeenCalledWith(
|
|
96
|
+
'OnChangeEditableData',
|
|
97
|
+
expect.anything(),
|
|
98
|
+
'fallback changed',
|
|
99
|
+
);
|
|
100
|
+
});
|
|
101
|
+
|
|
81
102
|
it('should ignore ckeditor5 change events from other editors', async () => {
|
|
82
103
|
createEditableBlazorInterop(element, dotnetInterop);
|
|
83
104
|
|
|
@@ -2,6 +2,7 @@ import type { DotNetInterop } from '../types';
|
|
|
2
2
|
|
|
3
3
|
import { EditorsRegistry } from '../elements/editor/editors-registry';
|
|
4
4
|
import { CKEditor5ChangeDataEvent } from '../elements/editor/plugins/dispatch-editor-roots-change-event';
|
|
5
|
+
import { queryAllEditorIds } from '../elements/editor/utils';
|
|
5
6
|
import { markElementAsInteractive } from '../shared';
|
|
6
7
|
import { createEditorValueSync, createNoopSync } from './utils/create-editor-value-sync';
|
|
7
8
|
|
|
@@ -14,7 +15,7 @@ import { createEditorValueSync, createNoopSync } from './utils/create-editor-val
|
|
|
14
15
|
* @returns An object containing lifecycle and synchronization methods.
|
|
15
16
|
*/
|
|
16
17
|
export function createEditableBlazorInterop(element: HTMLElement, interop: DotNetInterop) {
|
|
17
|
-
const editorId = element.getAttribute('data-cke-editor-id')
|
|
18
|
+
const editorId = element.getAttribute('data-cke-editor-id') ?? queryAllEditorIds()[0]!;
|
|
18
19
|
const rootName = element.getAttribute('data-cke-root-name') ?? 'main';
|
|
19
20
|
|
|
20
21
|
let unmounted = false;
|