All files / roosterjs-content-model-plugins/lib/edit/deleteSteps deleteList.ts

96.97% Statements 32/33
96.15% Branches 25/26
100% Functions 2/2
96.55% Lines 28/29

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 861x                           1x 13x       13x 13x         13x 13x   13x           8x 8x 8x 8x             8x 1x   1x 7x           1x   1x 1x   1x   6x         6x 1x         1x 2x     1x   5x     6x        
import {
    createListItem,
    getClosestAncestorBlockGroupIndex,
    mutateBlock,
} from 'roosterjs-content-model-dom';
import type {
    DeleteSelectionStep,
    ContentModelListItem,
    ContentModelBlock,
} from 'roosterjs-content-model-types';
 
/**
 * @internal
 */
export const deleteList: DeleteSelectionStep = context => {
    Iif (context.deleteResult != 'notDeleted') {
        return;
    }
 
    const { paragraph, marker, path } = context.insertPoint;
    const index = getClosestAncestorBlockGroupIndex<ContentModelListItem>(
        path,
        ['ListItem'],
        ['TableCell', 'FormatContainer']
    );
    const item = path[index];
    const parent = path[index + 1];
 
    if (
        item?.blockGroupType == 'ListItem' &&
        item.levels.length > 0 &&
        paragraph.segments[0] == marker &&
        parent
    ) {
        const mutableList = mutateBlock(item);
        const lastLevel = mutableList.levels[mutableList.levels.length - 1];
        const listItemIndex = parent.blocks.indexOf(item);
        const previousItem = parent.blocks[listItemIndex - 1];
 
        // 1. If the last level is dummy, just remove it (legacy behavior)
        // 2. If focus is at the beginning of list item and previous block is a list item with the same level count,
        //    merge current list item into previous one
        // 3. Otherwise, split the list item. Keep the blocks before the paragraph in the current list item,
        //    move the rest to a new list item (if there are multiple levels) or directly to parent (if only one level)
        if (lastLevel.format.displayForDummyItem == 'block') {
            mutableList.levels.pop();
 
            context.deleteResult = 'range';
        } else if (
            item.blocks[0] == paragraph &&
            previousItem?.blockType == 'BlockGroup' &&
            previousItem.blockGroupType == 'ListItem' &&
            previousItem.levels.length == mutableList.levels.length
        ) {
            const mutablePreviousItem = mutateBlock(previousItem);
 
            mutablePreviousItem.blocks.push(...mutableList.blocks);
            mutateBlock(parent).blocks.splice(listItemIndex, 1);
 
            context.deleteResult = 'range';
        } else {
            const removedBlocks = mutableList.blocks.splice(
                mutableList.blocks.indexOf(paragraph),
                mutableList.blocks.length
            );
 
            if (mutableList.levels.length > 1) {
                const newListItem = createListItem(
                    mutableList.levels.slice(0, -1),
                    mutableList.format
                );
 
                newListItem.blocks = removedBlocks.map(
                    block => mutateBlock(block) as ContentModelBlock
                );
 
                mutateBlock(parent).blocks.splice(listItemIndex + 1, 0, newListItem);
            } else {
                mutateBlock(parent).blocks.splice(listItemIndex + 1, 0, ...removedBlocks);
            }
 
            context.deleteResult = 'range';
        }
    }
};