Skip to content

Commit

Permalink
Merge branch 'master' into xiaozhe/fix-gboard-cursor-issue
Browse files Browse the repository at this point in the history
  • Loading branch information
JiuqingSong authored May 20, 2024
2 parents 99f1d77 + 7ac3ac7 commit 4b176e6
Show file tree
Hide file tree
Showing 5 changed files with 388 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ const HIDE_CURSOR_CSS_KEY = '_DOMSelectionHideCursor';
const HIDE_SELECTION_CSS_KEY = '_DOMSelectionHideSelection';
const IMAGE_ID = 'image';
const TABLE_ID = 'table';
const DEFAULT_SELECTION_BORDER_COLOR = '#DB626C';
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';
Expand All @@ -36,7 +34,7 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC
const skipReselectOnFocus = core.selection.skipReselectOnFocus;

const doc = core.physicalRoot.ownerDocument;

const isDarkMode = core.lifecycle.isDarkMode;
core.selection.skipReselectOnFocus = true;
core.api.setEditorStyle(core, DOM_SELECTION_CSS_KEY, null /*cssRule*/);
core.api.setEditorStyle(core, HIDE_CURSOR_CSS_KEY, null /*cssRule*/);
Expand All @@ -51,12 +49,14 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC
type: 'image',
image,
};
const imageSelectionColor = isDarkMode
? core.selection.imageSelectionBorderColorDark
: core.selection.imageSelectionBorderColor;

