From ad542f4842921731c0d0ada3bc926377025f05c0 Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Thu, 16 Jan 2025 19:33:22 -0300 Subject: [PATCH 01/23] remove margins on Autoformat plugin --- .../editorOptions/EditorOptionsPlugin.ts | 1 + .../sidePane/editorOptions/Plugins.tsx | 8 ++ .../lib/autoFormat/AutoFormatPlugin.ts | 15 ++- .../autoFormat/interface/AutoFormatOptions.ts | 5 + .../autoFormat/list/keyboardListTrigger.ts | 10 +- .../test/autoFormat/AutoFormatPluginTest.ts | 67 ++++++++++ .../list/keyboardListTriggerTest.ts | 122 +++++++++++++++++- 7 files changed, 219 insertions(+), 9 deletions(-) diff --git a/demo/scripts/controlsV2/sidePane/editorOptions/EditorOptionsPlugin.ts b/demo/scripts/controlsV2/sidePane/editorOptions/EditorOptionsPlugin.ts index 1e9085c302b..b6dd6a3b73b 100644 --- a/demo/scripts/controlsV2/sidePane/editorOptions/EditorOptionsPlugin.ts +++ b/demo/scripts/controlsV2/sidePane/editorOptions/EditorOptionsPlugin.ts @@ -47,6 +47,7 @@ const initialState: OptionState = { autoOrdinals: true, autoMailto: true, autoTel: true, + removeListMargins: false, }, markdownOptions: { bold: true, diff --git a/demo/scripts/controlsV2/sidePane/editorOptions/Plugins.tsx b/demo/scripts/controlsV2/sidePane/editorOptions/Plugins.tsx index d1ba83fee58..c9e83bc06b0 100644 --- a/demo/scripts/controlsV2/sidePane/editorOptions/Plugins.tsx +++ b/demo/scripts/controlsV2/sidePane/editorOptions/Plugins.tsx @@ -112,6 +112,7 @@ export class Plugins extends PluginsBase { private autoOrdinals = React.createRef(); private autoTel = React.createRef(); private autoMailto = React.createRef(); + private removeListMargins = React.createRef(); private markdownBold = React.createRef(); private markdownItalic = React.createRef(); private markdownStrikethrough = React.createRef(); @@ -180,6 +181,13 @@ export class Plugins extends PluginsBase { this.props.state.autoFormatOptions.autoMailto, (state, value) => (state.autoFormatOptions.autoMailto = value) )} + {this.renderCheckBox( + 'Remove List Margins', + this.removeListMargins, + this.props.state.autoFormatOptions.removeListMargins, + (state, value) => + (state.autoFormatOptions.removeListMargins = value) + )} )} {this.renderPluginItem( diff --git a/packages/roosterjs-content-model-plugins/lib/autoFormat/AutoFormatPlugin.ts b/packages/roosterjs-content-model-plugins/lib/autoFormat/AutoFormatPlugin.ts index 13c35dedff2..b47bc496fdc 100644 --- a/packages/roosterjs-content-model-plugins/lib/autoFormat/AutoFormatPlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/autoFormat/AutoFormatPlugin.ts @@ -28,6 +28,7 @@ const DefaultOptions: Partial = { autoHyphen: false, autoFraction: false, autoOrdinals: false, + removeListMargins: false, }; /** @@ -40,6 +41,7 @@ export class AutoFormatPlugin implements EditorPlugin { * @param options An optional parameter that takes in an object of type AutoFormatOptions, which includes the following properties: * - autoBullet: A boolean that enables or disables automatic bullet list formatting. Defaults to false. * - autoNumbering: A boolean that enables or disables automatic numbering formatting. Defaults to false. + * - removeListMargins: A boolean to remove list margins when it is automatically triggered. Defaults to false. * - autoHyphen: A boolean that enables or disables automatic hyphen transformation. Defaults to false. * - autoFraction: A boolean that enables or disables automatic fraction transformation. Defaults to false. * - autoOrdinals: A boolean that enables or disables automatic ordinal number transformation. Defaults to false. @@ -125,6 +127,7 @@ export class AutoFormatPlugin implements EditorPlugin { autoOrdinals, autoMailto, autoTel, + removeListMargins, } = this.options; let shouldHyphen = false; let shouldLink = false; @@ -138,7 +141,8 @@ export class AutoFormatPlugin implements EditorPlugin { paragraph, context, autoBullet, - autoNumbering + autoNumbering, + removeListMargins ); } @@ -211,7 +215,11 @@ export class AutoFormatPlugin implements EditorPlugin { formatTextSegmentBeforeSelectionMarker( editor, (model, _previousSegment, paragraph, _markerFormat, context) => { - const { autoBullet, autoNumbering } = this.options; + const { + autoBullet, + autoNumbering, + removeListMargins, + } = this.options; let shouldList = false; if (autoBullet || autoNumbering) { shouldList = keyboardListTrigger( @@ -219,7 +227,8 @@ export class AutoFormatPlugin implements EditorPlugin { paragraph, context, autoBullet, - autoNumbering + autoNumbering, + removeListMargins ); context.canUndoByBackspace = shouldList; } diff --git a/packages/roosterjs-content-model-plugins/lib/autoFormat/interface/AutoFormatOptions.ts b/packages/roosterjs-content-model-plugins/lib/autoFormat/interface/AutoFormatOptions.ts index 49b941092c2..ad814cede15 100644 --- a/packages/roosterjs-content-model-plugins/lib/autoFormat/interface/AutoFormatOptions.ts +++ b/packages/roosterjs-content-model-plugins/lib/autoFormat/interface/AutoFormatOptions.ts @@ -28,4 +28,9 @@ export interface AutoFormatOptions extends AutoLinkOptions { * Transform ordinal numbers into superscript */ autoOrdinals?: boolean; + + /** + * Remove the margins of auto triggered list + */ + removeListMargins?: boolean; } diff --git a/packages/roosterjs-content-model-plugins/lib/autoFormat/list/keyboardListTrigger.ts b/packages/roosterjs-content-model-plugins/lib/autoFormat/list/keyboardListTrigger.ts index 08686fbe122..15c9755e698 100644 --- a/packages/roosterjs-content-model-plugins/lib/autoFormat/list/keyboardListTrigger.ts +++ b/packages/roosterjs-content-model-plugins/lib/autoFormat/list/keyboardListTrigger.ts @@ -21,13 +21,14 @@ export function keyboardListTrigger( paragraph: ShallowMutableContentModelParagraph, context: FormatContentModelContext, shouldSearchForBullet: boolean = true, - shouldSearchForNumbering: boolean = true + shouldSearchForNumbering: boolean = true, + removeListMargins?: boolean ) { const listStyleType = getListTypeStyle(model, shouldSearchForBullet, shouldSearchForNumbering); if (listStyleType) { paragraph.segments.splice(0, 1); const { listType, styleType, index } = listStyleType; - triggerList(model, listType, styleType, index); + triggerList(model, listType, styleType, index, removeListMargins); context.canUndoByBackspace = true; setAnnounceData(model, context); @@ -40,9 +41,10 @@ const triggerList = ( model: ReadonlyContentModelDocument, listType: 'OL' | 'UL', styleType: number, - index?: number + index?: number, + removeListMargins?: boolean ) => { - setListType(model, listType); + setListType(model, listType, removeListMargins); const isOrderedList = listType == 'OL'; if (index && index > 0 && isOrderedList) { setModelListStartNumber(model, index); diff --git a/packages/roosterjs-content-model-plugins/test/autoFormat/AutoFormatPluginTest.ts b/packages/roosterjs-content-model-plugins/test/autoFormat/AutoFormatPluginTest.ts index 05aa2ed03a9..2c08e079405 100644 --- a/packages/roosterjs-content-model-plugins/test/autoFormat/AutoFormatPluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/autoFormat/AutoFormatPluginTest.ts @@ -378,6 +378,73 @@ describe('Content Model Auto Format Plugin Test', () => { false ); }); + + it('should trigger keyboardListTrigger with no margins', () => { + const event: EditorInputEvent = { + eventType: 'input', + rawEvent: { data: ' ', defaultPrevented: false, inputType: 'insertText' } as any, + }; + runTest( + event, + true, + { + blockGroupType: 'Document', + format: {}, + blocks: [ + { + blockType: 'BlockGroup', + blockGroupType: 'ListItem', + levels: [ + { + listType: 'UL', + format: { + startNumberOverride: 1, + direction: undefined, + textAlign: undefined, + marginBottom: '0px', + marginTop: '0px', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"unorderedStyleType":1}', + }, + }, + ], + + formatHolder: { + segmentType: 'SelectionMarker', + isSelected: false, + format: { + fontFamily: undefined, + fontSize: undefined, + textColor: undefined, + }, + }, + format: {}, + blocks: [ + { + blockType: 'Paragraph', + format: {}, + segments: [ + marker, + { + segmentType: 'Br', + format: {}, + }, + ], + }, + ], + }, + ], + }, + { + autoBullet: true, + autoNumbering: true, + removeListMargins: true, + }, + true + ); + }); }); describe('onPluginEvent - [TAB] - keyboardListTrigger', () => { diff --git a/packages/roosterjs-content-model-plugins/test/autoFormat/list/keyboardListTriggerTest.ts b/packages/roosterjs-content-model-plugins/test/autoFormat/list/keyboardListTriggerTest.ts index efcc84ef7e9..8e97d289388 100644 --- a/packages/roosterjs-content-model-plugins/test/autoFormat/list/keyboardListTriggerTest.ts +++ b/packages/roosterjs-content-model-plugins/test/autoFormat/list/keyboardListTriggerTest.ts @@ -13,14 +13,16 @@ describe('keyboardListTrigger', () => { expectedResult: boolean, shouldSearchForBullet: boolean = true, shouldSearchForNumbering: boolean = true, - expectedContext?: any + expectedContext?: any, + removeListMargins?: boolean ) { const result = keyboardListTrigger( model, paragraph, context, shouldSearchForBullet, - shouldSearchForNumbering + shouldSearchForNumbering, + removeListMargins ); expect(result).toBe(expectedResult); if (expectedContext) { @@ -542,4 +544,120 @@ describe('keyboardListTrigger', () => { } ); }); + + it('trigger a new numbering list after a numbering list no margins', () => { + const paragraph: ContentModelParagraph = { + blockType: 'Paragraph', + segments: [ + { + segmentType: 'Text', + text: 'A)', + format: {}, + }, + { + segmentType: 'SelectionMarker', + isSelected: true, + format: {}, + }, + ], + format: {}, + }; + runTest( + { + blockGroupType: 'Document', + blocks: [ + { + blockType: 'BlockGroup', + blockGroupType: 'ListItem', + blocks: [ + { + blockType: 'Paragraph', + segments: [ + { + segmentType: 'Text', + text: 'test', + format: {}, + }, + ], + format: {}, + }, + ], + levels: [ + { + listType: 'OL', + format: {}, + dataset: { + editingInfo: '{"orderedStyleType":3}', + }, + }, + ], + formatHolder: { + segmentType: 'SelectionMarker', + isSelected: false, + format: {}, + }, + format: { + listStyleType: '"1) "', + }, + }, + { + blockType: 'BlockGroup', + blockGroupType: 'ListItem', + blocks: [ + { + blockType: 'Paragraph', + segments: [ + { + segmentType: 'Text', + text: 'test', + format: {}, + }, + ], + format: {}, + }, + ], + levels: [ + { + listType: 'OL', + format: {}, + dataset: { + editingInfo: '{"orderedStyleType":3}', + }, + }, + ], + formatHolder: { + segmentType: 'SelectionMarker', + isSelected: false, + format: {}, + }, + format: { + listStyleType: '"2) "', + }, + }, + { + blockType: 'Paragraph', + segments: [ + { + segmentType: 'Br', + format: {}, + }, + ], + format: {}, + }, + paragraph, + ], + format: {}, + }, + paragraph, + { canUndoByBackspace: true } as any, + true, + undefined /* shouldSearchForBullet */, + undefined /* shouldSearchForNumbering */, + { + canUndoByBackspace: true, + announceData: { defaultStrings: 'announceListItemNumbering', formatStrings: ['A'] }, + }, + true /* removeListMargins */ + ); + }); }); From 903719192366b0545b318c05651444e287c9818f Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Tue, 21 Jan 2025 19:45:42 -0300 Subject: [PATCH 02/23] fixes --- .../lib/edit/inputSteps/handleEnterOnList.ts | 23 +- .../edit/inputSteps/handleEnterOnListTest.ts | 894 ++++++++++++++++++ 2 files changed, 903 insertions(+), 14 deletions(-) diff --git a/packages/roosterjs-content-model-plugins/lib/edit/inputSteps/handleEnterOnList.ts b/packages/roosterjs-content-model-plugins/lib/edit/inputSteps/handleEnterOnList.ts index 2cae41ef692..00660bbc1c3 100644 --- a/packages/roosterjs-content-model-plugins/lib/edit/inputSteps/handleEnterOnList.ts +++ b/packages/roosterjs-content-model-plugins/lib/edit/inputSteps/handleEnterOnList.ts @@ -67,14 +67,14 @@ export const handleEnterOnList: DeleteSelectionStep = context => { }); if (listItem.levels.length == 0) { - const index = findIndex( + const nextBlockIndex = findIndex( listParent.blocks, - nextBlock.levels.length, - listIndex + nextBlock.levels.length ); + nextBlock.levels[ nextBlock.levels.length - 1 - ].format.startNumberOverride = index; + ].format.startNumberOverride = nextBlockIndex; } } } @@ -148,24 +148,19 @@ const createNewListLevel = (listItem: ReadonlyContentModelListItem) => { }); }; -const findIndex = ( - blocks: readonly ReadonlyContentModelBlock[], - levelLength: number, - index: number -) => { +const findIndex = (blocks: readonly ReadonlyContentModelBlock[], levelLength: number) => { let counter = 1; - for (let i = index; i > -1; i--) { + for (let i = 0; i > -1; i++) { const listItem = blocks[i]; + if ( isBlockGroupOfType(listItem, 'ListItem') && listItem.levels.length === levelLength ) { counter++; } else if ( - !( - isBlockGroupOfType(listItem, 'ListItem') && - listItem.levels.length == 0 - ) + isBlockGroupOfType(listItem, 'ListItem') && + listItem.levels.length == 0 ) { return counter; } diff --git a/packages/roosterjs-content-model-plugins/test/edit/inputSteps/handleEnterOnListTest.ts b/packages/roosterjs-content-model-plugins/test/edit/inputSteps/handleEnterOnListTest.ts index 75d7b96a537..2a3b4c9bda1 100644 --- a/packages/roosterjs-content-model-plugins/test/edit/inputSteps/handleEnterOnListTest.ts +++ b/packages/roosterjs-content-model-plugins/test/edit/inputSteps/handleEnterOnListTest.ts @@ -3198,4 +3198,898 @@ describe('handleEnterOnList - keyboardEnter', () => { }; runTest(model, false, expectedModel, false, 1); }); + + it('Should maintain list chain when split the list ', () => { + const model: ContentModelDocument = { + blockGroupType: 'Document', + blocks: [ + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + startNumberOverride: 1, + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'one', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'two', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + isSelected: true, + segmentType: 'SelectionMarker', + format: {}, + }, + { + segmentType: 'Br', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'three', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'four', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + ], + format: {}, + }; + const expectedModel: ContentModelDocument = { + blockGroupType: 'Document', + blocks: [ + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + startNumberOverride: 1, + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'one', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'two', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + segments: [ + { + isSelected: true, + segmentType: 'SelectionMarker', + format: {}, + }, + { + segmentType: 'Br', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + startNumberOverride: 3, + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'three', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'four', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + ], + format: {}, + }; + runTest(model, false, expectedModel, false, 1); + }); + + it('Should maintain list chain when split the list two times', () => { + const model: ContentModelDocument = { + blockGroupType: 'Document', + blocks: [ + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + startNumberOverride: 1, + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'one', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'two', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + segments: [ + { + segmentType: 'Br', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + startNumberOverride: 3, + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'three', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'four', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'five', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + isSelected: true, + segmentType: 'SelectionMarker', + format: {}, + }, + { + segmentType: 'Br', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'six', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + ], + format: {}, + }; + const expectedModel: ContentModelDocument = { + blockGroupType: 'Document', + blocks: [ + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + startNumberOverride: 1, + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'one', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'two', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + segments: [ + { + segmentType: 'Br', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + startNumberOverride: 3, + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'three', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'four', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'five', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + { + segments: [ + { + isSelected: true, + segmentType: 'SelectionMarker', + format: {}, + }, + { + segmentType: 'Br', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + { + formatHolder: { + isSelected: false, + segmentType: 'SelectionMarker', + format: {}, + }, + levels: [ + { + listType: 'OL', + format: { + listStyleType: 'decimal', + startNumberOverride: 6, + }, + dataset: { + editingInfo: + '{"applyListStyleFromLevel":false,"orderedStyleType":1}', + }, + }, + ], + blockType: 'BlockGroup', + format: {}, + blockGroupType: 'ListItem', + blocks: [ + { + segments: [ + { + text: 'six', + segmentType: 'Text', + format: {}, + }, + ], + segmentFormat: {}, + blockType: 'Paragraph', + format: {}, + }, + ], + }, + ], + format: {}, + }; + runTest(model, false, expectedModel, false, 1); + }); }); From 5d5981af2c42f66bc62bd932064555b4a39cfdbd Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Wed, 22 Jan 2025 12:36:44 -0300 Subject: [PATCH 03/23] fix --- .../lib/edit/inputSteps/handleEnterOnList.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/roosterjs-content-model-plugins/lib/edit/inputSteps/handleEnterOnList.ts b/packages/roosterjs-content-model-plugins/lib/edit/inputSteps/handleEnterOnList.ts index 00660bbc1c3..d50d91b0616 100644 --- a/packages/roosterjs-content-model-plugins/lib/edit/inputSteps/handleEnterOnList.ts +++ b/packages/roosterjs-content-model-plugins/lib/edit/inputSteps/handleEnterOnList.ts @@ -150,7 +150,7 @@ const createNewListLevel = (listItem: ReadonlyContentModelListItem) => { const findIndex = (blocks: readonly ReadonlyContentModelBlock[], levelLength: number) => { let counter = 1; - for (let i = 0; i > -1; i++) { + for (let i = 0; i < blocks.length; i++) { const listItem = blocks[i]; if ( From 45786694edc2c5b46e9bd54502edd327b967a442 Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Tue, 28 Jan 2025 14:17:29 -0300 Subject: [PATCH 04/23] remove data-istrue --- .../lib/imageEdit/ImageEditPlugin.ts | 9 +++++ .../test/imageEdit/ImageEditPluginTest.ts | 39 +++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts b/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts index 2c14e9898dd..d4c723df34a 100644 --- a/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts @@ -55,6 +55,7 @@ const MouseRightButton = 2; const DRAG_ID = '_dragging'; const IMAGE_EDIT_CLASS = 'imageEdit'; const IMAGE_EDIT_CLASS_CARET = 'imageEditCaretColor'; +const IMAGE_EDIT_DATASET = 'data-is-editing="true"'; /** * ImageEdit plugin handles the following image editing features: @@ -177,6 +178,9 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { case 'extractContentWithDom': this.removeImageEditing(event.clonedRoot); break; + case 'beforeSetContent': + this.beforeSetContentHandler(this.editor, event.newContent); + break; } } @@ -279,6 +283,11 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { } } + private beforeSetContentHandler(editor: IEditor, newContent: string) { + newContent.replace(IMAGE_EDIT_DATASET, ''); + editor?.setEditorStyle(IMAGE_EDIT_CLASS_CARET, null); + } + /** * EXPOSED FOR TESTING PURPOSE ONLY */ diff --git a/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts b/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts index 1bc97e1075c..22e47507008 100644 --- a/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts @@ -670,6 +670,45 @@ describe('ImageEditPlugin', () => { expect(event.clonedRoot).toEqual(expectedClonedRoot); plugin.dispose(); }); + + it('beforeSetContent - should remove isEditing', () => { + const plugin = new ImageEditPlugin(); + plugin.initialize(editor); + const clonedRoot = document.createElement('div'); + const image = document.createElement('img'); + clonedRoot.appendChild(image); + image.dataset['editingInfo'] = JSON.stringify({ + src: 'test', + }); + image.dataset['isEditing'] = 'true'; + const event = { + eventType: 'beforeSetContent', + newContent: JSON.stringify(clonedRoot), + } as any; + plugin.onPluginEvent(event); + const isEditing = event.newContent.includes('data-is-editing="true"'); + expect(isEditing).toBeFalse(); + plugin.dispose(); + }); + + it('beforeSetContent - should editor caret style', () => { + const plugin = new ImageEditPlugin(); + plugin.initialize(editor); + const clonedRoot = document.createElement('div'); + const image = document.createElement('img'); + clonedRoot.appendChild(image); + image.dataset['editingInfo'] = JSON.stringify({ + src: 'test', + }); + image.dataset['isEditing'] = 'true'; + const event = { + eventType: 'beforeSetContent', + newContent: JSON.stringify(clonedRoot), + } as any; + plugin.onPluginEvent(event); + expect(editor.setEditorStyle).toHaveBeenCalledWith('imageEditCaretColor', null); + plugin.dispose(); + }); }); class TestPlugin extends ImageEditPlugin { From 685ff406cbfbda8e28d7d3f95806c82c0d15242e Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Tue, 28 Jan 2025 18:03:02 -0300 Subject: [PATCH 05/23] use content change --- .../lib/imageEdit/ImageEditPlugin.ts | 37 +++++++++---- .../test/imageEdit/ImageEditPluginTest.ts | 54 +++++++++++-------- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts b/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts index d4c723df34a..f60b99828ad 100644 --- a/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts @@ -29,6 +29,7 @@ import type { DragAndDropContext } from './types/DragAndDropContext'; import type { ImageHtmlOptions } from './types/ImageHtmlOptions'; import type { ImageEditOptions } from './types/ImageEditOptions'; import type { + ContentChangedEvent, ContentModelImage, EditorPlugin, IEditor, @@ -55,7 +56,7 @@ const MouseRightButton = 2; const DRAG_ID = '_dragging'; const IMAGE_EDIT_CLASS = 'imageEdit'; const IMAGE_EDIT_CLASS_CARET = 'imageEditCaretColor'; -const IMAGE_EDIT_DATASET = 'data-is-editing="true"'; +const IMAGE_EDIT_FORMAT_EVENT = 'ImageEditEvent'; /** * ImageEdit plugin handles the following image editing features: @@ -171,16 +172,11 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { this.keyDownHandler(this.editor, event); break; case 'contentChanged': - if (event.source == ChangeSource.Drop) { - this.onDropHandler(this.editor); - } + this.contentChangedHandler(this.editor, event); break; case 'extractContentWithDom': this.removeImageEditing(event.clonedRoot); break; - case 'beforeSetContent': - this.beforeSetContentHandler(this.editor, event.newContent); - break; } } @@ -283,9 +279,29 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { } } - private beforeSetContentHandler(editor: IEditor, newContent: string) { - newContent.replace(IMAGE_EDIT_DATASET, ''); - editor?.setEditorStyle(IMAGE_EDIT_CLASS_CARET, null); + private setContentHandler(editor: IEditor) { + const selection = editor.getDOMSelection(); + if (selection?.type == 'image' && selection.image.dataset.isEditing && !this.isEditing) { + delete selection.image.dataset.isEditing; + } + } + + private contentChangedHandler(editor: IEditor, event: ContentChangedEvent) { + switch (event.source) { + case ChangeSource.SetContent: + this.setContentHandler(editor); + break; + case ChangeSource.Format: + if (this.isEditing && event.formatApiName !== IMAGE_EDIT_FORMAT_EVENT) { + this.cleanInfo(); + this.isEditing = false; + this.isCropMode = false; + } + break; + case ChangeSource.Drop: + this.onDropHandler(editor); + break; + } } /** @@ -401,6 +417,7 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { } } }, + apiName: IMAGE_EDIT_FORMAT_EVENT, }, { tryGetFromCache: true, diff --git a/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts b/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts index 22e47507008..53a51cd097c 100644 --- a/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/imageEdit/ImageEditPluginTest.ts @@ -10,9 +10,11 @@ import { ContentModelDocument, ContentModelFormatter, DOMEventRecord, + DOMSelection, EditorEnvironment, FormatContentModelOptions, IEditor, + ImageSelection, } from 'roosterjs-content-model-types'; describe('ImageEditPlugin', () => { @@ -671,44 +673,54 @@ describe('ImageEditPlugin', () => { plugin.dispose(); }); - it('beforeSetContent - should remove isEditing', () => { + it('contentChanged - should remove isEditing', () => { const plugin = new ImageEditPlugin(); + const editor = initEditor('image_edit', [plugin], model); plugin.initialize(editor); - const clonedRoot = document.createElement('div'); const image = document.createElement('img'); - clonedRoot.appendChild(image); - image.dataset['editingInfo'] = JSON.stringify({ - src: 'test', - }); image.dataset['isEditing'] = 'true'; + const selection = { + type: 'image', + image, + } as DOMSelection; + spyOn(editor, 'getDOMSelection').and.returnValue(selection); const event = { - eventType: 'beforeSetContent', - newContent: JSON.stringify(clonedRoot), + eventType: 'contentChanged', + source: ChangeSource.SetContent, } as any; plugin.onPluginEvent(event); - const isEditing = event.newContent.includes('data-is-editing="true"'); - expect(isEditing).toBeFalse(); + const newSelection = editor.getDOMSelection() as ImageSelection; + expect(newSelection!.type).toBe('image'); + expect(newSelection!.image.dataset.isEditing).toBeUndefined(); plugin.dispose(); }); - it('beforeSetContent - should editor caret style', () => { - const plugin = new ImageEditPlugin(); + it('contentChanged - should remove editor caret style', () => { + const plugin = new TestPlugin(); plugin.initialize(editor); - const clonedRoot = document.createElement('div'); - const image = document.createElement('img'); - clonedRoot.appendChild(image); - image.dataset['editingInfo'] = JSON.stringify({ - src: 'test', - }); - image.dataset['isEditing'] = 'true'; + plugin.setIsEditing(true); const event = { - eventType: 'beforeSetContent', - newContent: JSON.stringify(clonedRoot), + eventType: 'contentChanged', + source: ChangeSource.Format, } as any; plugin.onPluginEvent(event); expect(editor.setEditorStyle).toHaveBeenCalledWith('imageEditCaretColor', null); plugin.dispose(); }); + + it('contentChanged - should not remove editor caret style', () => { + const plugin = new TestPlugin(); + plugin.initialize(editor); + plugin.setIsEditing(true); + const event = { + eventType: 'contentChanged', + source: ChangeSource.Format, + formatApiName: 'ImageEditEvent', + } as any; + plugin.onPluginEvent(event); + expect(editor.setEditorStyle).not.toHaveBeenCalled(); + plugin.dispose(); + }); }); class TestPlugin extends ImageEditPlugin { From 27c11ffc2702b5d2049072c4a2e667922fe09fbd Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Tue, 28 Jan 2025 18:34:43 -0300 Subject: [PATCH 06/23] refactor --- .../lib/imageEdit/ImageEditPlugin.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts b/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts index f60b99828ad..849a9b69e9a 100644 --- a/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/imageEdit/ImageEditPlugin.ts @@ -286,17 +286,21 @@ export class ImageEditPlugin implements ImageEditor, EditorPlugin { } } + private formatEventHandler(event: ContentChangedEvent) { + if (this.isEditing && event.formatApiName !== IMAGE_EDIT_FORMAT_EVENT) { + this.cleanInfo(); + this.isEditing = false; + this.isCropMode = false; + } + } + private contentChangedHandler(editor: IEditor, event: ContentChangedEvent) { switch (event.source) { case ChangeSource.SetContent: this.setContentHandler(editor); break; case ChangeSource.Format: - if (this.isEditing && event.formatApiName !== IMAGE_EDIT_FORMAT_EVENT) { - this.cleanInfo(); - this.isEditing = false; - this.isCropMode = false; - } + this.formatEventHandler(event); break; case ChangeSource.Drop: this.onDropHandler(editor); From 5f67c4984ccc15981046ea6b362ae3b4364c6d85 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Fri, 31 Jan 2025 10:25:14 -0800 Subject: [PATCH 07/23] Allow skipping marking hasNewContent in `formatContentModel` (#2933) --- .../formatContentModel/formatContentModel.ts | 9 +- .../formatContentModelTest.ts | 155 +++++++++++++++++- .../parameter/FormatContentModelContext.ts | 7 +- 3 files changed, 164 insertions(+), 7 deletions(-) diff --git a/packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts b/packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts index 480a7ad3a28..835f5084082 100644 --- a/packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts +++ b/packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts @@ -47,7 +47,10 @@ export const formatContentModel: FormatContentModel = ( if (changed) { const isNested = core.undo.isNested; - const shouldAddSnapshot = !skipUndoSnapshot && !isNested; + const shouldAddSnapshot = + (!skipUndoSnapshot || skipUndoSnapshot == 'DoNotSkip') && !isNested; + const shouldMarkNewContent = + (skipUndoSnapshot === true || skipUndoSnapshot == 'MarkNewContent') && !isNested; let selection: DOMSelection | undefined; if (shouldAddSnapshot) { @@ -94,7 +97,9 @@ export const formatContentModel: FormatContentModel = ( if (shouldAddSnapshot) { core.api.addUndoSnapshot(core, false /*canUndoByBackspace*/, entityStates); - } else { + } + + if (shouldMarkNewContent) { core.undo.snapshotsManager.hasNewContent = true; } } finally { diff --git a/packages/roosterjs-content-model-core/test/coreApi/formatContentModel/formatContentModelTest.ts b/packages/roosterjs-content-model-core/test/coreApi/formatContentModel/formatContentModelTest.ts index 6583eff98a6..82de8c4fa4e 100644 --- a/packages/roosterjs-content-model-core/test/coreApi/formatContentModel/formatContentModelTest.ts +++ b/packages/roosterjs-content-model-core/test/coreApi/formatContentModel/formatContentModelTest.ts @@ -123,7 +123,7 @@ describe('formatContentModel', () => { expect(announce).not.toHaveBeenCalled(); }); - it('Skip undo snapshot', () => { + it('Skip undo snapshot = true', () => { const callback = jasmine.createSpy('callback').and.callFake((model, context) => { context.skipUndoSnapshot = true; return true; @@ -157,6 +157,155 @@ describe('formatContentModel', () => { true ); expect(announce).not.toHaveBeenCalled(); + expect(core.undo.snapshotsManager.hasNewContent).toBeTrue(); + }); + + it('Skip undo snapshot = false', () => { + const callback = jasmine.createSpy('callback').and.callFake((model, context) => { + context.skipUndoSnapshot = false; + return true; + }); + + formatContentModel(core, callback, { apiName }); + + expect(callback).toHaveBeenCalledWith(mockedModel, { + newEntities: [], + deletedEntities: [], + rawEvent: undefined, + skipUndoSnapshot: false, + newImages: [], + }); + expect(createContentModel).toHaveBeenCalledTimes(1); + expect(addUndoSnapshot).toHaveBeenCalledTimes(2); + expect(setContentModel).toHaveBeenCalledTimes(1); + expect(setContentModel).toHaveBeenCalledWith(core, mockedModel, undefined, undefined); + expect(triggerEvent).toHaveBeenCalledTimes(1); + expect(triggerEvent).toHaveBeenCalledWith( + core, + { + eventType: 'contentChanged', + contentModel: mockedModel, + selection: mockedSelection, + source: ChangeSource.Format, + data: undefined, + formatApiName: apiName, + changedEntities: [], + }, + true + ); + expect(announce).not.toHaveBeenCalled(); + expect(core.undo.snapshotsManager.hasNewContent).toBeFalsy(); + }); + + it('Skip undo snapshot = DoNotSkip', () => { + const callback = jasmine.createSpy('callback').and.callFake((model, context) => { + context.skipUndoSnapshot = 'DoNotSkip'; + return true; + }); + + formatContentModel(core, callback, { apiName }); + + expect(callback).toHaveBeenCalledWith(mockedModel, { + newEntities: [], + deletedEntities: [], + rawEvent: undefined, + skipUndoSnapshot: 'DoNotSkip', + newImages: [], + }); + expect(createContentModel).toHaveBeenCalledTimes(1); + expect(addUndoSnapshot).toHaveBeenCalledTimes(2); + expect(setContentModel).toHaveBeenCalledTimes(1); + expect(setContentModel).toHaveBeenCalledWith(core, mockedModel, undefined, undefined); + expect(triggerEvent).toHaveBeenCalledTimes(1); + expect(triggerEvent).toHaveBeenCalledWith( + core, + { + eventType: 'contentChanged', + contentModel: mockedModel, + selection: mockedSelection, + source: ChangeSource.Format, + data: undefined, + formatApiName: apiName, + changedEntities: [], + }, + true + ); + expect(announce).not.toHaveBeenCalled(); + expect(core.undo.snapshotsManager.hasNewContent).toBeFalsy(); + }); + + it('Skip undo snapshot = MarkNewContent', () => { + const callback = jasmine.createSpy('callback').and.callFake((model, context) => { + context.skipUndoSnapshot = 'MarkNewContent'; + return true; + }); + + formatContentModel(core, callback, { apiName }); + + expect(callback).toHaveBeenCalledWith(mockedModel, { + newEntities: [], + deletedEntities: [], + rawEvent: undefined, + skipUndoSnapshot: 'MarkNewContent', + newImages: [], + }); + expect(createContentModel).toHaveBeenCalledTimes(1); + expect(addUndoSnapshot).toHaveBeenCalledTimes(0); + expect(setContentModel).toHaveBeenCalledTimes(1); + expect(setContentModel).toHaveBeenCalledWith(core, mockedModel, undefined, undefined); + expect(triggerEvent).toHaveBeenCalledTimes(1); + expect(triggerEvent).toHaveBeenCalledWith( + core, + { + eventType: 'contentChanged', + contentModel: mockedModel, + selection: mockedSelection, + source: ChangeSource.Format, + data: undefined, + formatApiName: apiName, + changedEntities: [], + }, + true + ); + expect(announce).not.toHaveBeenCalled(); + expect(core.undo.snapshotsManager.hasNewContent).toBeTruthy(); + }); + + it('Skip undo snapshot = SkipAll', () => { + const callback = jasmine.createSpy('callback').and.callFake((model, context) => { + context.skipUndoSnapshot = 'SkipAll'; + return true; + }); + + formatContentModel(core, callback, { apiName }); + + expect(callback).toHaveBeenCalledWith(mockedModel, { + newEntities: [], + deletedEntities: [], + rawEvent: undefined, + skipUndoSnapshot: 'SkipAll', + newImages: [], + }); + expect(createContentModel).toHaveBeenCalledTimes(1); + expect(addUndoSnapshot).toHaveBeenCalledTimes(0); + expect(setContentModel).toHaveBeenCalledTimes(1); + expect(setContentModel).toHaveBeenCalledWith(core, mockedModel, undefined, undefined); + expect(triggerEvent).toHaveBeenCalledTimes(1); + expect(triggerEvent).toHaveBeenCalledWith( + core, + { + eventType: 'contentChanged', + contentModel: mockedModel, + selection: mockedSelection, + source: ChangeSource.Format, + data: undefined, + formatApiName: apiName, + changedEntities: [], + }, + true + ); + expect(announce).not.toHaveBeenCalled(); + expect(core.undo.snapshotsManager.hasNewContent).toBeFalsy(); }); it('Customize change source', () => { @@ -1028,9 +1177,7 @@ describe('formatContentModel', () => { expect(setContentModel).toHaveBeenCalledWith(core, mockedModel, undefined, undefined); expect(core.undo).toEqual({ isNested: true, - snapshotsManager: { - hasNewContent: true, - }, + snapshotsManager: {}, } as any); }); }); diff --git a/packages/roosterjs-content-model-types/lib/parameter/FormatContentModelContext.ts b/packages/roosterjs-content-model-types/lib/parameter/FormatContentModelContext.ts index 7ee2b7f1894..eb27499b737 100644 --- a/packages/roosterjs-content-model-types/lib/parameter/FormatContentModelContext.ts +++ b/packages/roosterjs-content-model-types/lib/parameter/FormatContentModelContext.ts @@ -71,8 +71,13 @@ export interface FormatContentModelContext { * @optional * When pass true, skip adding undo snapshot when write Content Model back to DOM. * Need to be set by the formatter function + * Default value is false, which means add undo snapshot + * When set to true, it will skip adding undo snapshot but mark "hasNewContent" so that next undo snapshot will be added, this is same with "MarkNewContent" + * When set to 'DoNotSkip', it will add undo snapshot (default behavior) + * When set to 'MarkNewContent', it will skip adding undo snapshot but mark "hasNewContent" so that next undo snapshot will be added + * When set to 'SkipAll', it will skip adding undo snapshot and not mark "hasNewContent", as if no change is made */ - skipUndoSnapshot?: boolean; + skipUndoSnapshot?: boolean | 'DoNotSkip' | 'MarkNewContent' | 'SkipAll'; /** * @optional From f46f90ec2e5b1007bbeb2fd95e200afe55240c4a Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Wed, 5 Feb 2025 11:07:42 -0800 Subject: [PATCH 08/23] Fix bridge plugin to be able to handle new event (#2935) * Fix bridge plugin to be able to handle new event * add test --- .../lib/corePlugins/BridgePlugin.ts | 36 +++---- .../test/corePlugins/BridgePluginTest.ts | 96 +++++++++++++++++++ 2 files changed, 114 insertions(+), 18 deletions(-) diff --git a/packages/roosterjs-editor-adapter/lib/corePlugins/BridgePlugin.ts b/packages/roosterjs-editor-adapter/lib/corePlugins/BridgePlugin.ts index 5ddc1f36732..43804e5f667 100644 --- a/packages/roosterjs-editor-adapter/lib/corePlugins/BridgePlugin.ts +++ b/packages/roosterjs-editor-adapter/lib/corePlugins/BridgePlugin.ts @@ -126,15 +126,15 @@ export class BridgePlugin implements ContextMenuProvider { onPluginEvent(event: PluginEvent) { const oldEvent = this.cacheGetOldEvent(event); - if (oldEvent) { - const exclusivelyHandleEventPlugin = this.cacheGetExclusivelyHandlePlugin(event); + const exclusivelyHandleEventPlugin = this.cacheGetExclusivelyHandlePlugin(event); - if (exclusivelyHandleEventPlugin) { - this.handleEvent(exclusivelyHandleEventPlugin, oldEvent, event); - } else { - this.legacyPlugins.forEach(plugin => this.handleEvent(plugin, oldEvent, event)); - } + if (exclusivelyHandleEventPlugin) { + this.handleEvent(exclusivelyHandleEventPlugin, oldEvent, event); + } else { + this.legacyPlugins.forEach(plugin => this.handleEvent(plugin, oldEvent, event)); + } + if (oldEvent) { Object.assign(event, oldEventToNewEvent(oldEvent, event)); } } @@ -165,17 +165,15 @@ export class BridgePlugin implements ContextMenuProvider { return cacheGetEventData(event, ExclusivelyHandleEventPluginKey, event => { const oldEvent = this.cacheGetOldEvent(event); - if (oldEvent) { - for (let i = 0; i < this.legacyPlugins.length; i++) { - const plugin = this.legacyPlugins[i]; + for (let i = 0; i < this.legacyPlugins.length; i++) { + const plugin = this.legacyPlugins[i]; - if (plugin.willHandleEventExclusively?.(oldEvent)) { - return plugin; - } + if (oldEvent && plugin.willHandleEventExclusively?.(oldEvent)) { + return plugin; + } - if (isMixedPlugin(plugin) && plugin.willHandleEventExclusivelyV9?.(event)) { - return plugin; - } + if (isMixedPlugin(plugin) && plugin.willHandleEventExclusivelyV9?.(event)) { + return plugin; } } @@ -200,10 +198,12 @@ export class BridgePlugin implements ContextMenuProvider { private handleEvent( plugin: LegacyEditorPlugin, - oldEvent: LegacyPluginEvent, + oldEvent: LegacyPluginEvent | undefined, newEvent: PluginEvent ) { - plugin.onPluginEvent?.(oldEvent); + if (oldEvent && plugin.onPluginEvent) { + plugin.onPluginEvent(oldEvent); + } if (isMixedPlugin(plugin)) { plugin.onPluginEventV9?.(newEvent); diff --git a/packages/roosterjs-editor-adapter/test/corePlugins/BridgePluginTest.ts b/packages/roosterjs-editor-adapter/test/corePlugins/BridgePluginTest.ts index a0db7f68882..be44572a2cb 100644 --- a/packages/roosterjs-editor-adapter/test/corePlugins/BridgePluginTest.ts +++ b/packages/roosterjs-editor-adapter/test/corePlugins/BridgePluginTest.ts @@ -608,4 +608,100 @@ describe('BridgePlugin', () => { expect(disposeSpy).toHaveBeenCalledTimes(1); }); + + it('MixedPlugin with event that is not supported in v8', () => { + const initializeV8Spy = jasmine.createSpy('initializeV8'); + const initializeV9Spy = jasmine.createSpy('initializeV9'); + const onPluginEventV8Spy = jasmine.createSpy('onPluginEventV8'); + const onPluginEventV9Spy = jasmine.createSpy('onPluginEventV9'); + const willHandleEventExclusivelyV8Spy = jasmine.createSpy('willHandleEventExclusivelyV8'); + const willHandleEventExclusivelyV9Spy = jasmine.createSpy('willHandleEventExclusivelyV9'); + const disposeSpy = jasmine.createSpy('dispose'); + + const mockedPlugin = { + initialize: initializeV8Spy, + initializeV9: initializeV9Spy, + onPluginEvent: onPluginEventV8Spy, + onPluginEventV9: onPluginEventV9Spy, + willHandleEventExclusively: willHandleEventExclusivelyV8Spy, + willHandleEventExclusivelyV9: willHandleEventExclusivelyV9Spy, + dispose: disposeSpy, + getName: () => '', + } as any; + const mockedEditor = {} as any; + const onInitializeSpy = jasmine.createSpy('onInitialize').and.returnValue(mockedEditor); + const plugin = new BridgePlugin.BridgePlugin(onInitializeSpy, [mockedPlugin]); + + expect(initializeV8Spy).not.toHaveBeenCalled(); + expect(initializeV9Spy).not.toHaveBeenCalled(); + expect(onPluginEventV8Spy).not.toHaveBeenCalled(); + expect(onPluginEventV9Spy).not.toHaveBeenCalled(); + expect(disposeSpy).not.toHaveBeenCalled(); + expect(onInitializeSpy).not.toHaveBeenCalled(); + expect(willHandleEventExclusivelyV8Spy).not.toHaveBeenCalled(); + expect(willHandleEventExclusivelyV9Spy).not.toHaveBeenCalled(); + + const mockedZoomScale = 'ZOOM' as any; + const calculateZoomScaleSpy = jasmine + .createSpy('calculateZoomScale') + .and.returnValue(mockedZoomScale); + const mockedColorManager = 'COLOR' as any; + const mockedInnerDarkColorHandler = 'INNERCOLOR' as any; + const mockedInnerEditor = { + getDOMHelper: () => ({ + calculateZoomScale: calculateZoomScaleSpy, + }), + getColorManager: () => mockedInnerDarkColorHandler, + } as any; + + const createDarkColorHandlerSpy = spyOn( + DarkColorHandler, + 'createDarkColorHandler' + ).and.returnValue(mockedColorManager); + + plugin.initialize(mockedInnerEditor); + + expect(onInitializeSpy).toHaveBeenCalledWith({ + customData: {}, + experimentalFeatures: [], + sizeTransformer: jasmine.anything(), + darkColorHandler: mockedColorManager, + edit: 'edit', + contextMenuProviders: [], + } as any); + expect(createDarkColorHandlerSpy).toHaveBeenCalledWith(mockedInnerDarkColorHandler); + expect(initializeV8Spy).toHaveBeenCalledTimes(1); + expect(initializeV9Spy).toHaveBeenCalledTimes(1); + expect(disposeSpy).not.toHaveBeenCalled(); + expect(initializeV8Spy).toHaveBeenCalledWith(mockedEditor); + expect(initializeV9Spy).toHaveBeenCalledWith(mockedInnerEditor); + expect(onPluginEventV8Spy).not.toHaveBeenCalled(); + expect(onPluginEventV9Spy).not.toHaveBeenCalled(); + expect(willHandleEventExclusivelyV8Spy).not.toHaveBeenCalled(); + expect(willHandleEventExclusivelyV9Spy).not.toHaveBeenCalled(); + + plugin.onPluginEvent({ + eventType: 'rewriteFromModel', + addedBlockElements: [], + removedBlockElements: [], + }); + + expect(onPluginEventV8Spy).toHaveBeenCalledTimes(0); + expect(onPluginEventV9Spy).toHaveBeenCalledTimes(1); + expect(onPluginEventV9Spy).toHaveBeenCalledWith({ + eventType: 'rewriteFromModel', + addedBlockElements: [], + removedBlockElements: [], + eventDataCache: { + __OldEventFromNewEvent: undefined, + __ExclusivelyHandleEventPlugin: null, + }, + }); + expect(willHandleEventExclusivelyV8Spy).toHaveBeenCalledTimes(0); + expect(willHandleEventExclusivelyV9Spy).toHaveBeenCalledTimes(1); + + plugin.dispose(); + + expect(disposeSpy).toHaveBeenCalledTimes(1); + }); }); From 4b838c402a058538dbc07fec74af54040f24a23b Mon Sep 17 00:00:00 2001 From: "Julia Roldi (from Dev Box)" Date: Thu, 6 Feb 2025 14:59:00 -0300 Subject: [PATCH 09/23] clean LegacySelectionPlugin --- .../editorOptions/ExperimentalFeatures.tsx | 1 - .../corePlugin/selection/SelectionPlugin.ts | 101 +++++------------- .../lib/editor/ExperimentalFeature.ts | 1 + 3 files changed, 26 insertions(+), 77 deletions(-) diff --git a/demo/scripts/controlsV2/sidePane/editorOptions/ExperimentalFeatures.tsx b/demo/scripts/controlsV2/sidePane/editorOptions/ExperimentalFeatures.tsx index 2bd26858972..3a3fcc563ec 100644 --- a/demo/scripts/controlsV2/sidePane/editorOptions/ExperimentalFeatures.tsx +++ b/demo/scripts/controlsV2/sidePane/editorOptions/ExperimentalFeatures.tsx @@ -13,7 +13,6 @@ export class ExperimentalFeatures extends React.Component {this.renderFeature('PersistCache')} {this.renderFeature('HandleEnterKey')} - {this.renderFeature('LegacyImageSelection')} ); } diff --git a/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts b/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts index 420514d1466..57ea5a632d9 100644 --- a/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts +++ b/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts @@ -21,11 +21,9 @@ import type { ParsedTable, TableSelectionInfo, TableCellCoordinate, - MouseUpEvent, } from 'roosterjs-content-model-types'; const MouseLeftButton = 0; -const MouseMiddleButton = 1; const MouseRightButton = 2; const Up = 'ArrowUp'; const Down = 'ArrowDown'; @@ -141,7 +139,7 @@ class SelectionPlugin implements PluginWithState { break; case 'mouseUp': - this.onMouseUp(this.editor, event); + this.onMouseUp(); break; case 'keyDown': @@ -165,46 +163,30 @@ class SelectionPlugin implements PluginWithState { let image: HTMLImageElement | null; // Image selection - if (editor.isExperimentalFeatureEnabled('LegacyImageSelection')) { - if ( - rawEvent.button === MouseRightButton && - (image = - this.getClickingImage(rawEvent) ?? - this.getContainedTargetImage(rawEvent, selection)) && - image.isContentEditable - ) { - this.selectImageWithRange(image, rawEvent); - return; - } else if (selection?.type == 'image' && selection.image !== rawEvent.target) { - this.selectBeforeOrAfterElement(editor, selection.image); - return; - } - } else { - if ( - selection?.type == 'image' && - (rawEvent.button == MouseLeftButton || - (rawEvent.button == MouseRightButton && - !this.getClickingImage(rawEvent) && - !this.getContainedTargetImage(rawEvent, selection))) - ) { - this.setDOMSelection(null /*domSelection*/, null /*tableSelection*/); - } + if ( + selection?.type == 'image' && + (rawEvent.button == MouseLeftButton || + (rawEvent.button == MouseRightButton && + !this.getClickingImage(rawEvent) && + !this.getContainedTargetImage(rawEvent, selection))) + ) { + this.setDOMSelection(null /*domSelection*/, null /*tableSelection*/); + } - if ( - (image = - this.getClickingImage(rawEvent) ?? - this.getContainedTargetImage(rawEvent, selection)) && - image.isContentEditable - ) { - this.setDOMSelection( - { - type: 'image', - image: image, - }, - null - ); - return; - } + if ( + (image = + this.getClickingImage(rawEvent) ?? + this.getContainedTargetImage(rawEvent, selection)) && + image.isContentEditable + ) { + this.setDOMSelection( + { + type: 'image', + image: image, + }, + null + ); + return; } // Table selection @@ -245,25 +227,6 @@ class SelectionPlugin implements PluginWithState { } } - private selectImageWithRange(image: HTMLImageElement, event: Event) { - const range = image.ownerDocument.createRange(); - range.selectNode(image); - - const domSelection = this.editor?.getDOMSelection(); - if (domSelection?.type == 'image' && image == domSelection.image) { - event.preventDefault(); - } else { - this.setDOMSelection( - { - type: 'range', - isReverted: false, - range, - }, - null - ); - } - } - private onMouseMove = (event: Event) => { if (this.editor && this.state.tableSelection) { const hasTableSelection = !!this.state.tableSelection.lastCo; @@ -324,21 +287,7 @@ class SelectionPlugin implements PluginWithState { } }; - private onMouseUp(editor: IEditor, event: MouseUpEvent) { - let image: HTMLImageElement | null; - - if ( - editor.isExperimentalFeatureEnabled('LegacyImageSelection') && - (image = this.getClickingImage(event.rawEvent)) && - image.isContentEditable && - event.rawEvent.button != MouseMiddleButton && - (event.rawEvent.button == - MouseRightButton /* it's not possible to drag using right click */ || - event.isClicking) - ) { - this.selectImageWithRange(image, event.rawEvent); - } - + private onMouseUp() { this.detachMouseEvent(); } diff --git a/packages/roosterjs-content-model-types/lib/editor/ExperimentalFeature.ts b/packages/roosterjs-content-model-types/lib/editor/ExperimentalFeature.ts index 46e58d639df..80ec87a960a 100644 --- a/packages/roosterjs-content-model-types/lib/editor/ExperimentalFeature.ts +++ b/packages/roosterjs-content-model-types/lib/editor/ExperimentalFeature.ts @@ -10,6 +10,7 @@ export type ExperimentalFeature = */ | 'PersistCache' /** + * @deprecated * Workaround for the Legacy Image Edit */ | 'LegacyImageSelection' From aab0fdaabd1698a032fad77ef9c534e1bf6b5f22 Mon Sep 17 00:00:00 2001 From: Julia Roldi <87443959+juliaroldi@users.noreply.github.com> Date: Thu, 6 Feb 2025 16:41:18 -0300 Subject: [PATCH 10/23] Send anchor event in Auto Link (#2934) After a text is automatically transformed into a link by typing and pressing space, send the anchor element when the ContentChanged event is triggered. Also add more unit tests. --- .../formatContentModel/formatContentModel.ts | 11 +- .../lib/autoFormat/AutoFormatPlugin.ts | 19 ++- .../test/autoFormat/AutoFormatPluginTest.ts | 144 +++++++++++++++++- 3 files changed, 156 insertions(+), 18 deletions(-) diff --git a/packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts b/packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts index 835f5084082..ccb9bc4a52e 100644 --- a/packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts +++ b/packages/roosterjs-content-model-core/lib/coreApi/formatContentModel/formatContentModel.ts @@ -25,13 +25,8 @@ export const formatContentModel: FormatContentModel = ( options, domToModelOptions ) => { - const { - onNodeCreated, - getChangeData, - rawEvent, - selectionOverride, - scrollCaretIntoView: scroll, - } = options || {}; + const { onNodeCreated, rawEvent, selectionOverride, scrollCaretIntoView: scroll } = + options || {}; const model = core.api.createContentModel(core, domToModelOptions, selectionOverride); const context: FormatContentModelContext = { newEntities: [], @@ -81,7 +76,7 @@ export const formatContentModel: FormatContentModel = ( contentModel: clearModelCache ? undefined : model, selection: clearModelCache ? undefined : selection, source: options?.changeSource || ChangeSource.Format, - data: getChangeData?.(), + data: options?.getChangeData?.(), formatApiName: options?.apiName, changedEntities: getChangedEntities(context, rawEvent), }; diff --git a/packages/roosterjs-content-model-plugins/lib/autoFormat/AutoFormatPlugin.ts b/packages/roosterjs-content-model-plugins/lib/autoFormat/AutoFormatPlugin.ts index b47bc496fdc..da594c5812d 100644 --- a/packages/roosterjs-content-model-plugins/lib/autoFormat/AutoFormatPlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/autoFormat/AutoFormatPlugin.ts @@ -114,6 +114,7 @@ export class AutoFormatPlugin implements EditorPlugin { const formatOptions: FormatContentModelOptions = { changeSource: '', apiName: '', + getChangeData: undefined, }; formatTextSegmentBeforeSelectionMarker( editor, @@ -147,13 +148,20 @@ export class AutoFormatPlugin implements EditorPlugin { } if (autoLink || autoTel || autoMailto) { - shouldLink = !!promoteLink(previousSegment, paragraph, { + const linkSegment = promoteLink(previousSegment, paragraph, { autoLink, autoTel, autoMailto, }); - if (shouldLink) { + if (linkSegment) { + const anchor = createAnchor( + linkSegment.link?.format.href || '', + linkSegment.text + ); + formatOptions.getChangeData = () => anchor; + + shouldLink = true; context.canUndoByBackspace = true; } } @@ -270,3 +278,10 @@ const getChangeSource = (shouldList: boolean, shouldHyphen: boolean, shouldLink: ? ChangeSource.AutoLink : ''; }; + +const createAnchor = (url: string, text: string) => { + const anchor = document.createElement('a'); + anchor.href = url; + anchor.textContent = text; + return anchor; +}; diff --git a/packages/roosterjs-content-model-plugins/test/autoFormat/AutoFormatPluginTest.ts b/packages/roosterjs-content-model-plugins/test/autoFormat/AutoFormatPluginTest.ts index 2c08e079405..ca071dba5b8 100644 --- a/packages/roosterjs-content-model-plugins/test/autoFormat/AutoFormatPluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/autoFormat/AutoFormatPluginTest.ts @@ -3,6 +3,7 @@ import * as formatTextSegmentBeforeSelectionMarker from 'roosterjs-content-model import * as unlink from '../../lib/autoFormat/link/unlink'; import { AutoFormatOptions } from '../../lib/autoFormat/interface/AutoFormatOptions'; import { AutoFormatPlugin } from '../../lib/autoFormat/AutoFormatPlugin'; +import { ChangeSource } from 'roosterjs-content-model-dom'; import { ContentChangedEvent, ContentModelDocument, @@ -10,6 +11,7 @@ import { ContentModelSelectionMarker, ContentModelText, EditorInputEvent, + FormatContentModelOptions, IEditor, KeyDownEvent, } from 'roosterjs-content-model-types'; @@ -966,16 +968,18 @@ describe('Content Model Auto Format Plugin Test', () => { describe('onPluginEvent - promoteLink', () => { function runTest( event: EditorInputEvent, + text: string, expectResult: ContentModelParagraph, options: AutoFormatOptions, - shouldCallFormat: boolean + shouldCallFormat: boolean, + expectedOptions: FormatContentModelOptions ) { const plugin = new AutoFormatPlugin(options as AutoFormatOptions); plugin.initialize(editor); const segment: ContentModelText = { segmentType: 'Text', - text: 'www.test.com', + text, format: {}, }; const paragraph: ContentModelParagraph = { @@ -984,7 +988,7 @@ describe('Content Model Auto Format Plugin Test', () => { format: {}, }; - formatTextSegmentBeforeSelectionMarkerSpy.and.callFake((editor, callback) => { + formatTextSegmentBeforeSelectionMarkerSpy.and.callFake((editor, callback, options) => { callback( null!, segment, @@ -993,6 +997,12 @@ describe('Content Model Auto Format Plugin Test', () => { { deletedEntities: [], newEntities: [], newImages: [] } ); + expect(options?.changeSource).toEqual(expectedOptions.changeSource); + expect(options?.apiName).toEqual(expectedOptions.apiName); + expect(JSON.stringify(options?.getChangeData?.())).toEqual( + JSON.stringify(expectedOptions.getChangeData?.()) + ); + return true; }); @@ -1009,8 +1019,12 @@ describe('Content Model Auto Format Plugin Test', () => { eventType: 'input', rawEvent: { data: ' ', preventDefault: () => {}, inputType: 'insertText' } as any, }; + const link = document.createElement('a'); + link.href = 'www.test.com'; + link.textContent = 'www.test.com'; runTest( event, + 'www.test.com', { blockType: 'Paragraph', format: {}, @@ -1033,7 +1047,12 @@ describe('Content Model Auto Format Plugin Test', () => { { autoLink: true, }, - true + true, + { + changeSource: ChangeSource.AutoLink, + apiName: '', + getChangeData: () => link, + } ); }); @@ -1042,8 +1061,10 @@ describe('Content Model Auto Format Plugin Test', () => { eventType: 'input', rawEvent: { data: ' ', preventDefault: () => {}, inputType: 'insertText' } as any, }; + runTest( event, + 'www.test.com', { blockType: 'Paragraph', format: {}, @@ -1058,7 +1079,54 @@ describe('Content Model Auto Format Plugin Test', () => { { autoTel: true, }, - true + true, + { + changeSource: '', + apiName: '', + getChangeData: undefined, + } + ); + }); + + it('should call promoteLink with telephone | autoTel', () => { + const event: EditorInputEvent = { + eventType: 'input', + rawEvent: { data: ' ', preventDefault: () => {}, inputType: 'insertText' } as any, + }; + const link = document.createElement('a'); + link.href = 'tel:9999999'; + link.textContent = 'tel:9999999'; + runTest( + event, + 'tel:9999999', + { + blockType: 'Paragraph', + format: {}, + segments: [ + { + segmentType: 'Text', + text: 'tel:9999999', + format: {}, + isSelected: undefined, + link: { + dataset: {}, + format: { + href: 'tel:9999999', + underline: true, + }, + }, + }, + ], + }, + { + autoTel: true, + }, + true, + { + changeSource: ChangeSource.AutoLink, + apiName: '', + getChangeData: () => link, + } ); }); @@ -1069,6 +1137,7 @@ describe('Content Model Auto Format Plugin Test', () => { }; runTest( event, + 'www.test.com', { blockType: 'Paragraph', format: {}, @@ -1083,7 +1152,54 @@ describe('Content Model Auto Format Plugin Test', () => { { autoMailto: true, }, - true + true, + { + changeSource: '', + apiName: '', + getChangeData: undefined, + } + ); + }); + + it('should call promoteLink with mailto | autoMailto', () => { + const event: EditorInputEvent = { + eventType: 'input', + rawEvent: { data: ' ', preventDefault: () => {}, inputType: 'insertText' } as any, + }; + const link = document.createElement('a'); + link.href = 'mailto:test@mail.com'; + link.textContent = 'mailto:test@mail.com'; + runTest( + event, + 'mailto:test@mail.com', + { + blockType: 'Paragraph', + format: {}, + segments: [ + { + segmentType: 'Text', + text: 'mailto:test@mail.com', + format: {}, + isSelected: undefined, + link: { + dataset: {}, + format: { + href: 'mailto:test@mail.com', + underline: true, + }, + }, + }, + ], + }, + { + autoMailto: true, + }, + true, + { + changeSource: ChangeSource.AutoLink, + apiName: '', + getChangeData: () => link, + } ); }); @@ -1094,6 +1210,7 @@ describe('Content Model Auto Format Plugin Test', () => { }; runTest( event, + 'www.test.com', { blockType: 'Paragraph', format: {}, @@ -1108,7 +1225,12 @@ describe('Content Model Auto Format Plugin Test', () => { { autoLink: false, }, - true + true, + { + changeSource: '', + apiName: '', + getChangeData: undefined, + } ); }); @@ -1123,6 +1245,7 @@ describe('Content Model Auto Format Plugin Test', () => { }; runTest( event, + 'www.test.com', { blockType: 'Paragraph', format: {}, @@ -1137,7 +1260,12 @@ describe('Content Model Auto Format Plugin Test', () => { { autoLink: true, }, - false + false, + { + changeSource: '', + apiName: '', + getChangeData: undefined, + } ); }); }); From 9dbe3293fe265d3ced71f602c6e02e3a239abbbe Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Fri, 7 Feb 2025 11:29:59 -0800 Subject: [PATCH 11/23] Respect font weight in TH element (#2939) * Respect font weight in TH element * add test * improve * fix build --- .../config/defaultContentModelFormatMap.ts | 3 + .../lib/config/defaultHTMLStyleMap.ts | 1 + .../lib/modelApi/editing/applyTableFormat.ts | 5 + .../lib/modelToDom/handlers/handleTable.ts | 32 ++-- .../test/endToEndTest.ts | 153 ++++++++++++++++++ .../format/ContentModelTableCellFormat.ts | 4 +- 6 files changed, 185 insertions(+), 13 deletions(-) diff --git a/packages/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts b/packages/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts index 4576d1bc708..b686d5144cf 100644 --- a/packages/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts +++ b/packages/roosterjs-content-model-dom/lib/config/defaultContentModelFormatMap.ts @@ -52,4 +52,7 @@ export const defaultContentModelFormatMap: DefaultImplicitFormatMap = { marginTop: '1em', marginBottom: '1em', }, + th: { + fontWeight: 'bold', + }, }; diff --git a/packages/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts b/packages/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts index 5ee760791ee..f8d911cf198 100644 --- a/packages/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts +++ b/packages/roosterjs-content-model-dom/lib/config/defaultHTMLStyleMap.ts @@ -117,6 +117,7 @@ export const defaultHTMLStyleMap: DefaultStyleMap = { }, th: { display: 'table-cell', + fontWeight: 'bold', }, u: { textDecoration: 'underline', diff --git a/packages/roosterjs-content-model-dom/lib/modelApi/editing/applyTableFormat.ts b/packages/roosterjs-content-model-dom/lib/modelApi/editing/applyTableFormat.ts index 942a638fe91..25d1ea35c3f 100644 --- a/packages/roosterjs-content-model-dom/lib/modelApi/editing/applyTableFormat.ts +++ b/packages/roosterjs-content-model-dom/lib/modelApi/editing/applyTableFormat.ts @@ -262,6 +262,10 @@ export function setFirstColumnFormatBorders( switch (rowIndex) { case 0: cell.isHeader = !!format.hasHeaderRow; + + if (cell.isHeader) { + cell.format.fontWeight = 'bold'; + } break; case rows.length - 1: setBorderColor(cell.format, 'borderTop'); @@ -295,6 +299,7 @@ function setHeaderRowFormat( const cell = mutateBlock(readonlyCell); cell.isHeader = true; + cell.format.fontWeight = 'bold'; if (format.headerRowColor) { if (!metaOverrides.bgColorOverrides[rowIndex][cellIndex]) { diff --git a/packages/roosterjs-content-model-dom/lib/modelToDom/handlers/handleTable.ts b/packages/roosterjs-content-model-dom/lib/modelToDom/handlers/handleTable.ts index 5f564971b79..2f183cf580b 100644 --- a/packages/roosterjs-content-model-dom/lib/modelToDom/handlers/handleTable.ts +++ b/packages/roosterjs-content-model-dom/lib/modelToDom/handlers/handleTable.ts @@ -3,6 +3,7 @@ import { hasMetadata } from '../../modelApi/metadata/updateMetadata'; import { isBlockEmpty } from '../../modelApi/common/isEmpty'; import { moveChildNodes } from '../../domUtils/moveChildNodes'; import { reuseCachedElement } from '../../domUtils/reuseCachedElement'; +import { stackFormat } from '../utils/stackFormat'; import type { ContentModelBlockHandler, ContentModelTable, @@ -96,9 +97,9 @@ export const handleTable: ContentModelBlockHandler = ( } if (!cell.spanAbove && !cell.spanLeft) { + const tag = cell.isHeader ? 'th' : 'td'; const td = - (context.allowCacheElement && cell.cachedElement) || - doc.createElement(cell.isHeader ? 'th' : 'td'); + (context.allowCacheElement && cell.cachedElement) || doc.createElement(tag); tr.appendChild(td); @@ -132,18 +133,25 @@ export const handleTable: ContentModelBlockHandler = ( } } - if (!cell.cachedElement) { - if (context.allowCacheElement) { - cell.cachedElement = td; + stackFormat(context, tag, () => { + if (!cell.cachedElement) { + if (context.allowCacheElement) { + cell.cachedElement = td; + } + + applyFormat(td, context.formatAppliers.block, cell.format, context); + applyFormat(td, context.formatAppliers.tableCell, cell.format, context); + applyFormat( + td, + context.formatAppliers.tableCellBorder, + cell.format, + context + ); + applyFormat(td, context.formatAppliers.dataset, cell.dataset, context); } - applyFormat(td, context.formatAppliers.block, cell.format, context); - applyFormat(td, context.formatAppliers.tableCell, cell.format, context); - applyFormat(td, context.formatAppliers.tableCellBorder, cell.format, context); - applyFormat(td, context.formatAppliers.dataset, cell.dataset, context); - } - - context.modelHandlers.blockGroupChildren(doc, td, cell, context); + context.modelHandlers.blockGroupChildren(doc, td, cell, context); + }); context.onNodeCreated?.(cell, td); } diff --git a/packages/roosterjs-content-model-dom/test/endToEndTest.ts b/packages/roosterjs-content-model-dom/test/endToEndTest.ts index 1313bb9918e..3d07f2a9972 100644 --- a/packages/roosterjs-content-model-dom/test/endToEndTest.ts +++ b/packages/roosterjs-content-model-dom/test/endToEndTest.ts @@ -2226,4 +2226,157 @@ describe('End to end test for DOM => Model => DOM/TEXT', () => { '
test
' ); }); + + it('TH without font-weight', () => { + runTest( + '
test
', + { + blockGroupType: 'Document', + blocks: [ + { + widths: [], + rows: [ + { + height: 0, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: true, + blockGroupType: 'TableCell', + blocks: [ + { + isImplicit: true, + segments: [ + { + text: 'test', + segmentType: 'Text', + format: { + fontWeight: 'bold', + }, + }, + ], + blockType: 'Paragraph', + format: {}, + }, + ], + format: {}, + dataset: {}, + }, + ], + format: {}, + }, + ], + blockType: 'Table', + format: {}, + dataset: {}, + }, + ], + }, + 'test', + '
test
' + ); + }); + + it('TH with font-weight: 400', () => { + runTest( + '
test
', + { + blockGroupType: 'Document', + blocks: [ + { + widths: [], + rows: [ + { + height: 0, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: true, + blockGroupType: 'TableCell', + blocks: [ + { + isImplicit: true, + segments: [ + { + text: 'test', + segmentType: 'Text', + format: { + fontWeight: '400', + }, + }, + ], + blockType: 'Paragraph', + format: {}, + }, + ], + format: {}, + dataset: {}, + }, + ], + format: {}, + }, + ], + blockType: 'Table', + format: {}, + dataset: {}, + }, + ], + }, + 'test', + '
test
' + ); + }); + + it('TH with font-weight: bold', () => { + runTest( + '
test
', + { + blockGroupType: 'Document', + blocks: [ + { + widths: [], + rows: [ + { + height: 0, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: true, + blockGroupType: 'TableCell', + blocks: [ + { + isImplicit: true, + segments: [ + { + text: 'test', + segmentType: 'Text', + format: { + fontWeight: 'bold', + }, + }, + ], + blockType: 'Paragraph', + format: {}, + }, + ], + format: {}, + dataset: {}, + }, + ], + format: {}, + }, + ], + blockType: 'Table', + format: {}, + dataset: {}, + }, + ], + }, + 'test', + '
test
' + ); + }); }); diff --git a/packages/roosterjs-content-model-types/lib/contentModel/format/ContentModelTableCellFormat.ts b/packages/roosterjs-content-model-types/lib/contentModel/format/ContentModelTableCellFormat.ts index 651535c95f0..3640a14efe0 100644 --- a/packages/roosterjs-content-model-types/lib/contentModel/format/ContentModelTableCellFormat.ts +++ b/packages/roosterjs-content-model-types/lib/contentModel/format/ContentModelTableCellFormat.ts @@ -1,3 +1,4 @@ +import type { BoldFormat } from './formatParts/BoldFormat'; import type { BorderBoxFormat } from './formatParts/BorderBoxFormat'; import type { ContentModelBlockFormat } from './ContentModelBlockFormat'; import type { SizeFormat } from './formatParts/SizeFormat'; @@ -13,4 +14,5 @@ export type ContentModelTableCellFormat = ContentModelBlockFormat & VerticalAlignFormat & WordBreakFormat & TextColorFormat & - SizeFormat; + SizeFormat & + BoldFormat; From 6388b93c2772115fbff30f3d2384170d669078c7 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Fri, 7 Feb 2025 11:39:50 -0800 Subject: [PATCH 12/23] Fix resize table with width (#2940) * Fix resize table with width * fix test --- .../tableEdit/editors/features/CellResizer.ts | 19 ++++++++++++++++++- .../test/tableEdit/cellResizerTest.ts | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts index fa2d8f79e0c..e70ccc63fcf 100644 --- a/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts +++ b/packages/roosterjs-content-model-plugins/lib/tableEdit/editors/features/CellResizer.ts @@ -8,6 +8,7 @@ import { MIN_ALLOWED_TABLE_CELL_WIDTH, mutateBlock, MIN_ALLOWED_TABLE_CELL_HEIGHT, + parseValueWithUnit, } from 'roosterjs-content-model-dom'; import type { DragAndDropHandler } from '../../../pluginUtils/DragAndDrop/DragAndDropHandler'; import type { IEditor, ReadonlyContentModelTable } from 'roosterjs-content-model-types'; @@ -46,7 +47,15 @@ export function createCellResizer( (anchorContainer || document.body).appendChild(div); - const context: CellResizerContext = { editor, td, table, isRTL, zoomScale, onStart }; + const context: CellResizerContext = { + editor, + td, + table, + isRTL, + zoomScale, + onStart, + originalWidth: parseValueWithUnit(table.style.width), + }; const setPosition = isHorizontal ? setHorizontalPosition : setVerticalPosition; setPosition(context, div); @@ -79,6 +88,7 @@ export interface CellResizerContext { table: HTMLTableElement; isRTL: boolean; zoomScale: number; + originalWidth: number; onStart: () => void; } @@ -230,6 +240,13 @@ export function onDraggingVertical( } } + if (context.originalWidth > 0) { + const newWidth = context.originalWidth + change + 'px'; + + mutableTable.format.width = newWidth; + table.style.width = newWidth; + } + return true; } else { return false; diff --git a/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts b/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts index 8ee0b13b051..4f37138a3cb 100644 --- a/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts +++ b/packages/roosterjs-content-model-plugins/test/tableEdit/cellResizerTest.ts @@ -69,6 +69,7 @@ describe('Cell Resizer tests', () => { isRTL: false, zoomScale: 1, onStart: onStartSpy, + originalWidth: 0, }; const editorCMTable = getCMTableFromTable(editor, target as HTMLTableElement); @@ -119,6 +120,7 @@ describe('Cell Resizer tests', () => { isRTL: false, zoomScale: 1, onStart: onStartSpy, + originalWidth: 0, }; const delta = 10 * growth; const beforeHeight = getCurrentTable(editor).rows[cellRow].getBoundingClientRect() @@ -194,6 +196,7 @@ describe('Cell Resizer tests', () => { isRTL: false, zoomScale: 1, onStart: onStartSpy, + originalWidth: 0, }; const delta = 10 * growth; const beforeWidth = getCurrentTable(editor).rows[cellRow].cells[ From 01531e2ecb4fa5a9884a3078725e6e505161477a Mon Sep 17 00:00:00 2001 From: Haowen Chen Date: Wed, 12 Feb 2025 07:17:14 +0800 Subject: [PATCH 13/23] Support text/uri-list when pasting (#2943) --- .../lib/domUtils/event/extractClipboardItems.ts | 1 + .../domUtils/event/extractClipboardItemsTest.ts | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/packages/roosterjs-content-model-dom/lib/domUtils/event/extractClipboardItems.ts b/packages/roosterjs-content-model-dom/lib/domUtils/event/extractClipboardItems.ts index 0ce95a924eb..5e6f8af9824 100644 --- a/packages/roosterjs-content-model-dom/lib/domUtils/event/extractClipboardItems.ts +++ b/packages/roosterjs-content-model-dom/lib/domUtils/event/extractClipboardItems.ts @@ -8,6 +8,7 @@ const ContentHandlers: { ['text/plain']: (data, value) => (data.text = value), ['text/*']: (data, value, type?) => !!type && (data.customValues[type] = value), ['text/link-preview']: tryParseLinkPreview, + ['text/uri-list']: (data, value) => (data.text = value), }; /** diff --git a/packages/roosterjs-content-model-dom/test/domUtils/event/extractClipboardItemsTest.ts b/packages/roosterjs-content-model-dom/test/domUtils/event/extractClipboardItemsTest.ts index 26a92c57759..585a63b8c91 100644 --- a/packages/roosterjs-content-model-dom/test/domUtils/event/extractClipboardItemsTest.ts +++ b/packages/roosterjs-content-model-dom/test/domUtils/event/extractClipboardItemsTest.ts @@ -292,4 +292,20 @@ describe('extractClipboardItems', () => { pasteNativeEvent: true, }); }); + + it('input with text/uri-list', async () => { + const text = 'https://example.com'; + const clipboardData = await extractClipboardItems([ + createStringItem('text/uri-list', text), + ]); + expect(clipboardData).toEqual({ + types: ['text/uri-list'], + text: text, + image: null, + files: [], + rawHtml: null, + customValues: {}, + pasteNativeEvent: true, + }); + }); }); From bbe7d52a3bb654f0442478ffc70987705d435f38 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Wed, 12 Feb 2025 10:49:19 -0800 Subject: [PATCH 14/23] Fix #317607 (#2945) * Fix #317607 * fix build * fix test --- .../lib/publicApi/segment/toggleUnderline.ts | 4 +--- .../test/publicApi/segment/toggleUnderlineTest.ts | 8 +------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/packages/roosterjs-content-model-api/lib/publicApi/segment/toggleUnderline.ts b/packages/roosterjs-content-model-api/lib/publicApi/segment/toggleUnderline.ts index 0cb6e7fd5a4..a3742913394 100644 --- a/packages/roosterjs-content-model-api/lib/publicApi/segment/toggleUnderline.ts +++ b/packages/roosterjs-content-model-api/lib/publicApi/segment/toggleUnderline.ts @@ -1,4 +1,3 @@ -import { adjustTrailingSpaceSelection } from '../../modelApi/selection/adjustTrailingSpaceSelection'; import { formatSegmentWithContentModel } from '../utils/formatSegmentWithContentModel'; import type { IEditor } from 'roosterjs-content-model-types'; @@ -20,7 +19,6 @@ export function toggleUnderline(editor: IEditor) { } }, (format, segment) => !!format.underline || !!segment?.link?.format?.underline, - false /*includingFormatHolder*/, - adjustTrailingSpaceSelection + false /*includingFormatHolder*/ ); } diff --git a/packages/roosterjs-content-model-api/test/publicApi/segment/toggleUnderlineTest.ts b/packages/roosterjs-content-model-api/test/publicApi/segment/toggleUnderlineTest.ts index 41e740e437c..c27bebc8061 100644 --- a/packages/roosterjs-content-model-api/test/publicApi/segment/toggleUnderlineTest.ts +++ b/packages/roosterjs-content-model-api/test/publicApi/segment/toggleUnderlineTest.ts @@ -491,18 +491,12 @@ describe('toggleUnderline', () => { segments: [ { segmentType: 'Text', - text: 'Test', + text: 'Test ', format: { underline: true, }, isSelected: true, }, - { - segmentType: 'Text', - text: ' ', - format: {}, - isSelected: true, - }, ], format: {}, }, From ce1afbe03fabf6294075f8a52b7632c3f427532c Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Wed, 12 Feb 2025 10:56:16 -0800 Subject: [PATCH 15/23] Fix #2927 (#2944) --- .../roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts b/packages/roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts index b77a32a4284..f0d88ccf3bc 100644 --- a/packages/roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts +++ b/packages/roosterjs-react/lib/ribbon/plugin/createRibbonPlugin.ts @@ -153,9 +153,11 @@ class RibbonPluginImpl implements RibbonPlugin { private updateFormat() { if (this.editor && this.onFormatChanged) { const newFormatState = getFormatState(this.editor); + const newFormatKeys = getObjectKeys(newFormatState); if ( !this.formatState || + newFormatKeys.length != getObjectKeys(this.formatState).length || getObjectKeys(newFormatState).some( key => newFormatState[key] != this.formatState?.[key] ) From e120cce9c97909aa5ca2167882e07e1909ed4d3e Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Tue, 18 Feb 2025 11:04:30 -0800 Subject: [PATCH 16/23] Skip unstable test (#2946) Co-authored-by: Bryan Valverde U --- .../test/command/paste/pasteTest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/roosterjs-content-model-core/test/command/paste/pasteTest.ts b/packages/roosterjs-content-model-core/test/command/paste/pasteTest.ts index 4e0cf9cf5f4..8f2de35f3c1 100644 --- a/packages/roosterjs-content-model-core/test/command/paste/pasteTest.ts +++ b/packages/roosterjs-content-model-core/test/command/paste/pasteTest.ts @@ -396,7 +396,7 @@ describe('Paste with clipboardData', () => { expect(mergePasteContentSpy.calls.argsFor(0)[2]).toBeTrue(); }); - it('Second paste', () => { + xit('Second paste', () => { clipboardData.rawHtml = ''; clipboardData.modelBeforePaste = { blockGroupType: 'Document', From 7ee78058c8d6d232ddacd54e5a413674947aa409 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Tue, 18 Feb 2025 11:16:52 -0800 Subject: [PATCH 17/23] Let contentModelToText accepts readonly types (#2947) --- .../lib/modelToText/contentModelToText.ts | 8 +++--- .../lib/parameter/ModelToTextCallbacks.ts | 28 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/roosterjs-content-model-dom/lib/modelToText/contentModelToText.ts b/packages/roosterjs-content-model-dom/lib/modelToText/contentModelToText.ts index 7332c47b3ca..4d140ba3eda 100644 --- a/packages/roosterjs-content-model-dom/lib/modelToText/contentModelToText.ts +++ b/packages/roosterjs-content-model-dom/lib/modelToText/contentModelToText.ts @@ -1,7 +1,7 @@ import type { - ContentModelBlockGroup, - ContentModelDocument, ModelToTextCallbacks, + ReadonlyContentModelBlockGroup, + ReadonlyContentModelDocument, } from 'roosterjs-content-model-types'; const TextForHR = '________________________________________'; @@ -24,7 +24,7 @@ const defaultCallbacks: Required = { * @param callbacks Callbacks to customize the behavior of contentModelToText function */ export function contentModelToText( - model: ContentModelDocument, + model: ReadonlyContentModelDocument, separator: string = '\r\n', callbacks?: ModelToTextCallbacks ): string { @@ -37,7 +37,7 @@ export function contentModelToText( } function contentModelToTextArray( - group: ContentModelBlockGroup, + group: ReadonlyContentModelBlockGroup, textArray: string[], callbacks: Required ) { diff --git a/packages/roosterjs-content-model-types/lib/parameter/ModelToTextCallbacks.ts b/packages/roosterjs-content-model-types/lib/parameter/ModelToTextCallbacks.ts index 3ea41e57b31..767545e4426 100644 --- a/packages/roosterjs-content-model-types/lib/parameter/ModelToTextCallbacks.ts +++ b/packages/roosterjs-content-model-types/lib/parameter/ModelToTextCallbacks.ts @@ -1,11 +1,11 @@ -import type { ContentModelBlockGroup } from '../contentModel/blockGroup/ContentModelBlockGroup'; -import type { ContentModelDivider } from '../contentModel/block/ContentModelDivider'; +import type { ReadonlyContentModelBlockGroup } from '../contentModel/blockGroup/ContentModelBlockGroup'; +import type { ReadonlyContentModelDivider } from '../contentModel/block/ContentModelDivider'; import type { ContentModelEntity } from '../contentModel/entity/ContentModelEntity'; -import type { ContentModelGeneralSegment } from '../contentModel/segment/ContentModelGeneralSegment'; -import type { ContentModelImage } from '../contentModel/segment/ContentModelImage'; -import type { ContentModelParagraph } from '../contentModel/block/ContentModelParagraph'; -import type { ContentModelTable } from '../contentModel/block/ContentModelTable'; -import type { ContentModelText } from '../contentModel/segment/ContentModelText'; +import type { ReadonlyContentModelGeneralSegment } from '../contentModel/segment/ContentModelGeneralSegment'; +import type { ReadonlyContentModelImage } from '../contentModel/segment/ContentModelImage'; +import type { ReadonlyContentModelParagraph } from '../contentModel/block/ContentModelParagraph'; +import type { ReadonlyContentModelTable } from '../contentModel/block/ContentModelTable'; +import type { ReadonlyContentModelText } from '../contentModel/segment/ContentModelText'; /** * Callback function type for converting a given Content Model object to plain text @@ -36,35 +36,35 @@ export interface ModelToTextCallbacks { /** * Customize the behavior of converting general segment to plain text */ - onGeneralSegment?: ModelToTextCallback; + onGeneralSegment?: ModelToTextCallback; /** * Customize the behavior of converting text model to plain text */ - onText?: ModelToTextCallback; + onText?: ModelToTextCallback; /** * Customize the behavior of converting image model to plain text */ - onImage?: ModelToTextCallback; + onImage?: ModelToTextCallback; /** * Customize the behavior of converting divider model to plain text */ - onDivider?: ModelToTextCallback; + onDivider?: ModelToTextCallback; /** * Customize the check if we should convert a paragraph model to plain text */ - onParagraph?: ModelToTextChecker; + onParagraph?: ModelToTextChecker; /** * Customize the check if we should convert a table model to plain text */ - onTable?: ModelToTextChecker; + onTable?: ModelToTextChecker; /** * Customize the check if we should convert a block group model to plain text */ - onBlockGroup?: ModelToTextChecker; + onBlockGroup?: ModelToTextChecker; } From 9b93cf4b52073cfe58cc6220a67925ad57e438d4 Mon Sep 17 00:00:00 2001 From: Julia Roldi <87443959+juliaroldi@users.noreply.github.com> Date: Tue, 18 Feb 2025 19:21:14 -0300 Subject: [PATCH 18/23] select image with keyboard (#2951) Co-authored-by: Jiuqing Song --- .../corePlugin/selection/SelectionPlugin.ts | 32 +++++++++-------- .../selection/SelectionPluginTest.ts | 35 +++++++++++++++++++ 2 files changed, 52 insertions(+), 15 deletions(-) diff --git a/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts b/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts index 57ea5a632d9..7db90bbaa96 100644 --- a/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts +++ b/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts @@ -646,21 +646,23 @@ class SelectionPlugin implements PluginWithState { //If am image selection changed to a wider range due a keyboard event, we should update the selection const selection = this.editor.getDocument().getSelection(); - - if ( - newSelection?.type == 'image' && - selection && - selection.focusNode && - !isSingleImageInSelection(selection) - ) { - const range = selection.getRangeAt(0); - this.editor.setDOMSelection({ - type: 'range', - range, - isReverted: - selection.focusNode != range.endContainer || - selection.focusOffset != range.endOffset, - }); + if (selection && selection.focusNode) { + const image = isSingleImageInSelection(selection); + if (newSelection?.type == 'image' && !image) { + const range = selection.getRangeAt(0); + this.editor.setDOMSelection({ + type: 'range', + range, + isReverted: + selection.focusNode != range.endContainer || + selection.focusOffset != range.endOffset, + }); + } else if (newSelection?.type !== 'image' && image) { + this.editor.setDOMSelection({ + type: 'image', + image, + }); + } } // Safari has problem to handle onBlur event. When blur, we cannot get the original selection from editor. diff --git a/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts b/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts index 0d795d323ed..8b0c107e6d3 100644 --- a/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts +++ b/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts @@ -2857,4 +2857,39 @@ describe('SelectionPlugin selectionChange on image selected', () => { expect(setDOMSelectionSpy).not.toHaveBeenCalled(); expect(getDOMSelectionSpy).toHaveBeenCalledTimes(1); }); + + it('onSelectionChange on image | 4', () => { + const image = document.createElement('img'); + spyOn(isSingleImageInSelection, 'isSingleImageInSelection').and.returnValue(image); + + const plugin = createSelectionPlugin({}); + const state = plugin.getState(); + const mockedOldSelection = { + type: 'image', + image: {} as any, + } as DOMSelection; + + state.selection = mockedOldSelection; + + plugin.initialize(editor); + + const onSelectionChange = addEventListenerSpy.calls.argsFor(0)[1] as Function; + const mockedNewSelection = { + type: 'range', + range: {} as any, + } as any; + + hasFocusSpy.and.returnValue(true); + isInShadowEditSpy.and.returnValue(false); + getDOMSelectionSpy.and.returnValue(mockedNewSelection); + getRangeAtSpy.and.returnValue({ startContainer: {} }); + + onSelectionChange(); + + expect(setDOMSelectionSpy).toHaveBeenCalledWith({ + type: 'image', + image, + }); + expect(getDOMSelectionSpy).toHaveBeenCalledTimes(1); + }); }); From 3ddc5b8f234ed90ede6ac82f51cba1776f292e00 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 20 Feb 2025 13:14:10 -0800 Subject: [PATCH 19/23] Bump serialize-javascript from 6.0.1 to 6.0.2 (#2952) Bumps [serialize-javascript](https://github.com/yahoo/serialize-javascript) from 6.0.1 to 6.0.2. - [Release notes](https://github.com/yahoo/serialize-javascript/releases) - [Commits](https://github.com/yahoo/serialize-javascript/compare/v6.0.1...v6.0.2) --- updated-dependencies: - dependency-name: serialize-javascript dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 9e319cd6783..d0a924938af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6041,9 +6041,9 @@ send@0.19.0: statuses "2.0.1" serialize-javascript@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.1.tgz#b206efb27c3da0b0ab6b52f48d170b7996458e5c" - integrity sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w== + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" From 7beec85eecaf5cb9b047bd91fb14161ab6e2c1e4 Mon Sep 17 00:00:00 2001 From: Bryan Valverde U Date: Thu, 20 Feb 2025 16:11:00 -0600 Subject: [PATCH 20/23] Implement Excel non-native paste event handling and related utilities (#2950) * Implement Excel non-native event handling and related utilities * Update import statement for BeforePasteEvent and ClipboardData types * Fix typo in handleExcelContentFromNotNativeEventTest for handleForNativeEvent parameter * Remove deprecated Excel non-native event handling and related tests; add new HTML template for non-native Excel content * Fix tests in pipeline --------- Co-authored-by: Jiuqing Song --- .../controlsV2/demoButtons/pasteButton.ts | 15 +-- .../Excel/processPastedContentFromExcel.ts | 98 +++++++++------ .../lib/paste/PastePlugin.ts | 4 +- .../pasteSourceValidations/getPasteSource.ts | 5 +- .../isExcelNonNativeEvent.ts | 18 +++ .../test/paste/createBeforePasteEventMock.ts | 25 ++++ .../paste/e2e/cmPasteFromExcelNonNative.ts | 119 ++++++++++++++++++ .../htmlTemplates/htmlFromExcelNonNative.ts | 5 + .../processPastedContentFromExcelTest.ts | 20 ++- .../{ => excel}/validateExcelFragmentTest.ts | 2 +- .../getPasteSourceTest.ts | 24 +++- .../ContentModelPastePluginTest.ts | 46 +++++-- .../processPastedContentFromPowerPointTest.ts | 2 +- .../{ => utils}/deprecatedColorParserTest.ts | 2 +- .../paste/{ => utils}/getStyleMetadataTest.ts | 2 +- .../test/paste/{ => utils}/linkParserTest.ts | 4 +- .../processPastedContentFromWacTest.ts | 6 +- ...processPastedContentFromWordDesktopTest.ts | 34 +---- 18 files changed, 326 insertions(+), 105 deletions(-) create mode 100644 packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/isExcelNonNativeEvent.ts create mode 100644 packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts create mode 100644 packages/roosterjs-content-model-plugins/test/paste/e2e/cmPasteFromExcelNonNative.ts create mode 100644 packages/roosterjs-content-model-plugins/test/paste/e2e/htmlTemplates/htmlFromExcelNonNative.ts rename packages/roosterjs-content-model-plugins/test/paste/{ => excel}/processPastedContentFromExcelTest.ts (96%) rename packages/roosterjs-content-model-plugins/test/paste/{ => excel}/validateExcelFragmentTest.ts (94%) rename packages/roosterjs-content-model-plugins/test/paste/{ => plugin}/ContentModelPastePluginTest.ts (83%) rename packages/roosterjs-content-model-plugins/test/paste/{ => powerPoint}/processPastedContentFromPowerPointTest.ts (97%) rename packages/roosterjs-content-model-plugins/test/paste/{ => utils}/deprecatedColorParserTest.ts (90%) rename packages/roosterjs-content-model-plugins/test/paste/{ => utils}/getStyleMetadataTest.ts (98%) rename packages/roosterjs-content-model-plugins/test/paste/{ => utils}/linkParserTest.ts (97%) rename packages/roosterjs-content-model-plugins/test/paste/{ => word}/processPastedContentFromWacTest.ts (99%) rename packages/roosterjs-content-model-plugins/test/paste/{ => word}/processPastedContentFromWordDesktopTest.ts (99%) diff --git a/demo/scripts/controlsV2/demoButtons/pasteButton.ts b/demo/scripts/controlsV2/demoButtons/pasteButton.ts index dc507f5ac79..071f5bf824d 100644 --- a/demo/scripts/controlsV2/demoButtons/pasteButton.ts +++ b/demo/scripts/controlsV2/demoButtons/pasteButton.ts @@ -45,19 +45,14 @@ const createDataTransfer = ( const createDataTransferItems = (data: ClipboardItems) => { const isTEXT = (type: string) => type.startsWith('text/'); - const isIMAGE = (type: string) => type.startsWith('image/'); const dataTransferItems: Promise[] = []; data.forEach(item => { item.types.forEach(type => { - if (isTEXT(type) || isIMAGE(type)) { - dataTransferItems.push( - item - .getType(type) - .then(blob => - createDataTransfer(isTEXT(type) ? 'string' : 'file', type, blob) - ) - ); - } + dataTransferItems.push( + item + .getType(type) + .then(blob => createDataTransfer(isTEXT(type) ? 'string' : 'file', type, blob)) + ); }); }); return dataTransferItems; diff --git a/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts b/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts index 08a6e61a69f..330525ecf60 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/Excel/processPastedContentFromExcel.ts @@ -12,23 +12,29 @@ const LAST_TD_END_REGEX = /<\/\s*td\s*>((?!<\/\s*tr\s*>)[\s\S])*$/i; const LAST_TR_END_REGEX = /<\/\s*tr\s*>((?!<\/\s*table\s*>)[\s\S])*$/i; const LAST_TR_REGEX = /]*>[^<]*/i; const LAST_TABLE_REGEX = /]*>[^<]*/i; -const DEFAULT_BORDER_STYLE = 'solid 1px #d4d4d4'; const TABLE_SELECTOR = 'table'; +const DEFAULT_BORDER_STYLE = 'solid 1px #d4d4d4'; /** * @internal * Convert pasted content from Excel, add borders when source doc doesn't have a border * @param event The BeforePaste event + * @param domCreator The DOM creator + * @param allowExcelNoBorderTable Allow table copied from Excel without border + * @param isNativeEvent Whether the event is native event */ - export function processPastedContentFromExcel( event: BeforePasteEvent, domCreator: DOMCreator, - allowExcelNoBorderTable?: boolean + allowExcelNoBorderTable: boolean, + isNativeEvent: boolean ) { const { fragment, htmlBefore, htmlAfter, clipboardData } = event; - validateExcelFragment(fragment, domCreator, htmlBefore, clipboardData, htmlAfter); + // For non native event we already validated that the content contains a table + if (isNativeEvent) { + validateExcelFragment(fragment, domCreator, htmlBefore, clipboardData, htmlAfter); + } // For Excel Online const firstChild = fragment.firstChild; @@ -54,40 +60,13 @@ export function processPastedContentFromExcel( } } - addParser(event.domToModelOption, 'tableCell', (format, element) => { - if (!allowExcelNoBorderTable && element.style.borderStyle === 'none') { - format.borderBottom = DEFAULT_BORDER_STYLE; - format.borderLeft = DEFAULT_BORDER_STYLE; - format.borderRight = DEFAULT_BORDER_STYLE; - format.borderTop = DEFAULT_BORDER_STYLE; - } - }); - - setProcessor(event.domToModelOption, 'child', childProcessor); + setupExcelTableHandlers( + event, + allowExcelNoBorderTable, + isNativeEvent /* handleForNativeEvent */ + ); } -/** - * @internal - * Exported only for unit test - */ -export const childProcessor: ElementProcessor = (group, element, context) => { - const segmentFormat = { ...context.segmentFormat }; - if ( - group.blockGroupType === 'TableCell' && - group.format.textColor && - !context.segmentFormat.textColor - ) { - context.segmentFormat.textColor = group.format.textColor; - } - - context.defaultElementProcessors.child(group, element, context); - - if (group.blockGroupType === 'TableCell' && group.format.textColor) { - context.segmentFormat = segmentFormat; - delete group.format.textColor; - } -}; - /** * @internal * Exported only for unit test @@ -148,3 +127,50 @@ export function excelHandler(html: string, htmlBefore: string): string { return html; } } + +/** + * @internal + * Exported only for unit test + */ +export function setupExcelTableHandlers( + event: BeforePasteEvent, + allowExcelNoBorderTable: boolean | undefined, + isNativeEvent: boolean +) { + addParser(event.domToModelOption, 'tableCell', (format, element) => { + if ( + !allowExcelNoBorderTable && + (element.style.borderStyle === 'none' || + (!isNativeEvent && element.style.borderStyle == '')) + ) { + format.borderBottom = DEFAULT_BORDER_STYLE; + format.borderLeft = DEFAULT_BORDER_STYLE; + format.borderRight = DEFAULT_BORDER_STYLE; + format.borderTop = DEFAULT_BORDER_STYLE; + } + }); + + setProcessor(event.domToModelOption, 'child', childProcessor); +} + +/** + * @internal + * Exported only for unit test + */ +export const childProcessor: ElementProcessor = (group, element, context) => { + const segmentFormat = { ...context.segmentFormat }; + if ( + group.blockGroupType === 'TableCell' && + group.format.textColor && + !context.segmentFormat.textColor + ) { + context.segmentFormat.textColor = group.format.textColor; + } + + context.defaultElementProcessors.child(group, element, context); + + if (group.blockGroupType === 'TableCell' && group.format.textColor) { + context.segmentFormat = segmentFormat; + delete group.format.textColor; + } +}; diff --git a/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts b/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts index 5fea7005843..a68a3c4a26a 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/PastePlugin.ts @@ -106,12 +106,14 @@ export class PastePlugin implements EditorPlugin { break; case 'excelOnline': case 'excelDesktop': + case 'excelNonNativeEvent': if (pasteType === 'normal' || pasteType === 'mergeFormat') { // Handle HTML copied from Excel processPastedContentFromExcel( event, this.editor.getDOMCreator(), - this.allowExcelNoBorderTable + !!this.allowExcelNoBorderTable, + pasteSource != 'excelNonNativeEvent' /* isNativeEvent */ ); } break; diff --git a/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/getPasteSource.ts b/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/getPasteSource.ts index 031a57f338e..5d7cf90ce1f 100644 --- a/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/getPasteSource.ts +++ b/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/getPasteSource.ts @@ -1,5 +1,6 @@ import { documentContainWacElements } from './documentContainWacElements'; import { isExcelDesktopDocument } from './isExcelDesktopDocument'; +import { isExcelNotNativeEvent } from './isExcelNonNativeEvent'; import { isExcelOnlineDocument } from './isExcelOnlineDocument'; import { isGoogleSheetDocument } from './isGoogleSheetDocument'; import { isPowerPointDesktopDocument } from './isPowerPointDesktopDocument'; @@ -29,7 +30,8 @@ export type KnownPasteSourceType = | 'googleSheets' | 'wacComponents' | 'default' - | 'singleImage'; + | 'singleImage' + | 'excelNonNativeEvent'; /** * @internal @@ -44,6 +46,7 @@ const getSourceFunctions = new Map([ ['wacComponents', documentContainWacElements], ['googleSheets', isGoogleSheetDocument], ['singleImage', shouldConvertToSingleImage], + ['excelNonNativeEvent', isExcelNotNativeEvent], ]); /** diff --git a/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/isExcelNonNativeEvent.ts b/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/isExcelNonNativeEvent.ts new file mode 100644 index 00000000000..684362196d8 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/lib/paste/pasteSourceValidations/isExcelNonNativeEvent.ts @@ -0,0 +1,18 @@ +import type { GetSourceFunction, GetSourceInputParams } from './getPasteSource'; + +const ShadowWorkbookClipboardType = 'web data/shadow-workbook'; + +/** + * @internal + * When the clipboard content is retrieved programatically, the clipboard html does not contain the usual + * attributes we use to determine if the content is from Excel. This function is used to handle that case. + */ +export const isExcelNotNativeEvent: GetSourceFunction = (props: GetSourceInputParams) => { + const { clipboardData } = props; + + return ( + clipboardData.types.includes(ShadowWorkbookClipboardType) && + clipboardData.htmlFirstLevelChildTags?.length == 1 && + clipboardData.htmlFirstLevelChildTags[0] == 'TABLE' + ); +}; diff --git a/packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts b/packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts new file mode 100644 index 00000000000..d6357495297 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/test/paste/createBeforePasteEventMock.ts @@ -0,0 +1,25 @@ +import type { BeforePasteEvent, ClipboardData } from 'roosterjs-content-model-types'; + +export function createBeforePasteEventMock( + fragment: DocumentFragment, + htmlBefore: string = '' +): BeforePasteEvent { + return { + eventType: 'beforePaste', + clipboardData: {}, + fragment: fragment, + htmlBefore, + htmlAfter: '', + htmlAttributes: {}, + pasteType: 'normal', + domToModelOption: { + additionalAllowedTags: [], + additionalDisallowedTags: [], + additionalFormatParsers: {}, + attributeSanitizers: {}, + formatParserOverride: {}, + processorOverride: {}, + styleSanitizers: {}, + }, + }; +} diff --git a/packages/roosterjs-content-model-plugins/test/paste/e2e/cmPasteFromExcelNonNative.ts b/packages/roosterjs-content-model-plugins/test/paste/e2e/cmPasteFromExcelNonNative.ts new file mode 100644 index 00000000000..f865fa6a1e9 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/test/paste/e2e/cmPasteFromExcelNonNative.ts @@ -0,0 +1,119 @@ +import * as processPastedContentFromExcel from '../../../lib/paste/Excel/processPastedContentFromExcel'; +import { excelContentFromNonNativeTemplate } from './htmlTemplates/htmlFromExcelNonNative'; +import { expectEqual, initEditor } from './testUtils'; +import { paste } from 'roosterjs-content-model-core'; +import type { ClipboardData, IEditor } from 'roosterjs-content-model-types'; + +describe('Paste from Excel non native', () => { + let editor: IEditor = undefined!; + + beforeEach(() => { + editor = initEditor('Paste_from_Excel_non_native'); + }); + + afterEach(() => { + document.getElementById('Paste_from_Excel_non_native')?.remove(); + }); + + it('E2E', () => { + const clipboardData = { + types: ['web data/shadow-workbook', 'image/png', 'text/plain', 'text/html'], + text: '\r\n', + image: {}, + files: [{}], + rawHtml: excelContentFromNonNativeTemplate, + customValues: {}, + pasteNativeEvent: true, + imageDataUri: null, + } as ClipboardData; + spyOn(processPastedContentFromExcel, 'processPastedContentFromExcel').and.callThrough(); + + paste(editor, clipboardData); + const model = editor.getContentModelCopy('connected'); + + expect(processPastedContentFromExcel.processPastedContentFromExcel).toHaveBeenCalled(); + + expectEqual(model, { + blockGroupType: 'Document', + blocks: [ + { + widths: jasmine.anything() as any, + rows: [ + { + height: jasmine.anything() as any, + cells: [ + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [], + format: { + borderTop: '1px solid rgb(212, 212, 212)', + borderRight: '1px solid rgb(212, 212, 212)', + borderBottom: '1px solid rgb(212, 212, 212)', + borderLeft: '1px solid rgb(212, 212, 212)', + width: '50pt', + height: '14.4pt', + }, + dataset: {}, + }, + { + spanAbove: false, + spanLeft: false, + isHeader: false, + blockGroupType: 'TableCell', + blocks: [], + format: { + borderTop: '1px solid rgb(212, 212, 212)', + borderRight: '1px solid rgb(212, 212, 212)', + borderBottom: '1px solid rgb(212, 212, 212)', + borderLeft: '1px solid rgb(212, 212, 212)', + width: '50pt', + }, + dataset: {}, + }, + ], + format: {}, + }, + ], + blockType: 'Table', + format: { + width: '100pt', + useBorderBox: true, + borderCollapse: true, + }, + dataset: {}, + }, + { + segments: [ + { + isSelected: true, + segmentType: 'SelectionMarker', + format: { + backgroundColor: '', + fontFamily: '', + fontSize: '', + fontWeight: '', + italic: false, + letterSpacing: '', + lineHeight: '', + strikethrough: false, + superOrSubScriptSequence: '', + textColor: '', + underline: false, + }, + }, + { + segmentType: 'Br', + format: {}, + }, + ], + blockType: 'Paragraph', + format: {}, + }, + ], + format: {}, + }); + }); +}); diff --git a/packages/roosterjs-content-model-plugins/test/paste/e2e/htmlTemplates/htmlFromExcelNonNative.ts b/packages/roosterjs-content-model-plugins/test/paste/e2e/htmlTemplates/htmlFromExcelNonNative.ts new file mode 100644 index 00000000000..1336f064f07 --- /dev/null +++ b/packages/roosterjs-content-model-plugins/test/paste/e2e/htmlTemplates/htmlFromExcelNonNative.ts @@ -0,0 +1,5 @@ +export const excelContentFromNonNativeTemplate = ` + + +
`; diff --git a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromExcelTest.ts b/packages/roosterjs-content-model-plugins/test/paste/excel/processPastedContentFromExcelTest.ts similarity index 96% rename from packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromExcelTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/excel/processPastedContentFromExcelTest.ts index 42cd593c605..3e8bebfdb51 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromExcelTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/excel/processPastedContentFromExcelTest.ts @@ -1,7 +1,7 @@ -import * as PastePluginFile from '../../lib/paste/Excel/processPastedContentFromExcel'; +import * as PastePluginFile from '../../../lib/paste/Excel/processPastedContentFromExcel'; import { ContentModelDocument, DOMCreator } from 'roosterjs-content-model-types'; -import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; -import { processPastedContentFromExcel } from '../../lib/paste/Excel/processPastedContentFromExcel'; +import { createBeforePasteEventMock } from '../createBeforePasteEventMock'; +import { processPastedContentFromExcel } from '../../../lib/paste/Excel/processPastedContentFromExcel'; import { contentModelToDom, createDomToModelContext, @@ -29,7 +29,12 @@ describe('processPastedContentFromExcelTest', () => { const event = createBeforePasteEventMock(fragment); event.clipboardData.html = source; - processPastedContentFromExcel(event, domCreator); + processPastedContentFromExcel( + event, + domCreator, + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ + ); const model = domToContentModel( fragment, @@ -352,7 +357,12 @@ describe('Do not run scenarios', () => { if (excelHandler) { spyOn(PastePluginFile, 'excelHandler').and.returnValue(excelHandler); } - processPastedContentFromExcel(event, domCreator); + processPastedContentFromExcel( + event, + domCreator, + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ + ); // Assert while (div.firstChild) { diff --git a/packages/roosterjs-content-model-plugins/test/paste/validateExcelFragmentTest.ts b/packages/roosterjs-content-model-plugins/test/paste/excel/validateExcelFragmentTest.ts similarity index 94% rename from packages/roosterjs-content-model-plugins/test/paste/validateExcelFragmentTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/excel/validateExcelFragmentTest.ts index 8bb38d34982..c18b56039d2 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/validateExcelFragmentTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/excel/validateExcelFragmentTest.ts @@ -1,5 +1,5 @@ import { ClipboardData, DOMCreator } from 'roosterjs-content-model-types'; -import { validateExcelFragment } from '../../lib/paste/Excel/processPastedContentFromExcel'; +import { validateExcelFragment } from '../../../lib/paste/Excel/processPastedContentFromExcel'; describe('validateExcelFragment', () => { let domCreator: DOMCreator; diff --git a/packages/roosterjs-content-model-plugins/test/paste/pasteSourceValidations/getPasteSourceTest.ts b/packages/roosterjs-content-model-plugins/test/paste/pasteSourceValidations/getPasteSourceTest.ts index 136f4f36eb3..1aa7c39c160 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/pasteSourceValidations/getPasteSourceTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/pasteSourceValidations/getPasteSourceTest.ts @@ -51,6 +51,13 @@ describe('getPasteSourceTest | ', () => { const result = getPasteSource(defaultParam(), false /* shouldConvertSingleImage */); expect(result).toBe('default'); }); + it('Is Excel Non-Native Event', () => { + const result = getPasteSource( + excelNonNativeEventParam(), + false /* shouldConvertSingleImage */ + ); + expect(result).toBe('excelNonNativeEvent'); + }); }); function wacParam(): BeforePasteEvent { @@ -78,8 +85,9 @@ function googleSheetParam(): BeforePasteEvent { function converSingleImageParam(): BeforePasteEvent { const fragment = document.createDocumentFragment(); - const clipboardData = { + const clipboardData = { htmlFirstLevelChildTags: ['IMG'], + types: [], }; return { @@ -111,5 +119,17 @@ function wordParam(): BeforePasteEvent { function defaultParam(): BeforePasteEvent { const fragment = document.createDocumentFragment(); - return { htmlAttributes: {}, fragment, clipboardData: {} }; + return { htmlAttributes: {}, fragment, clipboardData: { types: [] } }; +} + +function excelNonNativeEventParam(): BeforePasteEvent { + const fragment = document.createDocumentFragment(); + + const clipboardData: ClipboardData = { + types: ['web data/shadow-workbook'], + rawHtml: '', + htmlFirstLevelChildTags: ['TABLE'], + } as any; + + return { fragment, clipboardData, htmlAttributes: {} }; } diff --git a/packages/roosterjs-content-model-plugins/test/paste/ContentModelPastePluginTest.ts b/packages/roosterjs-content-model-plugins/test/paste/plugin/ContentModelPastePluginTest.ts similarity index 83% rename from packages/roosterjs-content-model-plugins/test/paste/ContentModelPastePluginTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/plugin/ContentModelPastePluginTest.ts index 56f955ebafb..b41b21d605d 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/ContentModelPastePluginTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/plugin/ContentModelPastePluginTest.ts @@ -1,12 +1,12 @@ -import * as addParser from '../../lib/paste/utils/addParser'; -import * as ExcelFile from '../../lib/paste/Excel/processPastedContentFromExcel'; -import * as getPasteSource from '../../lib/paste/pasteSourceValidations/getPasteSource'; -import * as PowerPointFile from '../../lib/paste/PowerPoint/processPastedContentFromPowerPoint'; -import * as setProcessor from '../../lib/paste/utils/setProcessor'; -import * as WacFile from '../../lib/paste/WacComponents/processPastedContentWacComponents'; +import * as addParser from '../../../lib/paste/utils/addParser'; +import * as ExcelFile from '../../../lib/paste/Excel/processPastedContentFromExcel'; +import * as getPasteSource from '../../../lib/paste/pasteSourceValidations/getPasteSource'; +import * as PowerPointFile from '../../../lib/paste/PowerPoint/processPastedContentFromPowerPoint'; +import * as setProcessor from '../../../lib/paste/utils/setProcessor'; +import * as WacFile from '../../../lib/paste/WacComponents/processPastedContentWacComponents'; import { BeforePasteEvent, DOMCreator, IEditor } from 'roosterjs-content-model-types'; -import { PastePlugin } from '../../lib/paste/PastePlugin'; -import { PastePropertyNames } from '../../lib/paste/pasteSourceValidations/constants'; +import { PastePlugin } from '../../../lib/paste/PastePlugin'; +import { PastePropertyNames } from '../../../lib/paste/pasteSourceValidations/constants'; const trustedHTMLHandler = (val: string) => val; const domCreator: DOMCreator = { @@ -38,6 +38,7 @@ describe('Content Model Paste Plugin Test', () => { eventType: 'beforePaste', clipboardData: { html: '', + types: [], }, fragment: document.createDocumentFragment(), htmlBefore: '', @@ -77,7 +78,8 @@ describe('Content Model Paste Plugin Test', () => { expect(ExcelFile.processPastedContentFromExcel).toHaveBeenCalledWith( event, domCreator, - undefined /*allowExcelNoBorderTable*/ + false /*allowExcelNoBorderTable*/, + true ); expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 3); expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); @@ -94,7 +96,8 @@ describe('Content Model Paste Plugin Test', () => { expect(ExcelFile.processPastedContentFromExcel).not.toHaveBeenCalledWith( event, domCreator, - undefined /*allowExcelNoBorderTable*/ + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ ); expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED); expect(setProcessor.setProcessor).toHaveBeenCalledTimes(0); @@ -110,7 +113,8 @@ describe('Content Model Paste Plugin Test', () => { expect(ExcelFile.processPastedContentFromExcel).toHaveBeenCalledWith( event, domCreator, - undefined /*allowExcelNoBorderTable*/ + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ ); expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); @@ -126,7 +130,8 @@ describe('Content Model Paste Plugin Test', () => { expect(ExcelFile.processPastedContentFromExcel).toHaveBeenCalledWith( event, domCreator, - undefined /*allowExcelNoBorderTable*/ + false /* allowExcelNoBorderTable */, + true /* isNativeEvent */ ); expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); @@ -173,6 +178,23 @@ describe('Content Model Paste Plugin Test', () => { expect(Object.keys(event.domToModelOption.styleSanitizers).length).toEqual(4); }); + it('excelNonNativeEvent', () => { + spyOn(getPasteSource, 'getPasteSource').and.returnValue('excelNonNativeEvent'); + spyOn(ExcelFile, 'processPastedContentFromExcel').and.callThrough(); + + plugin.initialize(editor); + plugin.onPluginEvent(event); + + expect(ExcelFile.processPastedContentFromExcel).toHaveBeenCalledWith( + event, + domCreator, + false /* allowExcelNoBorderTable */, + false /* isNativeEvent */ + ); + expect(addParser.addParser).toHaveBeenCalledTimes(DEFAULT_TIMES_ADD_PARSER_CALLED + 1); + expect(setProcessor.setProcessor).toHaveBeenCalledTimes(1); + }); + it('Default with customized Sanitizing option', () => { spyOn(getPasteSource, 'getPasteSource').and.returnValue('default'); diff --git a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromPowerPointTest.ts b/packages/roosterjs-content-model-plugins/test/paste/powerPoint/processPastedContentFromPowerPointTest.ts similarity index 97% rename from packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromPowerPointTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/powerPoint/processPastedContentFromPowerPointTest.ts index 73fc764fb6e..31f6240ed84 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromPowerPointTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/powerPoint/processPastedContentFromPowerPointTest.ts @@ -1,5 +1,5 @@ import * as moveChildNodes from 'roosterjs-content-model-dom/lib/domUtils/moveChildNodes'; -import { processPastedContentFromPowerPoint } from '../../lib/paste/PowerPoint/processPastedContentFromPowerPoint'; +import { processPastedContentFromPowerPoint } from '../../../lib/paste/PowerPoint/processPastedContentFromPowerPoint'; import type { BeforePasteEvent, ClipboardData, DOMCreator } from 'roosterjs-content-model-types'; const getPasteEvent = (): BeforePasteEvent => { diff --git a/packages/roosterjs-content-model-plugins/test/paste/deprecatedColorParserTest.ts b/packages/roosterjs-content-model-plugins/test/paste/utils/deprecatedColorParserTest.ts similarity index 90% rename from packages/roosterjs-content-model-plugins/test/paste/deprecatedColorParserTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/utils/deprecatedColorParserTest.ts index 55e8efa4263..3de671cd2f4 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/deprecatedColorParserTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/utils/deprecatedColorParserTest.ts @@ -1,4 +1,4 @@ -import { deprecatedBorderColorParser } from '../../lib/paste/utils/deprecatedColorParser'; +import { deprecatedBorderColorParser } from '../../../lib/paste/utils/deprecatedColorParser'; const DeprecatedColors: string[] = [ 'activeborder', diff --git a/packages/roosterjs-content-model-plugins/test/paste/getStyleMetadataTest.ts b/packages/roosterjs-content-model-plugins/test/paste/utils/getStyleMetadataTest.ts similarity index 98% rename from packages/roosterjs-content-model-plugins/test/paste/getStyleMetadataTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/utils/getStyleMetadataTest.ts index 0aae54bd19c..0250615ad1f 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/getStyleMetadataTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/utils/getStyleMetadataTest.ts @@ -1,5 +1,5 @@ import { BeforePasteEvent } from 'roosterjs-content-model-types'; -import { getStyleMetadata } from '../../lib/paste/WordDesktop/getStyleMetadata'; +import { getStyleMetadata } from '../../../lib/paste/WordDesktop/getStyleMetadata'; const domCreator = { htmlToDOM: (html: string) => new DOMParser().parseFromString(html, 'text/html'), diff --git a/packages/roosterjs-content-model-plugins/test/paste/linkParserTest.ts b/packages/roosterjs-content-model-plugins/test/paste/utils/linkParserTest.ts similarity index 97% rename from packages/roosterjs-content-model-plugins/test/paste/linkParserTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/utils/linkParserTest.ts index a1be1151bf5..deb45736613 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/linkParserTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/utils/linkParserTest.ts @@ -1,6 +1,6 @@ import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; -import { parseLink } from '../../lib/paste/utils/linkParser'; +import { createBeforePasteEventMock } from '../createBeforePasteEventMock'; +import { parseLink } from '../../../lib/paste/utils/linkParser'; import { contentModelToDom, createDomToModelContext, diff --git a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWacTest.ts b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts similarity index 99% rename from packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWacTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts index 50ff7a50e80..76e099175e2 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWacTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWacTest.ts @@ -1,9 +1,9 @@ import { ContentModelDocument } from 'roosterjs-content-model-types'; -import { createBeforePasteEventMock } from './processPastedContentFromWordDesktopTest'; -import { expectEqual } from './e2e/testUtils'; +import { createBeforePasteEventMock } from '../createBeforePasteEventMock'; +import { expectEqual } from '../e2e/testUtils'; import { itChromeOnly } from 'roosterjs-content-model-dom/test/testUtils'; import { pasteDisplayFormatParser } from 'roosterjs-content-model-core/lib/override/pasteDisplayFormatParser'; -import { processPastedContentWacComponents } from '../../lib/paste/WacComponents/processPastedContentWacComponents'; +import { processPastedContentWacComponents } from '../../../lib/paste/WacComponents/processPastedContentWacComponents'; import { listItemMetadataApplier, listLevelMetadataApplier, diff --git a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWordDesktopTest.ts b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWordDesktopTest.ts similarity index 99% rename from packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWordDesktopTest.ts rename to packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWordDesktopTest.ts index 7b301c86fe8..f45d3510504 100644 --- a/packages/roosterjs-content-model-plugins/test/paste/processPastedContentFromWordDesktopTest.ts +++ b/packages/roosterjs-content-model-plugins/test/paste/word/processPastedContentFromWordDesktopTest.ts @@ -1,8 +1,8 @@ -import * as getStyleMetadata from '../../lib/paste/WordDesktop/getStyleMetadata'; -import { BeforePasteEvent, ClipboardData } from 'roosterjs-content-model-types'; -import { expectEqual } from './e2e/testUtils'; -import { processPastedContentFromWordDesktop } from '../../lib/paste/WordDesktop/processPastedContentFromWordDesktop'; -import { WordMetadata } from '../../lib/paste/WordDesktop/WordMetadata'; +import * as getStyleMetadata from '../../../lib/paste/WordDesktop/getStyleMetadata'; +import { createBeforePasteEventMock } from '../createBeforePasteEventMock'; +import { expectEqual } from '../e2e/testUtils'; +import { processPastedContentFromWordDesktop } from '../../../lib/paste/WordDesktop/processPastedContentFromWordDesktop'; +import { WordMetadata } from '../../../lib/paste/WordDesktop/WordMetadata'; import { createDomToModelContext, domToContentModel, @@ -5223,30 +5223,6 @@ describe('processPastedContentFromWordDesktopTest', () => { }); }); -export function createBeforePasteEventMock( - fragment: DocumentFragment, - htmlBefore: string = '' -): BeforePasteEvent { - return { - eventType: 'beforePaste', - clipboardData: {}, - fragment: fragment, - htmlBefore, - htmlAfter: '', - htmlAttributes: {}, - pasteType: 'normal', - domToModelOption: { - additionalAllowedTags: [], - additionalDisallowedTags: [], - additionalFormatParsers: {}, - attributeSanitizers: {}, - formatParserOverride: {}, - processorOverride: {}, - styleSanitizers: {}, - }, - }; -} - function createListElementFromWord( tag: string, content: string, From 1d8ace40ffd8412b426e15741791c95a53997425 Mon Sep 17 00:00:00 2001 From: biwu Date: Thu, 20 Feb 2025 23:51:33 -0800 Subject: [PATCH 21/23] version bumpup --- .../lib/compatibleEnum/Alignment.ts | 19 ++ .../lib/compatibleEnum/BulletListType.ts | 59 ++++ .../lib/compatibleEnum/Capitalization.ts | 26 ++ .../lib/compatibleEnum/ChangeSource.ts | 71 +++++ .../lib/compatibleEnum/ClearFormatMode.ts | 19 ++ .../compatibleEnum/ColorTransformDirection.ts | 14 + .../lib/compatibleEnum/ContentPosition.ts | 36 +++ .../lib/compatibleEnum/ContentType.ts | 29 ++ .../compatibleEnum/DarkModeDatasetNames.ts | 24 ++ .../lib/compatibleEnum/DefinitionType.ts | 34 +++ .../lib/compatibleEnum/DelimiterClasses.ts | 14 + .../lib/compatibleEnum/Direction.ts | 14 + .../lib/compatibleEnum/DocumentCommand.ts | 261 ++++++++++++++++++ .../lib/compatibleEnum/DocumentPosition.ts | 35 +++ .../lib/compatibleEnum/EntityClasses.ts | 24 ++ .../lib/compatibleEnum/EntityOperation.ts | 72 +++++ .../compatibleEnum/ExperimentalFeatures.ts | 155 +++++++++++ .../lib/compatibleEnum/FontSizeChange.ts | 15 + .../lib/compatibleEnum/GetContentMode.ts | 36 +++ .../lib/compatibleEnum/ImageEditOperation.ts | 44 +++ .../lib/compatibleEnum/Indentation.ts | 15 + .../lib/compatibleEnum/Keys.ts | 53 ++++ .../KnownCreateElementDataIndex.ts | 69 +++++ .../compatibleEnum/KnownPasteSourceType.ts | 13 + .../lib/compatibleEnum/ListType.ts | 22 ++ .../lib/compatibleEnum/NodeType.ts | 46 +++ .../lib/compatibleEnum/NumberingListType.ts | 114 ++++++++ .../lib/compatibleEnum/PluginEventType.ts | 124 +++++++++ .../lib/compatibleEnum/PositionType.ts | 24 ++ .../lib/compatibleEnum/QueryScope.ts | 20 ++ .../lib/compatibleEnum/RegionType.ts | 9 + .../lib/compatibleEnum/SelectionRangeTypes.ts | 17 ++ .../lib/compatibleEnum/TableBorderFormat.ts | 81 ++++++ .../lib/compatibleEnum/TableOperation.ts | 119 ++++++++ .../lib/compatibleEnum/index.ts | 34 +++ 35 files changed, 1761 insertions(+) create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Alignment.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/BulletListType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Capitalization.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ChangeSource.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ClearFormatMode.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ColorTransformDirection.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ContentPosition.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ContentType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DarkModeDatasetNames.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DefinitionType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DelimiterClasses.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Direction.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DocumentCommand.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DocumentPosition.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/EntityClasses.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/EntityOperation.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ExperimentalFeatures.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/FontSizeChange.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/GetContentMode.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ImageEditOperation.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Indentation.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Keys.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/KnownCreateElementDataIndex.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/KnownPasteSourceType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ListType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/NodeType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/NumberingListType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/PluginEventType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/PositionType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/QueryScope.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/RegionType.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/SelectionRangeTypes.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/TableBorderFormat.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/TableOperation.ts create mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/index.ts diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Alignment.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Alignment.ts new file mode 100644 index 00000000000..eea2b54e6ed --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/Alignment.ts @@ -0,0 +1,19 @@ +/** + * enum for setting block alignment, used by setAlignment API + */ +export enum CompatibleAlignment { + /** + * Align left + */ + Left, + + /** + * Align center + */ + Center, + + /** + * Align right + */ + Right, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/BulletListType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/BulletListType.ts new file mode 100644 index 00000000000..2abb4efddda --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/BulletListType.ts @@ -0,0 +1,59 @@ +/** + * Enum used to control the different types of bullet list + */ +export enum CompatibleBulletListType { + /** + * Minimum value of the enum + */ + Min = 1, + + /** + * Bullet triggered by * + */ + Disc = 1, + + /** + * Bullet triggered by - + */ + Dash = 2, + + /** + * Bullet triggered by -- + */ + Square = 3, + + /** + * Bullet triggered by > + */ + ShortArrow = 4, + + /** + * Bullet triggered by -> + */ + LongArrow = 5, + + /** + * Bullet triggered by => + */ + UnfilledArrow = 6, + + /** + * Bullet triggered by — + */ + Hyphen = 7, + + /** + * Bullet triggered by --> + */ + DoubleLongArrow = 8, + + /** + * Bullet type circle + */ + Circle = 9, + + /** + * Maximum value of the enum + */ + Max = 9, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Capitalization.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Capitalization.ts new file mode 100644 index 00000000000..3edc4ece0a6 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/Capitalization.ts @@ -0,0 +1,26 @@ +/** + * The enum used for controlling the capitalization of text. + * Used by changeCapitalization API + */ +export enum CompatibleCapitalization { + /** + * Transforms the first character after punctuation mark followed by space + * to uppercase and the rest of characters to lowercase. + */ + Sentence = 'sentence', + + /** + * Transforms all characters to lowercase + */ + Lowercase = 'lowercase', + + /** + * Transforms all characters to uppercase + */ + Uppercase = 'uppercase', + + /** + * Transforms the first character of each word to uppercase + */ + CapitalizeEachWord = 'capitalize', +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ChangeSource.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ChangeSource.ts new file mode 100644 index 00000000000..9661e6c4df3 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/ChangeSource.ts @@ -0,0 +1,71 @@ +/** + * Possible change sources. Here are the predefined sources. + * It can also be other string if the change source can't fall into these sources. + */ +export enum CompatibleChangeSource { + /** + * Content changed by auto link + */ + AutoLink = 'AutoLink', + + /** + * Content changed by create link + */ + CreateLink = 'CreateLink', + + /** + * Content changed by format + */ + Format = 'Format', + + /** + * Content changed by image resize + */ + ImageResize = 'ImageResize', + + /** + * Content changed by paste + */ + Paste = 'Paste', + + /** + * Content changed by setContent API + */ + SetContent = 'SetContent', + + /** + * Content changed by cut operation + */ + Cut = 'Cut', + + /** + * Content changed by drag & drop operation + */ + Drop = 'Drop', + + /** + * Insert a new entity into editor + */ + InsertEntity = 'InsertEntity', + + /** + * Editor is switched to dark mode, content color is changed + */ + SwitchToDarkMode = 'SwitchToDarkMode', + + /** + * Editor is switched to light mode, content color is changed + */ + SwitchToLightMode = 'SwitchToLightMode', + + /** + * List chain reorganized numbers of lists + */ + ListChain = 'ListChain', + + /** + * Keyboard event, used by Content Model. + * Data of this event will be the key code number + */ + Keyboard = 'Keyboard', +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ClearFormatMode.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ClearFormatMode.ts new file mode 100644 index 00000000000..8f95a3a19d6 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/ClearFormatMode.ts @@ -0,0 +1,19 @@ +/** + * Represents the strategy to clear the format of the current editor selection + */ +export enum CompatibleClearFormatMode { + /** + * Inline format. Remove text format. + */ + Inline, + + /** + * BLock format. Remove text and structure format of the block. + */ + Block, + + /** + * Detect Inline or Block format based on the current editor selector. + */ + AutoDetect, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ColorTransformDirection.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ColorTransformDirection.ts new file mode 100644 index 00000000000..a73f1368dae --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/ColorTransformDirection.ts @@ -0,0 +1,14 @@ +/** + * Represents the mode of color transformation + */ +export enum CompatibleColorTransformDirection { + /** + * Transform from light to dark + */ + LightToDark = 0, + + /** + * Transform from dark to light + */ + DarkToLight = 1, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ContentPosition.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ContentPosition.ts new file mode 100644 index 00000000000..bf606244567 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/ContentPosition.ts @@ -0,0 +1,36 @@ +/** + * The position. Mostly used for content insertion and traversing + * On insertion, we will need to specify where we want the content to be placed (begin, end, selection or outside) + * On content traversing, we will need to specify the start position of traversing + */ +export enum CompatibleContentPosition { + /** + * Begin of the container + */ + Begin, + + /** + * End of the container + */ + End, + + /** + * End of the content div domain. + */ + DomEnd, + + /** + * Selection start + */ + SelectionStart, + + /** + * Outside of editor + */ + Outside, + + /** + * Manually defined range + */ + Range, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ContentType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ContentType.ts new file mode 100644 index 00000000000..2702b39f95e --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/ContentType.ts @@ -0,0 +1,29 @@ +/** + * Prefix of content types + */ +export enum CompatibleContentTypePrefix { + /** + * Text type prefix + */ + Text = 'text/', + + /** + * Image type prefix + */ + Image = 'image/', +} + +/** + * Known content types + */ +export enum CompatibleContentType { + /** + * Plain text content type + */ + PlainText = 'text/plain', + + /** + * HTML content type + */ + HTML = 'text/html', +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DarkModeDatasetNames.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DarkModeDatasetNames.ts new file mode 100644 index 00000000000..d9220428fc9 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/DarkModeDatasetNames.ts @@ -0,0 +1,24 @@ +/** + * Constants string for dataset names used by dark mode + */ +export enum CompatibleDarkModeDatasetNames { + /** + * Original style text color + */ + OriginalStyleColor = 'ogsc', + + /** + * Original style background color + */ + OriginalStyleBackgroundColor = 'ogsb', + + /** + * Original attribute text color + */ + OriginalAttributeColor = 'ogac', + + /** + * Original attribute background color + */ + OriginalAttributeBackgroundColor = 'ogab', +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DefinitionType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DefinitionType.ts new file mode 100644 index 00000000000..6e2ab905ee0 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/DefinitionType.ts @@ -0,0 +1,34 @@ +/** + * Types of definitions, used by Definition type + */ +export enum CompatibleDefinitionType { + /** + * Boolean type definition, represents a boolean type value + */ + Boolean, + + /** + * Number type definition, represents a number type value + */ + Number, + + /** + * String type definition, represents a string type value + */ + String, + + /** + * Array type definition, represents an array with a given item type + */ + Array, + + /** + * Object type definition, represents an object with the given property types + */ + Object, + + /** + * Customize type definition, represents a customized type with a validator function + */ + Customize, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DelimiterClasses.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DelimiterClasses.ts new file mode 100644 index 00000000000..f12953fe0ec --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/DelimiterClasses.ts @@ -0,0 +1,14 @@ +/** + * Class names for Delimiter + */ +export enum CompatibleDelimiterClasses { + /** + * Class name to specify this delimiter is before an entity + */ + DELIMITER_BEFORE = 'entityDelimiterBefore', + + /** + * Class name to specify this delimiter is after an entity + */ + DELIMITER_AFTER = 'entityDelimiterAfter', +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Direction.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Direction.ts new file mode 100644 index 00000000000..cccaddb1c45 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/Direction.ts @@ -0,0 +1,14 @@ +/** + * enum for setting block direction, used by setDirection API + */ +export enum CompatibleDirection { + /** + * Left to right + */ + LeftToRight, + + /** + * Right to left + */ + RightToLeft, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentCommand.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentCommand.ts new file mode 100644 index 00000000000..1e17f8ede4a --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentCommand.ts @@ -0,0 +1,261 @@ +/** + * Command strings for Document.execCommand() API + * https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand + */ +export enum CompatibleDocumentCommand { + /** + * Changes the browser auto-link behavior (Internet Explorer only) + */ + AutoUrlDetect = 'AutoUrlDetect', + + /** + * Changes the document background color. In styleWithCss mode, it affects the background color of the containing block instead. + * This requires a <color> value string to be passed in as a value argument. Note that Internet Explorer uses this to set the + * text background color. + */ + BackColor = 'backColor', + + /** + * Toggles bold on/off for the selection or at the insertion point. Internet Explorer uses the <strong> tag instead of <b>. + */ + Bold = 'bold', + + /** + * Clears all authentication credentials from the cache. + */ + ClearAuthenticationCache = 'ClearAuthenticationCache', + + /** + * Makes the content document either read-only or editable. This requires a boolean true/false as the value argument. + * (Not supported by Internet Explorer.) + */ + ContentReadOnly = 'contentReadOnly', + + /** + * Copies the current selection to the clipboard. Conditions of having this behavior enabled vary from one browser to another, + * and have evolved over time. Check the compatibility table to determine if you can use it in your case. + */ + Copy = 'copy', + + /** + * Creates an hyperlink from the selection, but only if there is a selection. Requires a URI string as a value argument for the + * hyperlink's href. The URI must contain at least a single character, which may be whitespace. + * (Internet Explorer will create a link with a null value.) + */ + CreateLink = 'createLink', + + /** + * Removes the current selection and copies it to the clipboard. When this behavior is enabled varies between browsers, + * and its conditions have evolved over time. Check the compatibility table for usage details. + */ + Cut = 'cut', + + /** + * Adds a <small> tag around the selection or at the insertion point. (Not supported by Internet Explorer.) + */ + DecreaseFontSize = 'decreaseFontSize', + + /** + * Changes the paragraph separator used when new paragraphs are created in editable text regions. See Differences in markup + * generation for more details. + */ + DefaultParagraphSeparator = 'defaultParagraphSeparator', + + /** + * Deletes the current selection. + */ + Delete = 'delete', + + /** + * Enables or disables the table row/column insertion and deletion controls. (Not supported by Internet Explorer.) + */ + EnableInlineTableEditing = 'enableInlineTableEditing', + + /** + * Enables or disables the resize handles on images and other resizable objects. (Not supported by Internet Explorer.) + */ + EnableObjectResizing = 'enableObjectResizing', + + /** + * Changes the font name for the selection or at the insertion point. This requires a font name string (like "Arial") + * as a value argument. + */ + FontName = 'fontName', + + /** + * Changes the font size for the selection or at the insertion point. This requires an integer from 1-7 as a value argument. + */ + FontSize = 'fontSize', + + /** + * Changes a font color for the selection or at the insertion point. This requires a hexadecimal color value string + * as a value argument. + */ + ForeColor = 'foreColor', + + /** + * Adds an HTML block-level element around the line containing the current selection, replacing the block element containing + * the line if one exists (in Firefox, <blockquote> is the exception — it will wrap any containing block element). + * Requires a tag-name string as a value argument. Virtually all block-level elements can be used. + * (Internet Explorer supports only heading tags H1–H6, ADDRESS, and PRE, which must be wrapped in angle brackets, such as "<H1>".) + */ + FormatBlock = 'formatBlock', + + /** + * Deletes the character ahead of the cursor's position, identical to hitting the Delete key on a Windows keyboard. + */ + ForwardDelete = 'forwardDelete', + + /** + * Adds a heading element around a selection or insertion point line. Requires the tag-name strings a value argument (i.e. "H1", "H6"). + * (Not supported by Internet Explorer and Safari.) + */ + Heading = 'heading', + + /** + * Changes the background color for the selection or at the insertion point. Requires a color value string as a value argument. + * useCSS must be true for this to function. (Not supported by Internet Explorer.) + */ + HiliteColor = 'hiliteColor', + + /** + * Adds a <big> tag around the selection or at the insertion point. (Not supported by Internet Explorer.) + */ + IncreaseFontSize = 'increaseFontSize', + + /** + * Indents the line containing the selection or insertion point. In Firefox, if the selection spans multiple lines at different + * levels of indentation, only the least indented lines in the selection will be indented. + */ + Indent = 'indent', + + /** + * Controls whether the Enter key inserts a <br> element, or splits the current block element into two. + * (Not supported by Internet Explorer.) + */ + InsertBrOnReturn = 'insertBrOnReturn', + + /** + * Inserts a <hr> element at the insertion point, or replaces the selection with it. + */ + InsertHorizontalRule = 'insertHorizontalRule', + + /** + * Inserts an HTML string at the insertion point (deletes selection). Requires a valid HTML string as a value argument. + * (Not supported by Internet Explorer.) + */ + InsertHTML = 'insertHTML', + + /** + * Inserts an image at the insertion point (deletes selection). Requires a URL string for the image's src as a value argument. + * The requirements for this string are the same as createLink. + */ + InsertImage = 'insertImage', + + /** + * Creates a numbered ordered list for the selection or at the insertion point. + */ + InsertOrderedList = 'insertOrderedList', + + /** + * Creates a bulleted unordered list for the selection or at the insertion point. + */ + InsertUnorderedList = 'insertUnorderedList', + + /** + * Inserts a paragraph around the selection or the current line. + * (Internet Explorer inserts a paragraph at the insertion point and deletes the selection.) + */ + InsertParagraph = 'insertParagraph', + + /** + * Inserts the given plain text at the insertion point (deletes selection). + */ + InsertText = 'insertText', + + /** + * Toggles italics on/off for the selection or at the insertion point. + * (Internet Explorer uses the <em> element instead of <i>.) + */ + Italic = 'italic', + + /** + * Centers the selection or insertion point. + */ + JustifyCenter = 'justifyCenter', + + /** + * Justifies the selection or insertion point. + */ + JustifyFull = 'justifyFull', + + /** + * Justifies the selection or insertion point to the left. + */ + JustifyLeft = 'justifyLeft', + + /** + * Right-justifies the selection or the insertion point. + */ + JustifyRight = 'justifyRight', + + /** + * Outdents the line containing the selection or insertion point. + */ + Outdent = 'outdent', + + /** + * Pastes the clipboard contents at the insertion point (replaces current selection). Disabled for web content. See [1]. + */ + Paste = 'paste', + + /** + * Redoes the previous undo command. + */ + Redo = 'redo', + + /** + * Removes all formatting from the current selection. + */ + RemoveFormat = 'removeFormat', + + /** + * Selects all of the content of the editable region. + */ + SelectAll = 'selectAll', + + /** + * Toggles strikethrough on/off for the selection or at the insertion point. + */ + StrikeThrough = 'strikeThrough', + + /** + * Toggles subscript on/off for the selection or at the insertion point. + */ + Subscript = 'subscript', + + /** + * Toggles superscript on/off for the selection or at the insertion point. + */ + Superscript = 'superscript', + + /** + * Toggles underline on/off for the selection or at the insertion point. + */ + Underline = 'underline', + + /** + * Undoes the last executed command. + */ + Undo = 'undo', + + /** + * Removes the anchor element from a selected hyperlink. + */ + Unlink = 'unlink', + + /** + * Replaces the useCSS command. true modifies/generates style attributes in markup, false generates presentational elements. + */ + StyleWithCSS = 'styleWithCSS', +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentPosition.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentPosition.ts new file mode 100644 index 00000000000..7fe7d01053e --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentPosition.ts @@ -0,0 +1,35 @@ +/** + * The is essentially an enum representing result from browser compareDocumentPosition API + * https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition + */ +export enum CompatibleDocumentPosition { + /** + * Same node + */ + Same = 0, + + /** + * Node is disconnected from document + */ + Disconnected = 1, + + /** + * Node is preceding the comparing node + */ + Preceding = 2, + + /** + * Node is following the comparing node + */ + Following = 4, + + /** + * Node contains the comparing node + */ + Contains = 8, + + /** + * Node is contained by the comparing node + */ + ContainedBy = 16, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/EntityClasses.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/EntityClasses.ts new file mode 100644 index 00000000000..a314e90bad7 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/EntityClasses.ts @@ -0,0 +1,24 @@ +/** + * CSS Class names for Entity + */ +export enum CompatibleEntityClasses { + /** + * Class name to specify this is an entity + */ + ENTITY_INFO_NAME = '_Entity', + + /** + * Class name to specify the type of an entity + */ + ENTITY_TYPE_PREFIX = '_EType_', + + /** + * Class name to specify the ID of an entity + */ + ENTITY_ID_PREFIX = '_EId_', + + /** + * Class name to specify if the entity is readonly + */ + ENTITY_READONLY_PREFIX = '_EReadonly_', +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/EntityOperation.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/EntityOperation.ts new file mode 100644 index 00000000000..782760b993c --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/EntityOperation.ts @@ -0,0 +1,72 @@ +/** + * Define possible operations to an entity + */ +export enum CompatibleEntityOperation { + /** + * Notify plugins that there is a new plugin was added into editor. + * Plugin can handle this event to entity hydration. + * This event will be only fired once for each entity DOM node. + * After undo, or copy/paste, since new DOM nodes were added, this event will be fired + * for those entities represented by newly added nodes. + */ + NewEntity, + + /** + * Notify plugins that user is clicking target to an entity + */ + Click, + + /** + * Notify plugins that user is triggering context menu to an entity + */ + ContextMenu, + + /** + * Notify plugins that user is escaping from an entity by pressing ESC key + */ + Escape, + + /** + * Notify plugins that user is removing an entity from its start position using DELETE key + */ + RemoveFromStart, + + /** + * Notify plugins that user is remove an entity from its end position using BACKSPACE key + */ + RemoveFromEnd, + + /** + * Notify plugins that an entity is being overwritten. + * This can be caused by key in, cut, paste, delete, backspace ... on a selection + * which contains some entities. + */ + Overwrite, + + /** + * Notify plugins that an entity is being partially overwritten. + * This happens when user selected part of the entity then do key press, or cut, paste, delete, backspace, ... + */ + PartialOverwrite, + + /** + * Notify plugins that editor is generating HTML content for save. + * Plugin should use this event to remove any temporary content, and only leave DOM nodes that + * should be saved as HTML string. + * This event will provide a cloned DOM tree for each entity, do NOT compare the DOM nodes with cached nodes + * because it will always return false. + */ + ReplaceTemporaryContent, + + /** + * Notify plugins that editor has attached shadow root for an entity. + * Plugins can handle this event to do extra operations to the shadow root + */ + AddShadowRoot, + + /** + * Notify plugins that editor has removed the shadow root of an entity + * Plugins can handle this event to do any necessary clean up for shadow root + */ + RemoveShadowRoot, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ExperimentalFeatures.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ExperimentalFeatures.ts new file mode 100644 index 00000000000..4dc52c67f16 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/ExperimentalFeatures.ts @@ -0,0 +1,155 @@ +/** + * Experimental feature flags + */ +export enum CompatibleExperimentalFeatures { + // #region Graduated and deprecated features. + // These features will be removed in next major release + /** + * @deprecated This feature is always enabled + */ + NewIndentation = 'NewIndentation', + + /** + * @deprecated This feature is always enabled + */ + NewBullet = 'NewBullet', + + /** + * @deprecated This feature is always enabled + */ + NewNumbering = 'NewNumbering', + + /** + * @deprecated This feature is always enabled + */ + ListChain = 'ListChain', + + /** + * @deprecated This feature is always enabled + */ + MergePastedLine = 'MergePastedLine', + + /** + * @deprecated This feature is always enabled + */ + SingleDirectionResize = 'SingleDirectionResize', + + /** + * @deprecated This feature is always enabled + */ + PasteWithLinkPreview = 'PasteWithLinkPreview', + + /** + * @deprecated This feature is always enabled + */ + ImageRotate = 'ImageRotate', + + /** + * @deprecated This feature is always enabled + */ + ImageCrop = 'ImageCrop', + + /** + * @deprecated This feature is always enabled + * Check if the element has a style attribute, if not, apply the default format + */ + AlwaysApplyDefaultFormat = 'AlwaysApplyDefaultFormat', + + /** + * @deprecated This feature can be enabled/disabled using Paste Plugin contructor param + * Paste the Html instead of the Img when the Html Body only have one IMG Child node + */ + ConvertSingleImageBody = 'ConvertSingleImageBody', + + /** + * @deprecated This feature is always enabled + * Align table elements to left, center and right using setAlignment API + */ + TableAlignment = 'TableAlignment', + + /** + * @deprecated this feature is always enabled + * Provide a circular resize handles that adaptive the number od handles to the size of the image + */ + AdaptiveHandlesResizer = 'AdaptiveHandlesResizer', + + /** + * @deprecated this feature is always disabled + * Automatically transform -- into hyphen, if typed between two words. + */ + AutoHyphen = 'AutoHyphen', + + /** + * @deprecated this feature is always disabled + * Use pending format strategy to do style based format, e.g. Font size, Color. + * With this feature enabled, we don't need to insert temp ZeroWidthSpace character to hold pending format + * when selection is collapsed. Instead, we will hold the pending format in memory and only apply it when type something + */ + PendingStyleBasedFormat = 'PendingStyleBasedFormat', + + /** + * @deprecated this feature is always disabled + * Normalize list to make sure it can be displayed correctly in other client + * e.g. We will move list items with "display: block" into previous list item and change tag to be DIV + */ + NormalizeList = 'NormalizeList', + + /** + * @deprecated this feature is always enabled + * When a html image is selected, the selected image data will be stored by editor core. + */ + ImageSelection = 'ImageSelection', + + //#endregion + + /** + * Provide additional Tab Key Features. Requires Text Features Content Editable Features + */ + TabKeyTextFeatures = 'TabKeyTextFeatures', + + /** + * Align list elements elements to left, center and right using setAlignment API + */ + ListItemAlignment = 'ListItemAlignment', + + /** + * Trigger formatting by a especial characters. Ex: (A), 1. i). + */ + AutoFormatList = 'AutoFormatList', + + /** + * With this feature enabled, when writing back a list item we will re-use all + * ancestor list elements, even if they don't match the types currently in the + * listTypes array for that item. The only list that we will ensure is correct + * is the one closest to the item. + */ + ReuseAllAncestorListElements = 'ReuseAllAncestorListElements', + + /** + * When apply default format when initialize or user type, apply the format on a SPAN element rather than + * the block element (In most case, the DIV element) so keep the block element clean. + */ + DefaultFormatInSpan = 'DefaultFormatInSpan', + + /** + * Use variable-based dark mode solution rather than dataset-based solution. + * When enable this feature, need to pass in a DarkModelHandler object to each call of setColor and applyFormat + * if you need them work for dark mode + */ + VariableBasedDarkColor = 'VariableBasedDarkColor', + + /** + * Reuse existing DOM structure if possible when convert Content Model back to DOM tree + */ + ReusableContentModel = 'ReusableContentModel', + + /** + * Delete table with Backspace key with the whole was selected with table selector + */ + DeleteTableWithBackspace = 'DeleteTableWithBackspace', + + /** + * Add entities around a Read Only Inline entity to prevent cursor to be hidden when cursor is next of it. + */ + InlineEntityReadOnlyDelimiters = 'InlineEntityReadOnlyDelimiters', +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/FontSizeChange.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/FontSizeChange.ts new file mode 100644 index 00000000000..4fde0c39cae --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/FontSizeChange.ts @@ -0,0 +1,15 @@ +/** + * The enum used for increase or decrease font size + * Used by setFontSize API + */ +export enum CompatibleFontSizeChange { + /** + * Increase font size + */ + Increase, + + /** + * Decrease font size + */ + Decrease, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/GetContentMode.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/GetContentMode.ts new file mode 100644 index 00000000000..de26b8b1489 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/GetContentMode.ts @@ -0,0 +1,36 @@ +/** + * Represents a mode number to indicate what kind of content to retrieve when call Editor.getContent() + */ +export enum CompatibleGetContentMode { + /** + * The clean content without any temporary content only for editor. + * This is the default value. Call to Editor.getContent() with trigger an ExtractContentWithDom event + * so that plugins can remove their temporary content, and will return the HTML content + * which is ready for save to storage. + */ + CleanHTML = 0, + + /** + * Retrieve the raw HTML string in current editor. Temporary content will be included. + */ + RawHTMLOnly = 1, + + /** + * Retrieve the raw HTML string in current editor with a selection marker. This can be used for + * save current editor state and call to SetContent with this result can fully restore editor state + * including current selection + */ + RawHTMLWithSelection = 2, + + /** + * Get plain text content only, all format will be ignored + */ + PlainText, + + /** + * A fast way to get plain text content, the line-end positions may not be exactly same with HTML content, + * but the text content should be the same. This is used for quickly retrieve text content and check + * text only + */ + PlainTextFast, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ImageEditOperation.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ImageEditOperation.ts new file mode 100644 index 00000000000..8221b460cc5 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/ImageEditOperation.ts @@ -0,0 +1,44 @@ +/** + * Operation flags for ImageEdit plugin + */ +export enum CompatibleImageEditOperation { + /** + * No operation + */ + None = 0, + + /** + * Resize image from corner ( horizontal and vertical resize) + */ + CornerResize = 1, + + /** + * Resize image from side ( either horizontal or vertical resize) + */ + SideResize = 2, + + /** + * Image resize + */ + Resize = CornerResize | SideResize, + + /** + * Image rotate + */ + Rotate = 4, + + /** + * Image resize and rotate + */ + ResizeAndRotate = Resize | Rotate, + + /** + * Image crop + */ + Crop = 8, + + /** + * All operations + */ + All = ResizeAndRotate | Crop, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Indentation.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Indentation.ts new file mode 100644 index 00000000000..2e99b87a040 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/Indentation.ts @@ -0,0 +1,15 @@ +/** + * The enum used for increase or decrease indentation of a block + * Used by setIndentation API + */ +export enum CompatibleIndentation { + /** + * Increase indentation + */ + Increase, + + /** + * Decrease indentation + */ + Decrease, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Keys.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Keys.ts new file mode 100644 index 00000000000..5f0ebe4b6df --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/Keys.ts @@ -0,0 +1,53 @@ +/** + * Key numbers common used keys + */ +export enum CompatibleKeys { + NULL = 0, + BACKSPACE = 8, + TAB = 9, + ENTER = 13, + SHIFT = 16, + CTRL_LEFT = 17, + ALT = 18, + ESCAPE = 27, + SPACE = 32, + PAGEUP = 33, + END = 35, + HOME = 36, + LEFT = 37, + UP = 38, + RIGHT = 39, + DOWN = 40, + PRINT_SCREEN = 44, + DELETE = 46, + /** + * @deprecated Just for backward compatibility + */ + EIGHT_ASTIRISK = 56, + EIGHT_ASTERISK = 56, + B = 66, + I = 73, + U = 85, + Y = 89, + Z = 90, + META_LEFT = 91, + COMMA = 188, + DASH_UNDERSCORE = 189, + PERIOD = 190, + /** + * @deprecated Just for backward compatibility + */ + FORWARDSLASH = 191, + FORWARD_SLASH = 191, + GRAVE_TILDE = 192, + + /** + * Keys below are non-standard, and should be used in ContentEditFeatures only + */ + CONTENTCHANGED = 0x101, + RANGE = 0x102, + + Ctrl = 0x1000, + Meta = 0x2000, + Shift = 0x4000, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/KnownCreateElementDataIndex.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/KnownCreateElementDataIndex.ts new file mode 100644 index 00000000000..1b8e45acf2a --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/KnownCreateElementDataIndex.ts @@ -0,0 +1,69 @@ +/** + * Index of known CreateElementData used by createElement function + */ +export enum CompatibleKnownCreateElementDataIndex { + /** + * Set a none value to help createElement function ignore falsy value + */ + None = 0, + + /** + * An empty line without format + */ + EmptyLine = 1, + + /** + * Wrapper for blockquote + */ + BlockquoteWrapper = 2, + + /** + * Temp DIV for copy/paste + */ + CopyPasteTempDiv = 3, + + /** + * ListItem with block style + */ + BlockListItem = 4, + + /** + * Wrapper element for context menu + */ + ContextMenuWrapper = 5, + + /** + * Wrapper element for image edit + */ + ImageEditWrapper = 6, + + /** + * @deprecated + */ + TableHorizontalResizer = 7, + + /** + * @deprecated + */ + TableVerticalResizer = 8, + + /** + * @deprecated + */ + TableResizerLTR = 9, + + /** + * @deprecated + */ + TableResizerRTL = 10, + + /** + * @deprecated + */ + TableSelector = 11, + + /** + * An empty line without format with span inside of it. + */ + EmptyLineFormatInSpan = 12, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/KnownPasteSourceType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/KnownPasteSourceType.ts new file mode 100644 index 00000000000..1a9c309b6e8 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/KnownPasteSourceType.ts @@ -0,0 +1,13 @@ +/** + * Represent the types of sources to handle in the Paste Plugin + */ +export enum CompatibleKnownPasteSourceType { + WordDesktop, + ExcelDesktop, + ExcelOnline, + PowerPointDesktop, + GoogleSheets, + WacComponents, + Default, + SingleImage, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ListType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ListType.ts new file mode 100644 index 00000000000..a4fc67c562a --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/ListType.ts @@ -0,0 +1,22 @@ +/** + * Type of list (numbering or bullet) + */ +export enum CompatibleListType { + /** + * None list type + * It means this is not a list + */ + None = 0, + + /** + * Ordered List type (numbering list) + * Represented by "OL" tag + */ + Ordered = 1, + + /** + * Unordered List type (bullet list) + * Represented by "UL" tag + */ + Unordered = 2, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/NodeType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/NodeType.ts new file mode 100644 index 00000000000..f5115a3eb62 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/NodeType.ts @@ -0,0 +1,46 @@ +/** + * The is essentially an enum represents the type of the node + * https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType + * Values not listed here are deprecated. + */ +export enum CompatibleNodeType { + /** + * An Element node such as <p> or <div>. + */ + Element = 1, + + /** + * An Attribute node such as name="value". + */ + Attribute = 2, + + /** + * The actual Text of Element or Attr. + */ + Text = 3, + + /** + * A ProcessingInstruction of an XML document such as <?xml-stylesheet ... ?> declaration. + */ + ProcessingInstruction = 7, + + /** + * A Comment node. + */ + Comment = 8, + + /** + * A Document node. + */ + Document = 9, + + /** + * A DocumentType node e.g. <!DOCTYPE html> for HTML5 documents. + */ + DocumentType = 10, + + /** + * A DocumentFragment node. + */ + DocumentFragment = 11, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/NumberingListType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/NumberingListType.ts new file mode 100644 index 00000000000..5deefc28491 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/NumberingListType.ts @@ -0,0 +1,114 @@ +/** + * Enum used to control the different types of numbering list + */ +export enum CompatibleNumberingListType { + /** + * Minimum value of the enum + */ + Min = 1, + + /** + * Numbering triggered by 1. + */ + Decimal = 1, + + /** + * Numbering triggered by 1- + */ + DecimalDash = 2, + + /** + * Numbering triggered by 1) + */ + DecimalParenthesis = 3, + + /** + * Numbering triggered by (1) + */ + DecimalDoubleParenthesis = 4, + + /** + * Numbering triggered by a. + */ + LowerAlpha = 5, + + /** + * Numbering triggered by a) + */ + LowerAlphaParenthesis = 6, + + /** + * Numbering triggered by (a) + */ + LowerAlphaDoubleParenthesis = 7, + + /** + * Numbering triggered by a- + */ + LowerAlphaDash = 8, + + /** + * Numbering triggered by A. + */ + UpperAlpha = 9, + + /** + * Numbering triggered by A) + */ + UpperAlphaParenthesis = 10, + + /** + * Numbering triggered by (A) + */ + UpperAlphaDoubleParenthesis = 11, + + /** + * Numbering triggered by A- + */ + UpperAlphaDash = 12, + + /** + * Numbering triggered by i. + */ + LowerRoman = 13, + + /** + * Numbering triggered by i) + */ + LowerRomanParenthesis = 14, + + /** + * Numbering triggered by (i) + */ + LowerRomanDoubleParenthesis = 15, + + /** + * Numbering triggered by i- + */ + LowerRomanDash = 16, + + /** + * Numbering triggered by I. + */ + UpperRoman = 17, + + /** + * Numbering triggered by I) + */ + UpperRomanParenthesis = 18, + + /** + * Numbering triggered by (I) + */ + UpperRomanDoubleParenthesis = 19, + + /** + * Numbering triggered by I- + */ + UpperRomanDash = 20, + + /** + * Maximum value of the enum + */ + Max = 20, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/PluginEventType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/PluginEventType.ts new file mode 100644 index 00000000000..7aa377617d1 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/PluginEventType.ts @@ -0,0 +1,124 @@ +/** + * Editor plugin event type + */ +export enum CompatiblePluginEventType { + /** + * HTML KeyDown event + */ + KeyDown = 0, + + /** + * HTML KeyPress event + */ + KeyPress = 1, + + /** + * HTML KeyUp event + */ + KeyUp = 2, + + /** + * HTML Input / TextInput event + */ + Input = 3, + + /** + * HTML CompositionEnd event + */ + CompositionEnd = 4, + + /** + * HTML MouseDown event + */ + MouseDown = 5, + + /** + * HTML MouseUp event + */ + MouseUp = 6, + + /** + * Content changed event + */ + ContentChanged = 7, + + /** + * Extract Content with a DOM tree event + * This event is triggered when getContent() is called with triggerExtractContentEvent = true + * Plugin can handle this event to remove the UI only markups to return clean HTML + * by operating on a cloned DOM tree + */ + ExtractContentWithDom = 8, + + /** + * Before Paste event, provide a chance to change copied content + */ + BeforeCutCopy = 9, + + /** + * Before Paste event, provide a chance to change paste content + */ + BeforePaste = 10, + + /** + * Let plugin know editor is ready now + */ + EditorReady = 11, + + /** + * Let plugin know editor is about to dispose + */ + BeforeDispose = 12, + + /** + * Pending format state (bold, italic, underline, ... with collapsed selection) is changed + */ + PendingFormatStateChanged = 13, + + /** + * Scroll event triggered by scroll container + */ + Scroll = 14, + + /** + * Operating on an entity. See enum EntityOperation for more details about each operation + */ + EntityOperation = 15, + + /** + * HTML ContextMenu event + */ + ContextMenu = 16, + + /** + * Editor has entered shadow edit mode + */ + EnteredShadowEdit = 17, + + /** + * Editor is about to leave shadow edit mode + */ + LeavingShadowEdit = 18, + + /** + * Content of image is being changed from client side + */ + EditImage = 19, + + /** + * Content of editor is about to be cleared by SetContent API, handle this event to cache anything you need + * before it is gone + */ + BeforeSetContent = 20, + + /** + * Zoom scale value is changed, triggered by Editor.setZoomScale() when set a different scale number + */ + ZoomChanged = 21, + + /** + * EXPERIMENTAL FEATURE + * Editor changed the selection. + */ + SelectionChanged = 22, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/PositionType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/PositionType.ts new file mode 100644 index 00000000000..c3818537ca8 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/PositionType.ts @@ -0,0 +1,24 @@ +/** + * Represent the type of a position + */ +export enum CompatiblePositionType { + /** + * At the beginning of a node + */ + Begin = 0, + + /** + * At the end of a node + */ + End = -1, + + /** + * Before a node + */ + Before = -2, + + /** + * After a node + */ + After = -3, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/QueryScope.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/QueryScope.ts new file mode 100644 index 00000000000..9aad2872ad3 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/QueryScope.ts @@ -0,0 +1,20 @@ +/** + * Query scope for queryElements() API + */ +export enum CompatibleQueryScope { + /** + * Query from the whole body of root node. This is default value. + */ + Body, + + /** + * Query elements on a given selection (intersect) + * The result element can contain the selection, contain part of selection, or inside selection + */ + OnSelection, + + /** + * Query elements inside a given selection only + */ + InSelection, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/RegionType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/RegionType.ts new file mode 100644 index 00000000000..44c826ce5c1 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/RegionType.ts @@ -0,0 +1,9 @@ +/** + * Type of all possible regions. Currently we only support region of Table + */ +export enum CompatibleRegionType { + /** + * Region split by Table + */ + Table, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/SelectionRangeTypes.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/SelectionRangeTypes.ts new file mode 100644 index 00000000000..ec1123724f2 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/SelectionRangeTypes.ts @@ -0,0 +1,17 @@ +/** + * Types of Selection Ranges that the SelectionRangeEx can return + */ +export enum CompatibleSelectionRangeTypes { + /** + * Normal selection range provided by browser. + */ + Normal, + /** + * Selection made inside of a single table. + */ + TableSelection, + /** + * Selection made in a image. + */ + ImageSelection, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/TableBorderFormat.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/TableBorderFormat.ts new file mode 100644 index 00000000000..6490d7231d8 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/TableBorderFormat.ts @@ -0,0 +1,81 @@ +/** + * Table format border + */ +export enum CompatibleTableBorderFormat { + /** + * All border of the table are displayed + * __ __ __ + * |__|__|__| + * |__|__|__| + * |__|__|__| + */ + DEFAULT, + + /** + * Middle vertical border are not displayed + * __ __ __ + * |__ __ __| + * |__ __ __| + * |__ __ __| + */ + LIST_WITH_SIDE_BORDERS, + + /** + * All borders except header rows borders are displayed + * __ __ __ + * __|__|__ + * __|__|__ + */ + NO_HEADER_BORDERS, + + /** + * The left and right border of the table are not displayed + * __ __ __ + * __|__|__ + * __|__|__ + * __|__|__ + */ + NO_SIDE_BORDERS, + + /** + * Only the borders that divides the header row, first column and externals are displayed + * __ __ __ + * |__ __ __| + * | | | + * |__|__ __| + */ + FIRST_COLUMN_HEADER_EXTERNAL, + + /** + * The header row has no vertical border, except for the first one + * The first column has no horizontal border, except for the first one + * __ __ __ + * |__ __ __ + * | |__|__| + * | |__|__| + */ + ESPECIAL_TYPE_1, + + /** + * The header row has no vertical border, except for the first one + * The only horizontal border of the table is the top and bottom of header row + * __ __ __ + * |__ __ __ + * | | | + * | | | + */ + ESPECIAL_TYPE_2, + + /** + * The only borders are the bottom of header row and the right border of first column + * __ __ __ + * | + * | + */ + ESPECIAL_TYPE_3, + + /** + * No border + */ + CLEAR, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/TableOperation.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/TableOperation.ts new file mode 100644 index 00000000000..fb9b5feed56 --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/TableOperation.ts @@ -0,0 +1,119 @@ +/** + * Operations used by editTable() API + */ +export enum CompatibleTableOperation { + /** + * Insert a row above current row + */ + InsertAbove, + + /** + * Insert a row below current row + */ + InsertBelow, + + /** + * Insert a column on the left of current column + */ + InsertLeft, + + /** + * Insert a column on the right of current column + */ + InsertRight, + + /** + * Delete the whole table + */ + DeleteTable, + + /** + * Delete current column + */ + DeleteColumn, + + /** + * Delete current row + */ + DeleteRow, + + /** + * Merge current row with the row above + */ + MergeAbove, + + /** + * Merge current row with the row below + */ + MergeBelow, + + /** + * Merge current column with the column on the left + */ + MergeLeft, + + /** + * Merge current column with the column on the right + */ + MergeRight, + + /** + * Merge all selected cells + */ + MergeCells, + + /** + * Split current table cell horizontally + */ + SplitHorizontally, + + /** + * Split current table cell vertically + */ + SplitVertically, + + /** + * Align current table at the center + */ + AlignCenter, + + /** + * Align current table at the left + */ + AlignLeft, + + /** + * Align current table at the right + */ + AlignRight, + + /** + * Align current content table cell at the left + */ + AlignCellLeft, + + /** + * Align current content table cell at the center + */ + AlignCellCenter, + + /** + * Align current content table cell at the right + */ + AlignCellRight, + + /** + * Align current content table cell at the top + */ + AlignCellTop, + + /** + * Align current table cell at the middle + */ + AlignCellMiddle, + + /** + * Align current table cell at the bottom + */ + AlignCellBottom, +} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/index.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/index.ts new file mode 100644 index 00000000000..37299e2267a --- /dev/null +++ b/packages/roosterjs-editor-types/lib/compatibleEnum/index.ts @@ -0,0 +1,34 @@ +export { CompatibleAlignment } from './Alignment'; +export { CompatibleBulletListType } from './BulletListType'; +export { CompatibleCapitalization } from './Capitalization'; +export { CompatibleChangeSource } from './ChangeSource'; +export { CompatibleClearFormatMode } from './ClearFormatMode'; +export { CompatibleColorTransformDirection } from './ColorTransformDirection'; +export { CompatibleContentPosition } from './ContentPosition'; +export { CompatibleContentTypePrefix, CompatibleContentType } from './ContentType'; +export { CompatibleDarkModeDatasetNames } from './DarkModeDatasetNames'; +export { CompatibleDefinitionType } from './DefinitionType'; +export { CompatibleDelimiterClasses } from './DelimiterClasses'; +export { CompatibleDirection } from './Direction'; +export { CompatibleDocumentCommand } from './DocumentCommand'; +export { CompatibleDocumentPosition } from './DocumentPosition'; +export { CompatibleEntityClasses } from './EntityClasses'; +export { CompatibleEntityOperation } from './EntityOperation'; +export { CompatibleExperimentalFeatures } from './ExperimentalFeatures'; +export { CompatibleFontSizeChange } from './FontSizeChange'; +export { CompatibleGetContentMode } from './GetContentMode'; +export { CompatibleImageEditOperation } from './ImageEditOperation'; +export { CompatibleIndentation } from './Indentation'; +export { CompatibleKeys } from './Keys'; +export { CompatibleKnownCreateElementDataIndex } from './KnownCreateElementDataIndex'; +export { CompatibleKnownPasteSourceType } from './KnownPasteSourceType'; +export { CompatibleListType } from './ListType'; +export { CompatibleNodeType } from './NodeType'; +export { CompatibleNumberingListType } from './NumberingListType'; +export { CompatiblePluginEventType } from './PluginEventType'; +export { CompatiblePositionType } from './PositionType'; +export { CompatibleQueryScope } from './QueryScope'; +export { CompatibleRegionType } from './RegionType'; +export { CompatibleSelectionRangeTypes } from './SelectionRangeTypes'; +export { CompatibleTableBorderFormat } from './TableBorderFormat'; +export { CompatibleTableOperation } from './TableOperation'; From 2680252e5651db8c5162662f1053f8cc551eec74 Mon Sep 17 00:00:00 2001 From: biwu Date: Fri, 21 Feb 2025 00:50:05 -0800 Subject: [PATCH 22/23] version update --- versions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/versions.json b/versions.json index bc7089d1c5d..99cb2c85e73 100644 --- a/versions.json +++ b/versions.json @@ -1,6 +1,6 @@ { "react": "9.0.1", - "main": "9.19.0", + "main": "9.19.1", "legacyAdapter": "8.63.2", "overrides": {} } From afe82ff08b8f296039859416c901f80260faa1d0 Mon Sep 17 00:00:00 2001 From: biwu Date: Fri, 21 Feb 2025 14:44:35 -0800 Subject: [PATCH 23/23] address comment --- .../lib/compatibleEnum/Alignment.ts | 19 -- .../lib/compatibleEnum/BulletListType.ts | 59 ---- .../lib/compatibleEnum/Capitalization.ts | 26 -- .../lib/compatibleEnum/ChangeSource.ts | 71 ----- .../lib/compatibleEnum/ClearFormatMode.ts | 19 -- .../compatibleEnum/ColorTransformDirection.ts | 14 - .../lib/compatibleEnum/ContentPosition.ts | 36 --- .../lib/compatibleEnum/ContentType.ts | 29 -- .../compatibleEnum/DarkModeDatasetNames.ts | 24 -- .../lib/compatibleEnum/DefinitionType.ts | 34 --- .../lib/compatibleEnum/DelimiterClasses.ts | 14 - .../lib/compatibleEnum/Direction.ts | 14 - .../lib/compatibleEnum/DocumentCommand.ts | 261 ------------------ .../lib/compatibleEnum/DocumentPosition.ts | 35 --- .../lib/compatibleEnum/EntityClasses.ts | 24 -- .../lib/compatibleEnum/EntityOperation.ts | 72 ----- .../compatibleEnum/ExperimentalFeatures.ts | 155 ----------- .../lib/compatibleEnum/FontSizeChange.ts | 15 - .../lib/compatibleEnum/GetContentMode.ts | 36 --- .../lib/compatibleEnum/ImageEditOperation.ts | 44 --- .../lib/compatibleEnum/Indentation.ts | 15 - .../lib/compatibleEnum/Keys.ts | 53 ---- .../KnownCreateElementDataIndex.ts | 69 ----- .../compatibleEnum/KnownPasteSourceType.ts | 13 - .../lib/compatibleEnum/ListType.ts | 22 -- .../lib/compatibleEnum/NodeType.ts | 46 --- .../lib/compatibleEnum/NumberingListType.ts | 114 -------- .../lib/compatibleEnum/PluginEventType.ts | 124 --------- .../lib/compatibleEnum/PositionType.ts | 24 -- .../lib/compatibleEnum/QueryScope.ts | 20 -- .../lib/compatibleEnum/RegionType.ts | 9 - .../lib/compatibleEnum/SelectionRangeTypes.ts | 17 -- .../lib/compatibleEnum/TableBorderFormat.ts | 81 ------ .../lib/compatibleEnum/TableOperation.ts | 119 -------- .../lib/compatibleEnum/index.ts | 34 --- versions.json | 4 +- 36 files changed, 2 insertions(+), 1763 deletions(-) delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Alignment.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/BulletListType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Capitalization.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ChangeSource.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ClearFormatMode.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ColorTransformDirection.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ContentPosition.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ContentType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DarkModeDatasetNames.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DefinitionType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DelimiterClasses.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Direction.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DocumentCommand.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/DocumentPosition.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/EntityClasses.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/EntityOperation.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ExperimentalFeatures.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/FontSizeChange.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/GetContentMode.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ImageEditOperation.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Indentation.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/Keys.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/KnownCreateElementDataIndex.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/KnownPasteSourceType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/ListType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/NodeType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/NumberingListType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/PluginEventType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/PositionType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/QueryScope.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/RegionType.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/SelectionRangeTypes.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/TableBorderFormat.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/TableOperation.ts delete mode 100644 packages/roosterjs-editor-types/lib/compatibleEnum/index.ts diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Alignment.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Alignment.ts deleted file mode 100644 index eea2b54e6ed..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/Alignment.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * enum for setting block alignment, used by setAlignment API - */ -export enum CompatibleAlignment { - /** - * Align left - */ - Left, - - /** - * Align center - */ - Center, - - /** - * Align right - */ - Right, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/BulletListType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/BulletListType.ts deleted file mode 100644 index 2abb4efddda..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/BulletListType.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Enum used to control the different types of bullet list - */ -export enum CompatibleBulletListType { - /** - * Minimum value of the enum - */ - Min = 1, - - /** - * Bullet triggered by * - */ - Disc = 1, - - /** - * Bullet triggered by - - */ - Dash = 2, - - /** - * Bullet triggered by -- - */ - Square = 3, - - /** - * Bullet triggered by > - */ - ShortArrow = 4, - - /** - * Bullet triggered by -> - */ - LongArrow = 5, - - /** - * Bullet triggered by => - */ - UnfilledArrow = 6, - - /** - * Bullet triggered by — - */ - Hyphen = 7, - - /** - * Bullet triggered by --> - */ - DoubleLongArrow = 8, - - /** - * Bullet type circle - */ - Circle = 9, - - /** - * Maximum value of the enum - */ - Max = 9, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Capitalization.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Capitalization.ts deleted file mode 100644 index 3edc4ece0a6..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/Capitalization.ts +++ /dev/null @@ -1,26 +0,0 @@ -/** - * The enum used for controlling the capitalization of text. - * Used by changeCapitalization API - */ -export enum CompatibleCapitalization { - /** - * Transforms the first character after punctuation mark followed by space - * to uppercase and the rest of characters to lowercase. - */ - Sentence = 'sentence', - - /** - * Transforms all characters to lowercase - */ - Lowercase = 'lowercase', - - /** - * Transforms all characters to uppercase - */ - Uppercase = 'uppercase', - - /** - * Transforms the first character of each word to uppercase - */ - CapitalizeEachWord = 'capitalize', -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ChangeSource.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ChangeSource.ts deleted file mode 100644 index 9661e6c4df3..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/ChangeSource.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * Possible change sources. Here are the predefined sources. - * It can also be other string if the change source can't fall into these sources. - */ -export enum CompatibleChangeSource { - /** - * Content changed by auto link - */ - AutoLink = 'AutoLink', - - /** - * Content changed by create link - */ - CreateLink = 'CreateLink', - - /** - * Content changed by format - */ - Format = 'Format', - - /** - * Content changed by image resize - */ - ImageResize = 'ImageResize', - - /** - * Content changed by paste - */ - Paste = 'Paste', - - /** - * Content changed by setContent API - */ - SetContent = 'SetContent', - - /** - * Content changed by cut operation - */ - Cut = 'Cut', - - /** - * Content changed by drag & drop operation - */ - Drop = 'Drop', - - /** - * Insert a new entity into editor - */ - InsertEntity = 'InsertEntity', - - /** - * Editor is switched to dark mode, content color is changed - */ - SwitchToDarkMode = 'SwitchToDarkMode', - - /** - * Editor is switched to light mode, content color is changed - */ - SwitchToLightMode = 'SwitchToLightMode', - - /** - * List chain reorganized numbers of lists - */ - ListChain = 'ListChain', - - /** - * Keyboard event, used by Content Model. - * Data of this event will be the key code number - */ - Keyboard = 'Keyboard', -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ClearFormatMode.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ClearFormatMode.ts deleted file mode 100644 index 8f95a3a19d6..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/ClearFormatMode.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Represents the strategy to clear the format of the current editor selection - */ -export enum CompatibleClearFormatMode { - /** - * Inline format. Remove text format. - */ - Inline, - - /** - * BLock format. Remove text and structure format of the block. - */ - Block, - - /** - * Detect Inline or Block format based on the current editor selector. - */ - AutoDetect, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ColorTransformDirection.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ColorTransformDirection.ts deleted file mode 100644 index a73f1368dae..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/ColorTransformDirection.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Represents the mode of color transformation - */ -export enum CompatibleColorTransformDirection { - /** - * Transform from light to dark - */ - LightToDark = 0, - - /** - * Transform from dark to light - */ - DarkToLight = 1, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ContentPosition.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ContentPosition.ts deleted file mode 100644 index bf606244567..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/ContentPosition.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * The position. Mostly used for content insertion and traversing - * On insertion, we will need to specify where we want the content to be placed (begin, end, selection or outside) - * On content traversing, we will need to specify the start position of traversing - */ -export enum CompatibleContentPosition { - /** - * Begin of the container - */ - Begin, - - /** - * End of the container - */ - End, - - /** - * End of the content div domain. - */ - DomEnd, - - /** - * Selection start - */ - SelectionStart, - - /** - * Outside of editor - */ - Outside, - - /** - * Manually defined range - */ - Range, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ContentType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ContentType.ts deleted file mode 100644 index 2702b39f95e..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/ContentType.ts +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Prefix of content types - */ -export enum CompatibleContentTypePrefix { - /** - * Text type prefix - */ - Text = 'text/', - - /** - * Image type prefix - */ - Image = 'image/', -} - -/** - * Known content types - */ -export enum CompatibleContentType { - /** - * Plain text content type - */ - PlainText = 'text/plain', - - /** - * HTML content type - */ - HTML = 'text/html', -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DarkModeDatasetNames.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DarkModeDatasetNames.ts deleted file mode 100644 index d9220428fc9..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/DarkModeDatasetNames.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Constants string for dataset names used by dark mode - */ -export enum CompatibleDarkModeDatasetNames { - /** - * Original style text color - */ - OriginalStyleColor = 'ogsc', - - /** - * Original style background color - */ - OriginalStyleBackgroundColor = 'ogsb', - - /** - * Original attribute text color - */ - OriginalAttributeColor = 'ogac', - - /** - * Original attribute background color - */ - OriginalAttributeBackgroundColor = 'ogab', -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DefinitionType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DefinitionType.ts deleted file mode 100644 index 6e2ab905ee0..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/DefinitionType.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Types of definitions, used by Definition type - */ -export enum CompatibleDefinitionType { - /** - * Boolean type definition, represents a boolean type value - */ - Boolean, - - /** - * Number type definition, represents a number type value - */ - Number, - - /** - * String type definition, represents a string type value - */ - String, - - /** - * Array type definition, represents an array with a given item type - */ - Array, - - /** - * Object type definition, represents an object with the given property types - */ - Object, - - /** - * Customize type definition, represents a customized type with a validator function - */ - Customize, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DelimiterClasses.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DelimiterClasses.ts deleted file mode 100644 index f12953fe0ec..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/DelimiterClasses.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Class names for Delimiter - */ -export enum CompatibleDelimiterClasses { - /** - * Class name to specify this delimiter is before an entity - */ - DELIMITER_BEFORE = 'entityDelimiterBefore', - - /** - * Class name to specify this delimiter is after an entity - */ - DELIMITER_AFTER = 'entityDelimiterAfter', -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Direction.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Direction.ts deleted file mode 100644 index cccaddb1c45..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/Direction.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * enum for setting block direction, used by setDirection API - */ -export enum CompatibleDirection { - /** - * Left to right - */ - LeftToRight, - - /** - * Right to left - */ - RightToLeft, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentCommand.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentCommand.ts deleted file mode 100644 index 1e17f8ede4a..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentCommand.ts +++ /dev/null @@ -1,261 +0,0 @@ -/** - * Command strings for Document.execCommand() API - * https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand - */ -export enum CompatibleDocumentCommand { - /** - * Changes the browser auto-link behavior (Internet Explorer only) - */ - AutoUrlDetect = 'AutoUrlDetect', - - /** - * Changes the document background color. In styleWithCss mode, it affects the background color of the containing block instead. - * This requires a <color> value string to be passed in as a value argument. Note that Internet Explorer uses this to set the - * text background color. - */ - BackColor = 'backColor', - - /** - * Toggles bold on/off for the selection or at the insertion point. Internet Explorer uses the <strong> tag instead of <b>. - */ - Bold = 'bold', - - /** - * Clears all authentication credentials from the cache. - */ - ClearAuthenticationCache = 'ClearAuthenticationCache', - - /** - * Makes the content document either read-only or editable. This requires a boolean true/false as the value argument. - * (Not supported by Internet Explorer.) - */ - ContentReadOnly = 'contentReadOnly', - - /** - * Copies the current selection to the clipboard. Conditions of having this behavior enabled vary from one browser to another, - * and have evolved over time. Check the compatibility table to determine if you can use it in your case. - */ - Copy = 'copy', - - /** - * Creates an hyperlink from the selection, but only if there is a selection. Requires a URI string as a value argument for the - * hyperlink's href. The URI must contain at least a single character, which may be whitespace. - * (Internet Explorer will create a link with a null value.) - */ - CreateLink = 'createLink', - - /** - * Removes the current selection and copies it to the clipboard. When this behavior is enabled varies between browsers, - * and its conditions have evolved over time. Check the compatibility table for usage details. - */ - Cut = 'cut', - - /** - * Adds a <small> tag around the selection or at the insertion point. (Not supported by Internet Explorer.) - */ - DecreaseFontSize = 'decreaseFontSize', - - /** - * Changes the paragraph separator used when new paragraphs are created in editable text regions. See Differences in markup - * generation for more details. - */ - DefaultParagraphSeparator = 'defaultParagraphSeparator', - - /** - * Deletes the current selection. - */ - Delete = 'delete', - - /** - * Enables or disables the table row/column insertion and deletion controls. (Not supported by Internet Explorer.) - */ - EnableInlineTableEditing = 'enableInlineTableEditing', - - /** - * Enables or disables the resize handles on images and other resizable objects. (Not supported by Internet Explorer.) - */ - EnableObjectResizing = 'enableObjectResizing', - - /** - * Changes the font name for the selection or at the insertion point. This requires a font name string (like "Arial") - * as a value argument. - */ - FontName = 'fontName', - - /** - * Changes the font size for the selection or at the insertion point. This requires an integer from 1-7 as a value argument. - */ - FontSize = 'fontSize', - - /** - * Changes a font color for the selection or at the insertion point. This requires a hexadecimal color value string - * as a value argument. - */ - ForeColor = 'foreColor', - - /** - * Adds an HTML block-level element around the line containing the current selection, replacing the block element containing - * the line if one exists (in Firefox, <blockquote> is the exception — it will wrap any containing block element). - * Requires a tag-name string as a value argument. Virtually all block-level elements can be used. - * (Internet Explorer supports only heading tags H1–H6, ADDRESS, and PRE, which must be wrapped in angle brackets, such as "<H1>".) - */ - FormatBlock = 'formatBlock', - - /** - * Deletes the character ahead of the cursor's position, identical to hitting the Delete key on a Windows keyboard. - */ - ForwardDelete = 'forwardDelete', - - /** - * Adds a heading element around a selection or insertion point line. Requires the tag-name strings a value argument (i.e. "H1", "H6"). - * (Not supported by Internet Explorer and Safari.) - */ - Heading = 'heading', - - /** - * Changes the background color for the selection or at the insertion point. Requires a color value string as a value argument. - * useCSS must be true for this to function. (Not supported by Internet Explorer.) - */ - HiliteColor = 'hiliteColor', - - /** - * Adds a <big> tag around the selection or at the insertion point. (Not supported by Internet Explorer.) - */ - IncreaseFontSize = 'increaseFontSize', - - /** - * Indents the line containing the selection or insertion point. In Firefox, if the selection spans multiple lines at different - * levels of indentation, only the least indented lines in the selection will be indented. - */ - Indent = 'indent', - - /** - * Controls whether the Enter key inserts a <br> element, or splits the current block element into two. - * (Not supported by Internet Explorer.) - */ - InsertBrOnReturn = 'insertBrOnReturn', - - /** - * Inserts a <hr> element at the insertion point, or replaces the selection with it. - */ - InsertHorizontalRule = 'insertHorizontalRule', - - /** - * Inserts an HTML string at the insertion point (deletes selection). Requires a valid HTML string as a value argument. - * (Not supported by Internet Explorer.) - */ - InsertHTML = 'insertHTML', - - /** - * Inserts an image at the insertion point (deletes selection). Requires a URL string for the image's src as a value argument. - * The requirements for this string are the same as createLink. - */ - InsertImage = 'insertImage', - - /** - * Creates a numbered ordered list for the selection or at the insertion point. - */ - InsertOrderedList = 'insertOrderedList', - - /** - * Creates a bulleted unordered list for the selection or at the insertion point. - */ - InsertUnorderedList = 'insertUnorderedList', - - /** - * Inserts a paragraph around the selection or the current line. - * (Internet Explorer inserts a paragraph at the insertion point and deletes the selection.) - */ - InsertParagraph = 'insertParagraph', - - /** - * Inserts the given plain text at the insertion point (deletes selection). - */ - InsertText = 'insertText', - - /** - * Toggles italics on/off for the selection or at the insertion point. - * (Internet Explorer uses the <em> element instead of <i>.) - */ - Italic = 'italic', - - /** - * Centers the selection or insertion point. - */ - JustifyCenter = 'justifyCenter', - - /** - * Justifies the selection or insertion point. - */ - JustifyFull = 'justifyFull', - - /** - * Justifies the selection or insertion point to the left. - */ - JustifyLeft = 'justifyLeft', - - /** - * Right-justifies the selection or the insertion point. - */ - JustifyRight = 'justifyRight', - - /** - * Outdents the line containing the selection or insertion point. - */ - Outdent = 'outdent', - - /** - * Pastes the clipboard contents at the insertion point (replaces current selection). Disabled for web content. See [1]. - */ - Paste = 'paste', - - /** - * Redoes the previous undo command. - */ - Redo = 'redo', - - /** - * Removes all formatting from the current selection. - */ - RemoveFormat = 'removeFormat', - - /** - * Selects all of the content of the editable region. - */ - SelectAll = 'selectAll', - - /** - * Toggles strikethrough on/off for the selection or at the insertion point. - */ - StrikeThrough = 'strikeThrough', - - /** - * Toggles subscript on/off for the selection or at the insertion point. - */ - Subscript = 'subscript', - - /** - * Toggles superscript on/off for the selection or at the insertion point. - */ - Superscript = 'superscript', - - /** - * Toggles underline on/off for the selection or at the insertion point. - */ - Underline = 'underline', - - /** - * Undoes the last executed command. - */ - Undo = 'undo', - - /** - * Removes the anchor element from a selected hyperlink. - */ - Unlink = 'unlink', - - /** - * Replaces the useCSS command. true modifies/generates style attributes in markup, false generates presentational elements. - */ - StyleWithCSS = 'styleWithCSS', -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentPosition.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentPosition.ts deleted file mode 100644 index 7fe7d01053e..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/DocumentPosition.ts +++ /dev/null @@ -1,35 +0,0 @@ -/** - * The is essentially an enum representing result from browser compareDocumentPosition API - * https://developer.mozilla.org/en-US/docs/Web/API/Node/compareDocumentPosition - */ -export enum CompatibleDocumentPosition { - /** - * Same node - */ - Same = 0, - - /** - * Node is disconnected from document - */ - Disconnected = 1, - - /** - * Node is preceding the comparing node - */ - Preceding = 2, - - /** - * Node is following the comparing node - */ - Following = 4, - - /** - * Node contains the comparing node - */ - Contains = 8, - - /** - * Node is contained by the comparing node - */ - ContainedBy = 16, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/EntityClasses.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/EntityClasses.ts deleted file mode 100644 index a314e90bad7..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/EntityClasses.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * CSS Class names for Entity - */ -export enum CompatibleEntityClasses { - /** - * Class name to specify this is an entity - */ - ENTITY_INFO_NAME = '_Entity', - - /** - * Class name to specify the type of an entity - */ - ENTITY_TYPE_PREFIX = '_EType_', - - /** - * Class name to specify the ID of an entity - */ - ENTITY_ID_PREFIX = '_EId_', - - /** - * Class name to specify if the entity is readonly - */ - ENTITY_READONLY_PREFIX = '_EReadonly_', -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/EntityOperation.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/EntityOperation.ts deleted file mode 100644 index 782760b993c..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/EntityOperation.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Define possible operations to an entity - */ -export enum CompatibleEntityOperation { - /** - * Notify plugins that there is a new plugin was added into editor. - * Plugin can handle this event to entity hydration. - * This event will be only fired once for each entity DOM node. - * After undo, or copy/paste, since new DOM nodes were added, this event will be fired - * for those entities represented by newly added nodes. - */ - NewEntity, - - /** - * Notify plugins that user is clicking target to an entity - */ - Click, - - /** - * Notify plugins that user is triggering context menu to an entity - */ - ContextMenu, - - /** - * Notify plugins that user is escaping from an entity by pressing ESC key - */ - Escape, - - /** - * Notify plugins that user is removing an entity from its start position using DELETE key - */ - RemoveFromStart, - - /** - * Notify plugins that user is remove an entity from its end position using BACKSPACE key - */ - RemoveFromEnd, - - /** - * Notify plugins that an entity is being overwritten. - * This can be caused by key in, cut, paste, delete, backspace ... on a selection - * which contains some entities. - */ - Overwrite, - - /** - * Notify plugins that an entity is being partially overwritten. - * This happens when user selected part of the entity then do key press, or cut, paste, delete, backspace, ... - */ - PartialOverwrite, - - /** - * Notify plugins that editor is generating HTML content for save. - * Plugin should use this event to remove any temporary content, and only leave DOM nodes that - * should be saved as HTML string. - * This event will provide a cloned DOM tree for each entity, do NOT compare the DOM nodes with cached nodes - * because it will always return false. - */ - ReplaceTemporaryContent, - - /** - * Notify plugins that editor has attached shadow root for an entity. - * Plugins can handle this event to do extra operations to the shadow root - */ - AddShadowRoot, - - /** - * Notify plugins that editor has removed the shadow root of an entity - * Plugins can handle this event to do any necessary clean up for shadow root - */ - RemoveShadowRoot, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ExperimentalFeatures.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ExperimentalFeatures.ts deleted file mode 100644 index 4dc52c67f16..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/ExperimentalFeatures.ts +++ /dev/null @@ -1,155 +0,0 @@ -/** - * Experimental feature flags - */ -export enum CompatibleExperimentalFeatures { - // #region Graduated and deprecated features. - // These features will be removed in next major release - /** - * @deprecated This feature is always enabled - */ - NewIndentation = 'NewIndentation', - - /** - * @deprecated This feature is always enabled - */ - NewBullet = 'NewBullet', - - /** - * @deprecated This feature is always enabled - */ - NewNumbering = 'NewNumbering', - - /** - * @deprecated This feature is always enabled - */ - ListChain = 'ListChain', - - /** - * @deprecated This feature is always enabled - */ - MergePastedLine = 'MergePastedLine', - - /** - * @deprecated This feature is always enabled - */ - SingleDirectionResize = 'SingleDirectionResize', - - /** - * @deprecated This feature is always enabled - */ - PasteWithLinkPreview = 'PasteWithLinkPreview', - - /** - * @deprecated This feature is always enabled - */ - ImageRotate = 'ImageRotate', - - /** - * @deprecated This feature is always enabled - */ - ImageCrop = 'ImageCrop', - - /** - * @deprecated This feature is always enabled - * Check if the element has a style attribute, if not, apply the default format - */ - AlwaysApplyDefaultFormat = 'AlwaysApplyDefaultFormat', - - /** - * @deprecated This feature can be enabled/disabled using Paste Plugin contructor param - * Paste the Html instead of the Img when the Html Body only have one IMG Child node - */ - ConvertSingleImageBody = 'ConvertSingleImageBody', - - /** - * @deprecated This feature is always enabled - * Align table elements to left, center and right using setAlignment API - */ - TableAlignment = 'TableAlignment', - - /** - * @deprecated this feature is always enabled - * Provide a circular resize handles that adaptive the number od handles to the size of the image - */ - AdaptiveHandlesResizer = 'AdaptiveHandlesResizer', - - /** - * @deprecated this feature is always disabled - * Automatically transform -- into hyphen, if typed between two words. - */ - AutoHyphen = 'AutoHyphen', - - /** - * @deprecated this feature is always disabled - * Use pending format strategy to do style based format, e.g. Font size, Color. - * With this feature enabled, we don't need to insert temp ZeroWidthSpace character to hold pending format - * when selection is collapsed. Instead, we will hold the pending format in memory and only apply it when type something - */ - PendingStyleBasedFormat = 'PendingStyleBasedFormat', - - /** - * @deprecated this feature is always disabled - * Normalize list to make sure it can be displayed correctly in other client - * e.g. We will move list items with "display: block" into previous list item and change tag to be DIV - */ - NormalizeList = 'NormalizeList', - - /** - * @deprecated this feature is always enabled - * When a html image is selected, the selected image data will be stored by editor core. - */ - ImageSelection = 'ImageSelection', - - //#endregion - - /** - * Provide additional Tab Key Features. Requires Text Features Content Editable Features - */ - TabKeyTextFeatures = 'TabKeyTextFeatures', - - /** - * Align list elements elements to left, center and right using setAlignment API - */ - ListItemAlignment = 'ListItemAlignment', - - /** - * Trigger formatting by a especial characters. Ex: (A), 1. i). - */ - AutoFormatList = 'AutoFormatList', - - /** - * With this feature enabled, when writing back a list item we will re-use all - * ancestor list elements, even if they don't match the types currently in the - * listTypes array for that item. The only list that we will ensure is correct - * is the one closest to the item. - */ - ReuseAllAncestorListElements = 'ReuseAllAncestorListElements', - - /** - * When apply default format when initialize or user type, apply the format on a SPAN element rather than - * the block element (In most case, the DIV element) so keep the block element clean. - */ - DefaultFormatInSpan = 'DefaultFormatInSpan', - - /** - * Use variable-based dark mode solution rather than dataset-based solution. - * When enable this feature, need to pass in a DarkModelHandler object to each call of setColor and applyFormat - * if you need them work for dark mode - */ - VariableBasedDarkColor = 'VariableBasedDarkColor', - - /** - * Reuse existing DOM structure if possible when convert Content Model back to DOM tree - */ - ReusableContentModel = 'ReusableContentModel', - - /** - * Delete table with Backspace key with the whole was selected with table selector - */ - DeleteTableWithBackspace = 'DeleteTableWithBackspace', - - /** - * Add entities around a Read Only Inline entity to prevent cursor to be hidden when cursor is next of it. - */ - InlineEntityReadOnlyDelimiters = 'InlineEntityReadOnlyDelimiters', -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/FontSizeChange.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/FontSizeChange.ts deleted file mode 100644 index 4fde0c39cae..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/FontSizeChange.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * The enum used for increase or decrease font size - * Used by setFontSize API - */ -export enum CompatibleFontSizeChange { - /** - * Increase font size - */ - Increase, - - /** - * Decrease font size - */ - Decrease, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/GetContentMode.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/GetContentMode.ts deleted file mode 100644 index de26b8b1489..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/GetContentMode.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Represents a mode number to indicate what kind of content to retrieve when call Editor.getContent() - */ -export enum CompatibleGetContentMode { - /** - * The clean content without any temporary content only for editor. - * This is the default value. Call to Editor.getContent() with trigger an ExtractContentWithDom event - * so that plugins can remove their temporary content, and will return the HTML content - * which is ready for save to storage. - */ - CleanHTML = 0, - - /** - * Retrieve the raw HTML string in current editor. Temporary content will be included. - */ - RawHTMLOnly = 1, - - /** - * Retrieve the raw HTML string in current editor with a selection marker. This can be used for - * save current editor state and call to SetContent with this result can fully restore editor state - * including current selection - */ - RawHTMLWithSelection = 2, - - /** - * Get plain text content only, all format will be ignored - */ - PlainText, - - /** - * A fast way to get plain text content, the line-end positions may not be exactly same with HTML content, - * but the text content should be the same. This is used for quickly retrieve text content and check - * text only - */ - PlainTextFast, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ImageEditOperation.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ImageEditOperation.ts deleted file mode 100644 index 8221b460cc5..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/ImageEditOperation.ts +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Operation flags for ImageEdit plugin - */ -export enum CompatibleImageEditOperation { - /** - * No operation - */ - None = 0, - - /** - * Resize image from corner ( horizontal and vertical resize) - */ - CornerResize = 1, - - /** - * Resize image from side ( either horizontal or vertical resize) - */ - SideResize = 2, - - /** - * Image resize - */ - Resize = CornerResize | SideResize, - - /** - * Image rotate - */ - Rotate = 4, - - /** - * Image resize and rotate - */ - ResizeAndRotate = Resize | Rotate, - - /** - * Image crop - */ - Crop = 8, - - /** - * All operations - */ - All = ResizeAndRotate | Crop, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Indentation.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Indentation.ts deleted file mode 100644 index 2e99b87a040..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/Indentation.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * The enum used for increase or decrease indentation of a block - * Used by setIndentation API - */ -export enum CompatibleIndentation { - /** - * Increase indentation - */ - Increase, - - /** - * Decrease indentation - */ - Decrease, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/Keys.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/Keys.ts deleted file mode 100644 index 5f0ebe4b6df..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/Keys.ts +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Key numbers common used keys - */ -export enum CompatibleKeys { - NULL = 0, - BACKSPACE = 8, - TAB = 9, - ENTER = 13, - SHIFT = 16, - CTRL_LEFT = 17, - ALT = 18, - ESCAPE = 27, - SPACE = 32, - PAGEUP = 33, - END = 35, - HOME = 36, - LEFT = 37, - UP = 38, - RIGHT = 39, - DOWN = 40, - PRINT_SCREEN = 44, - DELETE = 46, - /** - * @deprecated Just for backward compatibility - */ - EIGHT_ASTIRISK = 56, - EIGHT_ASTERISK = 56, - B = 66, - I = 73, - U = 85, - Y = 89, - Z = 90, - META_LEFT = 91, - COMMA = 188, - DASH_UNDERSCORE = 189, - PERIOD = 190, - /** - * @deprecated Just for backward compatibility - */ - FORWARDSLASH = 191, - FORWARD_SLASH = 191, - GRAVE_TILDE = 192, - - /** - * Keys below are non-standard, and should be used in ContentEditFeatures only - */ - CONTENTCHANGED = 0x101, - RANGE = 0x102, - - Ctrl = 0x1000, - Meta = 0x2000, - Shift = 0x4000, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/KnownCreateElementDataIndex.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/KnownCreateElementDataIndex.ts deleted file mode 100644 index 1b8e45acf2a..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/KnownCreateElementDataIndex.ts +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Index of known CreateElementData used by createElement function - */ -export enum CompatibleKnownCreateElementDataIndex { - /** - * Set a none value to help createElement function ignore falsy value - */ - None = 0, - - /** - * An empty line without format - */ - EmptyLine = 1, - - /** - * Wrapper for blockquote - */ - BlockquoteWrapper = 2, - - /** - * Temp DIV for copy/paste - */ - CopyPasteTempDiv = 3, - - /** - * ListItem with block style - */ - BlockListItem = 4, - - /** - * Wrapper element for context menu - */ - ContextMenuWrapper = 5, - - /** - * Wrapper element for image edit - */ - ImageEditWrapper = 6, - - /** - * @deprecated - */ - TableHorizontalResizer = 7, - - /** - * @deprecated - */ - TableVerticalResizer = 8, - - /** - * @deprecated - */ - TableResizerLTR = 9, - - /** - * @deprecated - */ - TableResizerRTL = 10, - - /** - * @deprecated - */ - TableSelector = 11, - - /** - * An empty line without format with span inside of it. - */ - EmptyLineFormatInSpan = 12, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/KnownPasteSourceType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/KnownPasteSourceType.ts deleted file mode 100644 index 1a9c309b6e8..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/KnownPasteSourceType.ts +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Represent the types of sources to handle in the Paste Plugin - */ -export enum CompatibleKnownPasteSourceType { - WordDesktop, - ExcelDesktop, - ExcelOnline, - PowerPointDesktop, - GoogleSheets, - WacComponents, - Default, - SingleImage, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/ListType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/ListType.ts deleted file mode 100644 index a4fc67c562a..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/ListType.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Type of list (numbering or bullet) - */ -export enum CompatibleListType { - /** - * None list type - * It means this is not a list - */ - None = 0, - - /** - * Ordered List type (numbering list) - * Represented by "OL" tag - */ - Ordered = 1, - - /** - * Unordered List type (bullet list) - * Represented by "UL" tag - */ - Unordered = 2, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/NodeType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/NodeType.ts deleted file mode 100644 index f5115a3eb62..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/NodeType.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * The is essentially an enum represents the type of the node - * https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType - * Values not listed here are deprecated. - */ -export enum CompatibleNodeType { - /** - * An Element node such as <p> or <div>. - */ - Element = 1, - - /** - * An Attribute node such as name="value". - */ - Attribute = 2, - - /** - * The actual Text of Element or Attr. - */ - Text = 3, - - /** - * A ProcessingInstruction of an XML document such as <?xml-stylesheet ... ?> declaration. - */ - ProcessingInstruction = 7, - - /** - * A Comment node. - */ - Comment = 8, - - /** - * A Document node. - */ - Document = 9, - - /** - * A DocumentType node e.g. <!DOCTYPE html> for HTML5 documents. - */ - DocumentType = 10, - - /** - * A DocumentFragment node. - */ - DocumentFragment = 11, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/NumberingListType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/NumberingListType.ts deleted file mode 100644 index 5deefc28491..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/NumberingListType.ts +++ /dev/null @@ -1,114 +0,0 @@ -/** - * Enum used to control the different types of numbering list - */ -export enum CompatibleNumberingListType { - /** - * Minimum value of the enum - */ - Min = 1, - - /** - * Numbering triggered by 1. - */ - Decimal = 1, - - /** - * Numbering triggered by 1- - */ - DecimalDash = 2, - - /** - * Numbering triggered by 1) - */ - DecimalParenthesis = 3, - - /** - * Numbering triggered by (1) - */ - DecimalDoubleParenthesis = 4, - - /** - * Numbering triggered by a. - */ - LowerAlpha = 5, - - /** - * Numbering triggered by a) - */ - LowerAlphaParenthesis = 6, - - /** - * Numbering triggered by (a) - */ - LowerAlphaDoubleParenthesis = 7, - - /** - * Numbering triggered by a- - */ - LowerAlphaDash = 8, - - /** - * Numbering triggered by A. - */ - UpperAlpha = 9, - - /** - * Numbering triggered by A) - */ - UpperAlphaParenthesis = 10, - - /** - * Numbering triggered by (A) - */ - UpperAlphaDoubleParenthesis = 11, - - /** - * Numbering triggered by A- - */ - UpperAlphaDash = 12, - - /** - * Numbering triggered by i. - */ - LowerRoman = 13, - - /** - * Numbering triggered by i) - */ - LowerRomanParenthesis = 14, - - /** - * Numbering triggered by (i) - */ - LowerRomanDoubleParenthesis = 15, - - /** - * Numbering triggered by i- - */ - LowerRomanDash = 16, - - /** - * Numbering triggered by I. - */ - UpperRoman = 17, - - /** - * Numbering triggered by I) - */ - UpperRomanParenthesis = 18, - - /** - * Numbering triggered by (I) - */ - UpperRomanDoubleParenthesis = 19, - - /** - * Numbering triggered by I- - */ - UpperRomanDash = 20, - - /** - * Maximum value of the enum - */ - Max = 20, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/PluginEventType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/PluginEventType.ts deleted file mode 100644 index 7aa377617d1..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/PluginEventType.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Editor plugin event type - */ -export enum CompatiblePluginEventType { - /** - * HTML KeyDown event - */ - KeyDown = 0, - - /** - * HTML KeyPress event - */ - KeyPress = 1, - - /** - * HTML KeyUp event - */ - KeyUp = 2, - - /** - * HTML Input / TextInput event - */ - Input = 3, - - /** - * HTML CompositionEnd event - */ - CompositionEnd = 4, - - /** - * HTML MouseDown event - */ - MouseDown = 5, - - /** - * HTML MouseUp event - */ - MouseUp = 6, - - /** - * Content changed event - */ - ContentChanged = 7, - - /** - * Extract Content with a DOM tree event - * This event is triggered when getContent() is called with triggerExtractContentEvent = true - * Plugin can handle this event to remove the UI only markups to return clean HTML - * by operating on a cloned DOM tree - */ - ExtractContentWithDom = 8, - - /** - * Before Paste event, provide a chance to change copied content - */ - BeforeCutCopy = 9, - - /** - * Before Paste event, provide a chance to change paste content - */ - BeforePaste = 10, - - /** - * Let plugin know editor is ready now - */ - EditorReady = 11, - - /** - * Let plugin know editor is about to dispose - */ - BeforeDispose = 12, - - /** - * Pending format state (bold, italic, underline, ... with collapsed selection) is changed - */ - PendingFormatStateChanged = 13, - - /** - * Scroll event triggered by scroll container - */ - Scroll = 14, - - /** - * Operating on an entity. See enum EntityOperation for more details about each operation - */ - EntityOperation = 15, - - /** - * HTML ContextMenu event - */ - ContextMenu = 16, - - /** - * Editor has entered shadow edit mode - */ - EnteredShadowEdit = 17, - - /** - * Editor is about to leave shadow edit mode - */ - LeavingShadowEdit = 18, - - /** - * Content of image is being changed from client side - */ - EditImage = 19, - - /** - * Content of editor is about to be cleared by SetContent API, handle this event to cache anything you need - * before it is gone - */ - BeforeSetContent = 20, - - /** - * Zoom scale value is changed, triggered by Editor.setZoomScale() when set a different scale number - */ - ZoomChanged = 21, - - /** - * EXPERIMENTAL FEATURE - * Editor changed the selection. - */ - SelectionChanged = 22, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/PositionType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/PositionType.ts deleted file mode 100644 index c3818537ca8..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/PositionType.ts +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Represent the type of a position - */ -export enum CompatiblePositionType { - /** - * At the beginning of a node - */ - Begin = 0, - - /** - * At the end of a node - */ - End = -1, - - /** - * Before a node - */ - Before = -2, - - /** - * After a node - */ - After = -3, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/QueryScope.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/QueryScope.ts deleted file mode 100644 index 9aad2872ad3..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/QueryScope.ts +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Query scope for queryElements() API - */ -export enum CompatibleQueryScope { - /** - * Query from the whole body of root node. This is default value. - */ - Body, - - /** - * Query elements on a given selection (intersect) - * The result element can contain the selection, contain part of selection, or inside selection - */ - OnSelection, - - /** - * Query elements inside a given selection only - */ - InSelection, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/RegionType.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/RegionType.ts deleted file mode 100644 index 44c826ce5c1..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/RegionType.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Type of all possible regions. Currently we only support region of Table - */ -export enum CompatibleRegionType { - /** - * Region split by Table - */ - Table, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/SelectionRangeTypes.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/SelectionRangeTypes.ts deleted file mode 100644 index ec1123724f2..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/SelectionRangeTypes.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * Types of Selection Ranges that the SelectionRangeEx can return - */ -export enum CompatibleSelectionRangeTypes { - /** - * Normal selection range provided by browser. - */ - Normal, - /** - * Selection made inside of a single table. - */ - TableSelection, - /** - * Selection made in a image. - */ - ImageSelection, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/TableBorderFormat.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/TableBorderFormat.ts deleted file mode 100644 index 6490d7231d8..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/TableBorderFormat.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Table format border - */ -export enum CompatibleTableBorderFormat { - /** - * All border of the table are displayed - * __ __ __ - * |__|__|__| - * |__|__|__| - * |__|__|__| - */ - DEFAULT, - - /** - * Middle vertical border are not displayed - * __ __ __ - * |__ __ __| - * |__ __ __| - * |__ __ __| - */ - LIST_WITH_SIDE_BORDERS, - - /** - * All borders except header rows borders are displayed - * __ __ __ - * __|__|__ - * __|__|__ - */ - NO_HEADER_BORDERS, - - /** - * The left and right border of the table are not displayed - * __ __ __ - * __|__|__ - * __|__|__ - * __|__|__ - */ - NO_SIDE_BORDERS, - - /** - * Only the borders that divides the header row, first column and externals are displayed - * __ __ __ - * |__ __ __| - * | | | - * |__|__ __| - */ - FIRST_COLUMN_HEADER_EXTERNAL, - - /** - * The header row has no vertical border, except for the first one - * The first column has no horizontal border, except for the first one - * __ __ __ - * |__ __ __ - * | |__|__| - * | |__|__| - */ - ESPECIAL_TYPE_1, - - /** - * The header row has no vertical border, except for the first one - * The only horizontal border of the table is the top and bottom of header row - * __ __ __ - * |__ __ __ - * | | | - * | | | - */ - ESPECIAL_TYPE_2, - - /** - * The only borders are the bottom of header row and the right border of first column - * __ __ __ - * | - * | - */ - ESPECIAL_TYPE_3, - - /** - * No border - */ - CLEAR, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/TableOperation.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/TableOperation.ts deleted file mode 100644 index fb9b5feed56..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/TableOperation.ts +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Operations used by editTable() API - */ -export enum CompatibleTableOperation { - /** - * Insert a row above current row - */ - InsertAbove, - - /** - * Insert a row below current row - */ - InsertBelow, - - /** - * Insert a column on the left of current column - */ - InsertLeft, - - /** - * Insert a column on the right of current column - */ - InsertRight, - - /** - * Delete the whole table - */ - DeleteTable, - - /** - * Delete current column - */ - DeleteColumn, - - /** - * Delete current row - */ - DeleteRow, - - /** - * Merge current row with the row above - */ - MergeAbove, - - /** - * Merge current row with the row below - */ - MergeBelow, - - /** - * Merge current column with the column on the left - */ - MergeLeft, - - /** - * Merge current column with the column on the right - */ - MergeRight, - - /** - * Merge all selected cells - */ - MergeCells, - - /** - * Split current table cell horizontally - */ - SplitHorizontally, - - /** - * Split current table cell vertically - */ - SplitVertically, - - /** - * Align current table at the center - */ - AlignCenter, - - /** - * Align current table at the left - */ - AlignLeft, - - /** - * Align current table at the right - */ - AlignRight, - - /** - * Align current content table cell at the left - */ - AlignCellLeft, - - /** - * Align current content table cell at the center - */ - AlignCellCenter, - - /** - * Align current content table cell at the right - */ - AlignCellRight, - - /** - * Align current content table cell at the top - */ - AlignCellTop, - - /** - * Align current table cell at the middle - */ - AlignCellMiddle, - - /** - * Align current table cell at the bottom - */ - AlignCellBottom, -} diff --git a/packages/roosterjs-editor-types/lib/compatibleEnum/index.ts b/packages/roosterjs-editor-types/lib/compatibleEnum/index.ts deleted file mode 100644 index 37299e2267a..00000000000 --- a/packages/roosterjs-editor-types/lib/compatibleEnum/index.ts +++ /dev/null @@ -1,34 +0,0 @@ -export { CompatibleAlignment } from './Alignment'; -export { CompatibleBulletListType } from './BulletListType'; -export { CompatibleCapitalization } from './Capitalization'; -export { CompatibleChangeSource } from './ChangeSource'; -export { CompatibleClearFormatMode } from './ClearFormatMode'; -export { CompatibleColorTransformDirection } from './ColorTransformDirection'; -export { CompatibleContentPosition } from './ContentPosition'; -export { CompatibleContentTypePrefix, CompatibleContentType } from './ContentType'; -export { CompatibleDarkModeDatasetNames } from './DarkModeDatasetNames'; -export { CompatibleDefinitionType } from './DefinitionType'; -export { CompatibleDelimiterClasses } from './DelimiterClasses'; -export { CompatibleDirection } from './Direction'; -export { CompatibleDocumentCommand } from './DocumentCommand'; -export { CompatibleDocumentPosition } from './DocumentPosition'; -export { CompatibleEntityClasses } from './EntityClasses'; -export { CompatibleEntityOperation } from './EntityOperation'; -export { CompatibleExperimentalFeatures } from './ExperimentalFeatures'; -export { CompatibleFontSizeChange } from './FontSizeChange'; -export { CompatibleGetContentMode } from './GetContentMode'; -export { CompatibleImageEditOperation } from './ImageEditOperation'; -export { CompatibleIndentation } from './Indentation'; -export { CompatibleKeys } from './Keys'; -export { CompatibleKnownCreateElementDataIndex } from './KnownCreateElementDataIndex'; -export { CompatibleKnownPasteSourceType } from './KnownPasteSourceType'; -export { CompatibleListType } from './ListType'; -export { CompatibleNodeType } from './NodeType'; -export { CompatibleNumberingListType } from './NumberingListType'; -export { CompatiblePluginEventType } from './PluginEventType'; -export { CompatiblePositionType } from './PositionType'; -export { CompatibleQueryScope } from './QueryScope'; -export { CompatibleRegionType } from './RegionType'; -export { CompatibleSelectionRangeTypes } from './SelectionRangeTypes'; -export { CompatibleTableBorderFormat } from './TableBorderFormat'; -export { CompatibleTableOperation } from './TableOperation'; diff --git a/versions.json b/versions.json index 99cb2c85e73..e40632ec119 100644 --- a/versions.json +++ b/versions.json @@ -1,6 +1,6 @@ { - "react": "9.0.1", - "main": "9.19.1", + "react": "9.0.2", + "main": "9.20.0", "legacyAdapter": "8.63.2", "overrides": {} }