diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts index ebb60327368..aa8bcaca30c 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/corePlugins/ContentModelCopyPastePlugin.ts @@ -1,6 +1,7 @@ import paste from '../../publicApi/utils/paste'; import { cloneModel } from '../../modelApi/common/cloneModel'; -import { contentModelToDom } from 'roosterjs-content-model-dom'; +import { contentModelToDom, normalizeContentModel } from 'roosterjs-content-model-dom'; +import { DeleteResult } from '../../modelApi/edit/utils/DeleteSelectionStep'; import { deleteSelection } from '../../modelApi/edit/deleteSelection'; import { formatWithContentModel } from '../../publicApi/utils/formatWithContentModel'; import { IContentModelEditor } from '../../publicTypes/IContentModelEditor'; @@ -145,7 +146,12 @@ export default class ContentModelCopyPastePlugin implements PluginWithState { - deleteSelection(model, [], context); + if ( + deleteSelection(model, [], context).deleteResult == + DeleteResult.Range + ) { + normalizeContentModel(model); + } return true; }, diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/entity/insertEntityModel.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/entity/insertEntityModel.ts index 82f7f292066..fe5b5a6f0ec 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/entity/insertEntityModel.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/entity/insertEntityModel.ts @@ -1,9 +1,14 @@ -import { createBr, createParagraph, createSelectionMarker } from 'roosterjs-content-model-dom'; +import { + createBr, + createParagraph, + createSelectionMarker, + normalizeContentModel, +} from 'roosterjs-content-model-dom'; +import { DeleteResult, DeleteSelectionResult } from '../edit/utils/DeleteSelectionStep'; import { deleteSelection } from '../edit/deleteSelection'; import { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext'; import { getClosestAncestorBlockGroupIndex } from '../common/getClosestAncestorBlockGroupIndex'; import { InsertEntityPosition } from '../../publicTypes/parameter/InsertEntityOptions'; -import { InsertPoint } from '../../publicTypes/selection/InsertPoint'; import { setSelection } from '../selection/setSelection'; import { ContentModelBlock, @@ -26,13 +31,17 @@ export function insertEntityModel( ) { let blockParent: ContentModelBlockGroup | undefined; let blockIndex = -1; - let insertPoint: InsertPoint | null; + let deleteResult: DeleteSelectionResult; if (position == 'begin' || position == 'end') { blockParent = model; blockIndex = position == 'begin' ? 0 : model.blocks.length; - } else if ((insertPoint = deleteSelection(model, [], context).insertPoint)) { - const { marker, paragraph, path } = insertPoint; + } else if ((deleteResult = deleteSelection(model, [], context)).insertPoint) { + const { marker, paragraph, path } = deleteResult.insertPoint; + + if (deleteResult.deleteResult == DeleteResult.Range) { + normalizeContentModel(model); + } if (!isBlock) { const index = paragraph.segments.indexOf(marker); diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts index ca3e49f2632..639319f96b4 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts @@ -4,7 +4,9 @@ import * as createRangeF from 'roosterjs-editor-dom/lib/selection/createRange'; import * as deleteSelectionsFile from '../../../lib/modelApi/edit/deleteSelection'; import * as extractClipboardItemsFile from 'roosterjs-editor-dom/lib/clipboard/extractClipboardItems'; import * as iterateSelectionsFile from '../../../lib/modelApi/selection/iterateSelections'; +import * as normalizeContentModel from 'roosterjs-content-model-dom/lib/modelApi/common/normalizeContentModel'; import * as PasteFile from '../../../lib/publicApi/utils/paste'; +import { DeleteResult } from '../../../lib/modelApi/edit/utils/DeleteSelectionStep'; import { IContentModelEditor } from '../../../lib/publicTypes/IContentModelEditor'; import ContentModelCopyPastePlugin, { onNodeCreated, @@ -409,7 +411,10 @@ describe('ContentModelCopyPastePlugin |', () => { }; spyOn(createRangeF, 'default').and.callThrough(); - spyOn(deleteSelectionsFile, 'deleteSelection'); + spyOn(deleteSelectionsFile, 'deleteSelection').and.returnValue({ + deleteResult: DeleteResult.Range, + insertPoint: null!, + }); spyOn(contentModelToDomFile, 'contentModelToDom').and.callFake(() => { const container = document.createElement('div'); container.append(table); @@ -418,6 +423,7 @@ describe('ContentModelCopyPastePlugin |', () => { return selectionRangeExValue; }); spyOn(iterateSelectionsFile, 'iterateSelections').and.returnValue(undefined); + spyOn(normalizeContentModel, 'normalizeContentModel'); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); @@ -454,6 +460,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(setContentModelSpy).toHaveBeenCalledWith(modelValue, { onNodeCreated: undefined, }); + expect(normalizeContentModel.normalizeContentModel).toHaveBeenCalledWith(modelValue); }); it('Selection not Collapsed and image selection', () => { @@ -468,12 +475,16 @@ describe('ContentModelCopyPastePlugin |', () => { }; spyOn(createRangeF, 'default').and.callThrough(); - spyOn(deleteSelectionsFile, 'deleteSelection'); + spyOn(deleteSelectionsFile, 'deleteSelection').and.returnValue({ + deleteResult: DeleteResult.Range, + insertPoint: null!, + }); spyOn(contentModelToDomFile, 'contentModelToDom').and.callFake(() => { div.appendChild(image); return selectionRangeExValue; }); spyOn(iterateSelectionsFile, 'iterateSelections').and.returnValue(undefined); + spyOn(normalizeContentModel, 'normalizeContentModel'); triggerPluginEventSpy.and.callThrough(); focusSpy.and.callThrough(); @@ -509,6 +520,7 @@ describe('ContentModelCopyPastePlugin |', () => { expect(setContentModelSpy).toHaveBeenCalledWith(modelValue, { onNodeCreated: undefined, }); + expect(normalizeContentModel.normalizeContentModel).toHaveBeenCalledWith(modelValue); }); });