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

100% Statements 26/26
96.43% Branches 27/28
100% Functions 1/1
100% Lines 25/25

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 711x 1x                   1x             280x 280x   280x 84x           280x 294x 294x   294x               61x         280x     280x 198x 198x 198x   198x 198x   198x     198x     198x 198x   198x     280x    
import { applyFormat } from '../utils/applyFormat';
import { applyMetadata } from '../utils/applyMetadata';
import type {
    ContentModelBlockHandler,
    ContentModelListItem,
    ModelToDomContext,
} from 'roosterjs-content-model-types';
 
/**
 * @internal
 */
export const handleList: ContentModelBlockHandler<ContentModelListItem> = (
    doc: Document,
    parent: Node,
    listItem: ContentModelListItem,
    context: ModelToDomContext,
    refNode: Node | null
) => {
    let layer = 0;
    const { nodeStack } = context.listFormat;
 
    if (nodeStack.length == 0) {
        nodeStack.push({
            node: parent,
        });
    }
 
    // Skip existing list levels that has same properties so we can reuse them
    for (; layer < listItem.levels.length && layer + 1 < nodeStack.length; layer++) {
        const stackLevel = nodeStack[layer + 1];
        const itemLevel = listItem.levels[layer];
 
        if (
            stackLevel.listType != itemLevel.listType ||
            stackLevel.dataset?.editingInfo != itemLevel.dataset.editingInfo ||
            (itemLevel.listType == 'OL' &&
                typeof itemLevel.format.startNumberOverride === 'number') ||
            (itemLevel.listType == 'UL' &&
                itemLevel.format.listStyleType != stackLevel.format?.listStyleType)
        ) {
            break;
        }
    }
 
    // Cut off remained list levels that we can't reuse
    nodeStack.splice(layer + 1);
 
    // Create new list levels that are after reused ones
    for (; layer < listItem.levels.length; layer++) {
        const level = listItem.levels[layer];
        const newList = doc.createElement(level.listType || 'UL');
        const lastParent = nodeStack[nodeStack.length - 1].node;
 
        lastParent.insertBefore(newList, layer == 0 ? refNode : null);
        nodeStack.push({ node: newList, ...level });
 
        applyFormat(newList, context.formatAppliers.listLevelThread, level.format, context);
 
        // Need to apply metadata after applying list level format since the list numbers value relies on the result of list thread handling
        applyMetadata(level, context.metadataAppliers.listLevel, level.format, context);
 
        // Need to apply listItemElement formats after applying metadata since the list numbers value relies on the result of metadata handling
        applyFormat(newList, context.formatAppliers.listLevel, level.format, context);
        applyFormat(newList, context.formatAppliers.dataset, level.dataset, context);
 
        context.onNodeCreated?.(level, newList);
    }
 
    return refNode;
};