From 31698c9348f1ad4de5b69c2eabf2c51b6d095cf7 Mon Sep 17 00:00:00 2001 From: Jiuqing Song Date: Thu, 28 Sep 2023 16:55:46 -0700 Subject: [PATCH] standalone editor: Remove dependencies --- demo/scripts/controls/BuildInPluginState.ts | 1 + .../controls/ContentModelEditorMainPane.tsx | 5 ++- .../ContentModelEditorOptionsPlugin.ts | 4 +-- .../ContentModelExperimentalFeatures.tsx | 2 -- .../editorOptions/ContentModelOptionsPane.tsx | 18 +++++++++++ .../lib/editor/ContentModelEditor.ts | 3 +- .../editor/createContentModelEditorCore.ts | 10 ++---- .../lib/modelApi/selection/setSelection.ts | 31 ++++++------------- .../lib/publicTypes/IContentModelEditor.ts | 5 +++ .../createContentModelEditorCoreTest.ts | 15 +++------ .../ContentModelCopyPastePluginTest.ts | 1 - .../lib/enum/ExperimentalFeatures.ts | 5 --- 12 files changed, 48 insertions(+), 52 deletions(-) diff --git a/demo/scripts/controls/BuildInPluginState.ts b/demo/scripts/controls/BuildInPluginState.ts index 47ac1c80f26..3ce32529df7 100644 --- a/demo/scripts/controls/BuildInPluginState.ts +++ b/demo/scripts/controls/BuildInPluginState.ts @@ -34,6 +34,7 @@ export default interface BuildInPluginState { experimentalFeatures: ExperimentalFeatures[]; forcePreserveRatio: boolean; isRtl: boolean; + cacheModel?: boolean; tableFeaturesContainerSelector: string; } diff --git a/demo/scripts/controls/ContentModelEditorMainPane.tsx b/demo/scripts/controls/ContentModelEditorMainPane.tsx index 9ffa0397c7e..00f12795d47 100644 --- a/demo/scripts/controls/ContentModelEditorMainPane.tsx +++ b/demo/scripts/controls/ContentModelEditorMainPane.tsx @@ -182,7 +182,10 @@ class ContentModelEditorMainPane extends MainPaneBase { this.toggleablePlugins = null; this.setState({ editorCreator: (div: HTMLDivElement, options: EditorOptions) => - new ContentModelEditor(div, options), + new ContentModelEditor(div, { + ...options, + cacheModel: this.state.initState.cacheModel, + }), }); } diff --git a/demo/scripts/controls/sidePane/editorOptions/ContentModelEditorOptionsPlugin.ts b/demo/scripts/controls/sidePane/editorOptions/ContentModelEditorOptionsPlugin.ts index e44712d18eb..53f23dba7b7 100644 --- a/demo/scripts/controls/sidePane/editorOptions/ContentModelEditorOptionsPlugin.ts +++ b/demo/scripts/controls/sidePane/editorOptions/ContentModelEditorOptionsPlugin.ts @@ -2,7 +2,6 @@ import BuildInPluginState, { BuildInPluginProps, UrlPlaceholder } from '../../Bu import ContentModelOptionsPane from './ContentModelOptionsPane'; import getDefaultContentEditFeatureSettings from './getDefaultContentEditFeatureSettings'; import SidePanePluginImpl from '../SidePanePluginImpl'; -import { ExperimentalFeatures } from 'roosterjs-editor-types'; import { SidePaneElementProps } from '../SidePaneElement'; const initialState: BuildInPluginState = { @@ -28,8 +27,9 @@ const initialState: BuildInPluginState = { linkTitle: 'Ctrl+Click to follow the link:' + UrlPlaceholder, watermarkText: 'Type content here ...', forcePreserveRatio: false, - experimentalFeatures: [ExperimentalFeatures.ReusableContentModelV2], + experimentalFeatures: [], isRtl: false, + cacheModel: true, tableFeaturesContainerSelector: '#' + 'EditorContainer', }; diff --git a/demo/scripts/controls/sidePane/editorOptions/ContentModelExperimentalFeatures.tsx b/demo/scripts/controls/sidePane/editorOptions/ContentModelExperimentalFeatures.tsx index c8484ddf84a..47c52846e38 100644 --- a/demo/scripts/controls/sidePane/editorOptions/ContentModelExperimentalFeatures.tsx +++ b/demo/scripts/controls/sidePane/editorOptions/ContentModelExperimentalFeatures.tsx @@ -15,8 +15,6 @@ const FeatureNames: Partial> = { [ExperimentalFeatures.DeleteTableWithBackspace]: 'Delete a table selected with the table selector pressing Backspace key', [ExperimentalFeatures.DisableListChain]: 'Disable list chain functionality', - [ExperimentalFeatures.ReusableContentModelV2]: - 'Reuse existing DOM structure if possible, and update the model when content or selection is changed', }; export default class ContentModelExperimentalFeaturesPane extends React.Component< diff --git a/demo/scripts/controls/sidePane/editorOptions/ContentModelOptionsPane.tsx b/demo/scripts/controls/sidePane/editorOptions/ContentModelOptionsPane.tsx index e9a477ca398..0f3ac13e31f 100644 --- a/demo/scripts/controls/sidePane/editorOptions/ContentModelOptionsPane.tsx +++ b/demo/scripts/controls/sidePane/editorOptions/ContentModelOptionsPane.tsx @@ -34,6 +34,7 @@ export default class ContentModelOptionsPane extends React.Component< private exportForm = React.createRef(); private exportData = React.createRef(); private rtl = React.createRef(); + private cacheModel = React.createRef(); constructor(props: BuildInPluginProps) { super(props); @@ -96,6 +97,16 @@ export default class ContentModelOptionsPane extends React.Component< /> +
+ + +

