diff --git a/packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/setDOMSelection.ts b/packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/setDOMSelection.ts index a89fee21134..aee3db1fa81 100644 --- a/packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/setDOMSelection.ts +++ b/packages/roosterjs-content-model-core/lib/coreApi/setDOMSelection/setDOMSelection.ts @@ -22,7 +22,7 @@ const HIDE_SELECTION_CSS_KEY = '_DOMSelectionHideSelection'; const IMAGE_ID = 'image'; const TABLE_ID = 'table'; const DEFAULT_SELECTION_BORDER_COLOR = '#DB626C'; -const TABLE_CSS_RULE = 'background-color:#C6C6C6!important;'; +const DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR = '#C6C6C6'; const CARET_CSS_RULE = 'caret-color: transparent'; const TRANSPARENT_SELECTION_CSS_RULE = 'background-color: transparent !important;'; const SELECTION_SELECTOR = '*::selection'; @@ -115,7 +115,10 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC core.api.setEditorStyle( core, DOM_SELECTION_CSS_KEY, - TABLE_CSS_RULE, + `background-color:${ + core.selection.tableCellSelectionBackgroundColor || + DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR + }!important;`, tableSelectors ); core.api.setEditorStyle(core, HIDE_CURSOR_CSS_KEY, CARET_CSS_RULE); 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 4961fb7a964..f1605b5d7bf 100644 --- a/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts +++ b/packages/roosterjs-content-model-core/lib/corePlugin/selection/SelectionPlugin.ts @@ -47,6 +47,7 @@ class SelectionPlugin implements PluginWithState { selection: null, tableSelection: null, imageSelectionBorderColor: options.imageSelectionBorderColor, + tableCellSelectionBackgroundColor: options.tableCellSelectionBackgroundColor, }; } diff --git a/packages/roosterjs-content-model-core/test/coreApi/setDOMSelection/setDOMSelectionTest.ts b/packages/roosterjs-content-model-core/test/coreApi/setDOMSelection/setDOMSelectionTest.ts index f1db9866d87..26aa60b42c8 100644 --- a/packages/roosterjs-content-model-core/test/coreApi/setDOMSelection/setDOMSelectionTest.ts +++ b/packages/roosterjs-content-model-core/test/coreApi/setDOMSelection/setDOMSelectionTest.ts @@ -530,7 +530,8 @@ describe('setDOMSelection', () => { firstRow: number, lastColumn: number, lastRow: number, - result: string[] + result: string[], + selectionColor?: string ) { const mockedSelection = { type: 'table', @@ -546,6 +547,10 @@ describe('setDOMSelection', () => { selectNode: selectNodeSpy, collapse: collapseSpy, }; + const defaultSelectionColor = '#C6C6C6'; + if (selectionColor) { + core.selection.tableCellSelectionBackgroundColor = selectionColor; + } createRangeSpy.and.returnValue(mockedRange); @@ -557,6 +562,7 @@ describe('setDOMSelection', () => { expect(core.selection).toEqual({ skipReselectOnFocus: undefined, selection: mockedSelection, + ...(selectionColor ? { tableCellSelectionBackgroundColor: selectionColor } : {}), } as any); expect(triggerEventSpy).toHaveBeenCalledWith( core, @@ -578,7 +584,7 @@ describe('setDOMSelection', () => { expect(setEditorStyleSpy).toHaveBeenCalledWith( core, '_DOMSelection', - 'background-color:#C6C6C6!important;', + `background-color:${selectionColor ?? defaultSelectionColor}!important;`, result ); expect(setEditorStyleSpy).toHaveBeenCalledWith( @@ -774,6 +780,18 @@ describe('setDOMSelection', () => { '#table_0 *', ]); }); + + it('Select All with custom selection color', () => { + runTest( + buildTable(true /* tbody */, false, false), + 0, + 0, + 1, + 1, + ['#table_0', '#table_0 *'], + 'red' + ); + }); }); }); 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 0e012e57e4b..4654a277166 100644 --- a/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts +++ b/packages/roosterjs-content-model-core/test/corePlugin/selection/SelectionPluginTest.ts @@ -33,6 +33,7 @@ describe('SelectionPlugin', () => { expect(state).toEqual({ selection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, tableSelection: null, }); expect(attachDomEvent).toHaveBeenCalled(); @@ -48,6 +49,7 @@ describe('SelectionPlugin', () => { it('init with different options', () => { const plugin = createSelectionPlugin({ imageSelectionBorderColor: 'red', + tableCellSelectionBackgroundColor: 'blue', }); const state = plugin.getState(); const addEventListenerSpy = jasmine.createSpy('addEventListener'); @@ -69,6 +71,7 @@ describe('SelectionPlugin', () => { expect(state).toEqual({ selection: null, imageSelectionBorderColor: 'red', + tableCellSelectionBackgroundColor: 'blue', tableSelection: null, }); @@ -135,6 +138,7 @@ describe('SelectionPlugin handle onFocus and onBlur event', () => { expect(plugin.getState()).toEqual({ selection: mockedRange, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, skipReselectOnFocus: false, tableSelection: null, }); @@ -151,6 +155,7 @@ describe('SelectionPlugin handle onFocus and onBlur event', () => { expect(plugin.getState()).toEqual({ selection: mockedRange, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, skipReselectOnFocus: true, tableSelection: null, }); @@ -768,6 +773,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: mockedTableSelection, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); plugin.onPluginEvent!({ @@ -781,6 +787,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(mouseDispatcher).toBeUndefined(); }); @@ -814,6 +821,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); plugin.onPluginEvent!({ @@ -834,6 +842,7 @@ describe('SelectionPlugin handle table selection', () => { }, mouseDisposer: mouseMoveDisposer, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(mouseDispatcher).toBeDefined(); }); @@ -867,6 +876,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); plugin.onPluginEvent!({ @@ -881,6 +891,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(mouseDispatcher).toBeUndefined(); }); @@ -919,6 +930,7 @@ describe('SelectionPlugin handle table selection', () => { }, mouseDisposer: mouseMoveDisposer, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(mouseDispatcher).toBeDefined(); expect(preventDefaultSpy).toHaveBeenCalled(); @@ -1259,6 +1271,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).not.toHaveBeenCalled(); expect(announceSpy).not.toHaveBeenCalled(); @@ -1300,6 +1313,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(0); expect(announceSpy).not.toHaveBeenCalled(); @@ -1356,6 +1370,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -1419,6 +1434,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -1481,6 +1497,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -1544,6 +1561,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -1606,6 +1624,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -1668,6 +1687,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -1733,6 +1753,7 @@ describe('SelectionPlugin handle table selection', () => { startNode: td4, }, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -1798,6 +1819,7 @@ describe('SelectionPlugin handle table selection', () => { startNode: td2, }, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -1862,6 +1884,7 @@ describe('SelectionPlugin handle table selection', () => { startNode: td2, }, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(0); expect(announceSpy).not.toHaveBeenCalled(); @@ -1906,6 +1929,7 @@ describe('SelectionPlugin handle table selection', () => { startNode: td2, }, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).not.toHaveBeenCalled(); expect(preventDefaultSpy).not.toHaveBeenCalled(); @@ -1959,6 +1983,7 @@ describe('SelectionPlugin handle table selection', () => { selection: null, tableSelection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith(null); @@ -2008,6 +2033,7 @@ describe('SelectionPlugin handle table selection', () => { startNode: td2, }, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -2064,6 +2090,7 @@ describe('SelectionPlugin handle table selection', () => { startNode: td3, }, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, }); expect(setDOMSelectionSpy).toHaveBeenCalledTimes(1); expect(setDOMSelectionSpy).toHaveBeenCalledWith({ @@ -2133,6 +2160,7 @@ describe('SelectionPlugin on Safari', () => { expect(state).toEqual({ selection: null, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, tableSelection: null, }); expect(attachDomEvent).toHaveBeenCalled(); @@ -2168,6 +2196,7 @@ describe('SelectionPlugin on Safari', () => { expect(state).toEqual({ selection: mockedOldSelection, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, tableSelection: null, }); expect(getDOMSelectionSpy).toHaveBeenCalledTimes(1); @@ -2199,6 +2228,7 @@ describe('SelectionPlugin on Safari', () => { expect(state).toEqual({ selection: mockedNewSelection, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, tableSelection: null, }); expect(getDOMSelectionSpy).toHaveBeenCalledTimes(1); @@ -2227,6 +2257,7 @@ describe('SelectionPlugin on Safari', () => { expect(state).toEqual({ selection: mockedOldSelection, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, tableSelection: null, }); expect(getDOMSelectionSpy).toHaveBeenCalledTimes(1); @@ -2255,6 +2286,7 @@ describe('SelectionPlugin on Safari', () => { expect(state).toEqual({ selection: mockedOldSelection, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, tableSelection: null, }); expect(getDOMSelectionSpy).toHaveBeenCalledTimes(1); @@ -2283,6 +2315,7 @@ describe('SelectionPlugin on Safari', () => { expect(state).toEqual({ selection: mockedOldSelection, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, tableSelection: null, }); expect(getDOMSelectionSpy).toHaveBeenCalledTimes(0); @@ -2311,6 +2344,7 @@ describe('SelectionPlugin on Safari', () => { expect(state).toEqual({ selection: mockedOldSelection, imageSelectionBorderColor: undefined, + tableCellSelectionBackgroundColor: undefined, tableSelection: null, }); expect(getDOMSelectionSpy).toHaveBeenCalledTimes(0); diff --git a/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts b/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts index 82996e62c06..3288eb3c88f 100644 --- a/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts +++ b/packages/roosterjs-content-model-types/lib/editor/EditorOptions.ts @@ -87,6 +87,11 @@ export interface EditorOptions { */ imageSelectionBorderColor?: string; + /** + * Background color of a selected table cell. Default color: '#C6C6C6' + */ + tableCellSelectionBackgroundColor?: string; + /** * Initial Content Model */ diff --git a/packages/roosterjs-content-model-types/lib/pluginState/SelectionPluginState.ts b/packages/roosterjs-content-model-types/lib/pluginState/SelectionPluginState.ts index 556f4ecccb3..932fbcf5ddd 100644 --- a/packages/roosterjs-content-model-types/lib/pluginState/SelectionPluginState.ts +++ b/packages/roosterjs-content-model-types/lib/pluginState/SelectionPluginState.ts @@ -74,4 +74,9 @@ export interface SelectionPluginState { * Color of the border of a selectedImage. Default color: '#DB626C' */ imageSelectionBorderColor?: string; + + /** + * Background color of a selected table cell. Default color: '#C6C6C6' + */ + tableCellSelectionBackgroundColor?: string; }