diff --git a/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts b/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts index 7dbea590e45..8e88c134555 100644 --- a/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts +++ b/packages/roosterjs-content-model-core/lib/editor/core/createEditorCore.ts @@ -9,6 +9,8 @@ import type { EditorCore, EditorCorePlugins, EditorOptions, + DomToModelOption, + ModelToDomOption, } from 'roosterjs-content-model-types'; /** @@ -18,6 +20,20 @@ import type { */ export function createEditorCore(contentDiv: HTMLDivElement, options: EditorOptions): EditorCore { const corePlugins = createEditorCorePlugins(options, contentDiv); + const plugins = (options.plugins ?? []).filter(x => !!x); + const domToModelOptions: DomToModelOption[] = []; + const modelToDomOptions: ModelToDomOption[] = []; + + plugins.forEach(plugin => { + const contentModelConfig = plugin.getContentModelConfig?.(); + if (contentModelConfig?.domToModelOption) { + domToModelOptions.push(contentModelConfig.domToModelOption); + } + + if (contentModelConfig?.modelToDomOption) { + modelToDomOptions.push(contentModelConfig.modelToDomOption); + } + }); return { physicalRoot: contentDiv, @@ -31,12 +47,17 @@ export function createEditorCore(contentDiv: HTMLDivElement, options: EditorOpti corePlugins.domEvent, corePlugins.selection, corePlugins.entity, - ...(options.plugins ?? []).filter(x => !!x), + ...plugins, corePlugins.undo, corePlugins.contextMenu, corePlugins.lifecycle, ], - environment: createEditorEnvironment(contentDiv, options), + environment: createEditorEnvironment( + contentDiv, + options, + domToModelOptions, + modelToDomOptions + ), darkColorHandler: createDarkColorHandler( contentDiv, options.getDarkColor ?? getDarkColorFallback, @@ -53,15 +74,17 @@ export function createEditorCore(contentDiv: HTMLDivElement, options: EditorOpti function createEditorEnvironment( contentDiv: HTMLElement, - options: EditorOptions + options: EditorOptions, + domToModelOptionsFromPlugins: (DomToModelOption | undefined)[], + modelToDomOptionsFromPlugins: (ModelToDomOption | undefined)[] ): EditorEnvironment { const navigator = contentDiv.ownerDocument.defaultView?.navigator; const userAgent = navigator?.userAgent ?? ''; const appVersion = navigator?.appVersion ?? ''; return { - domToModelSettings: createDomToModelSettings(options), - modelToDomSettings: createModelToDomSettings(options), + domToModelSettings: createDomToModelSettings(options, domToModelOptionsFromPlugins), + modelToDomSettings: createModelToDomSettings(options, modelToDomOptionsFromPlugins), isMac: appVersion.indexOf('Mac') != -1, isAndroid: /android/i.test(userAgent), isSafari: diff --git a/packages/roosterjs-content-model-core/lib/editor/core/createEditorDefaultSettings.ts b/packages/roosterjs-content-model-core/lib/editor/core/createEditorDefaultSettings.ts index 52bd64b886f..7ff37067f36 100644 --- a/packages/roosterjs-content-model-core/lib/editor/core/createEditorDefaultSettings.ts +++ b/packages/roosterjs-content-model-core/lib/editor/core/createEditorDefaultSettings.ts @@ -19,7 +19,8 @@ import type { * @param options The editor options */ export function createDomToModelSettings( - options: EditorOptions + options: EditorOptions, + additionalOptions: (DomToModelOption | undefined)[] ): ContentModelSettings { const builtIn: DomToModelOption = { processorOverride: { @@ -31,7 +32,7 @@ export function createDomToModelSettings( return { builtIn, customized, - calculated: createDomToModelConfig([builtIn, customized]), + calculated: createDomToModelConfig([builtIn, customized, ...additionalOptions]), }; } @@ -41,7 +42,8 @@ export function createDomToModelSettings( * @param options The editor options */ export function createModelToDomSettings( - options: EditorOptions + options: EditorOptions, + additionalOptions: (ModelToDomOption | undefined)[] ): ContentModelSettings { const builtIn: ModelToDomOption = { metadataAppliers: { @@ -54,6 +56,6 @@ export function createModelToDomSettings( return { builtIn, customized, - calculated: createModelToDomConfig([builtIn, customized]), + calculated: createModelToDomConfig([builtIn, customized, ...additionalOptions]), }; } diff --git a/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts b/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts index b4af3645965..e42951811ee 100644 --- a/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts +++ b/packages/roosterjs-content-model-core/test/editor/core/createEditorCoreTest.ts @@ -108,8 +108,8 @@ describe('createEditorCore', () => { options, contentDiv ); - expect(createDefaultSettings.createDomToModelSettings).toHaveBeenCalledWith(options); - expect(createDefaultSettings.createModelToDomSettings).toHaveBeenCalledWith(options); + expect(createDefaultSettings.createDomToModelSettings).toHaveBeenCalledWith(options, []); + expect(createDefaultSettings.createModelToDomSettings).toHaveBeenCalledWith(options, []); } it('No options', () => { diff --git a/packages/roosterjs-content-model-core/test/editor/core/createEditorDefaultSettingsTest.ts b/packages/roosterjs-content-model-core/test/editor/core/createEditorDefaultSettingsTest.ts index 2c9a69aab21..b6923e13cd9 100644 --- a/packages/roosterjs-content-model-core/test/editor/core/createEditorDefaultSettingsTest.ts +++ b/packages/roosterjs-content-model-core/test/editor/core/createEditorDefaultSettingsTest.ts @@ -20,7 +20,7 @@ describe('createDomToModelSettings', () => { }); it('No options', () => { - const settings = createDomToModelSettings({}); + const settings = createDomToModelSettings({}, []); expect(settings).toEqual({ builtIn: { @@ -43,9 +43,12 @@ describe('createDomToModelSettings', () => { it('Has options', () => { const defaultDomToModelOptions = 'MockedOptions' as any; - const settings = createDomToModelSettings({ - defaultDomToModelOptions: defaultDomToModelOptions, - }); + const settings = createDomToModelSettings( + { + defaultDomToModelOptions: defaultDomToModelOptions, + }, + [] + ); expect(settings).toEqual({ builtIn: { @@ -77,7 +80,7 @@ describe('createModelToDomSettings', () => { }); it('No options', () => { - const settings = createModelToDomSettings({}); + const settings = createModelToDomSettings({}, []); expect(settings).toEqual({ builtIn: { @@ -102,9 +105,12 @@ describe('createModelToDomSettings', () => { it('Has options', () => { const defaultModelToDomOptions = 'MockedOptions' as any; - const settings = createModelToDomSettings({ - defaultModelToDomOptions: defaultModelToDomOptions, - }); + const settings = createModelToDomSettings( + { + defaultModelToDomOptions: defaultModelToDomOptions, + }, + [] + ); expect(settings).toEqual({ builtIn: { diff --git a/packages/roosterjs-content-model-types/lib/editor/EditorPlugin.ts b/packages/roosterjs-content-model-types/lib/editor/EditorPlugin.ts index 187003aa9dc..dd2b35c058c 100644 --- a/packages/roosterjs-content-model-types/lib/editor/EditorPlugin.ts +++ b/packages/roosterjs-content-model-types/lib/editor/EditorPlugin.ts @@ -1,6 +1,23 @@ +import type { DomToModelOption } from '../context/DomToModelOption'; +import type { ModelToDomOption } from '../context/ModelToDomOption'; import type { PluginEvent } from '../event/PluginEvent'; import type { IEditor } from './IEditor'; +/** + * Configuration for content model of a plugin + */ +export interface PluginContentModelConfig { + /** + * The option for additional format parses + */ + domToModelOption?: DomToModelOption; + + /** + * The option for additional format appliers + */ + modelToDomOption?: ModelToDomOption; +} + /** * Interface of an editor plugin */ @@ -42,4 +59,10 @@ export interface EditorPlugin { * @param event The event to handle: */ onPluginEvent?: (event: PluginEvent) => void; + + /** + * This configuration will add additional format parses and applier to the editor + * @returns The content model configuration for this plugin + */ + getContentModelConfig?: () => PluginContentModelConfig; } diff --git a/packages/roosterjs-content-model-types/lib/index.ts b/packages/roosterjs-content-model-types/lib/index.ts index a0fbcc00d7f..da39c73ac10 100644 --- a/packages/roosterjs-content-model-types/lib/index.ts +++ b/packages/roosterjs-content-model-types/lib/index.ts @@ -357,7 +357,7 @@ export { Announce, } from './editor/EditorCore'; export { EditorCorePlugins } from './editor/EditorCorePlugins'; -export { EditorPlugin } from './editor/EditorPlugin'; +export { EditorPlugin, PluginContentModelConfig } from './editor/EditorPlugin'; export { PluginWithState } from './editor/PluginWithState'; export { ContextMenuProvider } from './editor/ContextMenuProvider';