-
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.
Let Content Model handle ENTER key (#2610)
* KeyboardEnter * fix comment * fix test * improve * Scroll caret into view when call formatContentModel * scroll caret into view * Fix build * improve * fix build * Improve * fix test * add test * add test * improve * do not scroll caret into view for now * scroll the view when press enter if necessary
- Loading branch information
1 parent
091e319
commit 48855d1
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.