All files / roosterjs-content-model-plugins/lib/edit/utils getLeafSiblingBlock.ts

100% Statements 34/34
93.33% Branches 28/30
100% Functions 2/2
100% Lines 34/34

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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 1381x                                                                                                                                       1x         61x   61x 80x 80x   80x       78x   78x 43x 16x   16x 2x 14x 12x 12x   2x 2x       29x 35x     10x   10x 10x 10x 12x     10x   10x   10x   4x     6x           25x 13x 13x           14x    
import { isGeneralSegment } from 'roosterjs-content-model-dom';
import type {
    ContentModelParagraph,
    ReadonlyContentModelBlock,
    ReadonlyContentModelBlockGroup,
    ReadonlyContentModelSegment,
} from 'roosterjs-content-model-types';
 
/**
 * @internal
 */
export type BlockAndPath = {
    /**
     * The sibling block
     */
    block: ReadonlyContentModelBlock;
 
    /**
     * Path of this sibling block
     */
    path: ReadonlyContentModelBlockGroup[];
 
    /**
     * If the input block is under a general segment, it is possible there are sibling segments under the same paragraph.
     * Use this property to return the sibling sibling under the same paragraph
     */
    siblingSegment?: ReadonlyContentModelSegment;
};
 
/**
 * @internal
 */
export type ReadonlyBlockAndPath = {
    /**
     * The sibling block
     */
    block: ReadonlyContentModelBlock;
 
    /**
     * Path of this sibling block
     */
    path: ReadonlyContentModelBlockGroup[];
 
    /**
     * If the input block is under a general segment, it is possible there are sibling segments under the same paragraph.
     * Use this property to return the sibling sibling under the same paragraph
     */
    siblingSegment?: ReadonlyContentModelSegment;
};
 
/**
 * @internal
 */
export function getLeafSiblingBlock(
    path: ReadonlyContentModelBlockGroup[],
    block: ReadonlyContentModelBlock,
    isNext: boolean
): BlockAndPath | null;
 
/**
 * @internal (Readonly)
 */
export function getLeafSiblingBlock(
    path: ReadonlyContentModelBlockGroup[],
    block: ReadonlyContentModelBlock,
    isNext: boolean
): ReadonlyBlockAndPath | null;
 
export function getLeafSiblingBlock(
    path: ReadonlyContentModelBlockGroup[],
    block: ReadonlyContentModelBlock,
    isNext: boolean
): ReadonlyBlockAndPath | null {
    const newPath = [...path];
 
    while (newPath.length > 0) {
        const group = newPath[0];
        const index = group.blocks.indexOf(block);
 
        if (index < 0) {
            break;
        }
 
        let nextBlock = group.blocks[index + (isNext ? 1 : -1)];
 
        if (nextBlock) {
            while (nextBlock.blockType == 'BlockGroup') {
                const child = nextBlock.blocks[isNext ? 0 : nextBlock.blocks.length - 1];
 
                if (!child) {
                    return { block: nextBlock, path: newPath };
                } else if (child.blockType != 'BlockGroup') {
                    newPath.unshift(nextBlock);
                    return { block: child, path: newPath };
                } else {
                    newPath.unshift(nextBlock);
                    nextBlock = child;
                }
            }
 
            return { block: nextBlock, path: newPath };
        } else if (isGeneralSegment(group)) {
            // For general segment, we need to check if there is sibling segment under the same paragraph
            // First let's find the parent paragraph of this segment
            newPath.shift();
 
            let segmentIndex = -1;
            const segment = group;
            const para = newPath[0]?.blocks.find(
                x => x.blockType == 'Paragraph' && (segmentIndex = x.segments.indexOf(segment)) >= 0
            ) as ContentModelParagraph;
 
            Eif (para) {
                // Now we have found the parent paragraph, so let's check if it has a sibling segment
                const siblingSegment = para.segments[segmentIndex + (isNext ? 1 : -1)];
 
                if (siblingSegment) {
                    // Return this block, path and segment since we have found it
                    return { block: para, path: newPath, siblingSegment };
                } else {
                    // No sibling segment, let's keep go upper level
                    block = para;
                }
            } else {
                // Parent sibling is not found (in theory this should never happen), just return null
                break;
            }
        } else if (group.blockGroupType != 'Document' && group.blockGroupType != 'TableCell') {
            newPath.shift();
            block = group;
        } else {
            break;
        }
    }
 
    return null;
}