Skip to content

Commit

Permalink
Content Model: Paste plain text applies current format
Browse files Browse the repository at this point in the history
  • Loading branch information
JiuqingSong committed Sep 8, 2023
1 parent 2fc7830 commit 96e64f6
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 36 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export { unwrapBlock } from './modelApi/common/unwrapBlock';
export { addSegment } from './modelApi/common/addSegment';
export { isWhiteSpacePreserved } from './modelApi/common/isWhiteSpacePreserved';
export { normalizeSingleSegment } from './modelApi/common/normalizeSegment';
export { applySegmentFormatToElement } from './modelApi/common/applySegmentFormatToElement';

export { setParagraphNotImplicit } from './modelApi/block/setParagraphNotImplicit';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { applyFormat } from 'roosterjs-content-model-dom/lib/modelToDom/utils/applyFormat';
import { ContentModelSegmentFormat } from 'roosterjs-content-model-types';
import { createModelToDomContext } from '../../modelToDom/context/createModelToDomContext';

/**
* Format an existing HTML element using Segment Format
* @param element The element to format
* @param format The format to apply
*/
export function applySegmentFormatToElement(
element: HTMLElement,
format: ContentModelSegmentFormat
) {
const context = createModelToDomContext();
applyFormat(element, context.formatAppliers.segment, format, context);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { domToContentModel } from 'roosterjs-content-model-dom';
import getSelectedSegments from '../selection/getSelectedSegments';
import { applySegmentFormatToElement, domToContentModel } from 'roosterjs-content-model-dom';
import { ContentModelDocument, ContentModelSegmentFormat } from 'roosterjs-content-model-types';
import { formatWithContentModel } from './formatWithContentModel';
import { FormatWithContentModelContext } from '../../publicTypes/parameter/FormatWithContentModelContext';
import { IContentModelEditor } from '../../publicTypes/IContentModelEditor';
import { mergeModel } from '../../modelApi/common/mergeModel';
import { NodePosition } from 'roosterjs-editor-types';
import { ContentModelDocument } from 'roosterjs-content-model-types';
import ContentModelBeforePasteEvent, {
ContentModelBeforePasteEventData,
} from '../../publicTypes/event/ContentModelBeforePasteEvent';
Expand Down Expand Up @@ -47,39 +48,44 @@ export default function paste(
clipboardData.snapshotBeforePaste = editor.getContent(GetContentMode.RawHTMLWithSelection);
}

const eventData = createBeforePasteEventData(
formatWithContentModel(
editor,
clipboardData,
getPasteType(pasteAsText, applyCurrentFormat, pasteAsImage)
);

const {
domToModelOption,
fragment,
customizedMerge,
} = triggerPluginEventAndCreatePasteFragment(
editor,
clipboardData,
null /* position */,
pasteAsText,
pasteAsImage,
eventData
'Paste',
(model, context) => {
const eventData = createBeforePasteEventData(
editor,
clipboardData,
getPasteType(pasteAsText, applyCurrentFormat, pasteAsImage)
);
const currentSegment = getSelectedSegments(model, true /*includingFormatHolder*/)[0];
const { fontFamily, fontSize, textColor, backgroundColor, letterSpacing, lineHeight } =
currentSegment?.format ?? {};
const {
domToModelOption,
fragment,
customizedMerge,
} = triggerPluginEventAndCreatePasteFragment(
editor,
clipboardData,
null /* position */,
pasteAsText,
pasteAsImage,
eventData,
{ fontFamily, fontSize, textColor, backgroundColor, letterSpacing, lineHeight }
);

const pasteModel = domToContentModel(fragment, domToModelOption);

mergePasteContent(model, context, pasteModel, applyCurrentFormat, customizedMerge);

return true;
},

{
changeSource: ChangeSource.Paste,
getChangeData: () => clipboardData,
}
);

const pasteModel = domToContentModel(fragment, domToModelOption);

if (pasteModel) {
formatWithContentModel(
editor,
'Paste',
(model, context) =>
mergePasteContent(model, context, pasteModel, applyCurrentFormat, customizedMerge),
{
changeSource: ChangeSource.Paste,
getChangeData: () => clipboardData,
}
);
}
}

/**
Expand All @@ -94,7 +100,7 @@ export function mergePasteContent(
customizedMerge:
| undefined
| ((source: ContentModelDocument, target: ContentModelDocument) => void)
): boolean {
) {
if (customizedMerge) {
customizedMerge(model, pasteModel);
} else {
Expand All @@ -103,7 +109,6 @@ export function mergePasteContent(
mergeTable: shouldMergeTable(pasteModel),
});
}
return true;
}

function shouldMergeTable(pasteModel: ContentModelDocument): boolean | undefined {
Expand Down Expand Up @@ -153,7 +158,8 @@ function triggerPluginEventAndCreatePasteFragment(
position: NodePosition | null,
pasteAsText: boolean,
pasteAsImage: boolean,
eventData: ContentModelBeforePasteEventData
eventData: ContentModelBeforePasteEventData,
currentFormat: ContentModelSegmentFormat
): ContentModelBeforePasteEventData {
const event = {
eventType: PluginEventType.BeforePaste,
Expand Down Expand Up @@ -182,6 +188,13 @@ function triggerPluginEventAndCreatePasteFragment(
handleTextPaste(text, position, fragment);
}

const formatContainer = fragment.ownerDocument.createElement('span');

moveChildNodes(formatContainer, fragment);
fragment.appendChild(formatContainer);

applySegmentFormatToElement(formatContainer, currentFormat);

let pluginEvent: ContentModelBeforePasteEvent = event;
// Step 4: Trigger BeforePasteEvent so that plugins can do proper change before paste, when the type of paste is different than Plain Text
if (event.pasteType !== PasteType.AsPlainText) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as addParserF from '../../../lib/editor/plugins/PastePlugin/utils/addPa
import * as domToContentModel from 'roosterjs-content-model-dom/lib/domToModel/domToContentModel';
import * as ExcelF from '../../../lib/editor/plugins/PastePlugin/Excel/processPastedContentFromExcel';
import * as getPasteSourceF from 'roosterjs-editor-dom/lib/pasteSourceValidations/getPasteSource';
import * as getSelectedSegmentsF from '../../../lib/publicApi/selection/getSelectedSegments';
import * as mergeModelFile from '../../../lib/modelApi/common/mergeModel';
import * as PPT from '../../../lib/editor/plugins/PastePlugin/PowerPoint/processPastedContentFromPowerPoint';
import * as setProcessorF from '../../../lib/editor/plugins/PastePlugin/utils/setProcessor';
Expand Down Expand Up @@ -97,6 +98,14 @@ describe('Paste ', () => {
.createSpy('getTrustedHTMLHandler')
.and.returnValue((html: string) => html);
spyOn(mergeModelFile, 'mergeModel').and.callFake(() => (mockedModel = mockedMergeModel));
spyOn(getSelectedSegmentsF, 'default').and.returnValue([
{
format: {
fontSize: '1pt',
fontFamily: 'Arial',
},
} as any,
]);

editor = ({
focus,
Expand Down

0 comments on commit 96e64f6

Please sign in to comment.