@@ -138,6 +149,7 @@ export default class ContentModelOptionsPane extends React.Component< experimentalFeatures: this.state.experimentalFeatures, forcePreserveRatio: this.state.forcePreserveRatio, isRtl: this.state.isRtl, + cacheModel: this.state.cacheModel, tableFeaturesContainerSelector: this.state.tableFeaturesContainerSelector, }; @@ -173,6 +185,12 @@ export default class ContentModelOptionsPane extends React.Component< MainPaneBase.getInstance().setPageDirection(isRtl); }; + private onToggleCacheModel = () => { + this.resetState(state => { + state.cacheModel = this.cacheModel.current.checked; + }, true); + }; + private getHtml() { return `${htmlStart}${htmlButtons}${darkButton}${htmlEnd}`; } diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/ContentModelEditor.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/ContentModelEditor.ts index 29fec20a033..3659d6af7c6 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/ContentModelEditor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/ContentModelEditor.ts @@ -1,6 +1,5 @@ import { createContentModelEditorCore } from './createContentModelEditorCore'; import { EditorBase } from 'roosterjs-editor-core'; -import { ExperimentalFeatures } from 'roosterjs-editor-types'; import type { ContentModelEditorCore } from '../publicTypes/ContentModelEditorCore'; import type { ContentModelEditorOptions, @@ -29,7 +28,7 @@ export default class ContentModelEditor constructor(contentDiv: HTMLDivElement, options: ContentModelEditorOptions = {}) { super(contentDiv, options, createContentModelEditorCore); - if (this.isFeatureEnabled(ExperimentalFeatures.ReusableContentModelV2)) { + if (options.cacheModel) { // Create an initial content model to cache // TODO: Once we have standalone editor and get rid of `ensureTypeInContainer` function, we can set init content // using content model and cache the model directly diff --git a/packages-content-model/roosterjs-content-model-editor/lib/editor/createContentModelEditorCore.ts b/packages-content-model/roosterjs-content-model-editor/lib/editor/createContentModelEditorCore.ts index ef95eb11063..8d65ddc95d9 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/editor/createContentModelEditorCore.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/editor/createContentModelEditorCore.ts @@ -7,8 +7,7 @@ import { createContentModelEditPlugin } from './corePlugins/ContentModelEditPlug import { createContentModelFormatPlugin } from './corePlugins/ContentModelFormatPlugin'; import { createDomToModelConfig, createModelToDomConfig } from 'roosterjs-content-model-dom'; import { createEditorContext } from './coreApi/createEditorContext'; -import { createEditorCore, isFeatureEnabled } from 'roosterjs-editor-core'; -import { ExperimentalFeatures } from 'roosterjs-editor-types'; +import { createEditorCore } from 'roosterjs-editor-core'; import { getDOMSelection } from './coreApi/getDOMSelection'; import { setContentModel } from './coreApi/setContentModel'; import { setDOMSelection } from './coreApi/setDOMSelection'; @@ -108,12 +107,7 @@ function getPluginState(options: ContentModelEditorOptions): ContentModelPluginS const format = options.defaultFormat || {}; return { cache: { - domIndexer: isFeatureEnabled( - options.experimentalFeatures, - ExperimentalFeatures.ReusableContentModelV2 - ) - ? contentModelDomIndexer - : undefined, + domIndexer: options.cacheModel ? contentModelDomIndexer : undefined, }, copyPaste: { allowedCustomPasteType: options.allowedCustomPasteType || [], diff --git a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/setSelection.ts b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/setSelection.ts index 09cd5020254..6587d3b1349 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/setSelection.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/modelApi/selection/setSelection.ts @@ -1,5 +1,4 @@ import { isGeneralSegment } from 'roosterjs-content-model-dom'; -import type { Coordinates } from 'roosterjs-editor-types'; import type { ContentModelBlock, ContentModelBlockGroup, @@ -102,17 +101,16 @@ function setSelectionToTable( start: Selectable | null, end: Selectable | null ): boolean { - const notFoundCo: Coordinates = { x: -1, y: -1 }; - const startCo = findCell(table, start); - const { x: firstX, y: firstY } = startCo ?? notFoundCo; - const { x: lastX, y: lastY } = (end ? findCell(table, end) : startCo) ?? notFoundCo; + const first = findCell(table, start); + const last = end ? findCell(table, end) : first; if (!isInSelection) { for (let row = 0; row < table.rows.length; row++) { const currentRow = table.rows[row]; for (let col = 0; col < currentRow.cells.length; col++) { const currentCell = table.rows[row].cells[col]; - const isSelected = row >= firstY && row <= lastY && col >= firstX && col <= lastX; + const isSelected = + row >= first.row && row <= last.row && col >= first.col && col <= last.col; setIsSelected(currentCell, isSelected); @@ -137,22 +135,13 @@ function setSelectionToTable( return isInSelection; } -function findCell(table: ContentModelTable, cell: Selectable | null): Coordinates | undefined { - let x = -1; - let y = -1; +function findCell(table: ContentModelTable, cell: Selectable | null): { row: number; col: number } { + let col = -1; + const row = cell + ? table.rows.findIndex(row => (col = (row.cells as Selectable[]).indexOf(cell)) >= 0) + : -1; - if (cell) { - for (let row = 0; y < 0 && row < table.rows.length; row++) { - for (let col = 0; x < 0 && col < table.rows[row].cells.length; col++) { - if (table.rows[row].cells[col] == cell) { - x = col; - y = row; - } - } - } - } - - return x >= 0 && y >= 0 ? { x, y } : undefined; + return { row, col }; } function setSelectionToSegment( diff --git a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/IContentModelEditor.ts b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/IContentModelEditor.ts index 036db08bf22..b4af108f8b4 100644 --- a/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/IContentModelEditor.ts +++ b/packages-content-model/roosterjs-content-model-editor/lib/publicTypes/IContentModelEditor.ts @@ -63,4 +63,9 @@ export interface ContentModelEditorOptions extends EditorOptions { * Default options used for Content Model to DOM conversion */ defaultModelToDomOptions?: ModelToDomOption; + + /** + * Reuse existing DOM structure if possible, and update the model when content or selection is changed + */ + cacheModel?: boolean; } diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/createContentModelEditorCoreTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/createContentModelEditorCoreTest.ts index 10aff265dde..b788f661d50 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/createContentModelEditorCoreTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/createContentModelEditorCoreTest.ts @@ -4,13 +4,12 @@ import * as ContentModelFormatPlugin from '../../lib/editor/corePlugins/ContentM import * as createDomToModelContext from 'roosterjs-content-model-dom/lib/domToModel/context/createDomToModelContext'; import * as createEditorCore from 'roosterjs-editor-core/lib/editor/createEditorCore'; import * as createModelToDomContext from 'roosterjs-content-model-dom/lib/modelToDom/context/createModelToDomContext'; -import * as isFeatureEnabled from 'roosterjs-editor-core/lib/editor/isFeatureEnabled'; import ContentModelTypeInContainerPlugin from '../../lib/editor/corePlugins/ContentModelTypeInContainerPlugin'; import { contentModelDomIndexer } from '../../lib/editor/utils/contentModelDomIndexer'; +import { ContentModelEditorOptions } from '../../lib/publicTypes/IContentModelEditor'; import { createContentModel } from '../../lib/editor/coreApi/createContentModel'; import { createContentModelEditorCore } from '../../lib/editor/createContentModelEditorCore'; import { createEditorContext } from '../../lib/editor/coreApi/createEditorContext'; -import { ExperimentalFeatures } from 'roosterjs-editor-types'; import { getDOMSelection } from '../../lib/editor/coreApi/getDOMSelection'; import { setContentModel } from '../../lib/editor/coreApi/setContentModel'; import { setDOMSelection } from '../../lib/editor/coreApi/setDOMSelection'; @@ -350,18 +349,13 @@ describe('createContentModelEditorCore', () => { }); it('Allow dom indexer', () => { - mockedCore.lifecycle.experimentalFeatures.push(ExperimentalFeatures.ReusableContentModelV2); - - const options = { + const options: ContentModelEditorOptions = { corePluginOverride: { copyPaste: copyPastePlugin, }, + cacheModel: true, }; - spyOn(isFeatureEnabled, 'isFeatureEnabled').and.callFake( - (features, feature) => feature == ExperimentalFeatures.ReusableContentModelV2 - ); - const core = createContentModelEditorCore(contentDiv, options); expect(createEditorCoreSpy).toHaveBeenCalledWith(contentDiv, { @@ -370,10 +364,11 @@ describe('createContentModelEditorCore', () => { typeInContainer: new ContentModelTypeInContainerPlugin(), copyPaste: copyPastePlugin, }, + cacheModel: true, }); expect(core).toEqual({ lifecycle: { - experimentalFeatures: [ExperimentalFeatures.ReusableContentModelV2], + experimentalFeatures: [], }, api: { switchShadowEdit, diff --git a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts index 365982d2d45..9e7f8aa40b0 100644 --- a/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts +++ b/packages-content-model/roosterjs-content-model-editor/test/editor/plugins/ContentModelCopyPastePluginTest.ts @@ -549,7 +549,6 @@ describe('ContentModelCopyPastePlugin |', () => { let clipboardData = {}; it('Handle', () => { - editor.isFeatureEnabled = () => true; spyOn(PasteFile, 'default').and.callFake(() => {}); const preventDefaultSpy = jasmine.createSpy('preventDefaultPaste'); let clipboardEvent = { diff --git a/packages/roosterjs-editor-types/lib/enum/ExperimentalFeatures.ts b/packages/roosterjs-editor-types/lib/enum/ExperimentalFeatures.ts index ebb2f61a8a7..05208beed1e 100644 --- a/packages/roosterjs-editor-types/lib/enum/ExperimentalFeatures.ts +++ b/packages/roosterjs-editor-types/lib/enum/ExperimentalFeatures.ts @@ -178,9 +178,4 @@ export const enum ExperimentalFeatures { * Disable list chain functionality */ DisableListChain = 'DisableListChain', - - /** - * Reuse existing DOM structure if possible, and update the model when content or selection is changed - */ - ReusableContentModelV2 = 'ReusableContentModelV2', }