All files / roosterjs-content-model-core/lib/corePlugin/format applyDefaultFormat.ts

100% Statements 21/21
89.74% Branches 35/39
100% Functions 5/5
100% Lines 20/20

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 771x                 1x 16x   16x 14x 14x 9x 9x       9x 9x 9x   9x                         5x   5x 4x           6x 3x                 9x       14x                   7x            
import { iterateSelections } from 'roosterjs-content-model-dom';
import type { ContentModelSegmentFormat, IEditor } from 'roosterjs-content-model-types';
 
/**
 * @internal
 * When necessary, set default format as current pending format so it will be applied when Input event is fired
 * @param editor The editor object
 * @param defaultFormat The default segment format to apply
 */
export function applyDefaultFormat(editor: IEditor, defaultFormat: ContentModelSegmentFormat) {
    const selection = editor.getDOMSelection();
 
    if (selection?.type == 'range' && selection.range.collapsed) {
        editor.formatContentModel((model, context) => {
            iterateSelections(model, (path, _, paragraph, segments) => {
                const marker = segments?.[0];
                Eif (
                    paragraph?.blockType == 'Paragraph' &&
                    marker?.segmentType == 'SelectionMarker'
                ) {
                    const blocks = path[0].blocks;
                    const blockCount = blocks.length;
                    const blockIndex = blocks.indexOf(paragraph);
 
                    if (
                        paragraph.isImplicit &&
                        paragraph.segments.length == 1 &&
                        paragraph.segments[0] == marker &&
                        blockCount > 0 &&
                        blockIndex == blockCount - 1
                    ) {
                        // Focus is in the last paragraph which is implicit and there is not other segments.
                        // This can happen when focus is moved after all other content under current block group.
                        // We need to check if browser will merge focus into previous paragraph by checking if
                        // previous block is block. If previous block is paragraph, browser will most likely merge
                        // the input into previous paragraph, then nothing need to do here. Otherwise we need to
                        // apply pending format since this input event will start a new real paragraph.
                        const previousBlock = blocks[blockIndex - 1];
 
                        if (previousBlock?.blockType != 'Paragraph') {
                            context.newPendingFormat = getNewPendingFormat(
                                editor,
                                defaultFormat,
                                marker.format
                            );
                        }
                    } else if (paragraph.segments.every(x => x.segmentType != 'Text')) {
                        context.newPendingFormat = getNewPendingFormat(
                            editor,
                            defaultFormat,
                            marker.format
                        );
                    }
                }
 
                // Stop searching more selection
                return true;
            });
 
            // We didn't do any change but just apply default format to pending format, so no need to write back
            return false;
        });
    }
}
 
function getNewPendingFormat(
    editor: IEditor,
    defaultFormat: ContentModelSegmentFormat,
    markerFormat: ContentModelSegmentFormat
): ContentModelSegmentFormat {
    return {
        ...defaultFormat,
        ...editor.getPendingFormat(),
        ...markerFormat,
    };
}