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 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 260x 260x 260x 260x 260x 260x 260x 260x 5x 5x 6x 1x 5x 255x 255x 255x 255x 255x 1x 260x 257x 257x 257x 257x 257x 257x 3x 3x 3x 260x 111x 111x 103x 260x 255x 260x | import { applyFormat } from '../utils/applyFormat';
import { applyMetadata } from '../utils/applyMetadata';
import { isGenericRoleElement } from '../../domUtils/isGenericRoleElement';
import { reuseCachedElement } from '../../domUtils/reuseCachedElement';
import { setParagraphNotImplicit } from '../../modelApi/block/setParagraphNotImplicit';
import { stackFormat } from '../utils/stackFormat';
import { unwrap } from '../../domUtils/unwrap';
import type {
ContentModelBlockHandler,
ContentModelListItem,
ModelToDomContext,
ModelToDomListStackItem,
} from 'roosterjs-content-model-types';
const HtmlRoleAttribute = 'role';
const PresentationRoleValue = 'presentation';
/**
* @internal
*/
export const handleListItem: ContentModelBlockHandler<ContentModelListItem> = (
doc: Document,
parent: Node,
listItem: ContentModelListItem,
context: ModelToDomContext,
refNode: Node | null
) => {
refNode = context.modelHandlers.list(doc, parent, listItem, context, refNode);
const { nodeStack } = context.listFormat;
const leafLevel: Partial<ModelToDomListStackItem> = nodeStack?.[nodeStack.length - 1] ?? {};
const itemRefNode = leafLevel.refNode || null;
const listParent = leafLevel.node || parent;
const level = listItem.levels[listItem.levels.length - 1];
let li: HTMLLIElement;
let isNewlyCreated = false;
if (context.allowCacheListItem && listItem.cachedElement) {
li = listItem.cachedElement;
// Check if the cached LI is used as refNode under another list level,
// since we know we are going to move it under the current listParent,
// we need to update the refNode of the previous list level to avoid removing it later
for (let i = 0; i < nodeStack.length - 1; i++) {
if (nodeStack[i].refNode === li) {
nodeStack[i].refNode = li.nextSibling;
}
}
leafLevel.refNode = reuseCachedElement(
listParent,
li,
itemRefNode,
context.rewriteFromModel
);
} else {
li = doc.createElement('li');
isNewlyCreated = true;
// It is possible listParent is the same with parent param.
// This happens when outdent a list item to cause it has no list level
listParent.insertBefore(li, itemRefNode?.parentNode == listParent ? itemRefNode : null);
context.rewriteFromModel.addedBlockElements.push(li);
if (context.allowCacheListItem) {
listItem.cachedElement = li;
}
}
if (level) {
applyFormat(li, context.formatAppliers.segment, listItem.formatHolder.format, context);
applyFormat(li, context.formatAppliers.listItemThread, level.format, context);
// Need to apply metadata after applying listItem format since the list numbers value relies on the result of list thread handling
applyMetadata(level, context.metadataAppliers.listItem, listItem.format, context);
// Need to apply listItemElement formats after applying metadata since the list numbers value relies on the result of metadata handling
applyFormat(li, context.formatAppliers.listItemElement, listItem.format, context);
stackFormat(context, listItem.formatHolder.format, () => {
context.modelHandlers.blockGroupChildren(doc, li, listItem, context);
});
} else {
// There is no level for this list item, that means it should be moved out of the list
// For each paragraph, make it not implicit so it will have a DIV around it, to avoid more paragraphs connected together
listItem.blocks.forEach(setParagraphNotImplicit);
context.modelHandlers.blockGroupChildren(doc, li, listItem, context);
unwrap(li);
}
// Add role="presentation" to all generic role elements inside the LI element
// This is to make sure the elements are announced correctly by screen readers
// when using arrow keys to navigate the list.
for (let index = 0; index < li.children.length; index++) {
const element = li.children.item(index);
if (isGenericRoleElement(element)) {
element.setAttribute(HtmlRoleAttribute, PresentationRoleValue);
}
}
if (isNewlyCreated) {
context.onNodeCreated?.(listItem, li);
}
return refNode;
};
|