-
Notifications
You must be signed in to change notification settings - Fork 166
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query content model blocks. #2851
Changes from 5 commits
978a819
2a56e9a
b5d60cd
5945ba5
6370fde
c172ca4
1f3422c
3a00c66
d274484
7efa7de
a91d593
5a1957f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import type { | ||
ContentModelBlockType, | ||
ContentModelSegmentType, | ||
ReadonlyContentModelBlock, | ||
ReadonlyContentModelBlockGroup, | ||
ReadonlyContentModelParagraph, | ||
ReadonlyContentModelSegment, | ||
ReadonlyContentModelTable, | ||
} from 'roosterjs-content-model-types'; | ||
|
||
/** | ||
* Options for queryContentModel | ||
*/ | ||
export interface QueryContentModelOptions<T> { | ||
/** | ||
* The type of block to query @default 'Paragraph' | ||
*/ | ||
type?: ContentModelBlockType; | ||
|
||
/** | ||
* The type of segment to query | ||
*/ | ||
segmentType?: ContentModelSegmentType; | ||
|
||
/** | ||
* Optional selector to filter the blocks/segments | ||
*/ | ||
selector?: (element: T) => boolean; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about name it "filter"?
haven2world marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
/** | ||
* True to return the first block only, false to return all blocks | ||
*/ | ||
findFirstOnly?: boolean; | ||
} | ||
|
||
/** | ||
* Query content model blocks or segments | ||
* @param group The block group to query | ||
* @param options The query option | ||
*/ | ||
export function queryContentModel< | ||
T extends ReadonlyContentModelBlock | ReadonlyContentModelSegment | ||
>(group: ReadonlyContentModelBlockGroup, options: QueryContentModelOptions<T>): T[] { | ||
haven2world marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const elements: T[] = []; | ||
const searchOptions = options.type ? options : { ...options, type: 'Paragraph' }; | ||
const { type, segmentType, selector, findFirstOnly } = searchOptions; | ||
|
||
for (let i = 0; i < group.blocks.length; i++) { | ||
if (findFirstOnly && elements.length > 0) { | ||
return elements; | ||
} | ||
const block = group.blocks[i]; | ||
switch (block.blockType) { | ||
case 'BlockGroup': | ||
if (type == block.blockType && (!selector || selector(block as T))) { | ||
elements.push(block as T); | ||
} | ||
const blockGroupsResults = queryContentModel<T>(block, options); | ||
elements.push(...(blockGroupsResults as T[])); | ||
break; | ||
case 'Table': | ||
if (type == block.blockType && (!selector || selector(block as T))) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what if T is a segment type, are we converting block to segment? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually we should make sure only the correct type of object can trigger selector() |
||
elements.push(block as T); | ||
} | ||
const tableResults = searchInTables(block, options); | ||
elements.push(...(tableResults as T[])); | ||
break; | ||
case 'Divider': | ||
case 'Entity': | ||
if (type == block.blockType && (!selector || selector(block as T))) { | ||
elements.push(block as T); | ||
} | ||
break; | ||
case 'Paragraph': | ||
if (type == block.blockType) { | ||
if (!segmentType && (!selector || selector(block as T))) { | ||
elements.push(block as T); | ||
} else if (segmentType) { | ||
const segments = searchInParagraphs(block, segmentType, selector); | ||
elements.push(...(segments as T[])); | ||
} | ||
} | ||
break; | ||
} | ||
} | ||
|
||
return elements; | ||
} | ||
|
||
function searchInTables<T extends ReadonlyContentModelBlock | ReadonlyContentModelSegment>( | ||
table: ReadonlyContentModelTable, | ||
options: QueryContentModelOptions<T> | ||
): T[] { | ||
const blocks: T[] = []; | ||
for (const row of table.rows) { | ||
for (const cell of row.cells) { | ||
const items = queryContentModel<T>(cell, options); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better create a |
||
blocks.push(...items); | ||
} | ||
} | ||
return blocks; | ||
} | ||
|
||
function searchInParagraphs<P extends ReadonlyContentModelBlock | ReadonlyContentModelSegment>( | ||
block: ReadonlyContentModelParagraph, | ||
segmentType: ContentModelSegmentType, | ||
selector?: (element: P) => boolean | ||
): P[] { | ||
const segments: P[] = []; | ||
for (const segment of block.segments) { | ||
if (segment.segmentType == segmentType && (!selector || selector(segment as P))) { | ||
segments.push(segment as P); | ||
} | ||
} | ||
return segments; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add type constraint here for T.