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

74.29% Statements 26/35
59.38% Branches 19/32
50% Functions 3/6
74.29% Lines 26/35

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 1041x 1x 1x 1x 1x   1x                               1x         24x   24x   24x   24x                 24x                                                                 24x         24x   24x 2x 2x 2x 2x   22x       22x 12x 12x   10x 10x 10x          
import { handleTabOnList } from './tabUtils/handleTabOnList';
import { handleTabOnParagraph } from './tabUtils/handleTabOnParagraph';
import { handleTabOnTable } from './tabUtils/handleTabOnTable';
import { handleTabOnTableCell } from './tabUtils/handleTabOnTableCell';
import { setModelIndentation } from 'roosterjs-content-model-api';
import type { HandleTabOptions } from './EditOptions';
import {
    ChangeSource,
    getOperationalBlocks,
    isBlockGroupOfType,
} from 'roosterjs-content-model-dom';
import type {
    ContentModelListItem,
    ContentModelTableCell,
    FormatContentModelContext,
    IEditor,
    ReadonlyContentModelDocument,
} from 'roosterjs-content-model-types';
 
/**
 * @internal
 */
export function keyboardTab(
    editor: IEditor,
    rawEvent: KeyboardEvent,
    options: Required<HandleTabOptions>
) {
    const selection = editor.getDOMSelection();
 
    switch (selection?.type) {
        case 'range':
            editor.formatContentModel(
                (model, context) => {
                    return handleTab(model, rawEvent, context, options);
                },
                {
                    apiName: 'handleTabKey',
                    rawEvent,
                    changeSource: ChangeSource.Keyboard,
                    getChangeData: () => rawEvent.which,
                }
            );
            break;
 
        case 'table':
            if (options.indentTable) {
                editor.formatContentModel(
                    model => {
                        return handleTabOnTable(model, rawEvent);
                    },
                    {
                        apiName: 'handleTabKey',
                        rawEvent,
                        changeSource: ChangeSource.Keyboard,
                        getChangeData: () => rawEvent.which,
                    }
                );
            }
            break;
    }
}
 
/**
 * If multiple blocks are selected, indent or outdent the selected blocks with setModelIndentation.
 * If only one block is selected:
 * - If it is a table cell, call handleTabOnTableCell to handle the tab key.
 * - If it is a paragraph, call handleTabOnParagraph to handle the tab key.
 * - If it is a list item, call handleTabOnList to handle the tab key.
 */
function handleTab(
    model: ReadonlyContentModelDocument,
    rawEvent: KeyboardEvent,
    context: FormatContentModelContext,
    options: Required<HandleTabOptions>
) {
    const blocks = getOperationalBlocks<ContentModelListItem | ContentModelTableCell>(
        model,
        ['ListItem', 'TableCell'],
        []
    );
    const block = blocks.length > 0 ? blocks[0].block : undefined;
 
    if (blocks.length > 1) {
        Eif (options.indentMultipleBlocks) {
            setModelIndentation(model, rawEvent.shiftKey ? 'outdent' : 'indent');
            rawEvent.preventDefault();
            return true;
        }
    } else Iif (isBlockGroupOfType<ContentModelTableCell>(block, 'TableCell')) {
        if (options.appendTableRow) {
            return handleTabOnTableCell(model, block, rawEvent);
        }
    } else if (block?.blockType === 'Paragraph') {
        Eif (options.indentParagraph) {
            return handleTabOnParagraph(model, block, rawEvent, context);
        }
    } else Eif (isBlockGroupOfType<ContentModelListItem>(block, 'ListItem')) {
        Eif (options.indentList) {
            return handleTabOnList(model, block, rawEvent, context);
        }
    }
    return false;
}