Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Standalone editor: Remove dependencies ExperimentalFeatures, isFeatureEnabled, Coordinates #2115

Merged
merged 2 commits into from
Sep 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions demo/scripts/controls/BuildInPluginState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default interface BuildInPluginState {
experimentalFeatures: ExperimentalFeatures[];
forcePreserveRatio: boolean;
isRtl: boolean;
cacheModel?: boolean;
tableFeaturesContainerSelector: string;
}

Expand Down
5 changes: 4 additions & 1 deletion demo/scripts/controls/ContentModelEditorMainPane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}),
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -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',
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ const FeatureNames: Partial<Record<ExperimentalFeatures, string>> = {
[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<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export default class ContentModelOptionsPane extends React.Component<
private exportForm = React.createRef<HTMLFormElement>();
private exportData = React.createRef<HTMLInputElement>();
private rtl = React.createRef<HTMLInputElement>();
private cacheModel = React.createRef<HTMLInputElement>();

constructor(props: BuildInPluginProps) {
super(props);
Expand Down Expand Up @@ -96,6 +97,16 @@ export default class ContentModelOptionsPane extends React.Component<
/>
<label htmlFor="pageRtl">Show controls from right to left</label>
</div>
<div>
<input
id="cacheModel"
type="checkbox"
checked={this.state.cacheModel}
onChange={this.onToggleCacheModel}
ref={this.cacheModel}
/>
<label htmlFor="cacheModel">Use Content Model Cache</label>
</div>
<hr />
<details>
<summary>
Expand Down Expand Up @@ -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,
};

Expand Down Expand Up @@ -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}`;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 || [],
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { isGeneralSegment } from 'roosterjs-content-model-dom';
import type { Coordinates } from 'roosterjs-editor-types';
import type {
ContentModelBlock,
ContentModelBlockGroup,
Expand Down Expand Up @@ -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);

Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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, {
Expand All @@ -370,10 +364,11 @@ describe('createContentModelEditorCore', () => {
typeInContainer: new ContentModelTypeInContainerPlugin(),
copyPaste: copyPastePlugin,
},
cacheModel: true,
});
expect(core).toEqual({
lifecycle: {
experimentalFeatures: [ExperimentalFeatures.ReusableContentModelV2],
experimentalFeatures: [],
},
api: {
switchShadowEdit,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -549,7 +549,6 @@ describe('ContentModelCopyPastePlugin |', () => {
let clipboardData = <ClipboardData>{};

it('Handle', () => {
editor.isFeatureEnabled = () => true;
spyOn(PasteFile, 'default').and.callFake(() => {});
const preventDefaultSpy = jasmine.createSpy('preventDefaultPaste');
let clipboardEvent = <ClipboardEvent>{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
}