Skip to content

Commit

Permalink
Merge pull request #2738 from microsoft/romasha/versionbump628
Browse files Browse the repository at this point in the history
Bump Rooster main version to 9.7
  • Loading branch information
romanisa authored Jul 1, 2024
2 parents 4ed6472 + afe0070 commit 288231d
Show file tree
Hide file tree
Showing 66 changed files with 2,104 additions and 1,059 deletions.
3 changes: 2 additions & 1 deletion demo/scripts/controlsV2/mainPane/MainPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -496,10 +496,11 @@ export class MainPane extends React.Component<{}, MainPaneState> {
autoFormatOptions,
linkTitle,
customReplacements,
editPluginOptions,
} = this.state.initState;
return [
pluginList.autoFormat && new AutoFormatPlugin(autoFormatOptions),
pluginList.edit && new EditPlugin(),
pluginList.edit && new EditPlugin(editPluginOptions),
pluginList.paste && new PastePlugin(allowExcelNoBorderTable),
pluginList.shortcut && new ShortcutPlugin(),
pluginList.tableEdit && new TableEditPlugin(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const initialState: OptionState = {
strikethrough: true,
codeFormat: {},
},
editPluginOptions: {
handleTabKey: true,
},
customReplacements: emojiReplacements,
experimentalFeatures: new Set<ExperimentalFeature>(['PersistCache']),
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { AutoFormatOptions, CustomReplace, MarkdownOptions } from 'roosterjs-content-model-plugins';
import {
AutoFormatOptions,
CustomReplace,
EditOptions,
MarkdownOptions,
} from 'roosterjs-content-model-plugins';
import type { SidePaneElementProps } from '../SidePaneElement';
import type { ContentModelSegmentFormat, ExperimentalFeature } from 'roosterjs-content-model-types';

Expand Down Expand Up @@ -31,6 +36,7 @@ export interface OptionState {
autoFormatOptions: AutoFormatOptions;
markdownOptions: MarkdownOptions;
customReplacements: CustomReplace[];
editPluginOptions: EditOptions;

// Legacy plugin options
defaultFormat: ContentModelSegmentFormat;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ export class OptionsPane extends React.Component<OptionPaneProps, OptionState> {
markdownOptions: { ...this.state.markdownOptions },
customReplacements: this.state.customReplacements,
experimentalFeatures: this.state.experimentalFeatures,
editPluginOptions: { ...this.state.editPluginOptions },
};

if (callback) {
Expand Down
12 changes: 11 additions & 1 deletion demo/scripts/controlsV2/sidePane/editorOptions/Plugins.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ abstract class PluginsBase<PluginKey extends keyof BuildInPluginList> extends Re

export class Plugins extends PluginsBase<keyof BuildInPluginList> {
private allowExcelNoBorderTable = React.createRef<HTMLInputElement>();
private handleTabKey = React.createRef<HTMLInputElement>();
private listMenu = React.createRef<HTMLInputElement>();
private tableMenu = React.createRef<HTMLInputElement>();
private imageMenu = React.createRef<HTMLInputElement>();
Expand Down Expand Up @@ -167,7 +168,16 @@ export class Plugins extends PluginsBase<keyof BuildInPluginList> {
)}
</>
)}
{this.renderPluginItem('edit', 'Edit')}
{this.renderPluginItem(
'edit',
'Edit',
this.renderCheckBox(
'Handle Tab Key',
this.handleTabKey,
this.props.state.editPluginOptions.handleTabKey,
(state, value) => (state.editPluginOptions.handleTabKey = value)
)
)}
{this.renderPluginItem(
'paste',
'Paste',
Expand Down
1 change: 1 addition & 0 deletions packages/roosterjs-content-model-api/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export { setTextColor } from './publicApi/segment/setTextColor';
export { changeFontSize } from './publicApi/segment/changeFontSize';
export { applySegmentFormat } from './publicApi/segment/applySegmentFormat';
export { changeCapitalization } from './publicApi/segment/changeCapitalization';
export { splitTextSegment } from './publicApi/segment/splitTextSegment';
export { insertImage } from './publicApi/image/insertImage';
export { setListStyle } from './publicApi/list/setListStyle';
export { setListStartNumber } from './publicApi/list/setListStartNumber';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import type {
} from 'roosterjs-content-model-types';

/**
* @internal
* Split given text segments from the given range
* @param textSegment segment to split
* @param parent parent paragraph the text segment exist in
* @param start starting point of the split
* @param end ending point of the split
* @returns text segment from the indicated split.
*/
export function splitTextSegment(
textSegment: ContentModelText,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { ContentModelParagraph, ContentModelText } from 'roosterjs-content-model-types';
import { splitTextSegment } from '../../lib/pluginUtils/splitTextSegment';
import { splitTextSegment } from '../../../lib/publicApi/segment/splitTextSegment';

describe('splitTextSegment', () => {
function runTest(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { updateCachedSelection } from '../../corePlugin/cache/updateCachedSelection';
import { updateCache } from '../../corePlugin/cache/updateCache';
import {
cloneModel,
createDomToModelContext,
Expand Down Expand Up @@ -47,8 +47,7 @@ export const createContentModel: CreateContentModel = (core, option, selectionOv
const model = domToContentModel(core.logicalRoot, domToModelContext);

if (saveIndex) {
core.cache.cachedModel = model;
updateCachedSelection(core.cache, selection);
updateCache(core.cache, model, selection);
}

return model;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { updateCachedSelection } from '../../corePlugin/cache/updateCachedSelection';
import { updateCache } from '../../corePlugin/cache/updateCache';
import {
contentModelToDom,
createModelToDomContext,
Expand Down Expand Up @@ -37,16 +37,16 @@ export const setContentModel: SetContentModel = (core, model, option, onNodeCrea
);

if (!core.lifecycle.shadowEditFragment) {
updateCachedSelection(core.cache, selection || undefined);
// Clear pending mutations since we will use our latest model object to replace existing cache
core.cache.textMutationObserver?.flushMutations(true /*ignoreMutations*/);

updateCache(core.cache, model, selection);

if (!option?.ignoreSelection && selection) {
core.api.setDOMSelection(core, selection);
} else {
core.selection.selection = selection;
}

// Clear pending mutations since we will use our latest model object to replace existing cache
core.cache.textMutationObserver?.flushMutations(model);
}

return selection;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { areSameRanges } from '../../corePlugin/cache/areSameSelections';

/**
* @internal
*/
Expand All @@ -6,13 +8,7 @@ export function addRangeToSelection(doc: Document, range: Range, isReverted: boo

if (selection) {
const currentRange = selection.rangeCount > 0 && selection.getRangeAt(0);
if (
currentRange &&
currentRange.startContainer == range.startContainer &&
currentRange.endContainer == range.endContainer &&
currentRange.startOffset == range.startOffset &&
currentRange.endOffset == range.endOffset
) {
if (currentRange && areSameRanges(currentRange, range)) {
return;
}
selection.removeAllRanges();
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { addRangeToSelection } from './addRangeToSelection';
import { ensureImageHasSpanParent } from './ensureImageHasSpanParent';
import { areSameSelections } from '../../corePlugin/cache/areSameSelections';
import { ensureUniqueId } from '../setEditorStyle/ensureUniqueId';
import { findLastedCoInMergedCell } from './findLastedCoInMergedCell';
import { findTableCellElement } from './findTableCellElement';
Expand All @@ -19,11 +19,18 @@ const TABLE_ID = 'table';
const CARET_CSS_RULE = 'caret-color: transparent';
const TRANSPARENT_SELECTION_CSS_RULE = 'background-color: transparent !important;';
const SELECTION_SELECTOR = '*::selection';
const DEFAULT_SELECTION_BORDER_COLOR = '#DB626C';

/**
* @internal
*/
export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionChangedEvent) => {
const existingSelection = core.api.getDOMSelection(core);

if (existingSelection && selection && areSameSelections(existingSelection, selection)) {
return;
}

// We are applying a new selection, so we don't need to apply cached selection in DOMEventPlugin.
// Set skipReselectOnFocus to skip this behavior
const skipReselectOnFocus = core.selection.skipReselectOnFocus;
Expand All @@ -38,23 +45,21 @@ export const setDOMSelection: SetDOMSelection = (core, selection, skipSelectionC
try {
switch (selection?.type) {
case 'image':
const image = ensureImageHasSpanParent(selection.image);
const image = selection.image;

core.selection.selection = selection;

core.selection.selection = {
type: 'image',
image,
};
const imageSelectionColor = isDarkMode
? core.selection.imageSelectionBorderColorDark
: core.selection.imageSelectionBorderColor;

core.api.setEditorStyle(
core,
DOM_SELECTION_CSS_KEY,
`outline-style:solid!important; outline-color:${imageSelectionColor}!important;display: ${
core.environment.isSafari ? '-webkit-inline-flex' : 'inline-flex'
};`,
[`span:has(>img#${ensureUniqueId(image, IMAGE_ID)})`]
`outline-style:solid!important; outline-color:${
imageSelectionColor || DEFAULT_SELECTION_BORDER_COLOR
}!important;`,
[`#${ensureUniqueId(image, IMAGE_ID)}`]
);
core.api.setEditorStyle(
core,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { areSameSelection } from './areSameSelection';
import { areSameSelections } from './areSameSelections';
import { createTextMutationObserver } from './textMutationObserver';
import { DomIndexerImpl } from './domIndexerImpl';
import { updateCachedSelection } from './updateCachedSelection';
import { updateCache } from './updateCache';
import type { Mutation } from './textMutationObserver';
import type {
CachePluginState,
IEditor,
PluginEvent,
PluginWithState,
EditorOptions,
ContentModelDocument,
} from 'roosterjs-content-model-types';

/**
Expand All @@ -24,24 +24,15 @@ class CachePlugin implements PluginWithState<CachePluginState> {
* @param contentDiv The editor content DIV
*/
constructor(option: EditorOptions, contentDiv: HTMLDivElement) {
if (option.disableCache) {
this.state = {};
} else {
const domIndexer = new DomIndexerImpl(
option.experimentalFeatures &&
option.experimentalFeatures.indexOf('PersistCache') >= 0
);

this.state = {
domIndexer: domIndexer,
textMutationObserver: createTextMutationObserver(
contentDiv,
domIndexer,
this.onMutation,
this.onSkipMutation
),
};
}
this.state = option.disableCache
? {}
: {
domIndexer: new DomIndexerImpl(
option.experimentalFeatures &&
option.experimentalFeatures.indexOf('PersistCache') >= 0
),
textMutationObserver: createTextMutationObserver(contentDiv, this.onMutation),
};
}

/**
Expand Down Expand Up @@ -115,8 +106,7 @@ class CachePlugin implements PluginWithState<CachePluginState> {
const { contentModel, selection } = event;

if (contentModel && this.state.domIndexer) {
this.state.cachedModel = contentModel;
updateCachedSelection(this.state, selection);
updateCache(this.state, contentModel, selection);
} else {
this.invalidateCache();
}
Expand All @@ -125,23 +115,31 @@ class CachePlugin implements PluginWithState<CachePluginState> {
}
}

private onMutation = (isTextChangeOnly: boolean) => {
private onMutation = (mutation: Mutation) => {
if (this.editor) {
if (isTextChangeOnly) {
this.updateCachedModel(this.editor, true /*forceUpdate*/);
} else {
this.invalidateCache();
switch (mutation.type) {
case 'childList':
if (
!this.state.domIndexer?.reconcileChildList(
mutation.addedNodes,
mutation.removedNodes
)
) {
this.invalidateCache();
}
break;

case 'text':
this.updateCachedModel(this.editor, true /*forceUpdate*/);
break;

case 'unknown':
this.invalidateCache();
break;
}
}
};

private onSkipMutation = (newModel: ContentModelDocument) => {
if (!this.editor?.isInShadowEdit()) {
this.state.cachedModel = newModel;
this.state.cachedSelection = undefined;
}
};

private onNativeSelectionChange = () => {
if (this.editor?.hasFocus()) {
this.updateCachedModel(this.editor);
Expand All @@ -156,6 +154,10 @@ class CachePlugin implements PluginWithState<CachePluginState> {
}

private updateCachedModel(editor: IEditor, forceUpdate?: boolean) {
if (editor.isInShadowEdit()) {
return;
}

const cachedSelection = this.state.cachedSelection;
this.state.cachedSelection = undefined; // Clear it to force getDOMSelection() retrieve the latest selection range

Expand All @@ -165,7 +167,7 @@ class CachePlugin implements PluginWithState<CachePluginState> {
forceUpdate ||
!cachedSelection ||
!newRangeEx ||
!areSameSelection(newRangeEx, cachedSelection);
!areSameSelections(newRangeEx, cachedSelection);

if (isSelectionChanged) {
if (
Expand All @@ -175,7 +177,7 @@ class CachePlugin implements PluginWithState<CachePluginState> {
) {
this.invalidateCache();
} else {
updateCachedSelection(this.state, newRangeEx);
updateCache(this.state, model, newRangeEx);
}
} else {
this.state.cachedSelection = cachedSelection;
Expand Down
Loading

0 comments on commit 288231d

Please sign in to comment.