All files / roosterjs-content-model-api/lib/modelApi/common queryContentModelBlocks.ts

100% Statements 40/40
95.83% Branches 23/24
100% Functions 3/3
100% Lines 33/33

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 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 1051x                                   1x             52x 52x 84x 7x   77x   77x       59x 15x   59x 1x 1x 1x 1x 1x             1x     59x   9x 3x   9x             9x 9x   9x 4x   11x 17x 17x             17x     9x     45x               77x             77x    
import { createDomToModelContext, domToContentModel } from 'roosterjs-content-model-dom';
import type {
    ContentModelBlockType,
    ContentModelEntity,
    EditorContext,
    ReadonlyContentModelBlock,
    ReadonlyContentModelBlockBase,
    ReadonlyContentModelBlockGroup,
} from 'roosterjs-content-model-types';
 
/**
 * Query content model blocks
 * @param group The block group to query
 * @param type The type of block to query
 * @param filter Optional selector to filter the blocks
 * @param findFirstOnly True to return the first block only, false to return all blocks
 * @param shouldExpandEntity Optional function to determine if an entity's children should be recursively queried, should return a EditorContext if the entity should be expanded, or null if not
 */
export function queryContentModelBlocks<T extends ReadonlyContentModelBlock>(
    group: ReadonlyContentModelBlockGroup,
    type: T extends ReadonlyContentModelBlockBase<infer U> ? U : never,
    filter?: (element: T) => element is T,
    findFirstOnly?: boolean,
    shouldExpandEntity?: (entity: ContentModelEntity) => EditorContext | null
): T[] {
    const elements: T[] = [];
    for (let i = 0; i < group.blocks.length; i++) {
        if (findFirstOnly && elements.length > 0) {
            return elements;
        }
        const block = group.blocks[i];
 
        switch (block.blockType) {
            case 'Paragraph':
            case 'Divider':
            case 'Entity':
                if (isExpectedBlockType(block, type, filter)) {
                    elements.push(block);
                }
                if (block.blockType == 'Entity' && shouldExpandEntity) {
                    const editorContext = shouldExpandEntity(block);
                    Eif (editorContext) {
                        const context = createDomToModelContext(editorContext);
                        const model = domToContentModel(block.wrapper, context);
                        const results = queryContentModelBlocks<T>(
                            model,
                            type,
                            filter,
                            findFirstOnly,
                            shouldExpandEntity
                        );
                        elements.push(...results);
                    }
                }
                break;
            case 'BlockGroup':
                if (isExpectedBlockType(block, type, filter)) {
                    elements.push(block);
                }
                const results = queryContentModelBlocks<T>(
                    block,
                    type,
                    filter,
                    findFirstOnly,
                    shouldExpandEntity
                );
                elements.push(...results);
                break;
            case 'Table':
                if (isExpectedBlockType(block, type, filter)) {
                    elements.push(block);
                }
                for (const row of block.rows) {
                    for (const cell of row.cells) {
                        const results = queryContentModelBlocks<T>(
                            cell,
                            type,
                            filter,
                            findFirstOnly,
                            shouldExpandEntity
                        );
                        elements.push(...results);
                    }
                }
                break;
        }
    }
    return elements;
}
 
function isExpectedBlockType<T extends ReadonlyContentModelBlock>(
    block: ReadonlyContentModelBlock,
    type: ContentModelBlockType,
    filter?: (element: T) => element is T
): block is T {
    return isBlockType<T>(block, type) && (!filter || filter(block));
}
 
function isBlockType<T extends ReadonlyContentModelBlock>(
    block: ReadonlyContentModelBlock,
    type: ContentModelBlockType
): block is T {
    return block.blockType == type;
}