diff --git a/demo/scripts/controlsV2/sidePane/apiPlayground/insertEntity/InsertEntityPane.tsx b/demo/scripts/controlsV2/sidePane/apiPlayground/insertEntity/InsertEntityPane.tsx index f03ddcd9e7f..e304a6b79aa 100644 --- a/demo/scripts/controlsV2/sidePane/apiPlayground/insertEntity/InsertEntityPane.tsx +++ b/demo/scripts/controlsV2/sidePane/apiPlayground/insertEntity/InsertEntityPane.tsx @@ -3,9 +3,9 @@ import { ApiPaneProps } from '../ApiPaneProps'; import { insertEntity } from 'roosterjs-content-model-api'; import { trustedHTMLHandler } from '../../../../utils/trustedHTMLHandler'; import { - ContentModelBlockGroup, ContentModelEntity, InsertEntityOptions, + ReadonlyContentModelBlockGroup, } from 'roosterjs-content-model-types'; const styles = require('./InsertEntityPane.scss'); @@ -155,7 +155,7 @@ export default class InsertEntityPane extends React.Component { switch (block.blockType) { case 'BlockGroup': diff --git a/packages/roosterjs-content-model-api/lib/modelApi/list/getListAnnounceData.ts b/packages/roosterjs-content-model-api/lib/modelApi/list/getListAnnounceData.ts index 20fc55e3ee1..16fa2e84df1 100644 --- a/packages/roosterjs-content-model-api/lib/modelApi/list/getListAnnounceData.ts +++ b/packages/roosterjs-content-model-api/lib/modelApi/list/getListAnnounceData.ts @@ -2,13 +2,14 @@ import { findListItemsInSameThread } from './findListItemsInSameThread'; import { getAutoListStyleType, getClosestAncestorBlockGroupIndex, + getListMetadata, getOrderedListNumberStr, - updateListMetadata, } from 'roosterjs-content-model-dom'; import type { AnnounceData, - ContentModelBlockGroup, ContentModelListItem, + ReadonlyContentModelBlockGroup, + ReadonlyContentModelListItem, } from 'roosterjs-content-model-types'; /** @@ -16,7 +17,7 @@ import type { * @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: ContentModelBlockGroup[]): AnnounceData | null { +export function getListAnnounceData(path: ReadonlyContentModelBlockGroup[]): AnnounceData | null { const index = getClosestAncestorBlockGroupIndex(path, ['ListItem'], ['TableCell']); if (index >= 0) { @@ -27,7 +28,7 @@ export function getListAnnounceData(path: ContentModelBlockGroup[]): AnnounceDat return null; } else if (level.listType == 'OL') { const listNumber = getListNumber(path, listItem); - const metadata = updateListMetadata(level); + const metadata = getListMetadata(level); const listStyle = getAutoListStyleType( 'OL', metadata ?? {}, @@ -51,7 +52,10 @@ export function getListAnnounceData(path: ContentModelBlockGroup[]): AnnounceDat } } -function getListNumber(path: ContentModelBlockGroup[], listItem: ContentModelListItem) { +function getListNumber( + path: ReadonlyContentModelBlockGroup[], + listItem: ReadonlyContentModelListItem +) { const items = findListItemsInSameThread(path[path.length - 1], listItem); let listNumber = 0; diff --git a/packages/roosterjs-content-model-api/lib/modelApi/table/canMergeCells.ts b/packages/roosterjs-content-model-api/lib/modelApi/table/canMergeCells.ts index 622ad9c8324..1c14ab605a7 100644 --- a/packages/roosterjs-content-model-api/lib/modelApi/table/canMergeCells.ts +++ b/packages/roosterjs-content-model-api/lib/modelApi/table/canMergeCells.ts @@ -1,10 +1,10 @@ -import type { ContentModelTableRow } from 'roosterjs-content-model-types'; +import type { ReadonlyContentModelTableRow } from 'roosterjs-content-model-types'; /** * @internal */ export function canMergeCells( - rows: ContentModelTableRow[], + rows: ReadonlyContentModelTableRow[], firstRow: number, firstCol: number, lastRow: number, @@ -40,7 +40,11 @@ export function canMergeCells( return noSpanAbove && noSpanLeft && noDifferentBelowSpan && noDifferentRightSpan; } -function getBelowSpanCount(rows: ContentModelTableRow[], rowIndex: number, colIndex: number) { +function getBelowSpanCount( + rows: ReadonlyContentModelTableRow[], + rowIndex: number, + colIndex: number +) { let spanCount = 0; for (let row = rowIndex + 1; row < rows.length; row++) { @@ -54,7 +58,11 @@ function getBelowSpanCount(rows: ContentModelTableRow[], rowIndex: number, colIn return spanCount; } -function getRightSpanCount(rows: ContentModelTableRow[], rowIndex: number, colIndex: number) { +function getRightSpanCount( + rows: ReadonlyContentModelTableRow[], + rowIndex: number, + colIndex: number +) { let spanCount = 0; for (let col = colIndex + 1; col < rows[rowIndex]?.cells.length; col++) { diff --git a/packages/roosterjs-content-model-api/lib/publicApi/image/changeImage.ts b/packages/roosterjs-content-model-api/lib/publicApi/image/changeImage.ts index 7067c9fae8e..a14a91db4a2 100644 --- a/packages/roosterjs-content-model-api/lib/publicApi/image/changeImage.ts +++ b/packages/roosterjs-content-model-api/lib/publicApi/image/changeImage.ts @@ -1,5 +1,5 @@ import { formatImageWithContentModel } from '../utils/formatImageWithContentModel'; -import { readFile, updateImageMetadata } from 'roosterjs-content-model-dom'; +import { getImageMetadata, readFile } from 'roosterjs-content-model-dom'; import type { ContentModelImage, IEditor } from 'roosterjs-content-model-types'; /** @@ -14,7 +14,7 @@ export function changeImage(editor: IEditor, file: File) { readFile(file, dataUrl => { if (dataUrl && !editor.isDisposed() && selection?.type === 'image') { formatImageWithContentModel(editor, 'changeImage', (image: ContentModelImage) => { - const originalSrc = updateImageMetadata(image)?.src ?? ''; + const originalSrc = getImageMetadata(image)?.src ?? ''; const previousSrc = image.src; image.src = dataUrl; diff --git a/packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts b/packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts index 55f2374297a..af149946960 100644 --- a/packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts +++ b/packages/roosterjs-content-model-core/lib/command/paste/mergePasteContent.ts @@ -15,6 +15,7 @@ import type { ContentModelSegmentFormat, IEditor, MergeModelOption, + ReadonlyContentModelDocument, } from 'roosterjs-content-model-types'; const EmptySegmentFormat: Required = { @@ -38,7 +39,7 @@ const CloneOption: CloneModelOptions = { /** * @internal */ -export function cloneModelForPaste(model: ContentModelDocument) { +export function cloneModelForPaste(model: ReadonlyContentModelDocument) { return cloneModel(model, CloneOption); } diff --git a/packages/roosterjs-content-model-core/lib/corePlugin/entity/findAllEntities.ts b/packages/roosterjs-content-model-core/lib/corePlugin/entity/findAllEntities.ts index 0f0b466eb92..b615cf49d8a 100644 --- a/packages/roosterjs-content-model-core/lib/corePlugin/entity/findAllEntities.ts +++ b/packages/roosterjs-content-model-core/lib/corePlugin/entity/findAllEntities.ts @@ -1,9 +1,9 @@ -import type { ChangedEntity, ContentModelBlockGroup } from 'roosterjs-content-model-types'; +import type { ChangedEntity, ReadonlyContentModelBlockGroup } from 'roosterjs-content-model-types'; /** * @internal */ -export function findAllEntities(group: ContentModelBlockGroup, entities: ChangedEntity[]) { +export function findAllEntities(group: ReadonlyContentModelBlockGroup, entities: ChangedEntity[]) { group.blocks.forEach(block => { switch (block.blockType) { case 'BlockGroup': diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/common/addBlock.ts b/packages/roosterjs-content-model-dom/lib/modelApi/common/addBlock.ts index 4c331427d7b..57a51f84640 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/common/addBlock.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/common/addBlock.ts @@ -1,5 +1,5 @@ import type { - ContentModelBlock, + ShallowMutableContentModelBlock, ShallowMutableContentModelBlockGroup, } from 'roosterjs-content-model-types'; @@ -8,6 +8,9 @@ import type { * @param group The block group to add block into * @param block The block to add */ -export function addBlock(group: ShallowMutableContentModelBlockGroup, block: ContentModelBlock) { +export function addBlock( + group: ShallowMutableContentModelBlockGroup, + block: ShallowMutableContentModelBlock +) { group.blocks.push(block); } diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/common/isEmpty.ts b/packages/roosterjs-content-model-dom/lib/modelApi/common/isEmpty.ts index 64a1944437b..ae399702e2d 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/common/isEmpty.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/common/isEmpty.ts @@ -1,13 +1,13 @@ import type { - ContentModelBlock, - ContentModelBlockGroup, - ContentModelSegment, + ReadonlyContentModelBlock, + ReadonlyContentModelBlockGroup, + ReadonlyContentModelSegment, } from 'roosterjs-content-model-types'; /** * @internal */ -export function isBlockEmpty(block: ContentModelBlock): boolean { +export function isBlockEmpty(block: ReadonlyContentModelBlock): boolean { switch (block.blockType) { case 'Paragraph': return block.segments.length == 0; @@ -29,7 +29,7 @@ export function isBlockEmpty(block: ContentModelBlock): boolean { /** * @internal */ -export function isBlockGroupEmpty(group: ContentModelBlockGroup): boolean { +export function isBlockGroupEmpty(group: ReadonlyContentModelBlockGroup): boolean { switch (group.blockGroupType) { case 'FormatContainer': // Format Container of DIV is a container for style, so we always treat it as not empty @@ -51,7 +51,7 @@ export function isBlockGroupEmpty(group: ContentModelBlockGroup): boolean { /** * @internal */ -export function isSegmentEmpty(segment: ContentModelSegment): boolean { +export function isSegmentEmpty(segment: ReadonlyContentModelSegment): boolean { switch (segment.segmentType) { case 'Text': return !segment.text; @@ -69,7 +69,7 @@ export function isSegmentEmpty(segment: ContentModelSegment): boolean { * @returns true if the model is empty. */ export function isEmpty( - model: ContentModelBlock | ContentModelBlockGroup | ContentModelSegment + model: ReadonlyContentModelBlock | ReadonlyContentModelBlockGroup | ReadonlyContentModelSegment ): boolean { if (isBlockGroup(model)) { return isBlockGroupEmpty(model); @@ -83,19 +83,19 @@ export function isEmpty( } function isSegment( - model: ContentModelBlock | ContentModelBlockGroup | ContentModelSegment -): model is ContentModelSegment { - return typeof (model).segmentType === 'string'; + model: ReadonlyContentModelBlock | ReadonlyContentModelBlockGroup | ReadonlyContentModelSegment +): model is ReadonlyContentModelSegment { + return typeof (model).segmentType === 'string'; } function isBlock( - model: ContentModelBlock | ContentModelBlockGroup | ContentModelSegment -): model is ContentModelBlock { - return typeof (model).blockType === 'string'; + model: ReadonlyContentModelBlock | ReadonlyContentModelBlockGroup | ReadonlyContentModelSegment +): model is ReadonlyContentModelBlock { + return typeof (model).blockType === 'string'; } function isBlockGroup( - model: ContentModelBlock | ContentModelBlockGroup | ContentModelSegment -): model is ContentModelBlockGroup { - return typeof (model).blockGroupType === 'string'; + model: ReadonlyContentModelBlock | ReadonlyContentModelBlockGroup | ReadonlyContentModelSegment +): model is ReadonlyContentModelBlockGroup { + return typeof (model).blockGroupType === 'string'; } diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/editing/getSegmentTextFormat.ts b/packages/roosterjs-content-model-dom/lib/modelApi/editing/getSegmentTextFormat.ts index cd91dbbde22..b8962f3205a 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/editing/getSegmentTextFormat.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/editing/getSegmentTextFormat.ts @@ -1,11 +1,16 @@ -import type { ContentModelSegment, ContentModelSegmentFormat } from 'roosterjs-content-model-types'; +import type { + ContentModelSegmentFormat, + ReadonlyContentModelSegment, +} from 'roosterjs-content-model-types'; /** * Get the text format of a segment, this function will return only format that is applicable to text * @param segment The segment to get format from * @returns */ -export function getSegmentTextFormat(segment: ContentModelSegment): ContentModelSegmentFormat { +export function getSegmentTextFormat( + segment: ReadonlyContentModelSegment +): ContentModelSegmentFormat { const { fontFamily, fontSize, textColor, backgroundColor, letterSpacing, lineHeight } = segment?.format ?? {}; diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/editing/retrieveModelFormatState.ts b/packages/roosterjs-content-model-dom/lib/modelApi/editing/retrieveModelFormatState.ts index 5cfd4d0333e..8b7cbb0a19b 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/editing/retrieveModelFormatState.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/editing/retrieveModelFormatState.ts @@ -1,20 +1,20 @@ import { extractBorderValues } from '../../domUtils/style/borderValues'; import { getClosestAncestorBlockGroupIndex } from './getClosestAncestorBlockGroupIndex'; +import { getTableMetadata } from '../metadata/updateTableMetadata'; import { isBold } from '../../domUtils/style/isBold'; import { iterateSelections } from '../selection/iterateSelections'; import { parseValueWithUnit } from '../../formatHandlers/utils/parseValueWithUnit'; -import { updateTableMetadata } from '../metadata/updateTableMetadata'; import type { ContentModelFormatState, - ContentModelBlock, - ContentModelBlockGroup, - ContentModelDocument, - ContentModelFormatContainer, - ContentModelImage, - ContentModelListItem, - ContentModelParagraph, ContentModelSegmentFormat, - TableSelectionContext, + ReadonlyContentModelBlockGroup, + ReadonlyContentModelBlock, + ReadonlyContentModelImage, + ReadonlyTableSelectionContext, + ReadonlyContentModelParagraph, + ReadonlyContentModelFormatContainer, + ReadonlyContentModelListItem, + ReadonlyContentModelDocument, } from 'roosterjs-content-model-types'; /** @@ -24,12 +24,12 @@ import type { * @param formatState Existing format state object, used for receiving the result */ export function retrieveModelFormatState( - model: ContentModelDocument, + model: ReadonlyContentModelDocument, pendingFormat: ContentModelSegmentFormat | null, formatState: ContentModelFormatState ) { - let firstTableContext: TableSelectionContext | undefined; - let firstBlock: ContentModelBlock | undefined; + let firstTableContext: ReadonlyTableSelectionContext | undefined; + let firstBlock: ReadonlyContentModelBlock | undefined; let isFirst = true; let isFirstImage = true; let isFirstSegment = true; @@ -56,10 +56,11 @@ export function retrieveModelFormatState( // Segment formats segments?.forEach(segment => { if (isFirstSegment || segment.segmentType != 'SelectionMarker') { - const modelFormat = Object.assign({}, model.format); - delete modelFormat?.italic; - delete modelFormat?.underline; - delete modelFormat?.fontWeight; + const modelFormat = { ...model.format }; + + delete modelFormat.italic; + delete modelFormat.underline; + delete modelFormat.fontWeight; retrieveSegmentFormat( formatState, @@ -165,7 +166,7 @@ function retrieveSegmentFormat( function retrieveParagraphFormat( result: ContentModelFormatState, - paragraph: ContentModelParagraph, + paragraph: ReadonlyContentModelParagraph, isFirst: boolean ) { const headingLevel = parseInt((paragraph.decorator?.tagName || '').substring(1)); @@ -180,14 +181,14 @@ function retrieveParagraphFormat( function retrieveStructureFormat( result: ContentModelFormatState, - path: ContentModelBlockGroup[], + path: ReadonlyContentModelBlockGroup[], isFirst: boolean ) { const listItemIndex = getClosestAncestorBlockGroupIndex(path, ['ListItem'], []); const containerIndex = getClosestAncestorBlockGroupIndex(path, ['FormatContainer'], []); if (listItemIndex >= 0) { - const listItem = path[listItemIndex] as ContentModelListItem; + const listItem = path[listItemIndex] as ReadonlyContentModelListItem; const listType = listItem?.levels[listItem.levels.length - 1]?.listType; mergeValue(result, 'isBullet', listType == 'UL', isFirst); @@ -198,13 +199,16 @@ function retrieveStructureFormat( result, 'isBlockQuote', containerIndex >= 0 && - (path[containerIndex] as ContentModelFormatContainer)?.tagName == 'blockquote', + (path[containerIndex] as ReadonlyContentModelFormatContainer)?.tagName == 'blockquote', isFirst ); } -function retrieveTableFormat(tableContext: TableSelectionContext, result: ContentModelFormatState) { - const tableFormat = updateTableMetadata(tableContext.table); +function retrieveTableFormat( + tableContext: ReadonlyTableSelectionContext, + result: ContentModelFormatState +) { + const tableFormat = getTableMetadata(tableContext.table); result.isInTable = true; result.tableHasHeader = tableContext.table.rows.some(row => @@ -216,7 +220,7 @@ function retrieveTableFormat(tableContext: TableSelectionContext, result: Conten } } -function retrieveImageFormat(image: ContentModelImage, result: ContentModelFormatState) { +function retrieveImageFormat(image: ReadonlyContentModelImage, result: ContentModelFormatState) { const { format } = image; const borderKey = 'borderTop'; const extractedBorder = extractBorderValues(format[borderKey]); diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/selection/getSelectedCells.ts b/packages/roosterjs-content-model-dom/lib/modelApi/selection/getSelectedCells.ts index beb8b46fe86..ab7484c24a5 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/selection/getSelectedCells.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/selection/getSelectedCells.ts @@ -1,11 +1,16 @@ import { hasSelectionInBlockGroup } from '../selection/hasSelectionInBlockGroup'; -import type { ContentModelTable, TableSelectionCoordinates } from 'roosterjs-content-model-types'; +import type { + ReadonlyContentModelTable, + TableSelectionCoordinates, +} from 'roosterjs-content-model-types'; /** * Get selection coordinates of a table. If there is no selection, return null * @param table The table model to get selection from */ -export function getSelectedCells(table: ContentModelTable): TableSelectionCoordinates | null { +export function getSelectedCells( + table: ReadonlyContentModelTable +): TableSelectionCoordinates | null { let firstRow = -1; let firstColumn = -1; let lastRow = -1; diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInBlock.ts b/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInBlock.ts index 8d721e4f048..395fb46da8d 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInBlock.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInBlock.ts @@ -1,12 +1,12 @@ import { hasSelectionInBlockGroup } from './hasSelectionInBlockGroup'; import { hasSelectionInSegment } from './hasSelectionInSegment'; -import type { ContentModelBlock } from 'roosterjs-content-model-types'; +import type { ReadonlyContentModelBlock } from 'roosterjs-content-model-types'; /** * Check if there is selection within the given block * @param block The block to check */ -export function hasSelectionInBlock(block: ContentModelBlock): boolean { +export function hasSelectionInBlock(block: ReadonlyContentModelBlock): boolean { switch (block.blockType) { case 'Paragraph': return block.segments.some(hasSelectionInSegment); diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInBlockGroup.ts b/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInBlockGroup.ts index eb9b2199e61..e01e82da76b 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInBlockGroup.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInBlockGroup.ts @@ -1,11 +1,11 @@ import { hasSelectionInBlock } from './hasSelectionInBlock'; -import type { ContentModelBlockGroup } from 'roosterjs-content-model-types'; +import type { ReadonlyContentModelBlockGroup } from 'roosterjs-content-model-types'; /** * Check if there is selection within the given block * @param block The block to check */ -export function hasSelectionInBlockGroup(group: ContentModelBlockGroup): boolean { +export function hasSelectionInBlockGroup(group: ReadonlyContentModelBlockGroup): boolean { if (group.blockGroupType == 'TableCell' && group.isSelected) { return true; } diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInSegment.ts b/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInSegment.ts index 8bc58860ef8..07238f1e8dd 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInSegment.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/selection/hasSelectionInSegment.ts @@ -1,11 +1,11 @@ import { hasSelectionInBlock } from './hasSelectionInBlock'; -import type { ContentModelSegment } from 'roosterjs-content-model-types'; +import type { ReadonlyContentModelSegment } from 'roosterjs-content-model-types'; /** * Check if there is selection within the given segment * @param segment The segment to check */ -export function hasSelectionInSegment(segment: ContentModelSegment): boolean { +export function hasSelectionInSegment(segment: ReadonlyContentModelSegment): boolean { return ( segment.isSelected || (segment.segmentType == 'General' && segment.blocks.some(hasSelectionInBlock)) diff --git a/packages/roosterjs-content-model-dom/test/modelApi/selection/collectSelectionsTest.ts b/packages/roosterjs-content-model-dom/test/modelApi/selection/collectSelectionsTest.ts index 51538c7552d..bd950e01685 100644 --- a/packages/roosterjs-content-model-dom/test/modelApi/selection/collectSelectionsTest.ts +++ b/packages/roosterjs-content-model-dom/test/modelApi/selection/collectSelectionsTest.ts @@ -77,6 +77,9 @@ describe('getSelectedSegmentsAndParagraphs', () => { const p1 = createParagraph(); const p2 = createParagraph(); + p1.segments.push(s1, s2); + p2.segments.push(s3, s4); + runTest( [ { @@ -132,6 +135,7 @@ describe('getSelectedSegmentsAndParagraphs', () => { const s3 = createText('test3'); const s4 = createText('test4'); const b1 = createDivider('div'); + const doc = createContentModelDocument(); runTest( [ @@ -141,7 +145,7 @@ describe('getSelectedSegmentsAndParagraphs', () => { segments: [s1, s2], }, { - path: [], + path: [doc], segments: [s3, s4], }, ], diff --git a/packages/roosterjs-content-model-plugins/lib/autoFormat/list/getListTypeStyle.ts b/packages/roosterjs-content-model-plugins/lib/autoFormat/list/getListTypeStyle.ts index 7a6c64dc584..8ab4ec1ccb0 100644 --- a/packages/roosterjs-content-model-plugins/lib/autoFormat/list/getListTypeStyle.ts +++ b/packages/roosterjs-content-model-plugins/lib/autoFormat/list/getListTypeStyle.ts @@ -1,9 +1,10 @@ import { findListItemsInSameThread } from 'roosterjs-content-model-api'; import { getNumberingListStyle } from './getNumberingListStyle'; import type { - ContentModelDocument, ContentModelListItem, - ContentModelParagraph, + ReadonlyContentModelDocument, + ReadonlyContentModelListItem, + ReadonlyContentModelParagraph, } from 'roosterjs-content-model-types'; import { BulletListType, @@ -26,7 +27,7 @@ interface ListTypeStyle { * @internal */ export function getListTypeStyle( - model: ContentModelDocument, + model: ReadonlyContentModelDocument, shouldSearchForBullet: boolean = true, shouldSearchForNumbering: boolean = true ): ListTypeStyle | undefined { @@ -77,13 +78,16 @@ export function getListTypeStyle( } const getPreviousListIndex = ( - model: ContentModelDocument, - previousListItem?: ContentModelListItem + model: ReadonlyContentModelDocument, + previousListItem?: ReadonlyContentModelListItem ) => { return previousListItem ? findListItemsInSameThread(model, previousListItem).length : undefined; }; -const getPreviousListLevel = (model: ContentModelDocument, paragraph: ContentModelParagraph) => { +const getPreviousListLevel = ( + model: ReadonlyContentModelDocument, + paragraph: ReadonlyContentModelParagraph +) => { const blocks = getOperationalBlocks( model, ['ListItem'], diff --git a/packages/roosterjs-content-model-plugins/lib/edit/utils/getLeafSiblingBlock.ts b/packages/roosterjs-content-model-plugins/lib/edit/utils/getLeafSiblingBlock.ts index b5913fce32c..f99390a79bc 100644 --- a/packages/roosterjs-content-model-plugins/lib/edit/utils/getLeafSiblingBlock.ts +++ b/packages/roosterjs-content-model-plugins/lib/edit/utils/getLeafSiblingBlock.ts @@ -4,6 +4,9 @@ import type { ContentModelBlockGroup, ContentModelParagraph, ContentModelSegment, + ReadonlyContentModelBlock, + ReadonlyContentModelBlockGroup, + ReadonlyContentModelSegment, } from 'roosterjs-content-model-types'; /** @@ -27,6 +30,27 @@ export type BlockAndPath = { siblingSegment?: ContentModelSegment; }; +/** + * @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 */ @@ -34,7 +58,22 @@ export function getLeafSiblingBlock( path: ContentModelBlockGroup[], block: ContentModelBlock, isNext: boolean -): BlockAndPath | null { +): 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) { diff --git a/packages/roosterjs-content-model-plugins/lib/watermark/isModelEmptyFast.ts b/packages/roosterjs-content-model-plugins/lib/watermark/isModelEmptyFast.ts index 4265e799e34..b17d285d964 100644 --- a/packages/roosterjs-content-model-plugins/lib/watermark/isModelEmptyFast.ts +++ b/packages/roosterjs-content-model-plugins/lib/watermark/isModelEmptyFast.ts @@ -1,10 +1,10 @@ -import type { ContentModelDocument } from 'roosterjs-content-model-types'; +import type { ReadonlyContentModelDocument } from 'roosterjs-content-model-types'; /** * @internal * A fast way to check if content model is empty */ -export function isModelEmptyFast(model: ContentModelDocument): boolean { +export function isModelEmptyFast(model: ReadonlyContentModelDocument): boolean { const firstBlock = model.blocks[0]; if (model.blocks.length > 1) {