All files / roosterjs-content-model-api/lib/modelApi/list getListAnnounceData.ts

93.75% Statements 30/32
88.24% Branches 30/34
100% Functions 2/2
93.55% Lines 29/31

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 851x 1x                                   1x 27x   27x 25x 25x   25x 2x 23x 17x 17x 17x             17x             6x         2x               17x 17x   17x 28x   28x 17x     28x   17x 11x     11x     11x   11x     17x    
import { findListItemsInSameThread } from './findListItemsInSameThread';
import {
    getAutoListStyleType,
    getClosestAncestorBlockGroupIndex,
    getListMetadata,
    getOrderedListNumberStr,
} from 'roosterjs-content-model-dom';
import type {
    AnnounceData,
    ContentModelListItem,
    ReadonlyContentModelBlockGroup,
    ReadonlyContentModelListItem,
} from 'roosterjs-content-model-types';
 
/**
 * Get announce data for list item
 * @param path Content model path that include the list item
 * @returns Announce data of current list item if any, or null
 */
export function getListAnnounceData(path: ReadonlyContentModelBlockGroup[]): AnnounceData | null {
    const index = getClosestAncestorBlockGroupIndex(path, ['ListItem'], ['TableCell']);
 
    if (index >= 0) {
        const listItem = path[index] as ContentModelListItem;
        const level = listItem.levels[listItem.levels.length - 1];
 
        if (!level || level.format.displayForDummyItem) {
            return null;
        } else if (level.listType == 'OL') {
            const listNumber = getListNumber(path, listItem);
            const metadata = getListMetadata(level);
            const listStyle = getAutoListStyleType(
                'OL',
                metadata ?? {},
                listItem.levels.length - 1,
                level.format.listStyleType
            );
 
            return listStyle === undefined
                ? null
                : {
                      defaultStrings: 'announceListItemNumbering',
                      formatStrings: [getOrderedListNumberStr(listStyle, listNumber)],
                  };
        } else {
            return {
                defaultStrings: 'announceListItemBullet',
            };
        }
    } else {
        return null;
    }
}
 
function getListNumber(
    path: ReadonlyContentModelBlockGroup[],
    listItem: ReadonlyContentModelListItem
) {
    const items = findListItemsInSameThread(path[path.length - 1], listItem);
    let listNumber = 0;
 
    for (let i = 0; i < items.length; i++) {
        const item = items[i];
 
        if (listNumber == 0 && item.levels.length == listItem.levels.length) {
            listNumber = item.levels[item.levels.length - 1]?.format.startNumberOverride ?? 1;
        }
 
        if (item == listItem) {
            // Found current item, so break and return
            break;
        } else Iif (item.levels.length < listItem.levels.length) {
            // Found upper level item, reset list number
            listNumber = 0;
        } else Iif (item.levels.length > listItem.levels.length) {
            // Found deeper level item, skip
            continue;
        } else Eif (!item.levels[item.levels.length - 1].format.displayForDummyItem) {
            // Save level, and is not dummy, number plus one
            listNumber++;
        }
    }
    return listNumber;
}