All files / roosterjs-content-model-dom/lib/modelToDom/handlers handleBlockGroupChildren.ts

100% Statements 26/26
96.3% Branches 26/27
100% Functions 3/3
100% Lines 24/24

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 77 781x                     1x           1264x 1264x 1264x   1264x 1264x         1623x         1396x   1396x 56x       1623x   1623x 6x       1264x     1264x   1264x                 2660x   95x 124x   124x     95x     39x 39x          
import { cleanUpRestNodes } from '../utils/cleanUpRestNodes';
import type {
    ContentModelBlockGroup,
    ContentModelHandler,
    ModelToDomContext,
    ModelToDomListStackItem,
} from 'roosterjs-content-model-types';
 
/**
 * @internal
 */
export const handleBlockGroupChildren: ContentModelHandler<ContentModelBlockGroup> = (
    doc: Document,
    parent: Node,
    group: ContentModelBlockGroup,
    context: ModelToDomContext
) => {
    const { listFormat } = context;
    const nodeStack = listFormat.nodeStack;
    let refNode: Node | null = parent.firstChild;
 
    try {
        group.blocks.forEach((childBlock, index) => {
            // When process list, we need a node stack.
            // When there are two continuous lists, they should share the same stack
            // so that list items with same type/threadId can be merged into the same list element
            // In other cases, clear the stack so that two separate lists won't share the same list element
            if (
                index == 0 ||
                childBlock.blockType != 'BlockGroup' ||
                childBlock.blockGroupType != 'ListItem'
            ) {
                cleanUpNodeStack(listFormat.nodeStack, context);
 
                if (listFormat.nodeStack.length > 0) {
                    listFormat.nodeStack = [];
                }
            }
 
            refNode = context.modelHandlers.block(doc, parent, childBlock, context, refNode);
 
            if (childBlock.blockType == 'Entity') {
                context.domIndexer?.onBlockEntity(childBlock, group);
            }
        });
 
        cleanUpNodeStack(listFormat.nodeStack, context, parent);
 
        // Remove all rest node if any since they don't appear in content model
        cleanUpRestNodes(refNode, context.rewriteFromModel);
    } finally {
        listFormat.nodeStack = nodeStack;
    }
};
 
function cleanUpNodeStack(
    nodeStack: ModelToDomListStackItem[],
    context: ModelToDomContext,
    leavingParent?: Node
) {
    if (nodeStack.length > 0) {
        // Clear list stack, only run to nodeStack[1] because nodeStack[0] is the parent node
        for (let i = nodeStack.length - 1; i > 0; i--) {
            const node = nodeStack.pop()?.refNode ?? null;
 
            cleanUpRestNodes(node, context.rewriteFromModel);
        }
 
        if (leavingParent && nodeStack[0].node == leavingParent) {
            // When leaving a parent node that is the same with the root of node stack
            // It means the whole list node stack is being invalidated, so we clear it
            while (nodeStack.length > 0) {
                nodeStack.pop();
            }
        }
    }
}