core.api.setEditorStyle(
core,
DOM_SELECTION_CSS_KEY,
`outline-style:auto!important; outline-color:${
core.selection.imageSelectionBorderColor || DEFAULT_SELECTION_BORDER_COLOR
}!important;`,
`outline-style:auto!important; outline-color:${imageSelectionColor}!important;`,
[`span:has(>img#${ensureUniqueId(image, IMAGE_ID)})`]
);
core.api.setEditorStyle(
Expand Down Expand Up @@ -112,13 +112,14 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC
: handleTableSelected(parsedTable, tableId, table, firstCell, lastCell);

core.selection.selection = selection;

const tableSelectionColor = isDarkMode
? core.selection.tableCellSelectionBackgroundColorDark
: core.selection.tableCellSelectionBackgroundColor;
core.api.setEditorStyle(
core,
DOM_SELECTION_CSS_KEY,
`background-color:${
core.selection.tableCellSelectionBackgroundColor ||
DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR
}!important;`,
`background-color:${tableSelectionColor}!important;`,
tableSelectors
);
core.api.setEditorStyle(core, HIDE_CURSOR_CSS_KEY, CARET_CSS_RULE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ const Left = 'ArrowLeft';
const Right = 'ArrowRight';
const Tab = 'Tab';

/**
* @internal
*/
export const DEFAULT_SELECTION_BORDER_COLOR = '#DB626C';
/**
* @internal
*/
export const DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR = '#C6C6C6';

class SelectionPlugin implements PluginWithState<SelectionPluginState> {
private editor: IEditor | null = null;
private state: SelectionPluginState;
Expand All @@ -46,8 +55,17 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
this.state = {
selection: null,
tableSelection: null,
imageSelectionBorderColor: options.imageSelectionBorderColor,
tableCellSelectionBackgroundColor: options.tableCellSelectionBackgroundColor,
imageSelectionBorderColor:
options.imageSelectionBorderColor ?? DEFAULT_SELECTION_BORDER_COLOR,
imageSelectionBorderColorDark: options.imageSelectionBorderColor
? undefined
: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor:
options.tableCellSelectionBackgroundColor ??
DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
tableCellSelectionBackgroundColorDark: options.tableCellSelectionBackgroundColor
? undefined
: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
};
}

Expand All @@ -58,6 +76,25 @@ class SelectionPlugin implements PluginWithState<SelectionPluginState> {
initialize(editor: IEditor) {
this.editor = editor;

if (!this.state.imageSelectionBorderColorDark && this.state.imageSelectionBorderColor) {
this.state.imageSelectionBorderColorDark = editor
.getColorManager()
.getDarkColor(this.state.imageSelectionBorderColor, undefined, 'border');
}

if (
!this.state.tableCellSelectionBackgroundColorDark &&
this.state.tableCellSelectionBackgroundColor
) {
this.state.tableCellSelectionBackgroundColorDark = editor
.getColorManager()
.getDarkColor(
this.state.tableCellSelectionBackgroundColor,
undefined,
'background'
);
}

const env = this.editor.getEnvironment();
const document = this.editor.getDocument();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import * as addRangeToSelection from '../../../lib/coreApi/setDOMSelection/addRangeToSelection';
import { DOMSelection, EditorCore } from 'roosterjs-content-model-types';
import { setDOMSelection } from '../../../lib/coreApi/setDOMSelection/setDOMSelection';
import {
DEFAULT_SELECTION_BORDER_COLOR,
DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} from '../../../lib/corePlugin/selection/SelectionPlugin';

const DEFAULT_DARK_COLOR_SUFFIX_COLOR = 'DarkColorMock-';
describe('setDOMSelection', () => {
let core: EditorCore;
let querySelectorAllSpy: jasmine.Spy;
Expand Down Expand Up @@ -47,14 +52,20 @@ describe('setDOMSelection', () => {
core = {
physicalRoot: contentDiv,
logicalRoot: contentDiv,
selection: {},
selection: {
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
},
api: {
triggerEvent: triggerEventSpy,
setEditorStyle: setEditorStyleSpy,
},
domHelper: {
hasFocus: hasFocusSpy,
},
lifecycle: {
isDarkMode: false,
},
} as any;
});

Expand All @@ -71,6 +82,8 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: null,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
core,
Expand Down Expand Up @@ -138,6 +151,8 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: null,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(setEditorStyleSpy).toHaveBeenCalledTimes(3);
expect(setEditorStyleSpy).toHaveBeenCalledWith(core, '_DOMSelection', null);
Expand Down Expand Up @@ -177,6 +192,8 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: null,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).not.toHaveBeenCalled();
expect(addRangeToSelectionSpy).toHaveBeenCalledWith(doc, mockedRange, false);
Expand Down Expand Up @@ -205,6 +222,8 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: mockedSelection,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
core,
Expand Down Expand Up @@ -262,6 +281,8 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: mockedSelection,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
core,
Expand Down Expand Up @@ -322,6 +343,7 @@ describe('setDOMSelection', () => {
skipReselectOnFocus: undefined,
selection: mockedSelection,
imageSelectionBorderColor: 'red',
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
core,
Expand Down Expand Up @@ -356,6 +378,73 @@ describe('setDOMSelection', () => {
);
});

it('image selection with customized selection border color and dark mode', () => {
const mockedSelection = {
type: 'image',
image: mockedImage,
} as any;
const selectNodeSpy = jasmine.createSpy('selectNode');
const collapseSpy = jasmine.createSpy('collapse');
const mockedRange = {
selectNode: selectNodeSpy,
collapse: collapseSpy,
};
const coreValue = { ...core, lifecycle: { isDarkMode: true } as any };

coreValue.selection.imageSelectionBorderColor = 'red';
coreValue.selection.imageSelectionBorderColorDark = `${DEFAULT_DARK_COLOR_SUFFIX_COLOR}red`;

createRangeSpy.and.returnValue(mockedRange);

querySelectorAllSpy.and.returnValue([]);
hasFocusSpy.and.returnValue(false);

setDOMSelection(coreValue, mockedSelection);

expect(coreValue.selection).toEqual({
skipReselectOnFocus: undefined,
selection: mockedSelection,
imageSelectionBorderColor: 'red',
imageSelectionBorderColorDark: `${DEFAULT_DARK_COLOR_SUFFIX_COLOR}red`,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
coreValue,
{
eventType: 'selectionChanged',
newSelection: mockedSelection,
},
true
);
expect(selectNodeSpy).toHaveBeenCalledWith(mockedImage);
expect(collapseSpy).not.toHaveBeenCalledWith();
expect(addRangeToSelectionSpy).toHaveBeenCalledWith(doc, mockedRange, undefined);
expect(setEditorStyleSpy).toHaveBeenCalledTimes(5);
expect(setEditorStyleSpy).toHaveBeenCalledWith(coreValue, '_DOMSelection', null);
expect(setEditorStyleSpy).toHaveBeenCalledWith(
coreValue,
'_DOMSelectionHideCursor',
null
);
expect(setEditorStyleSpy).toHaveBeenCalledWith(
coreValue,
'_DOMSelectionHideSelection',
null
);
expect(setEditorStyleSpy).toHaveBeenCalledWith(
coreValue,
'_DOMSelection',
'outline-style:auto!important; outline-color:DarkColorMock-red!important;',
['span:has(>img#image_0)']
);
expect(setEditorStyleSpy).toHaveBeenCalledWith(
coreValue,
'_DOMSelectionHideSelection',
'background-color: transparent !important;',
['*::selection']
);
});

it('do not select if node is out of document', () => {
const mockedSelection = {
type: 'image',
Expand All @@ -380,6 +469,8 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: mockedSelection,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
core,
Expand Down Expand Up @@ -440,6 +531,8 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: mockedSelection,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
core,
Expand Down Expand Up @@ -507,6 +600,8 @@ describe('setDOMSelection', () => {

expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).not.toHaveBeenCalled();
expect(selectNodeSpy).not.toHaveBeenCalled();
Expand All @@ -531,7 +626,8 @@ describe('setDOMSelection', () => {
lastColumn: number,
lastRow: number,
result: string[],
selectionColor?: string
selectionColor?: string,
expectedDarkSelectionColor?: string
) {
const mockedSelection = {
type: 'table',
Expand All @@ -547,10 +643,6 @@ describe('setDOMSelection', () => {
selectNode: selectNodeSpy,
collapse: collapseSpy,
};
const defaultSelectionColor = '#C6C6C6';
if (selectionColor) {
core.selection.tableCellSelectionBackgroundColor = selectionColor;
}

createRangeSpy.and.returnValue(mockedRange);

Expand All @@ -562,7 +654,12 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: mockedSelection,
...(selectionColor ? { tableCellSelectionBackgroundColor: selectionColor } : {}),
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor:
selectionColor ?? DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
...(expectedDarkSelectionColor
? { tableCellSelectionBackgroundColorDark: expectedDarkSelectionColor }
: {}),
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
core,
Expand All @@ -584,7 +681,11 @@ describe('setDOMSelection', () => {
expect(setEditorStyleSpy).toHaveBeenCalledWith(
core,
'_DOMSelection',
`background-color:${selectionColor ?? defaultSelectionColor}!important;`,
`background-color:${
expectedDarkSelectionColor ??
selectionColor ??
DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR
}!important;`,
result
);
expect(setEditorStyleSpy).toHaveBeenCalledWith(
Expand Down Expand Up @@ -703,6 +804,8 @@ describe('setDOMSelection', () => {
expect(core.selection).toEqual({
skipReselectOnFocus: undefined,
selection: resultSelection,
imageSelectionBorderColor: DEFAULT_SELECTION_BORDER_COLOR,
tableCellSelectionBackgroundColor: DEFAULT_TABLE_CELL_SELECTION_BACKGROUND_COLOR,
} as any);
expect(triggerEventSpy).toHaveBeenCalledWith(
core,
Expand Down Expand Up @@ -782,14 +885,34 @@ describe('setDOMSelection', () => {
});

it('Select All with custom selection color', () => {
const selectionColor = 'red';
core.selection.tableCellSelectionBackgroundColor = selectionColor;
runTest(
buildTable(true /* tbody */, false, false),
0,
0,
1,
1,
['#table_0', '#table_0 *'],
selectionColor
);
});

it('Select All with custom selection color and dark mode', () => {
const selectionColor = 'red';
const selectionColorDark = `${DEFAULT_DARK_COLOR_SUFFIX_COLOR}red`;
core.selection.tableCellSelectionBackgroundColor = selectionColor;
core.selection.tableCellSelectionBackgroundColorDark = selectionColorDark;
core.lifecycle.isDarkMode = true;
runTest(
buildTable(true /* tbody */, false, false),
0,
0,
1,
1,
['#table_0', '#table_0 *'],
'red'
selectionColor,
selectionColorDark
);
});
});
Expand Down
Loading

0 comments on commit 4b176e6

Please sign in to comment.