-
Notifications
You must be signed in to change notification settings - Fork 166
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'master' into u/jisong/colorkey
- Loading branch information
Showing
20 changed files
with
2,446 additions
and
456 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
packages/roosterjs-content-model-dom/lib/modelApi/editing/runEditSteps.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
import type { | ||
DeleteSelectionContext, | ||
DeleteSelectionResult, | ||
DeleteSelectionStep, | ||
ValidDeleteSelectionContext, | ||
} from 'roosterjs-content-model-types'; | ||
|
||
/** | ||
* Run editing steps on top of a given context object which includes current insert point and previous editing result | ||
* @param steps The editing steps to run | ||
* @param context Context for the editing steps. | ||
*/ | ||
export function runEditSteps(steps: DeleteSelectionStep[], context: DeleteSelectionResult) { | ||
steps.forEach(step => { | ||
if (step && isValidDeleteSelectionContext(context)) { | ||
step(context); | ||
} | ||
}); | ||
} | ||
|
||
function isValidDeleteSelectionContext( | ||
context: DeleteSelectionContext | ||
): context is ValidDeleteSelectionContext { | ||
return !!context.insertPoint; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 55 additions & 40 deletions
95
packages/roosterjs-content-model-plugins/lib/edit/deleteSteps/deleteEmptyQuote.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,88 +1,103 @@ | ||
import { | ||
createParagraph, | ||
createSelectionMarker, | ||
unwrapBlock, | ||
getClosestAncestorBlockGroupIndex, | ||
isBlockGroupOfType, | ||
createFormatContainer, | ||
mutateBlock, | ||
} from 'roosterjs-content-model-dom'; | ||
import type { | ||
ContentModelFormatContainer, | ||
DeleteSelectionStep, | ||
ReadonlyContentModelBlockGroup, | ||
ReadonlyContentModelFormatContainer, | ||
ReadonlyContentModelParagraph, | ||
ShallowMutableContentModelFormatContainer, | ||
ShallowMutableContentModelParagraph, | ||
} from 'roosterjs-content-model-types'; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export const deleteEmptyQuote: DeleteSelectionStep = context => { | ||
const { deleteResult } = context; | ||
|
||
if ( | ||
deleteResult == 'nothingToDelete' || | ||
deleteResult == 'notDeleted' || | ||
deleteResult == 'range' | ||
) { | ||
const { insertPoint, formatContext } = context; | ||
const { path } = insertPoint; | ||
const { path, paragraph } = insertPoint; | ||
const rawEvent = formatContext?.rawEvent as KeyboardEvent; | ||
const index = getClosestAncestorBlockGroupIndex( | ||
path, | ||
['FormatContainer', 'ListItem'], | ||
['TableCell'] | ||
['FormatContainer'], | ||
['TableCell', 'ListItem'] | ||
); | ||
const quote = path[index]; | ||
|
||
if (quote && quote.blockGroupType === 'FormatContainer' && quote.tagName == 'blockquote') { | ||
const parent = path[index + 1]; | ||
const quoteBlockIndex = parent.blocks.indexOf(quote); | ||
const blockQuote = parent.blocks[quoteBlockIndex]; | ||
if ( | ||
isBlockGroupOfType<ContentModelFormatContainer>(blockQuote, 'FormatContainer') && | ||
blockQuote.tagName === 'blockquote' | ||
|
||
if (isEmptyQuote(quote)) { | ||
unwrapBlock(parent, quote); | ||
rawEvent?.preventDefault(); | ||
context.deleteResult = 'range'; | ||
} else if ( | ||
rawEvent?.key === 'Enter' && | ||
quote.blocks.indexOf(paragraph) >= 0 && | ||
isEmptyParagraph(paragraph) | ||
) { | ||
if (isEmptyQuote(blockQuote)) { | ||
unwrapBlock(parent, blockQuote); | ||
rawEvent?.preventDefault(); | ||
context.deleteResult = 'range'; | ||
} else if (isSelectionOnEmptyLine(blockQuote) && rawEvent?.key === 'Enter') { | ||
insertNewLine(blockQuote, parent, quoteBlockIndex); | ||
rawEvent?.preventDefault(); | ||
context.deleteResult = 'range'; | ||
} | ||
insertNewLine(mutateBlock(quote), parent, quoteBlockIndex, paragraph); | ||
rawEvent?.preventDefault(); | ||
context.deleteResult = 'range'; | ||
} | ||
} | ||
} | ||
}; | ||
|
||
const isEmptyQuote = (quote: ContentModelFormatContainer) => { | ||
const isEmptyQuote = (quote: ReadonlyContentModelFormatContainer) => { | ||
return ( | ||
quote.blocks.length === 1 && | ||
quote.blocks[0].blockType === 'Paragraph' && | ||
quote.blocks[0].segments.every( | ||
s => s.segmentType === 'SelectionMarker' || s.segmentType === 'Br' | ||
) | ||
isEmptyParagraph(quote.blocks[0]) | ||
); | ||
}; | ||
|
||
const isSelectionOnEmptyLine = (quote: ContentModelFormatContainer) => { | ||
const quoteLength = quote.blocks.length; | ||
const lastParagraph = quote.blocks[quoteLength - 1]; | ||
if (lastParagraph && lastParagraph.blockType === 'Paragraph') { | ||
return lastParagraph.segments.every( | ||
s => s.segmentType === 'SelectionMarker' || s.segmentType === 'Br' | ||
); | ||
} | ||
const isEmptyParagraph = (paragraph: ReadonlyContentModelParagraph) => { | ||
return paragraph.segments.every( | ||
s => s.segmentType === 'SelectionMarker' || s.segmentType === 'Br' | ||
); | ||
}; | ||
|
||
const insertNewLine = ( | ||
quote: ContentModelFormatContainer, | ||
quote: ShallowMutableContentModelFormatContainer, | ||
parent: ReadonlyContentModelBlockGroup, | ||
index: number | ||
quoteIndex: number, | ||
paragraph: ShallowMutableContentModelParagraph | ||
) => { | ||
const quoteLength = quote.blocks.length; | ||
mutateBlock(quote).blocks.splice(quoteLength - 1, 1); | ||
const marker = createSelectionMarker(); | ||
const newParagraph = createParagraph(false /* isImplicit */); | ||
newParagraph.segments.push(marker); | ||
mutateBlock(parent).blocks.splice(index + 1, 0, newParagraph); | ||
const paraIndex = quote.blocks.indexOf(paragraph); | ||
|
||
if (paraIndex >= 0) { | ||
const mutableParent = mutateBlock(parent); | ||
|
||
if (paraIndex < quote.blocks.length - 1) { | ||
const newQuote: ShallowMutableContentModelFormatContainer = createFormatContainer( | ||
quote.tagName, | ||
quote.format | ||
); | ||
|
||
newQuote.blocks.push( | ||
...quote.blocks.splice(paraIndex + 1, quote.blocks.length - paraIndex - 1) | ||
); | ||
|
||
mutableParent.blocks.splice(quoteIndex + 1, 0, newQuote); | ||
} | ||
|
||
mutableParent.blocks.splice(quoteIndex + 1, 0, paragraph); | ||
quote.blocks.splice(paraIndex, 1); | ||
|
||
if (quote.blocks.length == 0) { | ||
mutableParent.blocks.splice(quoteIndex, 0); | ||
} | ||
} | ||
}; |
Oops, something went wrong.