Skip to content

Commit

Permalink
Update paste code to add a paragraph when the clipboard contains atle…
Browse files Browse the repository at this point in the history
…ast a block element (#2777)

* init

* add a link to tests and make sure it is handled correctly

* init

* try fix build

* fix build
  • Loading branch information
BryanValverdeU authored Sep 4, 2024
1 parent 92de0a6 commit f496042
Show file tree
Hide file tree
Showing 14 changed files with 619 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export function generatePasteOptionFromPlugins(
htmlAttributes: htmlFromClipboard.metadata,
pasteType: pasteType,
domToModelOption,
containsBlockElements: !!htmlFromClipboard.containsBlockElements,
};

return pasteType == 'asPlainText'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {
} from 'roosterjs-content-model-dom';
import type {
BeforePasteEvent,
ClipboardData,
CloneModelOptions,
ContentModelDocument,
ContentModelSegmentFormat,
Expand All @@ -37,12 +36,15 @@ export function cloneModelForPaste(model: ReadonlyContentModelDocument) {
/**
* @internal
*/
export function mergePasteContent(
editor: IEditor,
eventResult: BeforePasteEvent,
clipboardData: ClipboardData
) {
const { fragment, domToModelOption, customizedMerge, pasteType } = eventResult;
export function mergePasteContent(editor: IEditor, eventResult: BeforePasteEvent) {
const {
fragment,
domToModelOption,
customizedMerge,
pasteType,
clipboardData,
containsBlockElements,
} = eventResult;

editor.formatContentModel(
(model, context) => {
Expand All @@ -64,6 +66,7 @@ export function mergePasteContent(
const mergeOption: MergeModelOption = {
mergeFormat: pasteType == 'mergeFormat' ? 'keepSourceEmphasisFormat' : 'none',
mergeTable: shouldMergeTable(pasteModel),
addParagraphAfterMergedContent: containsBlockElements,
};

const insertPoint = customizedMerge
Expand All @@ -74,7 +77,9 @@ export function mergePasteContent(
context.newPendingFormat = {
...EmptySegmentFormat,
...model.format,
...insertPoint.marker.format,
...(pasteType == 'normal' && !containsBlockElements
? getLastSegmentFormat(pasteModel)
: insertPoint.marker.format),
};
}

Expand Down Expand Up @@ -124,3 +129,19 @@ function shouldMergeTable(pasteModel: ContentModelDocument): boolean | undefined
// Only merge table when the document contain a single table.
return pasteModel.blocks.length === 1 && pasteModel.blocks[0].blockType === 'Table';
}

function getLastSegmentFormat(pasteModel: ContentModelDocument): ContentModelSegmentFormat {
if (pasteModel.blocks.length == 1) {
const [firstBlock] = pasteModel.blocks;

if (firstBlock.blockType == 'Paragraph') {
const segment = firstBlock.segments[firstBlock.segments.length - 1];

return {
...segment.format,
};
}
}

return {};
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ export function paste(
convertInlineCss(eventResult.fragment, htmlFromClipboard.globalCssRules);

// 6. Merge pasted content into main Content Model
mergePasteContent(editor, eventResult, clipboardData);
mergePasteContent(editor, eventResult);
}

function createDOMFromHtml(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { isNodeOfType, toArray } from 'roosterjs-content-model-dom';
import { isBlockElement, isNodeOfType, toArray } from 'roosterjs-content-model-dom';
import { retrieveCssRules } from '../createModelFromHtml/convertInlineCss';
import type { ClipboardData } from 'roosterjs-content-model-types';
import type { CssRule } from '../createModelFromHtml/convertInlineCss';
Expand All @@ -14,6 +14,7 @@ export interface HtmlFromClipboard {
globalCssRules: CssRule[];
htmlBefore?: string;
htmlAfter?: string;
containsBlockElements?: boolean;
}

/**
Expand All @@ -33,6 +34,7 @@ export function retrieveHtmlInfo(
...retrieveHtmlStrings(clipboardData),
globalCssRules: retrieveCssRules(doc),
metadata: retrieveMetadata(doc),
containsBlockElements: checkBlockElements(doc),
};

clipboardData.htmlFirstLevelChildTags = retrieveTopLevelTags(doc);
Expand Down Expand Up @@ -96,3 +98,9 @@ function retrieveHtmlStrings(

return { htmlBefore, htmlAfter };
}

function checkBlockElements(doc: Document): boolean {
const elements = toArray(doc.body.querySelectorAll('*'));

return elements.some(el => isNodeOfType(el, 'ELEMENT_NODE') && isBlockElement(el));
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('generatePasteOptionFromPlugins', () => {
htmlBefore,
htmlAfter,
htmlAttributes: mockedMetadata,
containsBlockElements: false,
} as any);
expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1);
expect(originalEvent).toEqual({
Expand All @@ -74,6 +75,7 @@ describe('generatePasteOptionFromPlugins', () => {
styleSanitizers: {},
attributeSanitizers: {},
},
containsBlockElements: false,
});
expect(triggerPluginEventSpy).toHaveBeenCalledWith(
'beforePaste',
Expand All @@ -86,6 +88,7 @@ describe('generatePasteOptionFromPlugins', () => {
htmlAttributes: mockedMetadata,
pasteType: 'TypeResult',
domToModelOption: 'OptionResult',
containsBlockElements: false,
},
true
);
Expand Down Expand Up @@ -126,6 +129,7 @@ describe('generatePasteOptionFromPlugins', () => {
htmlBefore,
htmlAfter,
htmlAttributes: mockedMetadata,
containsBlockElements: false,
} as any);
expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1);
expect(triggerPluginEventSpy).toHaveBeenCalledWith(
Expand All @@ -140,6 +144,7 @@ describe('generatePasteOptionFromPlugins', () => {
pasteType: 'TypeResult',
domToModelOption: 'OptionResult',
customizedMerge: mockedCustomizedMerge,
containsBlockElements: false,
},
true
);
Expand Down Expand Up @@ -174,6 +179,7 @@ describe('generatePasteOptionFromPlugins', () => {
htmlBefore: '',
htmlAfter: '',
htmlAttributes: mockedMetadata,
containsBlockElements: false,
} as any);
expect(triggerPluginEventSpy).toHaveBeenCalledTimes(1);
expect(triggerPluginEventSpy).toHaveBeenCalledWith(
Expand All @@ -187,6 +193,7 @@ describe('generatePasteOptionFromPlugins', () => {
htmlAttributes: mockedMetadata,
pasteType: 'TypeResult',
domToModelOption: 'OptionResult',
containsBlockElements: false,
},
true
);
Expand All @@ -207,6 +214,7 @@ describe('generatePasteOptionFromPlugins', () => {
styleSanitizers: {},
attributeSanitizers: {},
},
containsBlockElements: false,
});
});

Expand Down Expand Up @@ -244,6 +252,7 @@ describe('generatePasteOptionFromPlugins', () => {
htmlBefore,
htmlAfter,
htmlAttributes: mockedMetadata,
containsBlockElements: false,
});
expect(triggerPluginEventSpy).toHaveBeenCalledTimes(0);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,9 @@ export const template: Readonly<string> = `
</body>
</html>
`;

export const inlineTemplate: Readonly<string> = `
<html>
<body>
<span>Inline text</span></body>
</html>`;
Loading

0 comments on commit f496042

Please sign in to comment.