-
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.
add formatTextSegmentBeforeSelectionMarker
- Loading branch information
1 parent
f9514d6
commit 80f1eca
Showing
3 changed files
with
255 additions
and
55 deletions.
There are no files selected for viewing
90 changes: 35 additions & 55 deletions
90
packages/roosterjs-content-model-plugins/lib/autoFormat/hyphen/transformHyphen.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,69 +1,49 @@ | ||
import { getSelectedSegmentsAndParagraphs } from 'roosterjs-content-model-dom'; | ||
import { formatTextSegmentBeforeSelectionMarker } from '../../pluginUtils/formatTextSegmentBeforeSelectionMarker'; | ||
import { splitTextSegment } from '../../pluginUtils/splitTextSegment'; | ||
import type { IEditor } from 'roosterjs-content-model-types'; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export function transformHyphen(editor: IEditor) { | ||
editor.formatContentModel((model, context) => { | ||
const selectedAndParagraphs = getSelectedSegmentsAndParagraphs( | ||
model, | ||
false /*includingFormatHolder*/ | ||
); | ||
if (selectedAndParagraphs.length > 0 && selectedAndParagraphs[0][1]) { | ||
const marker = selectedAndParagraphs[0][0]; | ||
if (marker.segmentType == 'SelectionMarker') { | ||
const paragraph = selectedAndParagraphs[0][1]; | ||
const markerIndex = paragraph.segments.indexOf(marker); | ||
if (markerIndex > 0) { | ||
const previousSegment = paragraph.segments[markerIndex - 1]; | ||
if ( | ||
previousSegment.segmentType === 'Text' && | ||
previousSegment.text.indexOf('--') > -1 | ||
) { | ||
const segments = previousSegment.text.split(' '); | ||
const dashes = segments[segments.length - 2]; | ||
if (dashes === '--') { | ||
const textIndex = previousSegment.text.lastIndexOf('--'); | ||
const textSegment = splitTextSegment( | ||
previousSegment, | ||
paragraph, | ||
textIndex, | ||
textIndex + 2 | ||
); | ||
formatTextSegmentBeforeSelectionMarker( | ||
editor, | ||
(previousSegment, paragraph, _marker, _markerIndex, context) => { | ||
const segments = previousSegment.text.split(' '); | ||
const dashes = segments[segments.length - 2]; | ||
if (dashes === '--') { | ||
const textIndex = previousSegment.text.lastIndexOf('--'); | ||
const textSegment = splitTextSegment( | ||
previousSegment, | ||
paragraph, | ||
textIndex, | ||
textIndex + 2 | ||
); | ||
|
||
textSegment.text = textSegment.text.replace('--', '—'); | ||
context.canUndoByBackspace = true; | ||
return true; | ||
} else { | ||
const text = segments.pop(); | ||
const hasDashes = text && text?.indexOf('--') > -1; | ||
if (hasDashes && text.trim() !== '--') { | ||
const textIndex = previousSegment.text.indexOf(text); | ||
const textSegment = splitTextSegment( | ||
previousSegment, | ||
paragraph, | ||
textIndex, | ||
textIndex + text.length - 1 | ||
); | ||
textSegment.text = textSegment.text.replace('--', '—'); | ||
context.canUndoByBackspace = true; | ||
return true; | ||
} else { | ||
const text = segments.pop(); | ||
const hasDashes = text && text?.indexOf('--') > -1; | ||
if (hasDashes && text.trim() !== '--') { | ||
const textIndex = previousSegment.text.indexOf(text); | ||
const textSegment = splitTextSegment( | ||
previousSegment, | ||
paragraph, | ||
textIndex, | ||
textIndex + text.length - 1 | ||
); | ||
|
||
const textLength = textSegment.text.length; | ||
if ( | ||
textSegment.text[0] !== '-' && | ||
textSegment.text[textLength - 1] !== '-' | ||
) { | ||
textSegment.text = textSegment.text.replace('--', '—'); | ||
context.canUndoByBackspace = true; | ||
return true; | ||
} | ||
} | ||
} | ||
const textLength = textSegment.text.length; | ||
if (textSegment.text[0] !== '-' && textSegment.text[textLength - 1] !== '-') { | ||
textSegment.text = textSegment.text.replace('--', '—'); | ||
context.canUndoByBackspace = true; | ||
return true; | ||
} | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
return false; | ||
}); | ||
); | ||
} |
42 changes: 42 additions & 0 deletions
42
...roosterjs-content-model-plugins/lib/pluginUtils/formatTextSegmentBeforeSelectionMarker.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,42 @@ | ||
import { getSelectedSegmentsAndParagraphs } from 'roosterjs-content-model-dom'; | ||
import type { | ||
ContentModelParagraph, | ||
ContentModelSelectionMarker, | ||
ContentModelText, | ||
FormatContentModelContext, | ||
IEditor, | ||
} from 'roosterjs-content-model-types'; | ||
|
||
/** | ||
* @internal | ||
*/ | ||
export function formatTextSegmentBeforeSelectionMarker( | ||
editor: IEditor, | ||
callback: ( | ||
previousSegment: ContentModelText, | ||
paragraph: ContentModelParagraph, | ||
marker: ContentModelSelectionMarker, | ||
markerIndex: number, | ||
context: FormatContentModelContext | ||
) => boolean | ||
) { | ||
editor.formatContentModel((model, context) => { | ||
const selectedSegmentsAndParagraphs = getSelectedSegmentsAndParagraphs( | ||
model, | ||
false /*includeFormatHolder*/ | ||
); | ||
|
||
if (selectedSegmentsAndParagraphs.length > 0 && selectedSegmentsAndParagraphs[0][1]) { | ||
const marker = selectedSegmentsAndParagraphs[0][0]; | ||
const paragraph = selectedSegmentsAndParagraphs[0][1]; | ||
const markerIndex = paragraph.segments.indexOf(marker); | ||
if (marker.segmentType === 'SelectionMarker' && markerIndex > 0) { | ||
const previousSegment = paragraph.segments[markerIndex - 1]; | ||
if (previousSegment && previousSegment.segmentType === 'Text') { | ||
return callback(previousSegment, paragraph, marker, markerIndex, context); | ||
} | ||
} | ||
} | ||
return false; | ||
}); | ||
} |
178 changes: 178 additions & 0 deletions
178
...erjs-content-model-plugins/test/pluginUtils/formatTextSegmentBeforeSelectionMarkerTest.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,178 @@ | ||
import { formatTextSegmentBeforeSelectionMarker } from '../../lib/pluginUtils/formatTextSegmentBeforeSelectionMarker'; | ||
import { | ||
ContentModelDocument, | ||
ContentModelParagraph, | ||
ContentModelSelectionMarker, | ||
ContentModelText, | ||
FormatContentModelContext, | ||
} from 'roosterjs-content-model-types'; | ||
|
||
describe('formatTextSegmentBeforeSelectionMarker', () => { | ||
function runTest( | ||
input: ContentModelDocument, | ||
callback: ( | ||
previousSegment: ContentModelText, | ||
paragraph: ContentModelParagraph, | ||
marker: ContentModelSelectionMarker, | ||
markerIndex: number, | ||
context: FormatContentModelContext | ||
) => boolean, | ||
expectedModel: ContentModelDocument, | ||
expectedResult: boolean | ||
) { | ||
const formatWithContentModelSpy = jasmine | ||
.createSpy('formatWithContentModel') | ||
.and.callFake((callback, options) => { | ||
const result = callback(input, { | ||
newEntities: [], | ||
deletedEntities: [], | ||
newImages: [], | ||
canUndoByBackspace: true, | ||
}); | ||
expect(result).toBe(expectedResult); | ||
}); | ||
|
||
formatTextSegmentBeforeSelectionMarker( | ||
{ | ||
focus: () => {}, | ||
formatContentModel: formatWithContentModelSpy, | ||
} as any, | ||
callback | ||
); | ||
|
||
expect(formatWithContentModelSpy).toHaveBeenCalled(); | ||
expect(input).toEqual(expectedModel); | ||
} | ||
|
||
it('no selection marker', () => { | ||
const input: ContentModelDocument = { | ||
blockGroupType: 'Document', | ||
blocks: [ | ||
{ | ||
blockType: 'Paragraph', | ||
segments: [ | ||
{ | ||
segmentType: 'Text', | ||
text: 'test', | ||
format: {}, | ||
}, | ||
], | ||
format: {}, | ||
}, | ||
], | ||
}; | ||
runTest(input, () => true, input, false); | ||
}); | ||
|
||
it('no previous segment', () => { | ||
const input: ContentModelDocument = { | ||
blockGroupType: 'Document', | ||
blocks: [ | ||
{ | ||
blockType: 'Paragraph', | ||
segments: [ | ||
{ | ||
segmentType: 'SelectionMarker', | ||
isSelected: true, | ||
format: {}, | ||
}, | ||
], | ||
format: {}, | ||
}, | ||
], | ||
}; | ||
runTest(input, () => true, input, false); | ||
}); | ||
|
||
it('previous segment is not text', () => { | ||
const input: ContentModelDocument = { | ||
blockGroupType: 'Document', | ||
blocks: [ | ||
{ | ||
blockType: 'Paragraph', | ||
segments: [ | ||
{ | ||
segmentType: 'Image', | ||
src: 'test', | ||
format: {}, | ||
dataset: {}, | ||
}, | ||
{ | ||
segmentType: 'SelectionMarker', | ||
isSelected: true, | ||
format: {}, | ||
}, | ||
], | ||
format: {}, | ||
}, | ||
], | ||
}; | ||
runTest(input, () => true, input, false); | ||
}); | ||
|
||
it('format segment', () => { | ||
const input: ContentModelDocument = { | ||
blockGroupType: 'Document', | ||
blocks: [ | ||
{ | ||
blockType: 'Paragraph', | ||
segments: [ | ||
{ | ||
segmentType: 'Text', | ||
text: 'first', | ||
format: {}, | ||
}, | ||
{ | ||
segmentType: 'Text', | ||
text: 'second', | ||
format: {}, | ||
}, | ||
{ | ||
segmentType: 'SelectionMarker', | ||
isSelected: true, | ||
format: {}, | ||
}, | ||
], | ||
format: {}, | ||
}, | ||
], | ||
}; | ||
const expectedModel: ContentModelDocument = { | ||
blockGroupType: 'Document', | ||
blocks: [ | ||
{ | ||
blockType: 'Paragraph', | ||
segments: [ | ||
{ | ||
segmentType: 'Text', | ||
text: 'first', | ||
format: {}, | ||
}, | ||
{ | ||
segmentType: 'Text', | ||
text: 'second', | ||
format: { | ||
textColor: 'red', | ||
}, | ||
}, | ||
{ | ||
segmentType: 'SelectionMarker', | ||
isSelected: true, | ||
format: {}, | ||
}, | ||
], | ||
format: {}, | ||
}, | ||
], | ||
}; | ||
runTest( | ||
input, | ||
previousSegment => { | ||
previousSegment.format = { textColor: 'red' }; | ||
return true; | ||
}, | ||
expectedModel, | ||
true | ||
); | ||
}); | ||
}); |