Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update paste code to add a paragraph when the clipboard contains atleast a block element #2777

Merged
merged 10 commits into from
Sep 4, 2024
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
Loading